diff --git a/extensions/image-preview/src/extension.ts b/extensions/image-preview/src/extension.ts index 80604db832b..cd68edf2e5c 100644 --- a/extensions/image-preview/src/extension.ts +++ b/extensions/image-preview/src/extension.ts @@ -22,8 +22,9 @@ export function activate(context: vscode.ExtensionContext) { context.subscriptions.push(vscode.window.registerWebviewEditorProvider( PreviewManager.viewType, { - async resolveWebviewEditor(resource: vscode.Uri, editor: vscode.WebviewEditor): Promise { + async resolveWebviewEditor({ resource }, editor: vscode.WebviewPanel): Promise { previewManager.resolve(resource, editor); + return {}; } })); diff --git a/extensions/image-preview/src/preview.ts b/extensions/image-preview/src/preview.ts index a5eb47e308f..b678daf6436 100644 --- a/extensions/image-preview/src/preview.ts +++ b/extensions/image-preview/src/preview.ts @@ -27,7 +27,7 @@ export class PreviewManager { public resolve( resource: vscode.Uri, - webviewEditor: vscode.WebviewEditor, + webviewEditor: vscode.WebviewPanel, ) { const preview = new Preview(this.extensionRoot, resource, webviewEditor, this.sizeStatusBarEntry, this.zoomStatusBarEntry); this._previews.add(preview); @@ -73,7 +73,7 @@ class Preview extends Disposable { constructor( private readonly extensionRoot: vscode.Uri, private readonly resource: vscode.Uri, - private readonly webviewEditor: vscode.WebviewEditor, + private readonly webviewEditor: vscode.WebviewPanel, private readonly sizeStatusBarEntry: SizeStatusBarEntry, private readonly zoomStatusBarEntry: ZoomStatusBarEntry, ) { @@ -208,7 +208,7 @@ class Preview extends Disposable { `; } - private getResourcePath(webviewEditor: vscode.WebviewEditor, resource: vscode.Uri, version: string) { + private getResourcePath(webviewEditor: vscode.WebviewPanel, resource: vscode.Uri, version: string) { switch (resource.scheme) { case 'data': return encodeURI(resource.toString(true)); diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 913a3fd4e53..67a01dd72cc 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -1444,14 +1444,6 @@ export interface IWebviewPanelOptions { readonly retainContextWhenHidden?: boolean; } -/** - * @internal - */ -export const enum WebviewContentState { - Readonly = 1, - Unchanged = 2, - Dirty = 3, -} export interface CodeLens { range: IRange; diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index b2dfc272b57..6dec851f04e 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -889,26 +889,89 @@ declare module 'vscode' { //#region Custom editors, mjbvz - export interface WebviewEditor extends WebviewPanel { + /** + * + */ + interface WebviewEditorCapabilities { + /** + * Invoked when the resource has been renamed in VS Code. + * + * This is called when the resource's new name also matches the custom editor selector. + * + * If this is not implemented—or if the new resource name does not match the existing selector—then VS Code + * will close and reopen the editor on rename. + * + * @param newResource Full path to the resource. + * + * @return Thenable that signals the save is complete. + */ + rename?(newResource: Uri): Thenable; + + readonly editingCapability?: WebviewEditorEditingCapability; + } + + interface WebviewEditorEditingCapability { + /** + * Persist the resource. + */ + save(resource: Uri): Thenable; + + /** + * Called when the editor exits. + */ + hotExit(hotExitPath: Uri): Thenable; + + /** + * Signal to VS Code that an edit has occurred. + * + * Edits must be a json serilizable object. + */ + readonly onEdit: Event; + + /** + * Apply a set of edits. + * + * This is triggered on redo and when restoring a custom editor after restart. Note that is not invoked + * when `onEdit` is called as `onEdit` implies also updating the view to reflect the edit. + * + * @param edit Array of edits. Sorted from oldest to most recent. + */ + applyEdits(edits: any[]): Thenable; + + /** + * Undo a set of edits. + * + * This is triggered when a user undoes an edit or when revert is called on a file. + * + * @param edit Array of edits. Sorted from most recent to oldest. + */ + undoEdits(edits: any[]): Thenable; } export interface WebviewEditorProvider { /** - * Fills out a `WebviewEditor` for a given resource. - * - * The provider should take ownership of passed in `editor`. - */ + * Fills out a `WebviewEditor` for a given resource. + * + * @param input Information about the resource being resolved. + * @param webview Webview being resolved. The provider should take ownership of this webview. + * + * @return Thenable to a `WebviewEditorCapabilities` indicating that the webview editor has been resolved. + * The `WebviewEditorCapabilities` defines how the custom editor interacts with VS Code. + * **❗️Note**: `WebviewEditorCapabilities` is not actually implemented... yet! + */ resolveWebviewEditor( - resource: Uri, - editor: WebviewEditor - ): Thenable; + input: { + readonly resource: Uri + }, + webview: WebviewPanel, + ): Thenable; } namespace window { export function registerWebviewEditorProvider( viewType: string, provider: WebviewEditorProvider, - options?: WebviewPanelOptions + options?: WebviewPanelOptions, ): Disposable; } diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts index 81fd60bb4ce..1ce660a06e8 100644 --- a/src/vs/workbench/api/browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/browser/mainThreadWebview.ts @@ -168,13 +168,6 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma webview.setName(value); } - public $setState(handle: extHostProtocol.WebviewPanelHandle, state: modes.WebviewContentState): void { - const webview = this.getWebviewInput(handle); - if (webview instanceof CustomFileEditorInput) { - webview.setState(state); - } - } - public $setIconPath(handle: extHostProtocol.WebviewPanelHandle, value: { light: UriComponents, dark: UriComponents; } | undefined): void { const webview = this.getWebviewInput(handle); webview.iconPath = reviveWebviewIcon(value); @@ -276,12 +269,6 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma webviewInput.webview.options = options; webviewInput.webview.extension = extension; - if (webviewInput instanceof CustomFileEditorInput) { - webviewInput.onWillSave(e => { - e.waitUntil(this._proxy.$save(handle)); - }); - } - try { await this._proxy.$resolveWebviewEditor( webviewInput.getResource(), diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 6d218ee0395..586824df3bb 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -558,7 +558,6 @@ export interface MainThreadWebviewsShape extends IDisposable { $disposeWebview(handle: WebviewPanelHandle): void; $reveal(handle: WebviewPanelHandle, showOptions: WebviewPanelShowOptions): void; $setTitle(handle: WebviewPanelHandle, value: string): void; - $setState(handle: WebviewPanelHandle, state: modes.WebviewContentState): void; $setIconPath(handle: WebviewPanelHandle, value: { light: UriComponents, dark: UriComponents } | undefined): void; $setHtml(handle: WebviewPanelHandle, value: string): void; @@ -588,7 +587,6 @@ export interface ExtHostWebviewsShape { $onDidDisposeWebviewPanel(handle: WebviewPanelHandle): Promise; $deserializeWebviewPanel(newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise; $resolveWebviewEditor(resource: UriComponents, newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise; - $save(handle: WebviewPanelHandle): Promise; } export interface MainThreadUrlsShape extends IDisposable { diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts index 8e752a002ff..50fdaec003e 100644 --- a/src/vs/workbench/api/common/extHostWebview.ts +++ b/src/vs/workbench/api/common/extHostWebview.ts @@ -91,7 +91,7 @@ export class ExtHostWebview implements vscode.Webview { } } -export class ExtHostWebviewEditor implements vscode.WebviewEditor { +export class ExtHostWebviewEditor implements vscode.WebviewPanel { private readonly _handle: WebviewPanelHandle; private readonly _proxy: MainThreadWebviewsShape; @@ -223,18 +223,6 @@ export class ExtHostWebviewEditor implements vscode.WebviewEditor { this._visible = value; } - private readonly _onWillSave = new Emitter<{ waitUntil: (thenable: Thenable) => void }>(); - public readonly onWillSave = this._onWillSave.event; - - async _save(): Promise { - const waitingOn: Thenable[] = []; - this._onWillSave.fire({ - waitUntil: (thenable: Thenable): void => { waitingOn.push(thenable); }, - }); - const result = await Promise.all(waitingOn); - return result.every(x => x); - } - public postMessage(message: any): Promise { this.assertNotDisposed(); return this._proxy.$postMessage(this._handle, message); @@ -434,15 +422,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { const webview = new ExtHostWebview(handle, this._proxy, options, this.initData, this.workspace, extension); const revivedPanel = new ExtHostWebviewEditor(handle, this._proxy, viewType, title, typeof position === 'number' && position >= 0 ? typeConverters.ViewColumn.to(position) : undefined, options, webview); this._webviewPanels.set(handle, revivedPanel); - return Promise.resolve(provider.resolveWebviewEditor(URI.revive(resource), revivedPanel)); - } - - async $save(handle: WebviewPanelHandle): Promise { - const panel = this.getWebviewPanel(handle); - if (panel) { - return panel._save(); - } - return false; + await Promise.resolve(provider.resolveWebviewEditor({ resource: URI.revive(resource) }, revivedPanel)); } } diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts index 464a0d8fe99..24a42571e89 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts @@ -4,28 +4,23 @@ *--------------------------------------------------------------------------------------------*/ import { memoize } from 'vs/base/common/decorators'; -import { Emitter } from 'vs/base/common/event'; import { Lazy } from 'vs/base/common/lazy'; import { UnownedDisposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { basename } from 'vs/base/common/path'; import { DataUri, isEqual } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; -import { WebviewContentState } from 'vs/editor/common/modes'; -import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { ILabelService } from 'vs/platform/label/common/label'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; -import { ConfirmResult, IEditorInput, Verbosity } from 'vs/workbench/common/editor'; +import { IEditorInput, Verbosity } from 'vs/workbench/common/editor'; import { WebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/webview'; import { IWebviewWorkbenchService, LazilyResolvedWebviewEditorInput } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService'; -import { promptSave } from 'vs/workbench/services/textfile/browser/textFileService'; export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput { public static typeId = 'workbench.editors.webviewEditor'; private readonly _editorResource: URI; - private _state = WebviewContentState.Readonly; constructor( resource: URI, @@ -34,7 +29,6 @@ export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput { webview: Lazy>, @ILifecycleService lifecycleService: ILifecycleService, @IWebviewWorkbenchService webviewWorkbenchService: IWebviewWorkbenchService, - @IDialogService private readonly dialogService: IDialogService, @ILabelService private readonly labelService: ILabelService, ) { super(id, viewType, '', webview, webviewWorkbenchService, lifecycleService); @@ -111,35 +105,4 @@ export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput { return this.longTitle; } } - - public setState(newState: WebviewContentState): void { - this._state = newState; - this._onDidChangeDirty.fire(); - } - - public isDirty() { - return this._state === WebviewContentState.Dirty; - } - - public async confirmSave(): Promise { - if (!this.isDirty()) { - return ConfirmResult.DONT_SAVE; - } - return promptSave(this.dialogService, [this.getResource()]); - } - - public async save(): Promise { - if (!this.isDirty) { - return true; - } - const waitingOn: Promise[] = []; - this._onWillSave.fire({ - waitUntil: (thenable: Promise): void => { waitingOn.push(thenable); }, - }); - const result = await Promise.all(waitingOn); - return result.every(x => x); - } - - private readonly _onWillSave = this._register(new Emitter<{ waitUntil: (thenable: Thenable) => void }>()); - public readonly onWillSave = this._onWillSave.event; }