⚡ perf(vscode): 预览切走标签页时页面留着,切回来不再重新生成、重发文档和整份重画
This commit is contained in:
1 parent
53283808a0
commit
5426c50cd9
8 files changed
+118
-59
No files matched your search
@@ -103,6 +103,14 @@ export class PreviewEditorProvider implements CustomTextEditorProvider {
|
||||
readonly askFeature: (subject: Uri, request: TopRequest) => Promise<string | undefined>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 注册成预览编辑器。标签页切走时页面留着(`retainContextWhenHidden`):切回来不用重新生成、重发文档,页面也不用整份重画,
|
||||
* 代价是每个开着的预览切走后仍占着它那个页面的内存。
|
||||
*/
|
||||
register(): Disposable {
|
||||
return window.registerCustomEditorProvider(PREVIEW_VIEW_TYPE, this, { webviewOptions: { retainContextWhenHidden: true } })
|
||||
}
|
||||
|
||||
/**
|
||||
* 用 `show` 打开 `uri` 的预览,把 `request` 交给它:新建的预览解析时带走;`show` 只是切到了已经开着的预览(没有新建)时,交给切到的那个。
|
||||
*
|
||||
@@ -174,7 +182,10 @@ export class PreviewEditorProvider implements CustomTextEditorProvider {
|
||||
}
|
||||
|
||||
class Preview {
|
||||
/** webview 发过 ready 之后才发消息;标签页切走时页面被销毁,切回来重新加载、再发 ready,这期间也不发 */
|
||||
/**
|
||||
* 页面发过 ready(装好了、能收消息)之后才发消息。标签页切走时页面留着,照常收消息,切回来什么都不用补;
|
||||
* 页面真的重新加载了(webview 进程崩了重建、开发者重载 webview)会再发 ready,那时整份重发
|
||||
*/
|
||||
private ready = false
|
||||
/** 侧栏「问 AI」打开的:页面准备好后把光标放进顶部输入框 */
|
||||
private pendingPathFocus = false
|
||||
@@ -215,8 +226,8 @@ class Preview {
|
||||
/** 这份文件相比对比基准改了什么:发完文档再算、补发 */
|
||||
private readonly diff: PreviewDiff
|
||||
/**
|
||||
* 随下一份真正发出去的文档走、只走一次:新预览第一份文档要滚到的位置,或页面正在重新加载时收不到的 reveal。
|
||||
* 之后页面因标签页切走重新加载,按 webview 自己记的位置恢复
|
||||
* 随下一份真正发出去的文档走、只走一次:新预览第一份文档要滚到的位置,或页面 ready 之前又来的 reveal。
|
||||
* 之后页面真的重新加载了,按 webview 自己记的位置恢复
|
||||
*/
|
||||
private pendingReveal: Reveal | undefined
|
||||
/** 这个标签页是从哪儿打开的,定对比什么;打开请求没说的(跳定义新开的、VS Code 重启后恢复的)先空着,页面 ready 时按它记着的定 */
|
||||
@@ -243,7 +254,7 @@ class Preview {
|
||||
},
|
||||
})
|
||||
this.names = new DocumentNames((version, names) => {
|
||||
if (this.ready && version === document.version)
|
||||
if (version === document.version)
|
||||
this.post({ type: 'displayNames', version, names })
|
||||
})
|
||||
this.lookups = new DocumentLookups(() => this.schedulePush())
|
||||
@@ -251,25 +262,15 @@ class Preview {
|
||||
this.diff = new PreviewDiff(
|
||||
() => this.from === undefined ? undefined : owner.diffBases.diffBase(document.uri, this.from),
|
||||
source => this.readOld(source),
|
||||
(diff) => {
|
||||
if (this.ready)
|
||||
this.post({ type: 'diff', diff })
|
||||
},
|
||||
diff => this.post({ type: 'diff', diff }),
|
||||
text => logger.add({ level: 'warn', source: 'codeReader', message: `${document.uri.fsPath}:${text}` }),
|
||||
)
|
||||
this.disposables = [
|
||||
owner.diffBases.onDidChange(() => {
|
||||
if (this.ready && this.sent)
|
||||
if (this.sent)
|
||||
void this.diff.update(this.sent)
|
||||
}),
|
||||
panel.webview.onDidReceiveMessage((msg: WebviewToExtension) => this.handleMessage(msg)),
|
||||
panel.onDidChangeViewState(() => {
|
||||
if (panel.visible)
|
||||
return
|
||||
// 页面被销毁了,高亮跟着没了
|
||||
this.ready = false
|
||||
owner.navigation.update(this, false)
|
||||
}),
|
||||
workspace.onDidChangeTextDocument((e) => {
|
||||
if (e.document !== document || e.contentChanges.length === 0)
|
||||
return
|
||||
@@ -391,7 +392,7 @@ class Preview {
|
||||
const { message, displayNames, symbols, imports } = await documentMessage({ languageId, version, getText: () => source, fileName: this.document.fileName, entryNames: this.owner.entries.namesOf(this.document.uri) }, this.registry, this.runtime, (text) => {
|
||||
logger.add({ level: 'warn', source: 'codeReader', message: `${this.document.uri.fsPath}:${text}` })
|
||||
}, session.lookup, callees.calleeKind)
|
||||
if (request !== this.latest || !this.ready)
|
||||
if (request !== this.latest)
|
||||
return
|
||||
this.definitions.remember(message.version, message.blocks)
|
||||
// 解析失败的文档没有块:问 AI 找不到块,也不拿它比差异
|
||||
@@ -490,7 +491,7 @@ class Preview {
|
||||
}
|
||||
}
|
||||
|
||||
/** 跳到高亮着的名字的下一处 / 上一处出现;页面不在就算了(页面重新加载时高亮已经没了)。 */
|
||||
/** 跳到高亮着的名字的下一处 / 上一处出现;页面还没 ready 就算了(还没有高亮)。 */
|
||||
moveOccurrence(step: 1 | -1): void {
|
||||
if (this.ready)
|
||||
this.post({ type: 'moveOccurrence', step })
|
||||
@@ -512,7 +513,7 @@ class Preview {
|
||||
this.focusPathAsk()
|
||||
}
|
||||
|
||||
/** 滚到 reveal:页面在就直接发,正在重新加载就等它发 ready 后随文档带过去。 */
|
||||
/** 滚到 reveal:页面 ready 了就直接发,还没 ready 就等它发 ready 后随文档带过去。 */
|
||||
private reveal(reveal: Reveal): void {
|
||||
if (this.ready)
|
||||
this.post({ type: 'reveal', ...reveal })
|
||||
@@ -556,11 +557,10 @@ class Preview {
|
||||
this.post({ type: 'focusPathAsk' })
|
||||
}
|
||||
|
||||
/** kind 那种对话的 profile 列表每次现读(同看板),读完页面还在才发。 */
|
||||
/** kind 那种对话的 profile 列表每次现读(同看板)。 */
|
||||
private async postProfiles(kind: ChatKind): Promise<void> {
|
||||
const choice = await this.owner.qa.profiles[kind].choice()
|
||||
if (this.ready)
|
||||
this.post({ type: 'profiles', kind, ...choice })
|
||||
this.post({ type: 'profiles', kind, ...choice })
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -602,6 +602,7 @@ class Preview {
|
||||
private async handleMessage(msg: WebviewToExtension): Promise<void> {
|
||||
switch (msg.type) {
|
||||
case 'ready':
|
||||
// 页面刚装好(新开的、VS Code 重启后恢复的、真的重新加载了的):眼下的状态和文档整份发一遍
|
||||
this.ready = true
|
||||
// 打开请求说了从哪儿打开的就按它;没说的按页面记着的(VS Code 重启后恢复的),页面也没记过的(新开的)不比
|
||||
this.from ??= msg.openedFrom ?? 'other'
|
||||
@@ -709,7 +710,7 @@ class Preview {
|
||||
if (version !== document.version)
|
||||
return
|
||||
const occurrences = await this.occurrences(offset)
|
||||
if (version === document.version && this.ready)
|
||||
if (version === document.version)
|
||||
this.post({ type: 'occurrencesFound', id, occurrences })
|
||||
}
|
||||
|
||||
@@ -719,7 +720,7 @@ class Preview {
|
||||
if (version !== document.version)
|
||||
return
|
||||
const found = (await this.definitions.locate(version, offset)).length > 0
|
||||
if (version === document.version && this.ready)
|
||||
if (version === document.version)
|
||||
this.post({ type: 'definitionProbed', id, found })
|
||||
}
|
||||
|
||||
@@ -774,7 +775,7 @@ class Preview {
|
||||
const [definition] = await findDefinitions(await this.definitions.locate(version, offset))
|
||||
const own = definition?.uri === document.uri.toString() && definition.offset === offset
|
||||
const peek = definition && !own ? await this.peekAt(definition.location.uri, definition.offset) : undefined
|
||||
if (version === document.version && this.ready)
|
||||
if (version === document.version)
|
||||
this.post({ type: 'definitionPeeked', id, ...(peek && { peek }) })
|
||||
}
|
||||
|
||||
@@ -789,8 +790,7 @@ class Preview {
|
||||
const link = rel === undefined ? `[${symbol}]` : `${rel}#${symbol}`
|
||||
logger.add({ level: 'warn', source: 'codeReader', message: `没能找 ${link} 指的符号:${err instanceof Error ? err.message : String(err)}` })
|
||||
}
|
||||
if (this.ready)
|
||||
this.post({ type: 'definitionPeeked', id, ...(peek && { peek }) })
|
||||
this.post({ type: 'definitionPeeked', id, ...(peek && { peek }) })
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -869,8 +869,7 @@ class Preview {
|
||||
})),
|
||||
Promise.all(references.map(async reference => await this.resolveSymbolLink(reference, symbolsOf) ? undefined : reference)),
|
||||
])
|
||||
if (this.ready)
|
||||
this.post({ type: 'repoLinksChecked', id, missing: missing.filter(rel => rel !== undefined), unresolved: unresolved.filter(reference => reference !== undefined) })
|
||||
this.post({ type: 'repoLinksChecked', id, missing: missing.filter(rel => rel !== undefined), unresolved: unresolved.filter(reference => reference !== undefined) })
|
||||
}
|
||||
|
||||
/** 仓库根放进这个预览能读的本地目录,图片地址才能加载。真实位置和 git 给的路径不一样时两个都放。 */
|
||||
@@ -908,8 +907,7 @@ class Preview {
|
||||
const src = this.panel.webview.asWebviewUri(Uri.file(entry.fsPath)).toString()
|
||||
return { path: rel, src }
|
||||
}))
|
||||
if (this.ready)
|
||||
this.post({ type: 'repoImagesChecked', id, images })
|
||||
this.post({ type: 'repoImagesChecked', id, images })
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1010,13 +1008,11 @@ class Preview {
|
||||
}
|
||||
|
||||
private markRepoLinkBroken(path: string | undefined, symbol?: string, block?: string): void {
|
||||
if (this.ready)
|
||||
this.post({ type: 'repoLinkBroken', ...(path !== undefined && { path }), ...(symbol !== undefined && { symbol }), ...(block !== undefined && { block }) })
|
||||
this.post({ type: 'repoLinkBroken', ...(path !== undefined && { path }), ...(symbol !== undefined && { symbol }), ...(block !== undefined && { block }) })
|
||||
}
|
||||
|
||||
private markRepoLinkOpened(path: string | undefined, symbol?: string, block?: string): void {
|
||||
if (this.ready)
|
||||
this.post({ type: 'repoLinkOpened', ...(path !== undefined && { path }), ...(symbol !== undefined && { symbol }), ...(block !== undefined && { block }) })
|
||||
this.post({ type: 'repoLinkOpened', ...(path !== undefined && { path }), ...(symbol !== undefined && { symbol }), ...(block !== undefined && { block }) })
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in new issue
Block a user