From 9c77f4d86aa5aeb9c753edd2e9b99222abfce5f4 Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 18 Feb 2021 11:50:25 -0800 Subject: [PATCH] add NotebookDocument.save(). --- .../notebook.document.test.ts | 33 +++++++++++++++++++ src/vs/vscode.proposed.d.ts | 9 +++++ .../api/browser/mainThreadNotebook.ts | 9 +++++ .../workbench/api/common/extHost.protocol.ts | 1 + .../workbench/api/common/extHostNotebook.ts | 1 + .../api/common/extHostNotebookDocument.ts | 13 ++++++-- 6 files changed, 64 insertions(+), 2 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.document.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.document.test.ts index e0a8912e0f6..1c19fd0d753 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.document.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.document.test.ts @@ -293,4 +293,37 @@ suite('Notebook Document', function () { assert.deepStrictEqual(data.cells[0].outputs, [thirdOutput]); } }); + + test('document save API', async function () { + const uri = await utils.createRandomFile(undefined, undefined, '.nbdtest'); + const notebook = await vscode.notebook.openNotebookDocument(uri); + + assert.strictEqual(notebook.uri.toString(), uri.toString()); + assert.strictEqual(notebook.isDirty, false); + assert.strictEqual(notebook.isUntitled, false); + assert.strictEqual(notebook.cells.length, 1); + assert.strictEqual(notebook.viewType, 'notebook.nbdtest'); + + const edit = new vscode.WorkspaceEdit(); + edit.replaceNotebookCells(notebook.uri, 0, 0, [{ + cellKind: vscode.NotebookCellKind.Markdown, + language: 'markdown', + metadata: undefined, + outputs: [], + source: 'new_markdown' + }, { + cellKind: vscode.NotebookCellKind.Code, + language: 'fooLang', + metadata: undefined, + outputs: [], + source: 'new_code' + }]); + + const success = await vscode.workspace.applyEdit(edit); + assert.strictEqual(success, true); + assert.strictEqual(notebook.isDirty, true); + + await notebook.save(); + assert.strictEqual(notebook.isDirty, false); + }); }); diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index a0301e5b0fa..b2139b9130f 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1281,6 +1281,15 @@ declare module 'vscode' { readonly cells: ReadonlyArray; readonly contentOptions: NotebookDocumentContentOptions; readonly metadata: NotebookDocumentMetadata; + + /** + * Save the document. The saving will be handled by the corresponding content provider + * + * @return A promise that will resolve to true when the document + * has been saved. If the file was not dirty or the save failed, + * will return false. + */ + save(): Thenable; } // todo@API maybe have a NotebookCellPosition sibling diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index 566299adf3b..1e8aa1b9a5b 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -669,6 +669,15 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo return uri; } + async $trySaveDocument(uriComponents: UriComponents) { + const uri = URI.revive(uriComponents); + + const ref = await this._notebookModelResolverService.resolve(uri); + const saveResult = await ref.object.save(); + ref.dispose(); + return saveResult; + } + async $tryShowNotebookDocument(resource: UriComponents, viewType: string, options: INotebookDocumentShowOptions): Promise { const editorOptions: ITextEditorOptions = { preserveFocus: options.preserveFocus, diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 5baca36a48b..142b870424c 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -790,6 +790,7 @@ export interface MainThreadNotebookShape extends IDisposable { $registerNotebookKernelProvider(extension: NotebookExtensionDescription, handle: number, documentFilter: INotebookDocumentFilter): Promise; $unregisterNotebookKernelProvider(handle: number): Promise; $onNotebookKernelChange(handle: number, uri: UriComponents | undefined): void; + $trySaveDocument(uri: UriComponents): Promise; $tryApplyEdits(viewType: string, resource: UriComponents, modelVersionId: number, edits: ICellEditOperation[]): Promise; $postMessage(editorId: string, forRendererId: string | undefined, value: any): Promise; $setStatusBarEntry(id: number, statusBarEntry: INotebookCellStatusBarEntryDto): Promise; diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 02dc80101d8..fbad27732be 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -715,6 +715,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN const that = this; const document = new ExtHostNotebookDocument( + this._proxy, this._documentsAndEditors, { emitModelChange(event: vscode.NotebookCellsChangeEvent): void { diff --git a/src/vs/workbench/api/common/extHostNotebookDocument.ts b/src/vs/workbench/api/common/extHostNotebookDocument.ts index 4bd68b65686..3c3c7913b7f 100644 --- a/src/vs/workbench/api/common/extHostNotebookDocument.ts +++ b/src/vs/workbench/api/common/extHostNotebookDocument.ts @@ -9,7 +9,7 @@ import { Disposable, DisposableStore, dispose } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; -import { CellKind, INotebookDocumentPropertiesChangeData } from 'vs/workbench/api/common/extHost.protocol'; +import { CellKind, INotebookDocumentPropertiesChangeData, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostDocumentsAndEditors, IExtHostModelAddedData } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import * as extHostTypeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import * as extHostTypes from 'vs/workbench/api/common/extHostTypes'; @@ -146,6 +146,7 @@ export class ExtHostNotebookDocument extends Disposable { private _disposed = false; constructor( + private readonly _proxy: MainThreadNotebookShape, private readonly _documentsAndEditors: ExtHostDocumentsAndEditors, private readonly _emitter: INotebookEventEmitter, private readonly _viewType: string, @@ -177,7 +178,8 @@ export class ExtHostNotebookDocument extends Disposable { get cells(): ReadonlyArray { return that._cells.map(cell => cell.cell); }, get metadata() { return that._metadata; }, set metadata(_value: Required) { throw new Error('Use WorkspaceEdit to update metadata.'); }, - get contentOptions() { return that._contentOptions; } + get contentOptions() { return that._contentOptions; }, + save() { return that._save(); } }); } return this._notebook; @@ -232,6 +234,13 @@ export class ExtHostNotebookDocument extends Disposable { }); } + private async _save(): Promise { + if (this._disposed) { + return Promise.reject(new Error('Document has been closed')); + } + return this._proxy.$trySaveDocument(this.uri); + } + private _spliceNotebookCells(splices: NotebookCellsSplice2[], initialization: boolean): void { if (this._disposed) { return;