⚡ perf(vscode): 语言插件按作用域规则认出名字的绑定,找别的文件起的中文名时同一个绑定只问一处、本文件定义的不问

This commit is contained in:
cruldra committed 2026-10-13 14:40:07 +11:00
1 parent 12ad047b4d
commit 0070933599
14 files changed
+1159 -36

No files matched your search

+36 -15
View File
@@ -1,5 +1,5 @@
import type { BlockView } from './blockView'
import type { DisplayName, IndexEntry, Inline, LanguageAdapter, PreviewText, PropertyItem, RuleLocation } from './contract'
import type { DisplayName, IndexEntry, Inline, LanguageAdapter, NameBinding, PreviewText, PropertyItem, RuleLocation } from './contract'
import type { IndexedEntry, WorkspaceIndexOf } from './workspaceIndex'
/**
@@ -84,12 +84,13 @@ export interface NameSources {
* 找这份文档里显示成中文名的各处,分两步交出(先交本文件的,不等工作区索引建好;最后一次 final):
* 1. 本文件定义的:每个定义问一次它的各处出现,落在视图数据里带下标的名字上的显示成中文名,定义处本身标 definition;
* 2. 别的文件定义的:原名和工作区索引里某个中文名一样的名字才问定义提供者,定义处正是索引里那个定义才算;
* 页面看得见的先问,问完先交一次,再问其余的。
* 同一个绑定(见 NameBinding)的各处只问一处、答案共用,这份文档自己定义的绑定不问;页面看得见的先问,问完先交一次,再问其余的。
* 返回语言扩展答没答(settled):定义处自己都查不到出现,或要问的定义一个都没答,是语言扩展还没起来,过一会儿再查。
*/
export async function resolveNames(
names: readonly DisplayName[],
blocks: readonly BlockView[],
bindings: readonly NameBinding[],
sources: NameSources,
report: (shown: ShownName[], final: boolean) => void,
): Promise<boolean> {
@@ -112,34 +113,54 @@ export async function resolveNames(
if (!shown.has(offset))
byName.set(original, [...(byName.get(original) ?? []), offset])
}
const asks: { offset: number, original: string, elsewhere: IndexedName[] }[] = []
const bindingAt = new Map<number, { binding: NameBinding, end: number }>()
for (const binding of bindings) {
for (const { startIndex, endIndex } of binding.occurrences)
bindingAt.set(startIndex, { binding, end: endIndex })
}
// 同一个绑定的各处问一处、答案共用;这份文档自己定义的(参数、赋值)定义就在这份文档里,对不上别的文件的中文名,不问
const asks = new Map<NameBinding | number, { offsets: number[], original: string, elsewhere: IndexedName[] }>()
await Promise.all([...byName].map(async ([original, offsets]) => {
const elsewhere = (await sources.indexed(original)).filter(entry => entry.location.uri !== sources.uri)
for (const offset of elsewhere.length > 0 ? offsets : [])
asks.push({ offset, original, elsewhere })
for (const offset of elsewhere.length > 0 ? offsets : []) {
const at = bindingAt.get(offset)
const binding = at?.end === offset + original.length ? at.binding : undefined
if (binding?.local)
continue
const ask = asks.get(binding ?? offset) ?? { offsets: [], original, elsewhere }
ask.offsets.push(offset)
asks.set(binding ?? offset, ask)
}
}))
let answered = 0
const ask = (list: typeof asks): Promise<unknown> => Promise.all(list.map(async ({ offset, original, elsewhere }) => {
const found = await sources.definitions(offset)
const ask = (list: { at: number, offsets: number[], original: string, elsewhere: IndexedName[] }[]): Promise<unknown> => Promise.all(list.map(async ({ at, offsets, original, elsewhere }) => {
const found = await sources.definitions(at)
if (found.length > 0)
answered++
const entry = elsewhere.find(({ location }) => found.some(at => at.uri === location.uri && at.line === location.start.line && at.character === location.start.character))
if (entry)
const entry = elsewhere.find(({ location }) => found.some(start => start.uri === location.uri && start.line === location.start.line && start.character === location.start.character))
if (!entry)
return
for (const offset of offsets)
shown.set(offset, { offset, length: original.length, name: entry.text })
}))
const near: typeof asks = []
const far: typeof asks = []
for (const item of asks)
(sources.visible(item.offset) ? near : far).push(item)
const near: Parameters<typeof ask>[0] = []
const far: Parameters<typeof ask>[0] = []
for (const item of asks.values()) {
const visible = item.offsets.find(offset => sources.visible(offset))
if (visible === undefined)
far.push({ ...item, at: item.offsets[0] })
else
near.push({ ...item, at: visible })
}
if (near.length > 0 && far.length > 0) {
await ask(near)
report([...shown.values()], false)
await ask(far)
}
else {
await ask(asks)
await ask([...near, ...far])
}
if (asks.length > 0 && answered === 0)
if (asks.size > 0 && answered === 0)
settled = false
report([...shown.values()], true)
return settled