⚡ perf(vscode): 预览发文档后在后台问语言扩展的请求排队发:同时在途最多 4 个、离视口近的先问、文档改了排着的作废,看得见的中文名先交

This commit is contained in:
cruldra committed 2026-10-13 14:15:37 +11:00
1 parent 5a0c5490ea
commit 12ad047b4d
7 files changed
+335 -45

No files matched your search

+39 -17
View File
@@ -76,12 +76,15 @@ export interface NameSources {
definitions: (offset: number) => Promise<DefinitionStart[]>
/** 工作区里定义的、原名叫 name 的中文名 */
indexed: (name: string) => Promise<IndexedName[]>
/** offset 处页面眼下看不看得见:别的文件定义的,看得见的先问、问完先交一次 */
visible: (offset: number) => boolean
}
/**
* 找这份文档里显示成中文名的各处,分两步交出(先交本文件的,不等工作区索引建好;最后一次 final):
* 1. 本文件定义的:每个定义问一次它的各处出现,落在视图数据里带下标的名字上的显示成中文名,定义处本身标 definition;
* 2. 别的文件定义的:原名和工作区索引里某个中文名一样的名字才问定义提供者,定义处正是索引里那个定义才算。
* 2. 别的文件定义的:原名和工作区索引里某个中文名一样的名字才问定义提供者,定义处正是索引里那个定义才算;
* 页面看得见的先问,问完先交一次,再问其余的。
* 返回语言扩展答没答(settled):定义处自己都查不到出现,或要问的定义一个都没答,是语言扩展还没起来,过一会儿再查。
*/
export async function resolveNames(
@@ -109,23 +112,34 @@ export async function resolveNames(
if (!shown.has(offset))
byName.set(original, [...(byName.get(original) ?? []), offset])
}
let asked = 0
let answered = 0
const asks: { offset: 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)
if (elsewhere.length === 0)
return
await Promise.all(offsets.map(async (offset) => {
asked++
const found = await sources.definitions(offset)
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)
shown.set(offset, { offset, length: original.length, name: entry.text })
}))
for (const offset of elsewhere.length > 0 ? offsets : [])
asks.push({ offset, original, elsewhere })
}))
if (asked > 0 && answered === 0)
let answered = 0
const ask = (list: typeof asks): Promise<unknown> => Promise.all(list.map(async ({ offset, original, elsewhere }) => {
const found = await sources.definitions(offset)
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)
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)
if (near.length > 0 && far.length > 0) {
await ask(near)
report([...shown.values()], false)
await ask(far)
}
else {
await ask(asks)
}
if (asks.length > 0 && answered === 0)
settled = false
report([...shown.values()], true)
return settled
@@ -184,6 +198,13 @@ export function shiftNames(names: readonly ShownName[], changes: readonly TextCh
return current
}
/** next 把 current 的每一个都盖住了(一模一样的一项),还多出几个。 */
function adds(next: readonly ShownName[], current: readonly ShownName[]): boolean {
const keyOf = ({ offset, length, name, definition }: ShownName): string => JSON.stringify([offset, length, name, definition])
const keys = new Set(next.map(keyOf))
return next.length > current.length && current.every(name => keys.has(keyOf(name)))
}
/** 语言扩展还没起来时隔多久再查(毫秒),查到最后一次还没答就算了 */
export const RETRY_DELAYS = [1000, 2000, 4000, 8000, 16000]
@@ -219,7 +240,8 @@ export class DocumentNames {
/**
* 发出去了 version 这份文档:查它的中文名;同一个版本只查一次(页面重新加载时照旧用查到的)。
* 手上还没有这个版本的中文名时查到一步交一步(先交本文件的),有(上一版挪过来的)就等查完一起换,免得少了的那些闪一下。
* 查到一步交一步(先交本文件的,再交看得见的);比眼下显示着的(上一版挪过来的、上一步交过的)少了哪个、或者没多出什么的那一步不交,
* 等查完一起换,免得少了的那些闪一下。
* 查的过程中文档又改了的,查到的丢掉。
*/
resolve(version: number, run: (report: (names: ShownName[], final: boolean) => void) => Promise<boolean>): void {
@@ -232,7 +254,7 @@ export class DocumentNames {
}
this.resolving = version
const report = (names: ShownName[], final: boolean): void => {
if (this.resolving !== version || (!final && this.names.length > 0))
if (this.resolving !== version || (!final && !adds(names, this.names)))
return
this.names = names
this.deliver(version, names)