Make sure custom editors restore state inside webview on reload

This commit is contained in:
Matt Bierner 2020-04-10 19:18:41 -07:00
parent 335de3b713
commit 49bcd96469
2 changed files with 67 additions and 50 deletions

View file

@ -5,13 +5,11 @@
import { Lazy } from 'vs/base/common/lazy';
import { URI, UriComponents } from 'vs/base/common/uri';
import { generateUuid } from 'vs/base/common/uuid';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IEditorInput } from 'vs/workbench/common/editor';
import { CustomEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput';
import { IWebviewService } from 'vs/workbench/contrib/webview/browser/webview';
import { WebviewEditorInputFactory, SerializedWebview } from 'vs/workbench/contrib/webview/browser/webviewEditorInputFactory';
import { IWebviewService, WebviewExtensionDescription } from 'vs/workbench/contrib/webview/browser/webview';
import { reviveWebviewExtensionDescription, SerializedWebview, WebviewEditorInputFactory, DeserializedWebview } from 'vs/workbench/contrib/webview/browser/webviewEditorInputFactory';
import { IWebviewWorkbenchService, WebviewInputOptions } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService';
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
@ -37,6 +35,10 @@ interface SerializedCustomEditor extends SerializedWebview {
readonly dirty?: boolean;
}
interface DeserializedCustomEditor extends DeserializedWebview {
readonly editorResource: URI;
}
export class CustomEditorInputFactory extends WebviewEditorInputFactory {
@ -64,36 +66,38 @@ export class CustomEditorInputFactory extends WebviewEditorInputFactory {
}
}
protected fromJson(input: SerializedCustomEditor): DeserializedCustomEditor {
return {
editorResource: URI.from(input.editorResource),
...super.fromJson(input),
};
}
public deserialize(
_instantiationService: IInstantiationService,
serializedEditorInput: string
): CustomEditorInput {
const data = this.fromJson(serializedEditorInput);
const id = data.id || generateUuid();
const webview = new Lazy(() => {
const webview = this._webviewService.createWebviewOverlay(id, {
enableFindWidget: data.options.enableFindWidget,
retainContextWhenHidden: data.options.retainContextWhenHidden
}, data.options);
if (data.extensionLocation && data.extensionId) {
webview.extension = {
location: data.extensionLocation,
id: data.extensionId
};
}
return webview;
});
const customInput = this._instantiationService.createInstance(CustomEditorInput, URI.from((data as any).editorResource), data.viewType, id, webview, { startsDirty: (data as any).dirty });
const data = this.fromJson(JSON.parse(serializedEditorInput));
const webview = CustomEditorInputFactory.reviveWebview(data, this._webviewService);
const customInput = this._instantiationService.createInstance(CustomEditorInput, data.editorResource, data.viewType, data.id, webview, { startsDirty: (data as any).dirty });
if (typeof data.group === 'number') {
customInput.updateGroup(data.group);
}
return customInput;
}
private static reviveWebview(data: { id: string, state: any, options: WebviewInputOptions, extension?: WebviewExtensionDescription, }, webviewService: IWebviewService) {
return new Lazy(() => {
const webview = webviewService.createWebviewOverlay(data.id, {
enableFindWidget: data.options.enableFindWidget,
retainContextWhenHidden: data.options.retainContextWhenHidden
}, data.options);
webview.state = data.state;
webview.extension = data.extension;
return webview;
});
}
public static createCustomEditorInput(resource: URI, instantiationService: IInstantiationService): Promise<IEditorInput> {
return instantiationService.invokeFunction(async accessor => {
const webviewService = accessor.get<IWebviewService>(IWebviewService);
@ -106,20 +110,8 @@ export class CustomEditorInputFactory extends WebviewEditorInputFactory {
const backupData = backup.meta;
const id = backupData.webview.id;
const webview = new Lazy(() => {
const webview = webviewService.createWebviewOverlay(id, {
enableFindWidget: backupData.webview.options.enableFindWidget,
retainContextWhenHidden: backupData.webview.options.retainContextWhenHidden
}, backupData.webview.options);
webview.extension = backupData.extension ? {
location: URI.revive(backupData.extension.location),
id: new ExtensionIdentifier(backupData.extension.id),
} : undefined;
return webview;
});
const extension = reviveWebviewExtensionDescription(backupData.extension?.id, backupData.extension?.location);
const webview = CustomEditorInputFactory.reviveWebview({ id, options: backupData.webview.options, state: backupData.webview.state, extension, }, webviewService);
const editor = instantiationService.createInstance(CustomEditorInput, URI.revive(backupData.editorResource), backupData.viewType, id, webview, { startsDirty: true, backupId: backupData.backupId });
editor.updateGroup(0);

View file

@ -4,10 +4,10 @@
*--------------------------------------------------------------------------------------------*/
import { URI, UriComponents } from 'vs/base/common/uri';
import { generateUuid } from 'vs/base/common/uuid';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IEditorInputFactory } from 'vs/workbench/common/editor';
import { WebviewExtensionDescription, WebviewIcons } from 'vs/workbench/contrib/webview/browser/webview';
import { WebviewInput } from './webviewEditorInput';
import { IWebviewWorkbenchService, WebviewInputOptions } from './webviewWorkbenchService';
@ -17,17 +17,28 @@ interface SerializedIconPath {
}
export interface SerializedWebview {
readonly id?: string;
readonly id: string;
readonly viewType: string;
readonly title: string;
readonly options: WebviewInputOptions;
readonly extensionLocation: string | UriComponents | undefined;
readonly extensionLocation: UriComponents | undefined;
readonly extensionId: string | undefined;
readonly state: any;
readonly iconPath: SerializedIconPath | undefined;
readonly group?: number;
}
export interface DeserializedWebview {
readonly id: string;
readonly viewType: string;
readonly title: string;
readonly options: WebviewInputOptions;
readonly extension: WebviewExtensionDescription | undefined;
readonly state: any;
readonly iconPath: WebviewIcons | undefined;
readonly group?: number;
}
export class WebviewEditorInputFactory implements IEditorInputFactory {
public static readonly ID = WebviewInput.typeId;
@ -57,19 +68,14 @@ export class WebviewEditorInputFactory implements IEditorInputFactory {
_instantiationService: IInstantiationService,
serializedEditorInput: string
): WebviewInput {
const data = this.fromJson(serializedEditorInput);
return this._webviewWorkbenchService.reviveWebview(data.id || generateUuid(), data.viewType, data.title, data.iconPath, data.state, data.options, data.extensionLocation && data.extensionId ? {
location: data.extensionLocation,
id: data.extensionId
} : undefined, data.group);
const data = this.fromJson(JSON.parse(serializedEditorInput));
return this._webviewWorkbenchService.reviveWebview(data.id, data.viewType, data.title, data.iconPath, data.state, data.options, data.extension, data.group);
}
protected fromJson(serializedEditorInput: string) {
const data: SerializedWebview = JSON.parse(serializedEditorInput);
protected fromJson(data: SerializedWebview): DeserializedWebview {
return {
...data,
extensionLocation: reviveUri(data.extensionLocation),
extensionId: data.extensionId ? new ExtensionIdentifier(data.extensionId) : undefined,
extension: reviveWebviewExtensionDescription(data.extensionId, data.extensionLocation),
iconPath: reviveIconPath(data.iconPath),
state: reviveState(data.state),
};
@ -90,6 +96,25 @@ export class WebviewEditorInputFactory implements IEditorInputFactory {
}
}
export function reviveWebviewExtensionDescription(
extensionId: string | undefined,
extensionLocation: UriComponents | undefined,
): WebviewExtensionDescription | undefined {
if (!extensionId) {
return undefined;
}
const location = reviveUri(extensionLocation);
if (!location) {
return undefined;
}
return {
id: new ExtensionIdentifier(extensionId),
location,
};
}
function reviveIconPath(data: SerializedIconPath | undefined) {
if (!data) {
return undefined;