Working on webview persistence API

#49022
This commit is contained in:
Matt Bierner 2018-05-14 10:34:44 -07:00
parent 704268958b
commit 21ee81c03b
13 changed files with 68 additions and 96 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -7,7 +7,7 @@ import { ActiveLineMarker } from './activeLineMarker';
import { onceDocumentLoaded } from './events';
import { createPosterForVsCode } from './messaging';
import { getEditorLineNumberForPageOffset, scrollToRevealSourceLine } from './scroll-sync';
import { getSettings } from './settings';
import { getSettings, getData } from './settings';
import throttle = require('lodash.throttle');
declare var acquireVsCodeApi: any;
@ -17,7 +17,10 @@ const marker = new ActiveLineMarker();
const settings = getSettings();
const vscode = acquireVsCodeApi();
vscode.postMessage({});
// Set VS Code state
const state = getData('data-state');
vscode.setState(state);
const messaging = createPosterForVsCode(vscode);

View file

@ -15,18 +15,26 @@ export interface PreviewSettings {
let cachedSettings: PreviewSettings | undefined = undefined;
export function getData(key: string): PreviewSettings {
const element = document.getElementById('vscode-markdown-preview-data');
if (element) {
const data = element.getAttribute(key);
if (data) {
return JSON.parse(data);
}
}
throw new Error(`Could not load data for ${key}`);
}
export function getSettings(): PreviewSettings {
if (cachedSettings) {
return cachedSettings;
}
const element = document.getElementById('vscode-markdown-preview-data');
if (element) {
const data = element.getAttribute('data-settings');
if (data) {
cachedSettings = JSON.parse(data);
return cachedSettings!;
}
cachedSettings = getData('data-settings');
if (cachedSettings) {
return cachedSettings;
}
throw new Error('Could not load settings');

View file

@ -325,7 +325,7 @@ export class MarkdownPreview {
this.forceUpdate = false;
this.currentVersion = { resource, version: document.version };
const content = await this._contentProvider.provideTextDocumentContent(document, this._previewConfigurations, this.line);
const content = await this._contentProvider.provideTextDocumentContent(document, this._previewConfigurations, this.line, this.state);
if (this._resource === resource) {
this.editor.title = MarkdownPreview.getPreviewTitle(this._resource, this._locked);
this.editor.webview.html = content;

View file

@ -47,7 +47,8 @@ export class MarkdownContentProvider {
public async provideTextDocumentContent(
markdownDocument: vscode.TextDocument,
previewConfigurations: MarkdownPreviewConfigurationManager,
initialLine: number | undefined = undefined
initialLine: number | undefined = undefined,
state?: any
): Promise<string> {
const sourceUri = markdownDocument.uri;
const config = previewConfigurations.loadAndCacheConfiguration(sourceUri);
@ -73,7 +74,10 @@ export class MarkdownContentProvider {
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
${csp}
<meta id="vscode-markdown-preview-data" data-settings="${JSON.stringify(initialData).replace(/"/g, '&quot;')}" data-strings="${JSON.stringify(previewStrings).replace(/"/g, '&quot;')}">
<meta id="vscode-markdown-preview-data"
data-settings="${JSON.stringify(initialData).replace(/"/g, '&quot;')}"
data-strings="${JSON.stringify(previewStrings).replace(/"/g, '&quot;')}"
data-state="${JSON.stringify(state || {}).replace(/"/g, '&quot;')}">
<script src="${this.extensionResourcePath('pre.js')}" nonce="${nonce}"></script>
${this.getStyles(sourceUri, nonce, config)}
<base href="${markdownDocument.uri.with({ scheme: 'vscode-resource' }).toString(true)}">

View file

@ -94,13 +94,6 @@ export class MarkdownPreviewManager implements vscode.WebviewPanelSerializer {
this.registerPreview(preview);
}
public async serializeWebviewPanel(
webview: vscode.WebviewPanel,
): Promise<any> {
const preview = this.previews.find(preview => preview.isWebviewOf(webview));
return preview ? preview.state : undefined;
}
private getExistingPreview(
resource: vscode.Uri,
previewSettings: PreviewSettings

View file

@ -263,7 +263,7 @@ export default class LanguageProvider {
}
public diagnosticsReceived(diagnosticsKind: DiagnosticKind, file: Uri, diagnostics: (Diagnostic & { reportUnnecessary: any })[]): void {
const config = workspace.getConfiguration(this.id);
const config = workspace.getConfiguration(this.id, file);
const reportUnnecessary = config.get<boolean>('showUnused.enabled', true);
if (diagnosticsKind === DiagnosticKind.Suggestion) {
this.ununsedHighlighter.diagnosticsReceived(file, diagnostics.filter(diag => diag.reportUnnecessary));

View file

@ -299,21 +299,9 @@ declare module 'vscode' {
//#region Matt: WebView Serializer
/**
* Save and restore webview panels that have been persisted when vscode shuts down.
* Restore webview panels that have been persisted when vscode shuts down.
*/
interface WebviewPanelSerializer {
/**
* Save a webview panel's `state`.
*
* Called before shutdown. Extensions have a 250ms timeframe to return a state. If serialization
* takes longer than 250ms, the panel will not be serialized.
*
* @param webviewPanel webview Panel to serialize. May or may not be visible.
*
* @returns JSON serializable state blob.
*/
serializeWebviewPanel(webviewPanel: WebviewPanel): Thenable<any>;
/**
* Restore a webview panel from its seriailzed `state`.
*

View file

@ -23,8 +23,6 @@ import { extHostNamedCustomer } from './extHostCustomers';
@extHostNamedCustomer(MainContext.MainThreadWebviews)
export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviver {
private static readonly serializeTimeout = 500; // ms
private static readonly viewType = 'mainThreadWebview';
private static readonly standardSupportedLinkSchemes = ['http', 'https', 'mailto'];
@ -136,7 +134,7 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv
this._webviews.set(handle, webview);
webview._events = this.createWebviewEventDelegate(handle);
return this._proxy.$deserializeWebviewPanel(handle, webview.state.viewType, webview.getTitle(), webview.state.state, webview.position, webview.options)
return this._proxy.$deserializeWebviewPanel(handle, webview.state.viewType, webview.getTitle(), JSON.parse(webview.state.state), webview.position, webview.options)
.then(undefined, () => {
webview.html = MainThreadWebviews.getDeserializationFailedContents(viewType);
});
@ -152,34 +150,14 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv
}
private _onWillShutdown(): TPromise<boolean> {
const toRevive: WebviewPanelHandle[] = [];
this._webviews.forEach((view, key) => {
this._webviews.forEach((view) => {
if (this.canRevive(view)) {
toRevive.push(key);
view.state.state = view.webviewState;
}
});
const reviveResponses = toRevive.map(handle =>
TPromise.any([
this._proxy.$serializeWebviewPanel(handle).then(
state => ({ handle, state }),
() => ({ handle, state: null })),
TPromise.timeout(MainThreadWebviews.serializeTimeout).then(() => ({ handle, state: null }))
]).then(x => x.value));
return TPromise.as(false); // Don't veto shutdown
return TPromise.join(reviveResponses).then(results => {
for (const result of results) {
const view = this._webviews.get(result.handle);
if (view) {
if (result.state) {
view.state.state = result.state;
} else {
view.state = null;
}
}
}
return false; // Don't veto shutdown
});
}
private createWebviewEventDelegate(handle: WebviewPanelHandle) {

View file

@ -372,7 +372,6 @@ export interface ExtHostWebviewsShape {
$onDidChangeWebviewPanelViewState(handle: WebviewPanelHandle, active: boolean, position: EditorPosition): void;
$onDidDisposeWebviewPanel(handle: WebviewPanelHandle): Thenable<void>;
$deserializeWebviewPanel(newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, state: any, position: EditorPosition, options: vscode.WebviewOptions): Thenable<void>;
$serializeWebviewPanel(webviewHandle: WebviewPanelHandle): Thenable<any>;
}
export interface MainThreadUrlsShape extends IDisposable {

View file

@ -288,22 +288,6 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
return serializer.deserializeWebviewPanel(revivedPanel, state);
}
$serializeWebviewPanel(
webviewHandle: WebviewPanelHandle
): Thenable<any> {
const panel = this.getWebviewPanel(webviewHandle);
if (!panel) {
return TPromise.as(undefined);
}
const serialzer = this._serializers.get(panel.viewType);
if (!serialzer) {
return TPromise.as(undefined);
}
return serialzer.serializeWebviewPanel(panel);
}
private getWebviewPanel(handle: WebviewPanelHandle): ExtHostWebviewPanel | undefined {
return this._webviewPanels.get(handle);
}

View file

@ -15,7 +15,7 @@ import { Position as EditorPosition } from 'vs/platform/editor/common/editor';
suite('ExtHostWebview', function () {
test('Cannot register multiple serializer for the same view type', async () => {
test('Cannot register multiple serializers for the same view type', async () => {
const viewType = 'view.type';
const shape = createNoopMainThreadWebviews();
@ -24,9 +24,7 @@ suite('ExtHostWebview', function () {
let lastInvokedDeserializer: vscode.WebviewPanelSerializer | undefined = undefined;
class NoopSerializer implements vscode.WebviewPanelSerializer {
async serializeWebviewPanel(webview: vscode.WebviewPanel): Promise<any> { /* noop */ }
async deserializeWebviewPanel(webview: vscode.WebviewPanel, state: any): Promise<void> {
async deserializeWebviewPanel(_webview: vscode.WebviewPanel, _state: any): Promise<void> {
lastInvokedDeserializer = this;
}
}