✨ feat(vscode): 代码阅读的函数和类可以看调用树
This commit is contained in:
1 parent
8dc572bf94
commit
645542eb5c
17 files changed
+1679
-5
No files matched your search
@@ -1,13 +1,14 @@
|
||||
import type { ReactElement } from 'react'
|
||||
import type { BlockTreeState } from './BlockTree'
|
||||
import type { NameMap, VersionNames } from './displayNames'
|
||||
import type { DocumentDiff, DocumentMessage, Excluded, Reveal } from './types'
|
||||
import type { BlockView, CallTree, DocumentDiff, DocumentMessage, Excluded, Reveal } from './types'
|
||||
import type { OccurrencePage } from './useOccurrences'
|
||||
import type { ViewState } from './viewState'
|
||||
import { TriangleAlert } from 'lucide-react'
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { AskThread } from './AskThread'
|
||||
import { BlockLevel } from './BlockTree'
|
||||
import { CallTreeDialog } from './CallTreeDialog'
|
||||
import { DefinitionPeekPopup } from './DefinitionPeekPopup'
|
||||
import { DiffBar, NewFileBanner } from './DiffBar'
|
||||
import { changeStops, diffTree, foldUnchanged, openedByDiff } from './diffView'
|
||||
@@ -42,6 +43,17 @@ function savedBlockModes(): BlockModes {
|
||||
return { editing, folds }
|
||||
}
|
||||
|
||||
function blockKind(blocks: readonly BlockView[], anchor: string): string {
|
||||
for (const block of blocks) {
|
||||
if (block.anchor === anchor)
|
||||
return block.kind
|
||||
const inner = blockKind(block.children, anchor)
|
||||
if (inner)
|
||||
return inner
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
export function App(): ReactElement | null {
|
||||
// 先于下面发 ready 的那个 effect 挂上收消息:ready 后扩展端马上发问答
|
||||
const qa = useQa()
|
||||
@@ -64,6 +76,18 @@ export function App(): ReactElement | null {
|
||||
const [diff, setDiff] = useState<DocumentDiff | null>(null)
|
||||
/** 收到消息时读眼下哪些块在编辑(消息回调只在挂载时注册一次) */
|
||||
const editing = useRef(view.editing)
|
||||
/** 页面上这份文档的版本:调用树的回答不是这一版就丢掉 */
|
||||
const docVersion = useRef<number | undefined>(undefined)
|
||||
const [callCounts, setCallCounts] = useState<{ version: number, counts: Record<string, number> }>()
|
||||
const [openCall, setOpenCall] = useState<{
|
||||
anchor: string
|
||||
version: number
|
||||
status: 'loading' | 'ready'
|
||||
name: string
|
||||
where: string
|
||||
kind: string
|
||||
trees: CallTree[]
|
||||
} | null>(null)
|
||||
/** 块树所在的页面内容:钉住卡片时在这里面量 */
|
||||
const content = useRef<HTMLDivElement>(null)
|
||||
// 解析失败的文档没有块,不标差异;删掉的块按块筛选同样藏
|
||||
@@ -113,12 +137,29 @@ export function App(): ReactElement | null {
|
||||
const off = onMessage((msg) => {
|
||||
switch (msg.type) {
|
||||
case 'document':
|
||||
docVersion.current = msg.version
|
||||
setDoc(msg)
|
||||
setNames(documentNames(msg.version, msg.names))
|
||||
setKeep(prev => keptWhileEditing(prev, editing.current))
|
||||
setCallCounts(prev => prev?.version === msg.version ? prev : undefined)
|
||||
setOpenCall(prev => prev?.version === msg.version ? prev : null)
|
||||
if (msg.reveal)
|
||||
setReveal(msg.reveal)
|
||||
return
|
||||
case 'callTreeCounts':
|
||||
if (msg.version !== docVersion.current)
|
||||
return
|
||||
setCallCounts({ version: msg.version, counts: msg.counts })
|
||||
return
|
||||
case 'callTree':
|
||||
if (msg.version !== docVersion.current)
|
||||
return
|
||||
setOpenCall((prev) => {
|
||||
if (!prev || prev.anchor !== msg.anchor || prev.version !== msg.version)
|
||||
return prev
|
||||
return { ...prev, status: 'ready', name: msg.name, where: msg.where, trees: msg.trees }
|
||||
})
|
||||
return
|
||||
case 'reveal':
|
||||
setReveal({ line: msg.line, definition: msg.definition })
|
||||
return
|
||||
@@ -148,6 +189,12 @@ export function App(): ReactElement | null {
|
||||
}, [view])
|
||||
|
||||
const onFocused = useCallback(() => setFocusId(null), [])
|
||||
const onOpenCall = useCallback((anchor: string) => {
|
||||
if (!doc)
|
||||
return
|
||||
setOpenCall({ anchor, version: doc.version, status: 'loading', name: '', where: '', kind: blockKind(doc.blocks, anchor) || 'function', trees: [] })
|
||||
postMessage({ type: 'openCallTree', version: doc.version, anchor })
|
||||
}, [doc])
|
||||
const kindNames = useMemo(() => new Map(doc?.attributes.find(attribute => attribute.id === 'kind')?.values.map(value => [value.id, value.name])), [doc])
|
||||
const kindName = useCallback((kind: string) => kindNames.get(kind) ?? kind, [kindNames])
|
||||
const tree = useMemo((): BlockTreeState | null => doc && {
|
||||
@@ -178,7 +225,12 @@ export function App(): ReactElement | null {
|
||||
...(changes && { diff: changes }),
|
||||
...(folded && { folded }),
|
||||
onOpenRun: id => setOpenedRuns(prev => new Set([...prev, id])),
|
||||
}, [doc, view, collapsed, filter, covered, kindName, focusId, onFocused, setFolded, rewrites, qa, changes, folded])
|
||||
callTree: {
|
||||
counts: callCounts?.version === doc.version ? new Map(Object.entries(callCounts.counts)) : new Map(),
|
||||
...(openCall?.version === doc.version && { openAnchor: openCall.anchor }),
|
||||
onOpen: onOpenCall,
|
||||
},
|
||||
}, [doc, view, collapsed, filter, covered, kindName, focusId, onFocused, setFolded, rewrites, qa, changes, folded, callCounts, openCall, onOpenCall])
|
||||
|
||||
if (!doc || !tree)
|
||||
return null
|
||||
@@ -248,6 +300,16 @@ export function App(): ReactElement | null {
|
||||
</div>
|
||||
{sticky?.enabled && <StickyLines content={content} blocks={doc.blocks} settings={sticky} />}
|
||||
<DefinitionPeekPopup shown={peek.shown} onJump={peek.jump} />
|
||||
{openCall && (
|
||||
<CallTreeDialog
|
||||
kind={openCall.kind}
|
||||
name={openCall.name}
|
||||
where={openCall.where}
|
||||
status={openCall.status === 'loading' ? 'loading' : openCall.trees.length === 0 ? 'empty' : 'ready'}
|
||||
trees={openCall.trees}
|
||||
onClose={() => setOpenCall(null)}
|
||||
/>
|
||||
)}
|
||||
</DisplayNameContext>
|
||||
</HighlightContext>
|
||||
)
|
||||
|
||||
Reference in new issue
Block a user