✨ feat(vscode): 代码阅读鼠标停在名字和注释链接上弹出定义所在那块的卡片,别的文件的也行
This commit is contained in:
1 parent
bc7380c716
commit
a48b016d73
20 files changed
+1145
-89
No files matched your search
@@ -0,0 +1,90 @@
|
||||
import type { ReactElement } from 'react'
|
||||
import type { BlockTreeState } from './BlockTree'
|
||||
import type { ShownPeek } from './definitionPeek'
|
||||
import type { DefinitionPeek } from './types'
|
||||
import { useLayoutEffect, useMemo, useRef, useState } from 'react'
|
||||
import { createPortal } from 'react-dom'
|
||||
import { BlockLevel } from './BlockTree'
|
||||
import { PEEK_ATTRIBUTE, placePeek } from './definitionPeek'
|
||||
import { DisplayNameContext, nameMap } from './displayNames'
|
||||
import { MarkdownKindContext } from './TextPreview'
|
||||
import { HighlightContext } from './useOccurrences'
|
||||
import { collapsedIds } from './viewState'
|
||||
|
||||
const NONE: ReadonlySet<string> = new Set()
|
||||
const NOT_COVERED: ReadonlyMap<string, string> = new Map()
|
||||
|
||||
/**
|
||||
* 浮窗里的那张卡片:只读的块树(不编辑、不问 AI、不重写注释、不标差异、不筛选),折叠展开只记在浮窗里,默认同页面(类、函数收起子块)。
|
||||
* 名字的下标是那个文件的:中文名换成卡片带来的,不画页面上的高亮;Markdown 不认不带路径的引用。
|
||||
*/
|
||||
export function PeekTree({ peek }: { peek: DefinitionPeek }): ReactElement {
|
||||
const [folds, setFolds] = useState<Record<string, boolean>>({})
|
||||
const collapsed = useMemo(() => collapsedIds([peek.block], folds), [peek, folds])
|
||||
const names = useMemo(() => nameMap(peek.names), [peek])
|
||||
const tree: BlockTreeState = {
|
||||
collapsed,
|
||||
hidden: NONE,
|
||||
temporary: NONE,
|
||||
covered: NOT_COVERED,
|
||||
kindName: kind => kind,
|
||||
onToggleFold: id => setFolds(prev => ({ ...prev, [id]: !collapsed.has(id) })),
|
||||
onSetFolds: next => setFolds(prev => ({ ...prev, ...next })),
|
||||
// 不标差异就没有「只看改动」折起来的块
|
||||
onOpenRun: () => {},
|
||||
}
|
||||
return (
|
||||
<HighlightContext value={undefined}>
|
||||
<DisplayNameContext value={names}>
|
||||
<MarkdownKindContext value="peek">
|
||||
<BlockLevel blocks={[peek.block]} tree={tree} />
|
||||
</MarkdownKindContext>
|
||||
</DisplayNameContext>
|
||||
</HighlightContext>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 定义卡片浮窗:标题一行「相对路径:行号」,点了跳到定义(同 ⌘ / Ctrl + 点击);下面是卡片,太大时在浮窗里滚。
|
||||
* 外框用 VS Code 悬停框的颜色;portal 到 body,先在左上角量出大小,再落到名字下方或上方(placePeek)。
|
||||
*/
|
||||
export function DefinitionPeekPopup({ shown, onJump }: { shown: ShownPeek<HTMLElement> | undefined, onJump: () => void }): ReactElement | null {
|
||||
const popupRef = useRef<HTMLDivElement | null>(null)
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const popup = popupRef.current
|
||||
if (!shown || !popup)
|
||||
return
|
||||
Object.assign(popup.style, { top: '0px', left: '0px', bottom: '', maxWidth: '', maxHeight: '', visibility: 'hidden' })
|
||||
const place = placePeek(shown.anchor.getBoundingClientRect(), popup.getBoundingClientRect(), { width: window.innerWidth, height: window.innerHeight })
|
||||
Object.assign(popup.style, {
|
||||
top: 'top' in place ? `${place.top}px` : '',
|
||||
bottom: 'bottom' in place ? `${place.bottom}px` : '',
|
||||
left: `${place.left}px`,
|
||||
maxWidth: `${place.maxWidth}px`,
|
||||
maxHeight: `${place.maxHeight}px`,
|
||||
visibility: 'visible',
|
||||
})
|
||||
}, [shown])
|
||||
|
||||
if (!shown)
|
||||
return null
|
||||
const { peek } = shown
|
||||
const where = `${peek.path}:${peek.line + 1}`
|
||||
return createPortal(
|
||||
<div
|
||||
ref={popupRef}
|
||||
{...{ [PEEK_ATTRIBUTE]: '' }}
|
||||
role="dialog"
|
||||
aria-label={`定义 ${where}`}
|
||||
className="cr-peek"
|
||||
style={{ top: 0, left: 0, visibility: 'hidden' }}
|
||||
>
|
||||
<button type="button" className="cr-peek-head" data-tooltip="跳到定义" onClick={onJump}>{where}</button>
|
||||
<div className="cr-peek-body">
|
||||
<PeekTree key={`${where}#${peek.block.id}`} peek={peek} />
|
||||
</div>
|
||||
</div>,
|
||||
document.body,
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user