🐛 fix(vscode): 代码阅读预览记住浏览位置,编辑 / 预览互切位置对齐
This commit is contained in:
1 parent
6897954644
commit
37306bd23e
13 files changed
+409
-37
No files matched your search
@@ -1,4 +1,4 @@
|
||||
import type { CustomTextEditorProvider, Disposable, TextDocument, Uri, Webview, WebviewPanel } from 'vscode'
|
||||
import type { CustomTextEditorProvider, Disposable, TextDocument, Uri, WebviewPanel } from 'vscode'
|
||||
import type { CodeReaderRegistry } from '../registry'
|
||||
import type { TreeSitterRuntime } from '../runtime'
|
||||
import type { ExtensionToWebview, WebviewToExtension } from './messages'
|
||||
@@ -19,6 +19,8 @@ const REFRESH_DELAY_MS = 150
|
||||
*/
|
||||
export class PreviewEditorProvider implements CustomTextEditorProvider {
|
||||
private readonly previews = new Set<Preview>()
|
||||
/** 按文档 uri:从文本编辑器切过来时它顶部那一行,等着交给马上要打开的新预览 */
|
||||
private readonly revealLines = new Map<string, number>()
|
||||
|
||||
constructor(
|
||||
private readonly extensionUri: Uri,
|
||||
@@ -29,7 +31,9 @@ export class PreviewEditorProvider implements CustomTextEditorProvider {
|
||||
resolveCustomTextEditor(document: TextDocument, panel: WebviewPanel): void {
|
||||
panel.webview.options = { enableScripts: true, localResourceRoots: [webviewUiRoot(this.extensionUri)] }
|
||||
panel.webview.html = webviewHtml(panel.webview, this.extensionUri, 'code-reader.html')
|
||||
const preview = new Preview(document, panel.webview, this.registry, this.runtime, languageId => this.refresh(languageId))
|
||||
const key = document.uri.toString()
|
||||
const preview = new Preview(document, panel, this.registry, this.runtime, this.revealLines.get(key), languageId => this.refresh(languageId))
|
||||
this.revealLines.delete(key)
|
||||
this.previews.add(preview)
|
||||
panel.onDidDispose(() => {
|
||||
this.previews.delete(preview)
|
||||
@@ -44,6 +48,26 @@ export class PreviewEditorProvider implements CustomTextEditorProvider {
|
||||
void preview.push()
|
||||
}
|
||||
}
|
||||
|
||||
/** 用 open 打开 uri 的预览,新预览先滚到包含 line 的块。 */
|
||||
async openAtLine(uri: Uri, line: number, open: () => Thenable<unknown>): Promise<void> {
|
||||
const key = uri.toString()
|
||||
this.revealLines.set(key, line)
|
||||
try {
|
||||
await open()
|
||||
}
|
||||
finally {
|
||||
// 切换命令等新预览解析完才返回;切换没成时也不能把这行留给以后别处的打开
|
||||
this.revealLines.delete(key)
|
||||
}
|
||||
}
|
||||
|
||||
/** uri 的预览视口顶部那块的起始行;几个编辑器组里都开着这个文件的预览时取活动的那个。 */
|
||||
visibleLine(uri: Uri): number | undefined {
|
||||
const key = uri.toString()
|
||||
const previews = [...this.previews].filter(p => p.uri.toString() === key)
|
||||
return (previews.find(p => p.active) ?? previews[0])?.visibleLine
|
||||
}
|
||||
}
|
||||
|
||||
class Preview {
|
||||
@@ -53,16 +77,20 @@ class Preview {
|
||||
private latest = 0
|
||||
private timer: NodeJS.Timeout | undefined
|
||||
private readonly disposables: Disposable[]
|
||||
/** webview 报来的视口顶部那块的起始行 */
|
||||
visibleLine: number | undefined
|
||||
|
||||
constructor(
|
||||
private readonly document: TextDocument,
|
||||
private readonly webview: Webview,
|
||||
private readonly panel: WebviewPanel,
|
||||
private readonly registry: CodeReaderRegistry,
|
||||
private readonly runtime: TreeSitterRuntime,
|
||||
/** 只随第一份真正发出去的文档走:之后页面因标签页切走重新加载,要按 webview 自己记的位置恢复 */
|
||||
private revealLine: number | undefined,
|
||||
private readonly onTranslatorSelected: (languageId: string) => void,
|
||||
) {
|
||||
this.disposables = [
|
||||
webview.onDidReceiveMessage((msg: WebviewToExtension) => this.handleMessage(msg)),
|
||||
panel.webview.onDidReceiveMessage((msg: WebviewToExtension) => this.handleMessage(msg)),
|
||||
workspace.onDidChangeTextDocument((e) => {
|
||||
if (e.document === document && e.contentChanges.length > 0)
|
||||
this.schedulePush()
|
||||
@@ -74,14 +102,25 @@ class Preview {
|
||||
return this.document.languageId
|
||||
}
|
||||
|
||||
get uri(): Uri {
|
||||
return this.document.uri
|
||||
}
|
||||
|
||||
get active(): boolean {
|
||||
return this.panel.active
|
||||
}
|
||||
|
||||
async push(): Promise<void> {
|
||||
clearTimeout(this.timer)
|
||||
if (!this.ready)
|
||||
return
|
||||
const request = ++this.latest
|
||||
const message: ExtensionToWebview = await documentMessage(this.document, this.registry, this.runtime)
|
||||
if (request === this.latest)
|
||||
void this.webview.postMessage(message)
|
||||
if (request !== this.latest)
|
||||
return
|
||||
const { revealLine } = this
|
||||
this.revealLine = undefined
|
||||
void this.panel.webview.postMessage(revealLine === undefined ? message : { ...message, revealLine })
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
@@ -107,6 +146,9 @@ class Preview {
|
||||
case 'selectTranslator':
|
||||
await this.registry.selectTranslator(this.languageId, msg.id)
|
||||
this.onTranslatorSelected(this.languageId)
|
||||
return
|
||||
case 'visibleLine':
|
||||
this.visibleLine = msg.line
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user