提交 dcecb9ee 编写于 作者: M Matt Bierner

Documenting thoughts on direction for custom editor API

This documents the current thinking on the custom editor api. The current api proposal is not actually implemented yet
上级 cd4d7e6c
......@@ -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<void> {
async resolveWebviewEditor({ resource }, editor: vscode.WebviewPanel): Promise<vscode.WebviewEditorCapabilities> {
previewManager.resolve(resource, editor);
return {};
}
}));
......
......@@ -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 {
</html>`;
}
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));
......
......@@ -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;
......
......@@ -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<void>;
readonly editingCapability?: WebviewEditorEditingCapability;
}
interface WebviewEditorEditingCapability {
/**
* Persist the resource.
*/
save(resource: Uri): Thenable<void>;
/**
* Called when the editor exits.
*/
hotExit(hotExitPath: Uri): Thenable<void>;
/**
* Signal to VS Code that an edit has occurred.
*
* Edits must be a json serilizable object.
*/
readonly onEdit: Event<any>;
/**
* 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<void>;
/**
* 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<void>;
}
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<void>;
input: {
readonly resource: Uri
},
webview: WebviewPanel,
): Thenable<WebviewEditorCapabilities>;
}
namespace window {
export function registerWebviewEditorProvider(
viewType: string,
provider: WebviewEditorProvider,
options?: WebviewPanelOptions
options?: WebviewPanelOptions,
): Disposable;
}
......
......@@ -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(),
......
......@@ -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<void>;
$deserializeWebviewPanel(newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise<void>;
$resolveWebviewEditor(resource: UriComponents, newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise<void>;
$save(handle: WebviewPanelHandle): Promise<boolean>;
}
export interface MainThreadUrlsShape extends IDisposable {
......
......@@ -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<boolean>) => void }>();
public readonly onWillSave = this._onWillSave.event;
async _save(): Promise<boolean> {
const waitingOn: Thenable<boolean>[] = [];
this._onWillSave.fire({
waitUntil: (thenable: Thenable<boolean>): void => { waitingOn.push(thenable); },
});
const result = await Promise.all(waitingOn);
return result.every(x => x);
}
public postMessage(message: any): Promise<boolean> {
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<boolean> {
const panel = this.getWebviewPanel(handle);
if (panel) {
return panel._save();
}
return false;
await Promise.resolve(provider.resolveWebviewEditor({ resource: URI.revive(resource) }, revivedPanel));
}
}
......
......@@ -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<UnownedDisposable<WebviewEditorOverlay>>,
@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<ConfirmResult> {
if (!this.isDirty()) {
return ConfirmResult.DONT_SAVE;
}
return promptSave(this.dialogService, [this.getResource()]);
}
public async save(): Promise<boolean> {
if (!this.isDirty) {
return true;
}
const waitingOn: Promise<boolean>[] = [];
this._onWillSave.fire({
waitUntil: (thenable: Promise<boolean>): 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<boolean>) => void }>());
public readonly onWillSave = this._onWillSave.event;
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册