Polishing of custom editor api proposal for 1.43 release

For #77131
This commit is contained in:
Matt Bierner
2020-02-25 10:51:47 -08:00
parent 858457d2e6
commit 8291f8c152
6 changed files with 74 additions and 129 deletions
+1 -1
View File
@@ -23,7 +23,7 @@ export function activate(context: vscode.ExtensionContext) {
const previewManager = new PreviewManager(extensionRoot, sizeStatusBarEntry, binarySizeStatusBarEntry, zoomStatusBarEntry);
context.subscriptions.push(vscode.window.registerWebviewCustomEditorProvider(PreviewManager.viewType, previewManager));
context.subscriptions.push(vscode.window.registerCustomEditorProvider(PreviewManager.viewType, previewManager));
context.subscriptions.push(vscode.commands.registerCommand('imagePreview.zoomIn', () => {
previewManager.activePreview?.zoomIn();
+5 -5
View File
@@ -13,7 +13,7 @@ import { BinarySizeStatusBarEntry } from './binarySizeStatusBarEntry';
const localize = nls.loadMessageBundle();
export class PreviewManager implements vscode.WebviewCustomEditorProvider {
export class PreviewManager implements vscode.CustomEditorProvider {
public static readonly viewType = 'imagePreview.previewEditor';
@@ -27,12 +27,12 @@ export class PreviewManager implements vscode.WebviewCustomEditorProvider {
private readonly zoomStatusBarEntry: ZoomStatusBarEntry,
) { }
public async provideWebviewCustomEditorDocument(resource: vscode.Uri) {
return vscode.window.createWebviewEditorCustomDocument(PreviewManager.viewType, resource, undefined, {});
public async resolveCustomDocument(_document: vscode.CustomDocument): Promise<vscode.CustomEditorCapabilities> {
return {};
}
public async resolveWebviewCustomEditor(
document: vscode.WebviewEditorCustomDocument,
public async resolveCustomEditor(
document: vscode.CustomDocument,
webviewEditor: vscode.WebviewPanel,
): Promise<void> {
const preview = new Preview(this.extensionRoot, document.uri, webviewEditor, this.sizeStatusBarEntry, this.binarySizeStatusBarEntry, this.zoomStatusBarEntry);
@@ -52,7 +52,7 @@ class PreviewStore extends Disposable {
}
}
export class MarkdownPreviewManager extends Disposable implements vscode.WebviewPanelSerializer, vscode.WebviewCustomEditorProvider {
export class MarkdownPreviewManager extends Disposable implements vscode.WebviewPanelSerializer, vscode.CustomEditorProvider {
private static readonly markdownPreviewActiveContextKey = 'markdownPreviewFocus';
private readonly _topmostLineMonitor = new TopmostLineMonitor();
@@ -70,7 +70,7 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview
) {
super();
this._register(vscode.window.registerWebviewPanelSerializer(DynamicMarkdownPreview.viewType, this));
this._register(vscode.window.registerWebviewCustomEditorProvider('vscode.markdown.preview.editor', this));
this._register(vscode.window.registerCustomEditorProvider('vscode.markdown.preview.editor', this));
}
public refresh() {
@@ -148,12 +148,12 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview
this.registerDynamicPreview(preview);
}
public async provideWebviewCustomEditorDocument(resource: vscode.Uri) {
return vscode.window.createWebviewEditorCustomDocument('vscode.markdown.preview.editor', resource, undefined, {});
public async resolveCustomDocument(_document: vscode.CustomDocument): Promise<vscode.CustomEditorCapabilities> {
return {};
}
public async resolveWebviewCustomEditor(
document: vscode.WebviewEditorCustomDocument,
public async resolveCustomEditor(
document: vscode.CustomDocument,
webview: vscode.WebviewPanel
): Promise<void> {
const preview = DynamicMarkdownPreview.revive(
+17 -57
View File
@@ -1188,22 +1188,21 @@ declare module 'vscode' {
//#region Custom editors: https://github.com/microsoft/vscode/issues/77131
// TODO:
// - Naming!
// - Think about where a rename would live.
// - Think about handling go to line?
// - Think about handling go to line? (add other editor options? reveal?)
// - Should we expose edits?
// - More properties from `TextDocument`?
/**
* Defines the capabilities of a custom webview editor.
*/
interface WebviewCustomEditorCapabilities {
interface CustomEditorCapabilities {
/**
* Defines the editing capability of a custom webview document.
*
* When not provided, the document is considered readonly.
*/
readonly editing?: WebviewCustomEditorEditingCapability;
readonly editing?: CustomEditorEditingCapability;
}
/**
@@ -1212,7 +1211,7 @@ declare module 'vscode' {
*
* @param EditType Type of edits.
*/
interface WebviewCustomEditorEditingCapability<EditType = unknown> {
interface CustomEditorEditingCapability<EditType = unknown> {
/**
* Save the resource.
*
@@ -1286,7 +1285,7 @@ declare module 'vscode' {
*
* @param UserDataType Type of custom object that extensions can store on the document.
*/
interface WebviewEditorCustomDocument<UserDataType = unknown> {
interface CustomDocument<UserDataType = unknown> {
/**
* The associated viewType for this document.
*/
@@ -1305,7 +1304,7 @@ declare module 'vscode' {
/**
* Custom data that an extension can store on the document.
*/
readonly userData: UserDataType;
userData?: UserDataType;
// TODO: Should we expose edits here?
// This could be helpful for tracking the life cycle of edits
@@ -1320,13 +1319,13 @@ declare module 'vscode' {
* You should use custom text based editors when dealing with binary files or more complex scenarios. For simple text
* based documents, use [`WebviewTextEditorProvider`](#WebviewTextEditorProvider) instead.
*/
export interface WebviewCustomEditorProvider {
export interface CustomEditorProvider {
/**
* Create the model for a given
*
* @param document Resource being resolved.
*/
provideWebviewCustomEditorDocument(resource: Uri): Thenable<WebviewEditorCustomDocument>;
resolveCustomDocument(document: CustomDocument): Thenable<CustomEditorCapabilities>;
/**
* Resolve a webview editor for a given resource.
@@ -1339,7 +1338,7 @@ declare module 'vscode' {
*
* @return Thenable indicating that the webview editor has been resolved.
*/
resolveWebviewCustomEditor(document: WebviewEditorCustomDocument, webview: WebviewPanel): Thenable<void>;
resolveCustomEditor(document: CustomDocument, webview: WebviewPanel): Thenable<void>;
}
/**
@@ -1352,7 +1351,7 @@ declare module 'vscode' {
* You should use text based webview editors when dealing with text based file formats, such as `xml` or `json`.
* For binary files or more specialized use cases, see [WebviewCustomEditorProvider](#WebviewCustomEditorProvider).
*/
export interface WebviewTextEditorProvider {
export interface CustomTextEditorProvider {
/**
* Resolve a webview editor for a given resource.
*
@@ -1364,64 +1363,25 @@ declare module 'vscode' {
*
* @return Thenable indicating that the webview editor has been resolved.
*/
resolveWebviewTextEditor(document: TextDocument, webview: WebviewPanel): Thenable<void>;
resolveCustomTextEditor(document: TextDocument, webview: WebviewPanel): Thenable<void>;
}
namespace window {
/**
* Register a new provider for text based webview editors.
*
* @param viewType Type of the webview editor provider. This should match the `viewType` from the
* `package.json` contributions
* @param provider Provider that resolves webview editors.
* @param webviewOptions Content settings for the webview panels that provider is given.
*
* @return Disposable that unregisters the `WebviewTextEditorProvider`.
*/
export function registerWebviewTextEditorProvider(
viewType: string,
provider: WebviewTextEditorProvider,
webviewOptions?: WebviewPanelOptions,
): Disposable;
/**
* Register a new provider for custom webview editors.
* Register a new provider for a custom editor.
*
* @param viewType Type of the webview editor provider. This should match the `viewType` from the
* `package.json` contributions.
* @param provider Provider that resolves webview editors.
* @param webviewOptions Content settings for the webview panels that provider is given.
* @param provider Provider that resolves editors.
* @param webviewOptions Content settings for the webview panels that the provider is given.
*
* @return Disposable that unregisters the `WebviewCustomEditorProvider`.
* @return Disposable that unregisters the provider.
*/
export function registerWebviewCustomEditorProvider(
export function registerCustomEditorProvider(
viewType: string,
provider: WebviewCustomEditorProvider,
provider: CustomEditorProvider | CustomTextEditorProvider,
webviewOptions?: WebviewPanelOptions,
): Disposable;
/**
* Create a new `WebviewEditorCustomDocument`.
*
* Note that this method only creates a custom document object. To have it be registered with VS Code, you
* must return the document from `WebviewCustomEditorProvider.provideWebviewCustomEditorDocument`.
*
* @param viewType Type of the webview editor provider. This should match the `viewType` from the
* `package.json` contributions.
* @param uri The document's resource.
* @param userData Custom data attached to the document.
* @param capabilities Controls the editing functionality of a webview editor. This allows the webview
* editor to hook into standard editor events such as `undo` or `save`.
*
* WebviewEditors that do not have `editingCapability` are considered to be readonly. Users can still interact
* with readonly editors, but these editors will not integrate with VS Code's standard editor functionality.
*/
export function createWebviewEditorCustomDocument<UserDataType>(
viewType: string,
uri: Uri,
userData: UserDataType,
capabilities: WebviewCustomEditorCapabilities
): WebviewEditorCustomDocument<UserDataType>;
}
//#endregion
@@ -561,17 +561,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
registerWebviewPanelSerializer: (viewType: string, serializer: vscode.WebviewPanelSerializer) => {
return extHostWebviews.registerWebviewPanelSerializer(extension, viewType, serializer);
},
registerWebviewTextEditorProvider: (viewType: string, provider: vscode.WebviewTextEditorProvider, options?: vscode.WebviewPanelOptions) => {
registerCustomEditorProvider: (viewType: string, provider: vscode.CustomEditorProvider | vscode.CustomTextEditorProvider, options?: vscode.WebviewPanelOptions) => {
checkProposedApiEnabled(extension);
return extHostWebviews.registerWebviewTextEditorProvider(extension, viewType, provider, options);
},
registerWebviewCustomEditorProvider: (viewType: string, provider: vscode.WebviewCustomEditorProvider, options?: vscode.WebviewPanelOptions) => {
checkProposedApiEnabled(extension);
return extHostWebviews.registerWebviewCustomEditorProvider(extension, viewType, provider, options);
},
createWebviewEditorCustomDocument: <UserDataType>(viewType: string, resource: vscode.Uri, userData: UserDataType, capabilities: vscode.WebviewCustomEditorCapabilities) => {
checkProposedApiEnabled(extension);
return extHostWebviews.createWebviewEditorCustomDocument<UserDataType>(viewType, resource, userData, capabilities);
return extHostWebviews.registerCustomEditorProvider(extension, viewType, provider, options);
},
registerDecorationProvider(provider: vscode.DecorationProvider) {
checkProposedApiEnabled(extension);
+43 -50
View File
@@ -247,22 +247,30 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa
type EditType = unknown;
class WebviewEditorCustomDocument extends Disposable implements vscode.WebviewEditorCustomDocument {
class WebviewEditorCustomDocument extends Disposable implements vscode.CustomDocument {
private _currentEditIndex: number = -1;
private _savePoint: number = -1;
private readonly _edits: Array<EditType> = [];
public userData: unknown;
public _capabilities?: vscode.CustomEditorCapabilities;
constructor(
private readonly _proxy: MainThreadWebviewsShape,
public readonly viewType: string,
public readonly uri: vscode.Uri,
public readonly userData: unknown,
public readonly _capabilities: vscode.WebviewCustomEditorCapabilities,
) {
super();
}
// Hook up events
_capabilities.editing?.onDidEdit(edit => {
_setCapabilities(capabilities: vscode.CustomEditorCapabilities) {
if (this._capabilities) {
throw new Error('Capabilities already provided');
}
this._capabilities = capabilities;
capabilities.editing?.onDidEdit(edit => {
this.pushEdit(edit, this);
});
}
@@ -356,12 +364,12 @@ class WebviewEditorCustomDocument extends Disposable implements vscode.WebviewEd
return this.getEditingCapability().saveAs(target);
}
backup(cancellation: CancellationToken): boolean | PromiseLike<boolean> {
throw new Error('Method not implemented.');
backup(cancellation: CancellationToken) {
return this.getEditingCapability().backup(cancellation);
}
private getEditingCapability(): vscode.WebviewCustomEditorEditingCapability {
if (!this._capabilities.editing) {
private getEditingCapability(): vscode.CustomEditorEditingCapability {
if (!this._capabilities?.editing) {
throw new Error('Document is not editable');
}
return this._capabilities.editing;
@@ -401,21 +409,21 @@ const enum WebviewEditorType {
type ProviderEntry = {
readonly extension: IExtensionDescription;
readonly type: WebviewEditorType.Text;
readonly provider: vscode.WebviewTextEditorProvider;
readonly provider: vscode.CustomTextEditorProvider;
} | {
readonly extension: IExtensionDescription;
readonly type: WebviewEditorType.Custom;
readonly provider: vscode.WebviewCustomEditorProvider;
readonly provider: vscode.CustomEditorProvider;
};
class EditorProviderStore {
private readonly _providers = new Map<string, ProviderEntry>();
public addTextProvider(viewType: string, extension: IExtensionDescription, provider: vscode.WebviewTextEditorProvider): vscode.Disposable {
public addTextProvider(viewType: string, extension: IExtensionDescription, provider: vscode.CustomTextEditorProvider): vscode.Disposable {
return this.add(WebviewEditorType.Text, viewType, extension, provider);
}
public addCustomProvider(viewType: string, extension: IExtensionDescription, provider: vscode.WebviewCustomEditorProvider): vscode.Disposable {
public addCustomProvider(viewType: string, extension: IExtensionDescription, provider: vscode.CustomEditorProvider): vscode.Disposable {
return this.add(WebviewEditorType.Custom, viewType, extension, provider);
}
@@ -423,7 +431,7 @@ class EditorProviderStore {
return this._providers.get(viewType);
}
private add(type: WebviewEditorType, viewType: string, extension: IExtensionDescription, provider: vscode.WebviewTextEditorProvider | vscode.WebviewCustomEditorProvider): vscode.Disposable {
private add(type: WebviewEditorType, viewType: string, extension: IExtensionDescription, provider: vscode.CustomTextEditorProvider | vscode.CustomEditorProvider): vscode.Disposable {
if (this._providers.has(viewType)) {
throw new Error(`Provider for viewType:${viewType} already registered`);
}
@@ -501,43 +509,26 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
});
}
public registerWebviewTextEditorProvider(
public registerCustomEditorProvider(
extension: IExtensionDescription,
viewType: string,
provider: vscode.WebviewTextEditorProvider,
provider: vscode.CustomEditorProvider | vscode.CustomTextEditorProvider,
options: vscode.WebviewPanelOptions | undefined = {}
): vscode.Disposable {
const unregisterProvider = this._editorProviders.addTextProvider(viewType, extension, provider);
this._proxy.$registerTextEditorProvider(toExtensionData(extension), viewType, options);
let disposable: vscode.Disposable;
if ('resolveCustomTextEditor' in provider) {
disposable = this._editorProviders.addTextProvider(viewType, extension, provider);
this._proxy.$registerTextEditorProvider(toExtensionData(extension), viewType, options);
} else {
disposable = this._editorProviders.addCustomProvider(viewType, extension, provider);
this._proxy.$registerCustomEditorProvider(toExtensionData(extension), viewType, options);
}
return new VSCodeDisposable(() => {
unregisterProvider.dispose();
this._proxy.$unregisterEditorProvider(viewType);
});
}
public registerWebviewCustomEditorProvider(
extension: IExtensionDescription,
viewType: string,
provider: vscode.WebviewCustomEditorProvider,
options: vscode.WebviewPanelOptions | undefined = {},
): vscode.Disposable {
const unregisterProvider = this._editorProviders.addCustomProvider(viewType, extension, provider);
this._proxy.$registerCustomEditorProvider(toExtensionData(extension), viewType, options);
return new VSCodeDisposable(() => {
unregisterProvider.dispose();
this._proxy.$unregisterEditorProvider(viewType);
});
}
public createWebviewEditorCustomDocument<UserDataType>(
viewType: string,
resource: vscode.Uri,
userData: UserDataType,
capabilities: vscode.WebviewCustomEditorCapabilities,
): vscode.WebviewEditorCustomDocument<UserDataType> {
return Object.seal(new WebviewEditorCustomDocument(this._proxy, viewType, resource, userData, capabilities) as vscode.WebviewEditorCustomDocument<UserDataType>);
return VSCodeDisposable.from(
disposable,
new VSCodeDisposable(() => {
this._proxy.$unregisterEditorProvider(viewType);
}));
}
public $onMessage(
@@ -631,10 +622,12 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
}
const revivedResource = URI.revive(resource);
const document = await entry.provider.provideWebviewCustomEditorDocument(revivedResource) as WebviewEditorCustomDocument;
const document = Object.seal(new WebviewEditorCustomDocument(this._proxy, viewType, revivedResource));
const capabilities = await entry.provider.resolveCustomDocument(document);
document._setCapabilities(capabilities);
this._documents.add(document);
return {
editable: !!document._capabilities.editing
editable: !!capabilities.editing
};
}
@@ -677,13 +670,13 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
case WebviewEditorType.Custom:
{
const document = this.getDocument(viewType, revivedResource);
return entry.provider.resolveWebviewCustomEditor(document, revivedPanel);
return entry.provider.resolveCustomEditor(document, revivedPanel);
}
case WebviewEditorType.Text:
{
await this._extHostDocuments.ensureDocumentData(revivedResource);
const document = this._extHostDocuments.getDocument(revivedResource);
return entry.provider.resolveWebviewTextEditor(document, revivedPanel);
return entry.provider.resolveCustomTextEditor(document, revivedPanel);
}
default:
{