Skip to content

Commit 579dab3

Browse files
committed
Update custom editor api
For microsoft#77131 - Use a class for `CustomDocument` instead of an interface. Extensions can now add their own data to a `CustomDocument` by sublassing - Renamed `resolveCustomDocument` to `openCustomDocument` and require that extensions return a `CustomDocument` - Exposed edits on `CustomDocument` - Made the third parameter of `registerCustomEditorProvider` a generic options bag that takes a `webviewOptions`
1 parent 414fc3c commit 579dab3

8 files changed

Lines changed: 223 additions & 198 deletions

File tree

extensions/image-preview/src/preview.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ export class PreviewManager implements vscode.CustomEditorProvider {
2727
private readonly zoomStatusBarEntry: ZoomStatusBarEntry,
2828
) { }
2929

30-
public async resolveCustomDocument(_document: vscode.CustomDocument): Promise<void> {
31-
// noop
30+
public async openCustomDocument(uri: vscode.Uri) {
31+
return new vscode.CustomDocument(PreviewManager.viewType, uri);
3232
}
3333

3434
public async resolveCustomEditor(

extensions/markdown-language-features/src/features/previewManager.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,16 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview
6363

6464
private _activePreview: DynamicMarkdownPreview | undefined = undefined;
6565

66+
private readonly customEditorViewType = 'vscode.markdown.preview.editor';
67+
6668
public constructor(
6769
private readonly _contentProvider: MarkdownContentProvider,
6870
private readonly _logger: Logger,
6971
private readonly _contributions: MarkdownContributionProvider
7072
) {
7173
super();
7274
this._register(vscode.window.registerWebviewPanelSerializer(DynamicMarkdownPreview.viewType, this));
73-
this._register(vscode.window.registerCustomEditorProvider('vscode.markdown.preview.editor', this));
75+
this._register(vscode.window.registerCustomEditorProvider(this.customEditorViewType, this));
7476
}
7577

7678
public refresh() {
@@ -148,8 +150,8 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview
148150
this.registerDynamicPreview(preview);
149151
}
150152

151-
public async resolveCustomDocument(_document: vscode.CustomDocument): Promise<void> {
152-
// noop
153+
public async openCustomDocument(uri: vscode.Uri) {
154+
return new vscode.CustomDocument(this.customEditorViewType, uri);
153155
}
154156

155157
public async resolveCustomTextEditor(

src/vs/vscode.proposed.d.ts

Lines changed: 47 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1289,17 +1289,11 @@ declare module 'vscode' {
12891289

12901290
//#region Custom editors: https://github.com/microsoft/vscode/issues/77131
12911291

1292-
// TODO:
1293-
// - Think about where a rename would live.
1294-
// - Think about handling go to line? (add other editor options? reveal?)
1295-
// - Should we expose edits?
1296-
// - More properties from `TextDocument`?
1297-
12981292
/**
12991293
* Defines the editing capability of a custom webview editor. This allows the webview editor to hook into standard
13001294
* editor events such as `undo` or `save`.
13011295
*
1302-
* @param EditType Type of edits.
1296+
* @param EditType Type of edits used for the documents this delegate handles.
13031297
*/
13041298
interface CustomEditorEditingDelegate<EditType = unknown> {
13051299
/**
@@ -1310,7 +1304,7 @@ declare module 'vscode' {
13101304
*
13111305
* @return Thenable signaling that the save has completed.
13121306
*/
1313-
save(document: CustomDocument, cancellation: CancellationToken): Thenable<void>;
1307+
save(document: CustomDocument<EditType>, cancellation: CancellationToken): Thenable<void>;
13141308

13151309
/**
13161310
* Save the existing resource at a new path.
@@ -1320,7 +1314,7 @@ declare module 'vscode' {
13201314
*
13211315
* @return Thenable signaling that the save has completed.
13221316
*/
1323-
saveAs(document: CustomDocument, targetResource: Uri): Thenable<void>;
1317+
saveAs(document: CustomDocument<EditType>, targetResource: Uri): Thenable<void>;
13241318

13251319
/**
13261320
* Event triggered by extensions to signal to VS Code that an edit has occurred.
@@ -1337,7 +1331,7 @@ declare module 'vscode' {
13371331
*
13381332
* @return Thenable signaling that the change has completed.
13391333
*/
1340-
applyEdits(document: CustomDocument, edits: readonly EditType[]): Thenable<void>;
1334+
applyEdits(document: CustomDocument<EditType>, edits: readonly EditType[]): Thenable<void>;
13411335

13421336
/**
13431337
* Undo a set of edits.
@@ -1349,7 +1343,7 @@ declare module 'vscode' {
13491343
*
13501344
* @return Thenable signaling that the change has completed.
13511345
*/
1352-
undoEdits(document: CustomDocument, edits: readonly EditType[]): Thenable<void>;
1346+
undoEdits(document: CustomDocument<EditType>, edits: readonly EditType[]): Thenable<void>;
13531347

13541348
/**
13551349
* Revert the file to its last saved state.
@@ -1359,7 +1353,7 @@ declare module 'vscode' {
13591353
*
13601354
* @return Thenable signaling that the change has completed.
13611355
*/
1362-
revert(document: CustomDocument, edits: CustomDocumentRevert<EditType>): Thenable<void>;
1356+
revert(document: CustomDocument<EditType>, edits: CustomDocumentRevert<EditType>): Thenable<void>;
13631357

13641358
/**
13651359
* Back up the resource in its current state.
@@ -1380,22 +1374,25 @@ declare module 'vscode' {
13801374
* in an operation that takes time to complete, your extension may decide to finish the ongoing backup rather
13811375
* than cancelling it to ensure that VS Code has some valid backup.
13821376
*/
1383-
backup(document: CustomDocument, cancellation: CancellationToken): Thenable<void>;
1377+
backup(document: CustomDocument<EditType>, cancellation: CancellationToken): Thenable<void>;
13841378
}
13851379

13861380
/**
1387-
* Event triggered by extensions to signal to VS Code that an edit has occurred on a CustomDocument``.
1381+
* Event triggered by extensions to signal to VS Code that an edit has occurred on a `CustomDocument`.
1382+
*
1383+
* @param EditType Type of edits used for the document.
13881384
*/
13891385
interface CustomDocumentEditEvent<EditType = unknown> {
13901386
/**
13911387
* Document the edit is for.
13921388
*/
1393-
readonly document: CustomDocument;
1389+
readonly document: CustomDocument<EditType>;
13941390

13951391
/**
13961392
* Object that describes the edit.
13971393
*
1398-
* Edit objects are passed back to your extension in `undoEdits`, `applyEdits`, and `revert`.
1394+
* Edit objects are passed back to your extension in `CustomEditorEditingDelegate.undoEdits`,
1395+
* `CustomEditorEditingDelegate.applyEdits`, and `CustomEditorEditingDelegate.revert`.
13991396
*/
14001397
readonly edit: EditType;
14011398

@@ -1423,13 +1420,19 @@ declare module 'vscode' {
14231420
/**
14241421
* Represents a custom document used by a `CustomEditorProvider`.
14251422
*
1426-
* Custom documents are only used within a given `CustomEditorProvider`. The lifecycle of a
1427-
* `CustomDocument` is managed by VS Code. When no more references remain to a given `CustomDocument`,
1428-
* then it is disposed of.
1423+
* All custom documents must subclass `CustomDocument`. Custom documents are only used within a given
1424+
* `CustomEditorProvider`. The lifecycle of a `CustomDocument` is managed by VS Code. When no more references
1425+
* remain to a `CustomDocument`, it is disposed of.
14291426
*
1430-
* @param UserDataType Type of custom object that extensions can store on the document.
1427+
* @param EditType Type of edits used in this document.
14311428
*/
1432-
interface CustomDocument<UserDataType = unknown> {
1429+
class CustomDocument<EditType = unknown> {
1430+
/**
1431+
* @param viewType The associated uri for this document.
1432+
* @param uri The associated viewType for this document.
1433+
*/
1434+
constructor(viewType: string, uri: Uri);
1435+
14331436
/**
14341437
* The associated viewType for this document.
14351438
*/
@@ -1446,12 +1449,17 @@ declare module 'vscode' {
14461449
readonly onDidDispose: Event<void>;
14471450

14481451
/**
1449-
* Custom data that an extension can store on the document.
1452+
* List of edits from document open to the document's current state.
14501453
*/
1451-
userData?: UserDataType;
1454+
readonly appliedEdits: ReadonlyArray<EditType>;
14521455

1453-
// TODO: Should we expose edits here?
1454-
// This could be helpful for tracking the life cycle of edits
1456+
/**
1457+
* List of edits from document open to the document's last saved point.
1458+
*
1459+
* The save point will be behind `appliedEdits` if the user saves and then continues editing,
1460+
* or in front of the last entry in `appliedEdits` if the user saves and then hits undo.
1461+
*/
1462+
readonly savedEdits: ReadonlyArray<EditType>;
14551463
}
14561464

14571465
/**
@@ -1463,7 +1471,8 @@ declare module 'vscode' {
14631471
* You should use custom text based editors when dealing with binary files or more complex scenarios. For simple text
14641472
* based documents, use [`WebviewTextEditorProvider`](#WebviewTextEditorProvider) instead.
14651473
*/
1466-
export interface CustomEditorProvider {
1474+
export interface CustomEditorProvider<EditType = unknown> {
1475+
14671476
/**
14681477
* Resolve the model for a given resource.
14691478
*
@@ -1472,18 +1481,18 @@ declare module 'vscode' {
14721481
* If all editors for a given resource are closed, the `CustomDocument` is disposed of. Opening an editor at
14731482
* this point will trigger another call to `resolveCustomDocument`.
14741483
*
1475-
* @param document Document to resolve.
1484+
* @param uri Uri of the document to open.
14761485
* @param token A cancellation token that indicates the result is no longer needed.
14771486
*
1478-
* @return The capabilities of the resolved document.
1487+
* @return The custom document.
14791488
*/
1480-
resolveCustomDocument(document: CustomDocument, token: CancellationToken): Thenable<void>; // TODO: rename to open?
1489+
openCustomDocument(uri: Uri, token: CancellationToken): Thenable<CustomDocument<EditType>>;
14811490

14821491
/**
14831492
* Resolve a webview editor for a given resource.
14841493
*
1485-
* This is called when a user first opens a resource for a `CustomTextEditorProvider`, or if they reopen an
1486-
* existing editor using this `CustomTextEditorProvider`.
1494+
* This is called when a user first opens a resource for a `CustomEditorProvider`, or if they reopen an
1495+
* existing editor using this `CustomEditorProvider`.
14871496
*
14881497
* To resolve a webview editor, the provider must fill in its initial html content and hook up all
14891498
* the event listeners it is interested it. The provider can also hold onto the `WebviewPanel` to use later,
@@ -1495,14 +1504,14 @@ declare module 'vscode' {
14951504
*
14961505
* @return Thenable indicating that the webview editor has been resolved.
14971506
*/
1498-
resolveCustomEditor(document: CustomDocument, webviewPanel: WebviewPanel, token: CancellationToken): Thenable<void>;
1507+
resolveCustomEditor(document: CustomDocument<EditType>, webviewPanel: WebviewPanel, token: CancellationToken): Thenable<void>;
14991508

15001509
/**
15011510
* Defines the editing capability of a custom webview document.
15021511
*
15031512
* When not provided, the document is considered readonly.
15041513
*/
1505-
readonly editingDelegate?: CustomEditorEditingDelegate;
1514+
readonly editingDelegate?: CustomEditorEditingDelegate<EditType>;
15061515
}
15071516

15081517
/**
@@ -1516,6 +1525,7 @@ declare module 'vscode' {
15161525
* For binary files or more specialized use cases, see [CustomEditorProvider](#CustomEditorProvider).
15171526
*/
15181527
export interface CustomTextEditorProvider {
1528+
15191529
/**
15201530
* Resolve a webview editor for a given text resource.
15211531
*
@@ -1549,8 +1559,6 @@ declare module 'vscode' {
15491559
* @return Thenable indicating that the webview editor has been moved.
15501560
*/
15511561
moveCustomTextEditor?(newDocument: TextDocument, existingWebviewPanel: WebviewPanel, token: CancellationToken): Thenable<void>;
1552-
1553-
// TODO: handlesMove?: boolean;
15541562
}
15551563

15561564
namespace window {
@@ -1560,14 +1568,16 @@ declare module 'vscode' {
15601568
* @param viewType Type of the webview editor provider. This should match the `viewType` from the
15611569
* `package.json` contributions.
15621570
* @param provider Provider that resolves editors.
1563-
* @param webviewOptions Content settings for the webview panels that the provider is given.
1571+
* @param options Options for the provider
15641572
*
15651573
* @return Disposable that unregisters the provider.
15661574
*/
15671575
export function registerCustomEditorProvider(
15681576
viewType: string,
15691577
provider: CustomEditorProvider | CustomTextEditorProvider,
1570-
webviewOptions?: WebviewPanelOptions, // TODO: move this onto provider?
1578+
options?: {
1579+
readonly webviewOptions?: WebviewPanelOptions;
1580+
}
15711581
): Disposable;
15721582
}
15731583

src/vs/workbench/api/browser/mainThreadWebview.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -663,13 +663,21 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
663663
}
664664

665665
const undoneEdit = this._edits[this._currentEditIndex];
666-
await this._proxy.$undo(this._realResource, this.viewType, undoneEdit);
666+
await this._proxy.$undo(this._realResource, this.viewType, undoneEdit, this.getEditState());
667667

668668
this.change(() => {
669669
--this._currentEditIndex;
670670
});
671671
}
672672

673+
private getEditState(): extHostProtocol.CustomDocumentEditState {
674+
return {
675+
allEdits: this._edits,
676+
currentIndex: this._currentEditIndex,
677+
saveIndex: this._savePoint,
678+
};
679+
}
680+
673681
private async redo(): Promise<void> {
674682
if (!this._editable) {
675683
return;
@@ -681,7 +689,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
681689
}
682690

683691
const redoneEdit = this._edits[this._currentEditIndex + 1];
684-
await this._proxy.$redo(this._realResource, this.viewType, redoneEdit);
692+
await this._proxy.$redo(this._realResource, this.viewType, redoneEdit, this.getEditState());
685693
this.change(() => {
686694
++this._currentEditIndex;
687695
});
@@ -728,7 +736,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
728736
editsToRedo = this._edits.slice(this._currentEditIndex, this._savePoint);
729737
}
730738

731-
this._proxy.$revert(this._realResource, this.viewType, { undoneEdits: editsToUndo, redoneEdits: editsToRedo });
739+
this._proxy.$revert(this._realResource, this.viewType, { undoneEdits: editsToUndo, redoneEdits: editsToRedo }, this.getEditState());
732740
this.change(() => {
733741
this._currentEditIndex = this._savePoint;
734742
this.spliceEdits();

src/vs/workbench/api/common/extHost.api.impl.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -583,9 +583,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
583583
registerWebviewPanelSerializer: (viewType: string, serializer: vscode.WebviewPanelSerializer) => {
584584
return extHostWebviews.registerWebviewPanelSerializer(extension, viewType, serializer);
585585
},
586-
registerCustomEditorProvider: (viewType: string, provider: vscode.CustomEditorProvider | vscode.CustomTextEditorProvider, options?: vscode.WebviewPanelOptions) => {
586+
registerCustomEditorProvider: (viewType: string, provider: vscode.CustomEditorProvider | vscode.CustomTextEditorProvider, options?: { webviewOptions?: vscode.WebviewPanelOptions }) => {
587587
checkProposedApiEnabled(extension);
588-
return extHostWebviews.registerCustomEditorProvider(extension, viewType, provider, options);
588+
return extHostWebviews.registerCustomEditorProvider(extension, viewType, provider, options?.webviewOptions);
589589
},
590590
registerDecorationProvider(provider: vscode.DecorationProvider) {
591591
checkProposedApiEnabled(extension);
@@ -1030,7 +1030,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
10301030
ColorThemeKind: extHostTypes.ColorThemeKind,
10311031
TimelineItem: extHostTypes.TimelineItem,
10321032
CellKind: extHostTypes.CellKind,
1033-
CellOutputKind: extHostTypes.CellOutputKind
1033+
CellOutputKind: extHostTypes.CellOutputKind,
1034+
CustomDocument: extHostTypes.CustomDocument,
10341035
};
10351036
};
10361037
}

src/vs/workbench/api/common/extHost.protocol.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,12 @@ export interface WebviewPanelViewStateData {
622622
};
623623
}
624624

625+
export interface CustomDocumentEditState {
626+
readonly allEdits: readonly number[];
627+
readonly currentIndex: number;
628+
readonly saveIndex: number;
629+
}
630+
625631
export interface ExtHostWebviewsShape {
626632
$onMessage(handle: WebviewPanelHandle, message: any): void;
627633
$onMissingCsp(handle: WebviewPanelHandle, extensionId: string): void;
@@ -634,9 +640,9 @@ export interface ExtHostWebviewsShape {
634640
$createWebviewCustomEditorDocument(resource: UriComponents, viewType: string, cancellation: CancellationToken): Promise<{ editable: boolean }>;
635641
$disposeWebviewCustomEditorDocument(resource: UriComponents, viewType: string): Promise<void>;
636642

637-
$undo(resource: UriComponents, viewType: string, editId: number): Promise<void>;
638-
$redo(resource: UriComponents, viewType: string, editId: number): Promise<void>;
639-
$revert(resource: UriComponents, viewType: string, changes: { undoneEdits: number[], redoneEdits: number[] }): Promise<void>;
643+
$undo(resource: UriComponents, viewType: string, editId: number, state: CustomDocumentEditState): Promise<void>;
644+
$redo(resource: UriComponents, viewType: string, editId: number, state: CustomDocumentEditState): Promise<void>;
645+
$revert(resource: UriComponents, viewType: string, changes: { undoneEdits: number[], redoneEdits: number[] }, state: CustomDocumentEditState): Promise<void>;
640646
$disposeEdits(resourceComponents: UriComponents, viewType: string, editIds: number[]): void;
641647

642648
$onSave(resource: UriComponents, viewType: string, cancellation: CancellationToken): Promise<void>;

0 commit comments

Comments
 (0)