⚡ perf(vscode): 标差异时旧版本的解析结果按旧内容记着,晚到的答案、翻译插件没变就不再重新解析
This commit is contained in:
1 parent
e9c1c67d6f
commit
5a0c5490ea
8 files changed
+87
-8
No files matched your search
@@ -43,6 +43,25 @@ describe('调用的名字定义成哪种块(DocumentCallees)', () => {
|
||||
expect(from.asked).toEqual([40, 120])
|
||||
})
|
||||
|
||||
it('revision:查到的答案变了才加一,新版本查到的一样不加(差异按它定旧版本要不要重新解析)', async () => {
|
||||
const callees = new DocumentCallees(() => {}, [])
|
||||
const session = callees.session()
|
||||
session.calleeKind(call('Detail', 40))
|
||||
session.calleeKind(call('build', 5))
|
||||
expect(callees.revision).toBe(0)
|
||||
const kinds: Record<number, BlockKind | null> = { 40: 'class', 5: null }
|
||||
callees.resolve(1, session.requests(), source(kinds).kindOf)
|
||||
await vi.waitFor(() => expect(callees.revision).toBe(1))
|
||||
const same = source(kinds)
|
||||
callees.resolve(2, session.requests(), same.kindOf)
|
||||
await vi.waitFor(() => expect(same.asked).toEqual([40, 5]))
|
||||
await Promise.resolve()
|
||||
expect(callees.revision).toBe(1)
|
||||
kinds[40] = 'function'
|
||||
callees.resolve(3, session.requests(), source(kinds).kindOf)
|
||||
await vi.waitFor(() => expect(callees.revision).toBe(2))
|
||||
})
|
||||
|
||||
it('被调用的不是名字(没有 name)不问;查了没有(null)不算变了', async () => {
|
||||
let changed = 0
|
||||
const callees = new DocumentCallees(() => changed++, [])
|
||||
|
||||
@@ -26,6 +26,11 @@ export class DocumentCallees {
|
||||
this.answers = new DeferredAnswers(({ callee }) => callee, (a, b) => (a ?? null) === (b ?? null), changed, delays, schedule)
|
||||
}
|
||||
|
||||
/** 查到的种类变过几次(见 DeferredAnswers.revision) */
|
||||
get revision(): number {
|
||||
return this.answers.revision
|
||||
}
|
||||
|
||||
/** 被调用的不是名字(没有 CallFacts.name)的不问,按调用算。 */
|
||||
session(): CalleeSession {
|
||||
const session = this.answers.session()
|
||||
|
||||
@@ -24,6 +24,8 @@ export class DeferredAnswers<Q, A> {
|
||||
/** 眼下这个版本查过(或正在查)的问题 */
|
||||
private readonly checked = new Set<string>()
|
||||
private timer: ReturnType<typeof setTimeout> | undefined
|
||||
/** 查到的答案变过几次:按眼下的答案排出来的东西(对比用的旧版本)比这个数就知道还能不能接着用 */
|
||||
private changes = 0
|
||||
|
||||
constructor(
|
||||
private readonly idOf: (question: Q) => string,
|
||||
@@ -34,6 +36,10 @@ export class DeferredAnswers<Q, A> {
|
||||
private readonly schedule: (run: () => void, ms: number) => ReturnType<typeof setTimeout> = setTimeout,
|
||||
) {}
|
||||
|
||||
get revision(): number {
|
||||
return this.changes
|
||||
}
|
||||
|
||||
session(): AnswerSession<Q, A> {
|
||||
const asked = new Map<string, Q>()
|
||||
return {
|
||||
@@ -88,8 +94,10 @@ export class DeferredAnswers<Q, A> {
|
||||
return
|
||||
}
|
||||
const id = this.idOf(question)
|
||||
if (!this.same(this.known.get(id), found))
|
||||
if (!this.same(this.known.get(id), found)) {
|
||||
changed = true
|
||||
this.changes++
|
||||
}
|
||||
this.known.set(id, found)
|
||||
}))
|
||||
return { unanswered, changed }
|
||||
|
||||
@@ -23,11 +23,12 @@ function base(text: string | null, key = 'k'): DiffBase & { read: ReturnType<typ
|
||||
return { label: '上一次提交', key, read: vi.fn(async () => text), missing: '上一次提交里没有这个文件' }
|
||||
}
|
||||
|
||||
function setup(current: { base: DiffBase | undefined }) {
|
||||
function setup(current: { base: DiffBase | undefined, inputs?: string }) {
|
||||
const posted: (DocumentDiff | null)[] = []
|
||||
const reports: string[] = []
|
||||
const diff = new PreviewDiff(() => current.base, parse, value => posted.push(value), message => reports.push(message))
|
||||
return { diff, posted, reports }
|
||||
const read = vi.fn(parse)
|
||||
const diff = new PreviewDiff(() => current.base, read, () => current.inputs ?? '', value => posted.push(value), message => reports.push(message))
|
||||
return { diff, posted, reports, read }
|
||||
}
|
||||
|
||||
describe('previewDiff', () => {
|
||||
@@ -75,6 +76,24 @@ describe('previewDiff', () => {
|
||||
expect(second.read).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('旧版本解析出来的按旧内容和解析时看的东西(翻译插件、晚到的答案……)记着:都没变不再解析,哪个变了重新解析', async () => {
|
||||
const current: { base: DiffBase | undefined, inputs: string } = { base: base('x = 1\n', 'a'), inputs: 'v1' }
|
||||
const { diff, posted, read } = setup(current)
|
||||
await diff.update(await sent('x = 2\n', 1))
|
||||
await diff.update(await sent('x = 3\n', 2))
|
||||
expect(read).toHaveBeenCalledTimes(1)
|
||||
expect(posted.at(-1)).toMatchObject({ version: 2, stats: { mod: 1 } })
|
||||
current.inputs = 'v2'
|
||||
await diff.update(await sent('x = 3\n', 3))
|
||||
expect(read).toHaveBeenCalledTimes(2)
|
||||
current.base = base('y = 1\n', 'b')
|
||||
await diff.update(await sent('x = 3\n', 4))
|
||||
expect(read).toHaveBeenCalledTimes(3)
|
||||
current.base = base('y = 1\n', 'c')
|
||||
await diff.update(await sent('x = 3\n', 5))
|
||||
expect(read).toHaveBeenCalledTimes(3)
|
||||
})
|
||||
|
||||
it('同一份文档、同一个基准不再比', async () => {
|
||||
const { diff, posted } = setup({ base: base('x = 1\n') })
|
||||
const doc = await sent('x = 2\n')
|
||||
|
||||
@@ -61,12 +61,14 @@ export function documentDiff(base: DiffBase, sent: SentDocument, old: { source:
|
||||
|
||||
/**
|
||||
* 一个打开着的预览的差异:每发出一份文档之后、对比基准变了之后按眼下的基准重新比,比完再发,预览先照常画、不等它。
|
||||
* 取过的旧内容按基准的 key 记着(编辑时不用每次去问 git),旧版本每次重新解析:规则查到的定义、调用的种类这些晚到的答案两边要一样。
|
||||
* 这里不碰 VS Code。
|
||||
* 取过的旧内容按基准的 key 记着(编辑时不用每次去问 git);旧版本解析出来的按旧内容和解析时还看的东西(oldInputs)记着,
|
||||
* 都没变不再解析,规则查到的定义、调用的种类这些晚到的答案变了才重新解析,两边同样的写法翻得一样。这里不碰 VS Code。
|
||||
*/
|
||||
export class PreviewDiff {
|
||||
/** 取过的旧内容;key 相同不再取,取失败了下次重取 */
|
||||
private old: { key: string, text: Promise<string | null> } | undefined
|
||||
/** 解析过的旧版本;旧内容、oldInputs 都没变不再解析,解析出错了下次重来 */
|
||||
private parsed: { text: string, inputs: string, document: Promise<OldDocument | undefined> } | undefined
|
||||
/** 只发最后一次比的结果:慢的旧结果不能盖掉新的 */
|
||||
private latest = 0
|
||||
/** 上一次比的是哪份文档、哪个基准:都没变就不再比(git 状态变了、基准没挪) */
|
||||
@@ -79,6 +81,8 @@ export class PreviewDiff {
|
||||
private readonly baseOf: () => DiffBase | undefined,
|
||||
/** 用这份文档的语言、翻译插件和库规则解析旧版本;解析失败是 undefined */
|
||||
private readonly readOld: (source: string) => Promise<OldDocument | undefined>,
|
||||
/** 解析旧版本除了旧内容还看的东西(翻译插件、项目入口、晚到的答案)拼成的一串:变了旧版本要重新解析 */
|
||||
private readonly oldInputs: () => string,
|
||||
private readonly post: (diff: DocumentDiff | null) => void,
|
||||
private readonly report: (message: string) => void,
|
||||
) {}
|
||||
@@ -97,7 +101,7 @@ export class PreviewDiff {
|
||||
}
|
||||
try {
|
||||
const text = await this.oldText(base)
|
||||
const document = text === null ? undefined : await this.readOld(text)
|
||||
const document = text === null ? undefined : await this.oldDocument(text)
|
||||
if (request !== this.latest)
|
||||
return
|
||||
if (text !== null && !document) {
|
||||
@@ -135,4 +139,17 @@ export class PreviewDiff {
|
||||
}
|
||||
return this.old.text
|
||||
}
|
||||
|
||||
private oldDocument(text: string): Promise<OldDocument | undefined> {
|
||||
const inputs = this.oldInputs()
|
||||
if (this.parsed?.text !== text || this.parsed.inputs !== inputs) {
|
||||
const document = this.readOld(text)
|
||||
this.parsed = { text, inputs, document }
|
||||
document.catch(() => {
|
||||
if (this.parsed?.document === document)
|
||||
this.parsed = undefined
|
||||
})
|
||||
}
|
||||
return this.parsed.document
|
||||
}
|
||||
}
|
||||
@@ -199,7 +199,7 @@ function opening(profiles = { ask: profileSet(''), rewrite: profileSet('') }, co
|
||||
}
|
||||
const provider = new PreviewEditorProvider(...[
|
||||
{},
|
||||
{},
|
||||
{ selectedTranslator: () => undefined },
|
||||
{},
|
||||
{ subscribe, current: { excluded: {}, onlyChanges: false } },
|
||||
colors,
|
||||
|
||||
@@ -271,6 +271,7 @@ class Preview {
|
||||
this.diff = new PreviewDiff(
|
||||
() => this.from === undefined ? undefined : owner.diffBases.diffBase(document.uri, this.from),
|
||||
source => this.readOld(source),
|
||||
() => this.oldInputs(),
|
||||
diff => this.post({ type: 'diff', diff }),
|
||||
text => logger.add({ level: 'warn', source: 'codeReader', message: `${document.uri.fsPath}:${text}` }),
|
||||
)
|
||||
@@ -452,6 +453,11 @@ class Preview {
|
||||
return message.error === undefined ? { blocks: message.blocks, attributes: message.attributes } : undefined
|
||||
}
|
||||
|
||||
/** 排旧版本除了旧内容还看的(见 readOld):选的翻译插件、项目入口、规则查到的定义和调用的种类变过几次。变了旧版本要重新解析 */
|
||||
private oldInputs(): string {
|
||||
return JSON.stringify([this.registry.selectedTranslator(this.document.languageId)?.id, this.owner.entries.namesOf(this.document.uri), this.lookups.revision, this.callees.revision])
|
||||
}
|
||||
|
||||
/** 这份文档里 offset 处名字的定义是哪种块:语言扩展查到的几处定义里第一处认得出的;语言扩展没答是 undefined。 */
|
||||
private async calleeKind(offset: number): Promise<BlockKind | null | undefined> {
|
||||
const { document } = this
|
||||
|
||||
@@ -43,6 +43,11 @@ export class DocumentLookups {
|
||||
this.answers = new DeferredAnswers(({ index, key }) => idOf(index, key), same, changed, delays, schedule)
|
||||
}
|
||||
|
||||
/** 查到的定义变过几次(见 DeferredAnswers.revision) */
|
||||
get revision(): number {
|
||||
return this.answers.revision
|
||||
}
|
||||
|
||||
session(): LookupSession {
|
||||
const session = this.answers.session()
|
||||
return {
|
||||
|
||||
Reference in new issue
Block a user