⚡ perf(vscode): 切走的预览文件改了不在后台生成文档,切回来再补一次
This commit is contained in:
1 parent
50672c70d1
commit
7d6e7592c9
2 files changed
+88
-9
No files matched your search
@@ -253,6 +253,13 @@ function tab() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 别处改了这份文件,文档变成 version 版:交给最近打开的预览听着的那个回调 */
|
||||||
|
function edit(document: { version: number }, version: number): void {
|
||||||
|
document.version = version
|
||||||
|
const changed = vi.mocked(workspace.onDidChangeTextDocument).mock.calls.at(-1)![0]
|
||||||
|
changed({ document, contentChanges: [{ rangeOffset: 0, rangeLength: 0, text: '// ' }] } as never)
|
||||||
|
}
|
||||||
|
|
||||||
const comparedWithHead = expect.objectContaining({ type: 'diff', diff: expect.objectContaining({ base: '上一次提交' }) })
|
const comparedWithHead = expect.objectContaining({ type: 'diff', diff: expect.objectContaining({ base: '上一次提交' }) })
|
||||||
|
|
||||||
describe('预览对比什么看这个标签页是从哪儿打开的', () => {
|
describe('预览对比什么看这个标签页是从哪儿打开的', () => {
|
||||||
@@ -365,7 +372,7 @@ describe('标签页切走时页面留着', () => {
|
|||||||
expect(commands.executeCommand).not.toHaveBeenCalledWith('setContext', OCCURRENCES_CONTEXT, false)
|
expect(commands.executeCommand).not.toHaveBeenCalledWith('setContext', OCCURRENCES_CONTEXT, false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('切走期间文件在别处改了:照常把新的文档发给留着的页面', async () => {
|
it('切走期间文件在别处改了:不生成、不发文档,切回来生成一次发出去', async () => {
|
||||||
const { provider, document } = opening()
|
const { provider, document } = opening()
|
||||||
const page = tab()
|
const page = tab()
|
||||||
provider.resolveCustomTextEditor(document as never, page.panel)
|
provider.resolveCustomTextEditor(document as never, page.panel)
|
||||||
@@ -373,9 +380,65 @@ describe('标签页切走时页面留着', () => {
|
|||||||
|
|
||||||
page.hide()
|
page.hide()
|
||||||
page.clear()
|
page.clear()
|
||||||
document.version = 2
|
vi.mocked(documentMessage).mockClear()
|
||||||
const edited = vi.mocked(workspace.onDidChangeTextDocument).mock.calls.at(-1)![0]
|
edit(document, 2)
|
||||||
edited({ document, contentChanges: [{ rangeOffset: 0, rangeLength: 0, text: '// ' }] } as never)
|
edit(document, 3)
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 200))
|
||||||
|
expect(documentMessage).not.toHaveBeenCalled()
|
||||||
|
expect(page.posted()).toEqual([])
|
||||||
|
|
||||||
|
page.show()
|
||||||
|
await vi.waitFor(() => expect(page.posted()).toContainEqual(expect.objectContaining({ type: 'document', version: 3 })))
|
||||||
|
expect(documentMessage).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('改完还没来得及生成就切走了:排着的那次不在后台跑,切回来再生成', async () => {
|
||||||
|
const { provider, document } = opening()
|
||||||
|
const page = tab()
|
||||||
|
provider.resolveCustomTextEditor(document as never, page.panel)
|
||||||
|
await page.send({ type: 'ready' })
|
||||||
|
|
||||||
|
page.clear()
|
||||||
|
vi.mocked(documentMessage).mockClear()
|
||||||
|
edit(document, 2)
|
||||||
|
page.hide()
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 200))
|
||||||
|
expect(documentMessage).not.toHaveBeenCalled()
|
||||||
|
|
||||||
|
page.show()
|
||||||
|
await vi.waitFor(() => expect(page.posted()).toContainEqual(expect.objectContaining({ type: 'document', version: 2 })))
|
||||||
|
expect(documentMessage).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('切回来时还要滚到别处(跳定义打到它):等补发的文档一起带过去,不先滚旧的', async () => {
|
||||||
|
const { provider, document } = opening()
|
||||||
|
const page = tab()
|
||||||
|
provider.resolveCustomTextEditor(document as never, page.panel)
|
||||||
|
await page.send({ type: 'ready' })
|
||||||
|
|
||||||
|
page.hide()
|
||||||
|
edit(document, 2)
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 200))
|
||||||
|
page.clear()
|
||||||
|
// 补生成要一会儿:跳过来的位置在生成完之前就到了
|
||||||
|
const generate = vi.mocked(documentMessage).getMockImplementation()!
|
||||||
|
vi.mocked(documentMessage).mockImplementationOnce(async (...args) => {
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 50))
|
||||||
|
return generate(...args)
|
||||||
|
})
|
||||||
|
await provider.open(document.uri as never, { reveal: { line: 1 } }, async () => page.show())
|
||||||
|
await vi.waitFor(() => expect(page.posted()).toContainEqual(expect.objectContaining({ type: 'document', version: 2, reveal: { line: 1 } })))
|
||||||
|
expect(page.posted()).not.toContainEqual(expect.objectContaining({ type: 'reveal' }))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('分屏同时看着:文件改了照常马上更新', async () => {
|
||||||
|
const { provider, document } = opening()
|
||||||
|
const page = tab()
|
||||||
|
provider.resolveCustomTextEditor(document as never, page.panel)
|
||||||
|
await page.send({ type: 'ready' })
|
||||||
|
|
||||||
|
page.clear()
|
||||||
|
edit(document, 2)
|
||||||
await vi.waitFor(() => expect(page.posted()).toContainEqual(expect.objectContaining({ type: 'document', version: 2 })))
|
await vi.waitFor(() => expect(page.posted()).toContainEqual(expect.objectContaining({ type: 'document', version: 2 })))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ export class PreviewEditorProvider implements CustomTextEditorProvider {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 注册成预览编辑器。标签页切走时页面留着(`retainContextWhenHidden`):切回来不用重新生成、重发文档,页面也不用整份重画,
|
* 注册成预览编辑器。标签页切走时页面留着(`retainContextWhenHidden`):切回来不用重新生成、重发文档(切走期间改过的才补一次),页面也不用整份重画,
|
||||||
* 代价是每个开着的预览切走后仍占着它那个页面的内存。
|
* 代价是每个开着的预览切走后仍占着它那个页面的内存。
|
||||||
*/
|
*/
|
||||||
register(): Disposable {
|
register(): Disposable {
|
||||||
@@ -183,10 +183,14 @@ export class PreviewEditorProvider implements CustomTextEditorProvider {
|
|||||||
|
|
||||||
class Preview {
|
class Preview {
|
||||||
/**
|
/**
|
||||||
* 页面发过 ready(装好了、能收消息)之后才发消息。标签页切走时页面留着,照常收消息,切回来什么都不用补;
|
* 页面发过 ready(装好了、能收消息)之后才发消息。标签页切走时页面留着,照常收消息;
|
||||||
* 页面真的重新加载了(webview 进程崩了重建、开发者重载 webview)会再发 ready,那时整份重发
|
* 页面真的重新加载了(webview 进程崩了重建、开发者重载 webview)会再发 ready,那时整份重发
|
||||||
*/
|
*/
|
||||||
private ready = false
|
private ready = false
|
||||||
|
/** 切走期间该重新生成的文档没生成(文件改了、规则变了):重新看得见时补一次;没过期的切回来什么都不用做 */
|
||||||
|
private outdated = false
|
||||||
|
/** 页面上那份文档的版本:还不是眼下这一版时(正在生成、切走期间改过),要滚到的位置等新的那份一起带过去 */
|
||||||
|
private shown: number | undefined
|
||||||
/** 侧栏「问 AI」打开的:页面准备好后把光标放进顶部输入框 */
|
/** 侧栏「问 AI」打开的:页面准备好后把光标放进顶部输入框 */
|
||||||
private pendingPathFocus = false
|
private pendingPathFocus = false
|
||||||
/** 解析是异步的:只发最后一次请求的结果,慢的旧结果不能盖掉新的 */
|
/** 解析是异步的:只发最后一次请求的结果,慢的旧结果不能盖掉新的 */
|
||||||
@@ -226,7 +230,7 @@ class Preview {
|
|||||||
/** 这份文件相比对比基准改了什么:发完文档再算、补发 */
|
/** 这份文件相比对比基准改了什么:发完文档再算、补发 */
|
||||||
private readonly diff: PreviewDiff
|
private readonly diff: PreviewDiff
|
||||||
/**
|
/**
|
||||||
* 随下一份真正发出去的文档走、只走一次:新预览第一份文档要滚到的位置,或页面 ready 之前又来的 reveal。
|
* 随下一份真正发出去的文档走、只走一次:新预览第一份文档要滚到的位置,或页面上还不是眼下这一版文档时来的 reveal。
|
||||||
* 之后页面真的重新加载了,按 webview 自己记的位置恢复
|
* 之后页面真的重新加载了,按 webview 自己记的位置恢复
|
||||||
*/
|
*/
|
||||||
private pendingReveal: Reveal | undefined
|
private pendingReveal: Reveal | undefined
|
||||||
@@ -271,6 +275,10 @@ class Preview {
|
|||||||
void this.diff.update(this.sent)
|
void this.diff.update(this.sent)
|
||||||
}),
|
}),
|
||||||
panel.webview.onDidReceiveMessage((msg: WebviewToExtension) => this.handleMessage(msg)),
|
panel.webview.onDidReceiveMessage((msg: WebviewToExtension) => this.handleMessage(msg)),
|
||||||
|
panel.onDidChangeViewState(() => {
|
||||||
|
if (panel.visible && this.outdated)
|
||||||
|
void this.push()
|
||||||
|
}),
|
||||||
workspace.onDidChangeTextDocument((e) => {
|
workspace.onDidChangeTextDocument((e) => {
|
||||||
if (e.document !== document || e.contentChanges.length === 0)
|
if (e.document !== document || e.contentChanges.length === 0)
|
||||||
return
|
return
|
||||||
@@ -366,12 +374,19 @@ class Preview {
|
|||||||
clearTimeout(this.timer)
|
clearTimeout(this.timer)
|
||||||
if (!this.ready)
|
if (!this.ready)
|
||||||
return
|
return
|
||||||
|
// 切走的预览不在后台生成,后面跟着文档的查询也不起;分屏同时看得见的照常马上生成
|
||||||
|
if (!this.panel.visible) {
|
||||||
|
this.outdated = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.outdated = false
|
||||||
const request = ++this.latest
|
const request = ++this.latest
|
||||||
// 路径页、功能对话页:文档是空的,只在顶部聊天
|
// 路径页、功能对话页:文档是空的,只在顶部聊天
|
||||||
if (this.document.uri.scheme === PATH_PREVIEW_SCHEME) {
|
if (this.document.uri.scheme === PATH_PREVIEW_SCHEME) {
|
||||||
this.sent = undefined
|
this.sent = undefined
|
||||||
const reveal = this.pendingReveal
|
const reveal = this.pendingReveal
|
||||||
this.pendingReveal = undefined
|
this.pendingReveal = undefined
|
||||||
|
this.shown = this.document.version
|
||||||
this.post({
|
this.post({
|
||||||
type: 'document',
|
type: 'document',
|
||||||
version: this.document.version,
|
version: this.document.version,
|
||||||
@@ -401,6 +416,7 @@ class Preview {
|
|||||||
this.imports = imports
|
this.imports = imports
|
||||||
const reveal = this.pendingReveal
|
const reveal = this.pendingReveal
|
||||||
this.pendingReveal = undefined
|
this.pendingReveal = undefined
|
||||||
|
this.shown = version
|
||||||
this.post({ ...message, names: this.names.known(version), ...(reveal && { reveal }) })
|
this.post({ ...message, names: this.names.known(version), ...(reveal && { reveal }) })
|
||||||
if (this.sent)
|
if (this.sent)
|
||||||
void this.diff.update(this.sent)
|
void this.diff.update(this.sent)
|
||||||
@@ -513,9 +529,9 @@ class Preview {
|
|||||||
this.focusPathAsk()
|
this.focusPathAsk()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 滚到 reveal:页面 ready 了就直接发,还没 ready 就等它发 ready 后随文档带过去。 */
|
/** 滚到 reveal:页面上已是眼下这一版文档就直接发;还不是(还没 ready、正在生成、切走期间改过)就随新的那份文档带过去。 */
|
||||||
private reveal(reveal: Reveal): void {
|
private reveal(reveal: Reveal): void {
|
||||||
if (this.ready)
|
if (this.shown === this.document.version)
|
||||||
this.post({ type: 'reveal', ...reveal })
|
this.post({ type: 'reveal', ...reveal })
|
||||||
else
|
else
|
||||||
this.pendingReveal = reveal
|
this.pendingReveal = reveal
|
||||||
|
|||||||
Reference in new issue
Block a user