🐛 fix(vscode): 切走标签再回来,调用树棵数不用重算,开着的弹框和选中的入口照旧

This commit is contained in:
cruldra committed 2026-10-13 11:00:06 +11:00
1 parent 6cf053dcd0
commit 32018e71f1
9 files changed
+542 -110

No files matched your search

+47 -17
View File
@@ -43,6 +43,28 @@ function savedBlockModes(): BlockModes {
return { editing, folds }
}
/** 开着的调用树弹框:哪一版文档的哪一块、选中第几个入口,扩展端发来的树 */
interface OpenCall {
anchor: string
version: number
entry: number
status: 'loading' | 'ready'
name: string
where: string
kind: string
badge?: KindBadge
trees: CallTree[]
contractMiss?: ContractMiss
}
/** 打开 doc 里 anchor 那块的调用树弹框,先是「正在查」,等扩展端发树来;这块不在了、或者它看不了调用树时是 undefined。 */
function openingCall(doc: DocumentMessage, anchor: string, entry: number): OpenCall | undefined {
const block = blockAt(doc.blocks, anchor)
if (!block?.callTree)
return undefined
return { anchor, version: doc.version, entry, status: 'loading', name: '', where: '', kind: block.kind, ...(block.badge && { badge: block.badge }), trees: [] }
}
function blockAt(blocks: readonly BlockView[], anchor: string): BlockView | undefined {
for (const block of blocks) {
if (block.anchor === anchor)
@@ -79,17 +101,9 @@ export function App(): ReactElement | null {
/** 页面上这份文档的版本:调用树的回答不是这一版就丢掉 */
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
badge?: KindBadge
trees: CallTree[]
contractMiss?: ContractMiss
} | null>(null)
const [openCall, setOpenCall] = useState<OpenCall | null>(null)
/** 页面重新加载前开着的调用树弹框:第一份文档到了,那块还在就重新打开;到之前记着的不动 */
const restoreCall = useRef(viewState.get().callTree)
/** 块树所在的页面内容:钉住卡片时在这里面量 */
const content = useRef<HTMLDivElement>(null)
// 解析失败的文档没有块,不标差异;删掉的块按块筛选同样藏
@@ -138,16 +152,24 @@ export function App(): ReactElement | null {
useEffect(() => {
const off = onMessage((msg) => {
switch (msg.type) {
case 'document':
case 'document': {
const restoring = restoreCall.current
restoreCall.current = undefined
const reopened = restoring && openingCall(msg, restoring.anchor, restoring.entry)
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)
setOpenCall(prev => prev?.version === msg.version ? prev : reopened ?? null)
if (reopened)
postMessage({ type: 'openCallTree', version: msg.version, anchor: reopened.anchor })
else if (restoring)
viewState.update({ callTree: undefined })
if (msg.reveal)
setReveal(msg.reveal)
return
}
case 'callTreeCounts':
if (msg.version !== docVersion.current)
return
@@ -195,13 +217,19 @@ export function App(): ReactElement | null {
editing.current = view.editing
}, [view])
useEffect(() => {
if (restoreCall.current)
return
viewState.update({ callTree: openCall ? { anchor: openCall.anchor, entry: openCall.entry } : undefined })
}, [openCall])
const onFocused = useCallback(() => setFocusId(null), [])
const onOpenCall = useCallback((anchor: string) => {
if (!doc)
const opening = doc && openingCall(doc, anchor, 0)
if (!opening)
return
const block = blockAt(doc.blocks, anchor)
setOpenCall({ anchor, version: doc.version, status: 'loading', name: '', where: '', kind: block?.kind ?? 'function', ...(block?.badge && { badge: block.badge }), trees: [] })
postMessage({ type: 'openCallTree', version: doc.version, anchor })
setOpenCall(opening)
postMessage({ type: 'openCallTree', version: opening.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])
@@ -315,7 +343,9 @@ export function App(): ReactElement | null {
where={openCall.where}
status={openCall.status === 'loading' ? 'loading' : openCall.trees.length === 0 ? 'empty' : 'ready'}
trees={openCall.trees}
selected={openCall.entry}
contractMiss={openCall.contractMiss}
onSelect={entry => setOpenCall(prev => prev && { ...prev, entry })}
onClose={() => setOpenCall(null)}
onJump={(jump) => {
setOpenCall(null)