diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 2edbc6a4220..3a59c0a4906 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1051,6 +1051,15 @@ declare module 'vscode' { export interface WebviewEditor extends WebviewPanel { state: WebviewEditorState; + + /** + * Fired when the webview editor is saved. + * + * Both `Unchanged` and `Dirty` editors can be saved. + * + * Extensions should call `waitUntil` to signal when the save operation complete + */ + readonly onWillSave: Event<{ waitUntil: (thenable: Thenable) => void }>; } export interface WebviewEditorProvider { diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts index 17f3257380f..4fa2a398e0e 100644 --- a/src/vs/workbench/api/browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/browser/mainThreadWebview.ts @@ -16,13 +16,13 @@ import { IProductService } from 'vs/platform/product/common/product'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ExtHostContext, ExtHostWebviewsShape, IExtHostContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelShowOptions, WebviewPanelViewStateData } from 'vs/workbench/api/common/extHost.protocol'; import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor'; +import { CustomFileEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput'; import { WebviewInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput'; import { ICreateWebViewShowOptions, IWebviewEditorService, WebviewInputOptions } from 'vs/workbench/contrib/webview/browser/webviewEditorService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { extHostNamedCustomer } from '../common/extHostCustomers'; -import { CustomFileEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput'; /** * Bi-directional map between webview handles and inputs. @@ -253,6 +253,12 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews this._webviewEditorInputs.add(handle, webview); this.hookupWebviewEventDelegate(handle, webview); + if (webview instanceof CustomFileEditorInput) { + webview.onWillSave(e => { + e.waitUntil(this._proxy.$save(handle)); + }); + } + try { await this._proxy.$resolveWebviewEditor( webview.getResource(), diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index b7bddc60c4d..31f17645dff 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -573,6 +573,7 @@ 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, state: any, 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 2ba9d4eb252..f3717e325c8 100644 --- a/src/vs/workbench/api/common/extHostWebview.ts +++ b/src/vs/workbench/api/common/extHostWebview.ts @@ -224,6 +224,18 @@ export class ExtHostWebviewEditor implements vscode.WebviewEditor { this._proxy.$setState(this._handle, typeConverters.WebviewEditorState.from(newState)); } + 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); @@ -422,6 +434,13 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { 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; + } } function convertWebviewOptions( diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts index 19dc5014d9c..776eb29464f 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { memoize } from 'vs/base/common/decorators'; +import { Emitter } from 'vs/base/common/event'; import { UnownedDisposable } from 'vs/base/common/lifecycle'; import { basename } from 'vs/base/common/path'; import { URI } from 'vs/base/common/uri'; @@ -115,7 +116,17 @@ export class CustomFileEditorInput extends WebviewInput { } public async save(): Promise { - // TODO - return true; + 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; }