Add webview restoration api proposal (#46380)

Adds a proposed webiew serialization api that allows webviews to be restored automatically when vscode restarts
This commit is contained in:
Matt Bierner
2018-04-03 18:25:22 -07:00
committed by GitHub
parent c7b37f5915
commit dd21d3520a
14 changed files with 690 additions and 176 deletions

View File

@@ -18,37 +18,85 @@ const localize = nls.loadMessageBundle();
export class MarkdownPreview {
public static previewViewType = 'markdown.preview';
public static viewType = 'markdown.preview';
private readonly webview: vscode.Webview;
private throttleTimer: any;
private initialLine: number | undefined = undefined;
private line: number | undefined = undefined;
private readonly disposables: vscode.Disposable[] = [];
private firstUpdate = true;
private currentVersion?: { resource: vscode.Uri, version: number };
private forceUpdate = false;
private isScrolling = false;
constructor(
private _resource: vscode.Uri,
public static revive(
webview: vscode.Webview,
state: any,
contentProvider: MarkdownContentProvider,
previewConfigurations: MarkdownPreviewConfigurationManager,
logger: Logger,
topmostLineMonitor: MarkdownFileTopmostLineMonitor
): MarkdownPreview {
const resource = vscode.Uri.parse(state.resource);
const locked = state.locked;
const line = state.line;
const preview = new MarkdownPreview(
webview,
resource,
locked,
contentProvider,
previewConfigurations,
logger,
topmostLineMonitor);
if (!isNaN(line)) {
preview.line = line;
}
return preview;
}
public static create(
resource: vscode.Uri,
previewColumn: vscode.ViewColumn,
public locked: boolean,
private readonly contentProvider: MarkdownContentProvider,
private readonly previewConfigurations: MarkdownPreviewConfigurationManager,
private readonly logger: Logger,
locked: boolean,
contentProvider: MarkdownContentProvider,
previewConfigurations: MarkdownPreviewConfigurationManager,
logger: Logger,
topmostLineMonitor: MarkdownFileTopmostLineMonitor,
private readonly contributions: MarkdownContributions
) {
this.webview = vscode.window.createWebview(
MarkdownPreview.previewViewType,
this.getPreviewTitle(this._resource),
contributions: MarkdownContributions
): MarkdownPreview {
const webview = vscode.window.createWebview(
MarkdownPreview.viewType,
MarkdownPreview.getPreviewTitle(resource, locked),
previewColumn, {
enableScripts: true,
enableCommandUris: true,
enableFindWidget: true,
localResourceRoots: this.getLocalResourceRoots(_resource)
localResourceRoots: MarkdownPreview.getLocalResourceRoots(resource, contributions)
});
return new MarkdownPreview(
webview,
resource,
locked,
contentProvider,
previewConfigurations,
logger,
topmostLineMonitor);
}
private constructor(
webview: vscode.Webview,
private _resource: vscode.Uri,
public locked: boolean,
private readonly contentProvider: MarkdownContentProvider,
private readonly previewConfigurations: MarkdownPreviewConfigurationManager,
private readonly logger: Logger,
topmostLineMonitor: MarkdownFileTopmostLineMonitor
) {
this.webview = webview;
this.webview.onDidDispose(() => {
this.dispose();
}, null, this.disposables);
@@ -111,6 +159,14 @@ export class MarkdownPreview {
return this._resource;
}
public get state() {
return {
resource: this.resource.toString(),
locked: this.locked,
line: this.line
};
}
public dispose() {
this._onDisposeEmitter.fire();
@@ -124,9 +180,7 @@ export class MarkdownPreview {
public update(resource: vscode.Uri) {
const editor = vscode.window.activeTextEditor;
if (editor && editor.document.uri.fsPath === resource.fsPath) {
this.initialLine = getVisibleLine(editor);
} else {
this.initialLine = undefined;
this.line = getVisibleLine(editor);
}
// If we have changed resources, cancel any pending updates
@@ -169,6 +223,10 @@ export class MarkdownPreview {
return this._resource.fsPath === resource.fsPath;
}
public isWebviewOf(webview: vscode.Webview): boolean {
return this.webview === webview;
}
public matchesResource(
otherResource: vscode.Uri,
otherViewColumn: vscode.ViewColumn | undefined,
@@ -195,11 +253,11 @@ export class MarkdownPreview {
public toggleLock() {
this.locked = !this.locked;
this.webview.title = this.getPreviewTitle(this._resource);
this.webview.title = MarkdownPreview.getPreviewTitle(this._resource, this.locked);
}
private getPreviewTitle(resource: vscode.Uri): string {
return this.locked
private static getPreviewTitle(resource: vscode.Uri, locked: boolean): string {
return locked
? localize('lockedPreviewTitle', '[Preview] {0}', path.basename(resource.fsPath))
: localize('previewTitle', 'Preview {0}', path.basename(resource.fsPath));
}
@@ -216,7 +274,7 @@ export class MarkdownPreview {
if (typeof topLine === 'number') {
this.logger.log('updateForView', { markdownFile: resource });
this.initialLine = topLine;
this.line = topLine;
this.webview.postMessage({
type: 'updateView',
line: topLine,
@@ -233,25 +291,28 @@ export class MarkdownPreview {
const document = await vscode.workspace.openTextDocument(resource);
if (!this.forceUpdate && this.currentVersion && this.currentVersion.resource.fsPath === resource.fsPath && this.currentVersion.version === document.version) {
if (this.initialLine) {
this.updateForView(resource, this.initialLine);
if (this.line) {
this.updateForView(resource, this.line);
}
return;
}
this.forceUpdate = false;
this.currentVersion = { resource, version: document.version };
this.contentProvider.provideTextDocumentContent(document, this.previewConfigurations, this.initialLine)
this.contentProvider.provideTextDocumentContent(document, this.previewConfigurations, this.line)
.then(content => {
if (this._resource === resource) {
this.webview.title = this.getPreviewTitle(this._resource);
this.webview.title = MarkdownPreview.getPreviewTitle(this._resource, this.locked);
this.webview.html = content;
}
});
}
private getLocalResourceRoots(resource: vscode.Uri): vscode.Uri[] {
const baseRoots = this.contributions.previewResourceRoots;
private static getLocalResourceRoots(
resource: vscode.Uri,
contributions: MarkdownContributions
): vscode.Uri[] {
const baseRoots = contributions.previewResourceRoots;
const folder = vscode.workspace.getWorkspaceFolder(resource);
if (folder) {
@@ -266,6 +327,7 @@ export class MarkdownPreview {
}
private onDidScrollPreview(line: number) {
this.line = line;
for (const editor of vscode.window.visibleTextEditors) {
if (!this.isPreviewOf(editor.document.uri)) {
continue;

View File

@@ -14,7 +14,7 @@ import { isMarkdownFile } from '../util/file';
import { MarkdownPreviewConfigurationManager } from './previewConfig';
import { MarkdownContributions } from '../markdownExtensions';
export class MarkdownPreviewManager {
export class MarkdownPreviewManager implements vscode.WebviewSerializer {
private static readonly markdownPreviewActiveContextKey = 'markdownPreviewFocus';
private readonly topmostLineMonitor = new MarkdownFileTopmostLineMonitor();
@@ -29,15 +29,14 @@ export class MarkdownPreviewManager {
private readonly contributions: MarkdownContributions
) {
vscode.window.onDidChangeActiveTextEditor(editor => {
if (editor) {
if (isMarkdownFile(editor.document)) {
for (const preview of this.previews.filter(preview => !preview.locked)) {
preview.update(editor.document.uri);
}
if (editor && isMarkdownFile(editor.document)) {
for (const preview of this.previews.filter(preview => !preview.locked)) {
preview.update(editor.document.uri);
}
}
}, null, this.disposables);
this.disposables.push(vscode.window.registerWebviewSerializer(MarkdownPreview.viewType, this));
}
public dispose(): void {
@@ -66,7 +65,6 @@ export class MarkdownPreviewManager {
preview.reveal(previewSettings.previewColumn);
} else {
preview = this.createNewPreview(resource, previewSettings);
this.previews.push(preview);
}
preview.update(resource);
@@ -90,6 +88,30 @@ export class MarkdownPreviewManager {
}
}
public async deserializeWebview(
webview: vscode.Webview,
state: any
): Promise<boolean> {
const preview = MarkdownPreview.revive(
webview,
state,
this.contentProvider,
this.previewConfigurations,
this.logger,
this.topmostLineMonitor);
this.registerPreview(preview);
preview.refresh();
return true;
}
public async serializeWebview(
webview: vscode.Webview,
): Promise<any> {
const preview = this.previews.find(preview => preview.isWebviewOf(webview));
return preview ? preview.state : undefined;
}
private getExistingPreview(
resource: vscode.Uri,
previewSettings: PreviewSettings
@@ -101,8 +123,8 @@ export class MarkdownPreviewManager {
private createNewPreview(
resource: vscode.Uri,
previewSettings: PreviewSettings
) {
const preview = new MarkdownPreview(
): MarkdownPreview {
const preview = MarkdownPreview.create(
resource,
previewSettings.previewColumn,
previewSettings.locked,
@@ -112,6 +134,14 @@ export class MarkdownPreviewManager {
this.topmostLineMonitor,
this.contributions);
return this.registerPreview(preview);
}
private registerPreview(
preview: MarkdownPreview
): MarkdownPreview {
this.previews.push(preview);
preview.onDispose(() => {
const existing = this.previews.indexOf(preview!);
if (existing >= 0) {