diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 53b44bb5e90..4b5e252d8ef 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1239,9 +1239,9 @@ declare module 'vscode' { } /** - * Event triggered by extensions to signal to VS Code that an edit has occurred on an [`EditableCustomDocument`](#EditableCustomDocument). + * Event triggered by extensions to signal to VS Code that an edit has occurred on an [`CustomEditableDocument`](#CustomEditableDocument). * - * @see [`EditableCustomDocument.onDidChange`](#EditableCustomDocument.onDidChange). + * @see [`CustomEditableDocument.onDidChange`](#CustomEditableDocument.onDidChange). */ interface CustomDocumentEditEvent { /** @@ -1267,17 +1267,17 @@ declare module 'vscode' { } /** - * Event triggered by extensions to signal to VS Code that the content of a [`EditableCustomDocument`](#EditableCustomDocument) + * Event triggered by extensions to signal to VS Code that the content of a [`CustomEditableDocument`](#CustomEditableDocument) * has changed. * - * @see [`EditableCustomDocument.onDidChange`](#EditableCustomDocument.onDidChange). + * @see [`CustomEditableDocument.onDidChange`](#CustomEditableDocument.onDidChange). */ interface CustomDocumentContentChangeEvent { // marker interface } /** - * A backup for an [`EditableCustomDocument`](#EditableCustomDocument). + * A backup for an [`CustomEditableDocument`](#CustomEditableDocument). */ interface CustomDocumentBackup { /** @@ -1288,21 +1288,33 @@ declare module 'vscode' { readonly backupId: string; /** - * Dispose of the current backup. + * Delete the current backup. * - * This is called by VS Code when it is clear the current backup, such as when a new backup is made or when the - * file is saved. + * This is called by VS Code when it is clear the current backup is no longer needed, such as when a new backup + * is made or when the file is saved. */ - dispose(): void; + delete(): void; + } + + /** + * Additional information about used to implement [`CustomEditableDocument.backup`](#CustomEditableDocument.backup). + */ + interface CustomDocumentBackupContext { + /** + * Uri of a workspace specific directory in which the extension can store backup data. + * + * The directory might not exist on disk and creation is up to the extension. + */ + readonly workspaceStorageUri: Uri | undefined; } /** * Represents an editable custom document used by a [`CustomEditorProvider`](#CustomEditorProvider). * - * `EditableCustomDocument` is how custom editors hook into standard VS Code operations such as save and undo. The + * `CustomEditableDocument` is how custom editors hook into standard VS Code operations such as save and undo. The * document is also how custom editors notify VS Code that an edit has taken place. */ - interface EditableCustomDocument extends CustomDocument { + interface CustomEditableDocument extends CustomDocument { /** * Signal that an edit has occurred inside a custom editor. @@ -1389,18 +1401,19 @@ declare module 'vscode' { * made in quick succession, `backup` is only triggered after the last one. `backup` is not invoked when * `auto save` is enabled (since auto save already persists resource ). * + * @param context Information that can be used to backup the document. * @param cancellation Token that signals the current backup since a new backup is coming in. It is up to your * extension to decided how to respond to cancellation. If for example your extension is backing up a large file * in an operation that takes time to complete, your extension may decide to finish the ongoing backup rather * than cancelling it to ensure that VS Code has some valid backup. */ - backup(cancellation: CancellationToken): Thenable; + backup(context: CustomDocumentBackupContext, cancellation: CancellationToken): Thenable; } /** * Additional information about the opening custom document. */ - interface OpenCustomDocumentContext { + interface CustomDocumentOpenContext { /** * The id of the backup to restore the document from or `undefined` if there is no backup. * @@ -1437,7 +1450,7 @@ declare module 'vscode' { * * @return The custom document. */ - openCustomDocument(uri: Uri, openContext: OpenCustomDocumentContext, token: CancellationToken): Thenable | T; + openCustomDocument(uri: Uri, openContext: CustomDocumentOpenContext, token: CancellationToken): Thenable | T; /** * Resolve a custom editor for a given resource. diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 4b48dc255dd..33b7ade6508 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -74,6 +74,7 @@ import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostAp import { ExtHostAuthentication } from 'vs/workbench/api/common/extHostAuthentication'; import { ExtHostTimeline } from 'vs/workbench/api/common/extHostTimeline'; import { ExtHostNotebookConcatDocument } from 'vs/workbench/api/common/extHostNotebookConcatDocument'; +import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths'; export interface IExtensionApiFactory { (extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode; @@ -92,6 +93,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const uriTransformer = accessor.get(IURITransformerService); const rpcProtocol = accessor.get(IExtHostRpcService); const extHostStorage = accessor.get(IExtHostStorage); + const extensionStoragePaths = accessor.get(IExtensionStoragePaths); const extHostLogService = accessor.get(ILogService); const extHostTunnelService = accessor.get(IExtHostTunnelService); const extHostApiDeprecation = accessor.get(IExtHostApiDeprecationService); @@ -136,7 +138,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostTheming = rpcProtocol.set(ExtHostContext.ExtHostTheming, new ExtHostTheming(rpcProtocol)); const extHostAuthentication = rpcProtocol.set(ExtHostContext.ExtHostAuthentication, new ExtHostAuthentication(rpcProtocol)); const extHostTimeline = rpcProtocol.set(ExtHostContext.ExtHostTimeline, new ExtHostTimeline(rpcProtocol, extHostCommands)); - const extHostWebviews = rpcProtocol.set(ExtHostContext.ExtHostWebviews, new ExtHostWebviews(rpcProtocol, initData.environment, extHostWorkspace, extHostLogService, extHostApiDeprecation, extHostDocuments)); + const extHostWebviews = rpcProtocol.set(ExtHostContext.ExtHostWebviews, new ExtHostWebviews(rpcProtocol, initData.environment, extHostWorkspace, extHostLogService, extHostApiDeprecation, extHostDocuments, extensionStoragePaths)); // Check that no named customers are missing const expected: ProxyIdentifier[] = values(ExtHostContext); diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts index e16d68783fc..5fe0c539953 100644 --- a/src/vs/workbench/api/common/extHostWebview.ts +++ b/src/vs/workbench/api/common/extHostWebview.ts @@ -21,6 +21,7 @@ import type * as vscode from 'vscode'; import { Cache } from './cache'; import * as extHostProtocol from './extHost.protocol'; import * as extHostTypes from './extHostTypes'; +import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths'; type IconPath = URI | { light: URI, dark: URI }; @@ -267,6 +268,7 @@ class CustomDocumentStoreEntry { constructor( public readonly document: vscode.CustomDocument, + public readonly backupContext: vscode.CustomDocumentBackupContext, ) { } private readonly _edits = new Cache('custom documents'); @@ -299,12 +301,12 @@ class CustomDocumentStoreEntry { } updateBackup(backup: vscode.CustomDocumentBackup): void { - this._backup?.dispose(); + this._backup?.delete(); this._backup = backup; } disposeBackup(): void { - this._backup?.dispose(); + this._backup?.delete(); this._backup = undefined; } @@ -324,12 +326,12 @@ class CustomDocumentStore { return this._documents.get(this.key(viewType, resource)); } - public add(viewType: string, document: vscode.CustomDocument): CustomDocumentStoreEntry { + public add(viewType: string, document: vscode.CustomDocument, backupContext: vscode.CustomDocumentBackupContext): CustomDocumentStoreEntry { const key = this.key(viewType, document.uri); if (this._documents.has(key)) { throw new Error(`Document already exists for viewType:${viewType} resource:${document.uri}`); } - const entry = new CustomDocumentStoreEntry(document); + const entry = new CustomDocumentStoreEntry(document, backupContext); this._documents.set(key, entry); return entry; } @@ -409,6 +411,7 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { private readonly _logService: ILogService, private readonly _deprecationService: IExtHostApiDeprecationService, private readonly _extHostDocuments: ExtHostDocuments, + private readonly _extensionStoragePaths?: IExtensionStoragePaths, ) { this._proxy = mainContext.getProxy(extHostProtocol.MainContext.MainThreadWebviews); } @@ -567,7 +570,10 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { const revivedResource = URI.revive(resource); const document = await entry.provider.openCustomDocument(revivedResource, { backupId }, cancellation); - const documentEntry = this._documents.add(viewType, document); + const workspaceStoragePath = this._extensionStoragePaths?.workspaceValue(entry.extension); + const documentEntry = this._documents.add(viewType, document, { + workspaceStorageUri: workspaceStoragePath ? URI.file(workspaceStoragePath) : undefined, + }); if (this.isEditable(document)) { document.onDidChange(e => { @@ -674,27 +680,27 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { async $revert(resourceComponents: UriComponents, viewType: string, cancellation: CancellationToken): Promise { const entry = this.getCustomDocumentEntry(viewType, resourceComponents); - const document = this.getEditableCustomDocument(viewType, resourceComponents); + const document = this.getCustomEditableDocument(viewType, resourceComponents); await document.revert(cancellation); entry.disposeBackup(); } async $onSave(resourceComponents: UriComponents, viewType: string, cancellation: CancellationToken): Promise { const entry = this.getCustomDocumentEntry(viewType, resourceComponents); - const document = this.getEditableCustomDocument(viewType, resourceComponents); + const document = this.getCustomEditableDocument(viewType, resourceComponents); await document.save(cancellation); entry.disposeBackup(); } async $onSaveAs(resourceComponents: UriComponents, viewType: string, targetResource: UriComponents, cancellation: CancellationToken): Promise { - const document = this.getEditableCustomDocument(viewType, resourceComponents); + const document = this.getCustomEditableDocument(viewType, resourceComponents); return document.saveAs(URI.revive(targetResource), cancellation); } async $backup(resourceComponents: UriComponents, viewType: string, cancellation: CancellationToken): Promise { const entry = this.getCustomDocumentEntry(viewType, resourceComponents); - const document = this.getEditableCustomDocument(viewType, resourceComponents); - const backup = await document.backup(cancellation); + const document = this.getCustomEditableDocument(viewType, resourceComponents); + const backup = await document.backup(entry.backupContext, cancellation); entry.updateBackup(backup); return backup.backupId; } @@ -711,11 +717,11 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { return entry; } - private isEditable(document: vscode.CustomDocument): document is vscode.EditableCustomDocument { - return !!(document as vscode.EditableCustomDocument).onDidChange; + private isEditable(document: vscode.CustomDocument): document is vscode.CustomEditableDocument { + return !!(document as vscode.CustomEditableDocument).onDidChange; } - private getEditableCustomDocument(viewType: string, resource: UriComponents): vscode.EditableCustomDocument { + private getCustomEditableDocument(viewType: string, resource: UriComponents): vscode.CustomEditableDocument { const { document } = this.getCustomDocumentEntry(viewType, resource); if (!this.isEditable(document)) { throw new Error('Custom document is not editable');