✨ feat(vscode): 代码阅读的调用与构造:跨多行写的调用排成「新建 / 调用 名字 · N 个参数」一行,实参摊成参数 / 值表;Python 查被调用名字的定义分新建和调用,查到后刷新
This commit is contained in:
1 parent
ebf228cdc7
commit
ecc415d763
30 files changed
+1033
-157
No files matched your search
@@ -1,7 +1,7 @@
|
||||
import type { IndexEntry, RuleContext, WorkspaceIndex } from './contract'
|
||||
import type { DefinitionStart } from './nameResolution'
|
||||
import type { IndexedEntry } from './workspaceIndex'
|
||||
import { RETRY_DELAYS } from './nameResolution'
|
||||
import { DeferredAnswers } from './deferredAnswers'
|
||||
|
||||
/**
|
||||
* 库规则认领时查的名字(RuleContext.lookup):名字的定义在规则的工作区索引里是哪一项。
|
||||
@@ -33,91 +33,44 @@ function same(a: IndexEntry | null | undefined, b: IndexEntry | null | undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* 一份打开着的文档里库规则问的名字查到了什么:按「索引 id + 名字原文」记,同一份文档里同名的名字按一处查。
|
||||
* 排视图数据是同步的:先交出已经查到的(文档改了也照旧用上一版查到的),记下这一遍问了什么;发出预览后再查,
|
||||
* 每个版本同一个名字查一次(索引里有同名的项、语言扩展却没答时照 RETRY_DELAYS 隔一会儿再查),查到的和已知的不一样就 changed,
|
||||
* 宿主按新结果重排、重发。
|
||||
* 一份打开着的文档里库规则问的名字查到了什么:按「索引 id + 名字原文」记,同一份文档里同名的名字按一处查;
|
||||
* 什么时候查、查到了怎么叫宿主重排见 DeferredAnswers。
|
||||
*/
|
||||
export class DocumentLookups {
|
||||
/** 查到的:没有这个定义是 null */
|
||||
private readonly known = new Map<string, IndexEntry | null>()
|
||||
private version: number | undefined
|
||||
/** 眼下这个版本查过(或正在查)的名字 */
|
||||
private readonly checked = new Set<string>()
|
||||
private timer: ReturnType<typeof setTimeout> | undefined
|
||||
private readonly answers: DeferredAnswers<LookupRequest, IndexEntry>
|
||||
|
||||
constructor(
|
||||
private readonly changed: () => void,
|
||||
private readonly delays: readonly number[] = RETRY_DELAYS,
|
||||
private readonly schedule: (run: () => void, ms: number) => ReturnType<typeof setTimeout> = setTimeout,
|
||||
) {}
|
||||
constructor(changed: () => void, delays?: readonly number[], schedule?: (run: () => void, ms: number) => ReturnType<typeof setTimeout>) {
|
||||
this.answers = new DeferredAnswers(({ index, key }) => idOf(index, key), same, changed, delays, schedule)
|
||||
}
|
||||
|
||||
session(): LookupSession {
|
||||
const requests = new Map<string, LookupRequest>()
|
||||
const session = this.answers.session()
|
||||
return {
|
||||
lookup: (index, name) => {
|
||||
const id = idOf(index, name.text)
|
||||
if (!requests.has(id))
|
||||
requests.set(id, { index, key: name.text, offset: name.startIndex })
|
||||
return this.known.get(id) ?? undefined
|
||||
},
|
||||
requests: () => [...requests.values()],
|
||||
lookup: (index, name) => session.ask({ index, key: name.text, offset: name.startIndex }),
|
||||
requests: session.questions,
|
||||
}
|
||||
}
|
||||
|
||||
/** 查 version 这一版规则问的名字:这个版本查过的不再查。 */
|
||||
resolve(version: number, requests: readonly LookupRequest[], sources: LookupSources): void {
|
||||
if (this.version !== version) {
|
||||
clearTimeout(this.timer)
|
||||
this.version = version
|
||||
this.checked.clear()
|
||||
}
|
||||
const fresh = requests.filter(request => !this.checked.has(idOf(request.index, request.key)))
|
||||
for (const request of fresh)
|
||||
this.checked.add(idOf(request.index, request.key))
|
||||
if (fresh.length === 0)
|
||||
return
|
||||
const attempt = (tries: number): void => {
|
||||
void this.check(fresh, sources).then(({ settled, changed }) => {
|
||||
if (this.version !== version)
|
||||
return
|
||||
if (changed)
|
||||
this.changed()
|
||||
if (!settled && tries < this.delays.length)
|
||||
this.timer = this.schedule(() => attempt(tries + 1), this.delays[tries])
|
||||
})
|
||||
}
|
||||
attempt(0)
|
||||
this.answers.resolve(version, requests, request => find(request, sources))
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
clearTimeout(this.timer)
|
||||
this.version = undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* 查一遍:索引里没有同名的项就是没有这个定义;有就问语言扩展,定义处正是其中一项才算。
|
||||
* 语言扩展没答(还没起来)的名字留着已知的不动,算没查完(settled 为 false)。
|
||||
*/
|
||||
private async check(requests: readonly LookupRequest[], sources: LookupSources): Promise<{ settled: boolean, changed: boolean }> {
|
||||
let settled = true
|
||||
let changed = false
|
||||
await Promise.all(requests.map(async ({ index, key, offset }) => {
|
||||
const id = idOf(index, key)
|
||||
const candidates = await sources.find(index, key)
|
||||
let found: IndexEntry | null = null
|
||||
if (candidates.length > 0) {
|
||||
const definitions = await sources.definitions(offset)
|
||||
if (definitions.length === 0) {
|
||||
settled = false
|
||||
return
|
||||
}
|
||||
found = candidates.find(({ location }) => definitions.some(at => at.uri === location.uri && at.line === location.start.line && at.character === location.start.character))?.entry ?? null
|
||||
}
|
||||
if (!same(this.known.get(id), found))
|
||||
changed = true
|
||||
this.known.set(id, found)
|
||||
}))
|
||||
return { settled, changed }
|
||||
this.answers.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查规则问的一个名字:索引里没有同名的项就是没有这个定义;有就问语言扩展,定义处正是其中一项才算。
|
||||
* 语言扩展没答(还没起来)是 undefined。
|
||||
*/
|
||||
async function find({ index, key, offset }: LookupRequest, sources: LookupSources): Promise<IndexEntry | null | undefined> {
|
||||
const candidates = await sources.find(index, key)
|
||||
if (candidates.length === 0)
|
||||
return null
|
||||
const definitions = await sources.definitions(offset)
|
||||
if (definitions.length === 0)
|
||||
return undefined
|
||||
return candidates.find(({ location }) => definitions.some(at => at.uri === location.uri && at.line === location.start.line && at.character === location.start.character))?.entry ?? null
|
||||
}
|
||||
Reference in new issue
Block a user