From 196bf678a152024ff55edeb493733dd929adfdfd Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 15 Feb 2021 11:44:51 +0100 Subject: [PATCH] reset dirty state when reverting a notebook, update extension host when dirty state of a notebook (working copy) changes --- .../notebook.document.test.ts | 27 +++++++++---------- extensions/vscode-api-tests/src/utils.ts | 4 +++ .../api/browser/mainThreadNotebook.ts | 21 ++++++++++++++- .../workbench/api/common/extHost.protocol.ts | 1 + .../workbench/api/common/extHostNotebook.ts | 9 ++++++- .../notebook/common/notebookEditorModel.ts | 1 + 6 files changed, 47 insertions(+), 16 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 cf9bd87bd24..4e126b0d475 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 @@ -5,7 +5,7 @@ import * as assert from 'assert'; import * as vscode from 'vscode'; -import { createRandomFile, disposeAll, asPromise, closeAllEditors, assertNoRpc } from '../utils'; +import * as utils from '../utils'; suite('Notebook Document', function () { @@ -33,14 +33,13 @@ suite('Notebook Document', function () { const disposables: vscode.Disposable[] = []; suiteTeardown(async function () { - assertNoRpc(); - await vscode.commands.executeCommand('workbench.action.files.saveAll'); - await closeAllEditors(); - disposeAll(disposables); + await utils.revertAllDirty(); + await utils.closeAllEditors(); + utils.disposeAll(disposables); disposables.length = 0; for (let doc of vscode.notebook.notebookDocuments) { - assert.strictEqual(doc.isDirty, false) + assert.strictEqual(doc.isDirty, false, doc.uri.toString()); } }); @@ -64,7 +63,7 @@ suite('Notebook Document', function () { }); test('document basics', async function () { - const uri = await createRandomFile(undefined, undefined, '.nbdtest'); + const uri = await utils.createRandomFile(undefined, undefined, '.nbdtest'); const notebook = await vscode.notebook.openNotebookDocument(uri); assert.strictEqual(notebook.uri.toString(), uri.toString()); @@ -76,9 +75,9 @@ suite('Notebook Document', function () { }); test('notebook open/close, notebook ready when cell-document open event is fired', async function () { - const uri = await createRandomFile(undefined, undefined, '.nbdtest'); + const uri = await utils.createRandomFile(undefined, undefined, '.nbdtest'); let didHappen = false; - const p = asPromise(vscode.workspace.onDidOpenTextDocument).then(doc => { + const p = utils.asPromise(vscode.workspace.onDidOpenTextDocument).then(doc => { if (doc.uri.scheme !== 'vscode-notebook-cell') { return; } @@ -96,9 +95,9 @@ suite('Notebook Document', function () { }); test('notebook open/close, all cell-documents are ready', async function () { - const uri = await createRandomFile(undefined, undefined, '.nbdtest'); + const uri = await utils.createRandomFile(undefined, undefined, '.nbdtest'); - const p = asPromise(vscode.notebook.onDidOpenNotebookDocument).then(notebook => { + const p = utils.asPromise(vscode.notebook.onDidOpenNotebookDocument).then(notebook => { for (let cell of notebook.cells) { const doc = vscode.workspace.textDocuments.find(doc => doc.uri.toString() === cell.uri.toString()); assert.ok(doc); @@ -116,7 +115,7 @@ suite('Notebook Document', function () { test('workspace edit API (replaceCells)', async function () { - const uri = await createRandomFile(undefined, undefined, '.nbdtest'); + const uri = await utils.createRandomFile(undefined, undefined, '.nbdtest'); const document = await vscode.notebook.openNotebookDocument(uri); assert.strictEqual(document.cells.length, 1); @@ -192,7 +191,7 @@ suite('Notebook Document', function () { }); test('workspace edit API (replaceCells, event)', async function () { - const uri = await createRandomFile(undefined, undefined, '.nbdtest'); + const uri = await utils.createRandomFile(undefined, undefined, '.nbdtest'); const document = await vscode.notebook.openNotebookDocument(uri); assert.strictEqual(document.cells.length, 1); @@ -211,7 +210,7 @@ suite('Notebook Document', function () { source: 'new_code' }]); - const event = asPromise(vscode.notebook.onDidChangeNotebookCells); + const event = utils.asPromise(vscode.notebook.onDidChangeNotebookCells); const success = await vscode.workspace.applyEdit(edit); assert.strictEqual(success, true); diff --git a/extensions/vscode-api-tests/src/utils.ts b/extensions/vscode-api-tests/src/utils.ts index a102e7d2329..4bddd45a5e3 100644 --- a/extensions/vscode-api-tests/src/utils.ts +++ b/extensions/vscode-api-tests/src/utils.ts @@ -48,6 +48,10 @@ export function closeAllEditors(): Thenable { return vscode.commands.executeCommand('workbench.action.closeAllEditors'); } +export function saveAllEditors(): Thenable { + return vscode.commands.executeCommand('workbench.action.files.saveAll'); +} + export async function revertAllDirty(): Promise { return vscode.commands.executeCommand('_workbench.revertAllDirty'); } diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index 871641799f6..5866a81d96a 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -10,7 +10,8 @@ import { Emitter } from 'vs/base/common/event'; import { IRelativePattern } from 'vs/base/common/glob'; import { combinedDisposable, Disposable, DisposableStore, dispose, IDisposable, IReference } from 'vs/base/common/lifecycle'; import { ResourceMap } from 'vs/base/common/map'; -import { IExtUri } from 'vs/base/common/resources'; +import { Schemas } from 'vs/base/common/network'; +import { IExtUri, isEqual } from 'vs/base/common/resources'; import { URI, UriComponents } from 'vs/base/common/uri'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -30,6 +31,7 @@ import { IEditorGroup, IEditorGroupsService, preferredSideBySideGroupDirection } import { openEditorWith } from 'vs/workbench/services/editor/common/editorOpenWith'; import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity'; +import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { ExtHostContext, ExtHostNotebookShape, IExtHostContext, INotebookCellStatusBarEntryDto, INotebookDocumentsAndEditorsDelta, INotebookDocumentShowOptions, INotebookModelAddedData, MainContext, MainThreadNotebookShape, NotebookEditorRevealType, NotebookExtensionDescription } from '../common/extHost.protocol'; class DocumentAndEditorState { @@ -121,6 +123,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo constructor( extHostContext: IExtHostContext, + @IWorkingCopyService private readonly _workingCopyService: IWorkingCopyService, @INotebookService private _notebookService: INotebookService, @IConfigurationService private readonly _configurationService: IConfigurationService, @IEditorService private readonly _editorService: IEditorService, @@ -202,6 +205,22 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo } registerListeners() { + + // forward changes to dirty state + // todo@bpasero this seem way too complicated... is there an easy way to + // the actual resource from a working copy? + this._register(this._workingCopyService.onDidChangeDirty(e => { + if (e.resource.scheme !== Schemas.vscodeNotebook) { + return; + } + for (const notebook of this._notebookService.getNotebookTextModels()) { + if (isEqual(notebook.uri.with({ scheme: Schemas.vscodeNotebook }), e.resource)) { + this._proxy.$acceptDirtyStateChanged(notebook.uri, e.isDirty()); + break; + } + } + })); + this._notebookService.listNotebookEditors().forEach((e) => { this._addNotebookEditor(e); }); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index a55a0c041a1..40b4891d0a6 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1803,6 +1803,7 @@ export interface ExtHostNotebookShape { $acceptNotebookActiveKernelChange(event: { uri: UriComponents, providerHandle: number | undefined, kernelFriendlyId: string | undefined }): void; $onDidReceiveMessage(editorId: string, rendererId: string | undefined, message: unknown): void; $acceptModelChanged(uriComponents: UriComponents, event: NotebookCellsChangedEventDto, isDirty: boolean): void; + $acceptDirtyStateChanged(uriComponents: UriComponents, isDirty: boolean): void; $acceptModelSaved(uriComponents: UriComponents): void; $acceptEditorPropertiesChanged(id: string, data: INotebookEditorPropertiesChangeData): void; $acceptDocumentPropertiesChanged(uriComponents: UriComponents, data: INotebookDocumentPropertiesChangeData): void; diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 40ff93d7dd5..2e76b5fe7c9 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -578,7 +578,14 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN } } - public $acceptModelSaved(uriComponents: UriComponents): void { + $acceptDirtyStateChanged(resource: UriComponents, isDirty: boolean): void { + const document = this._documents.get(URI.revive(resource)); + if (document) { + document.acceptModelChanged({ rawEvents: [], versionId: document.notebookDocument.version }, isDirty); + } + } + + $acceptModelSaved(uriComponents: UriComponents): void { const document = this._documents.get(URI.revive(uriComponents)); if (document) { // this.$acceptDirtyStateChanged(uriComponents, false); diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts index 1ad9b60a8b6..583dad5a8cf 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts @@ -125,6 +125,7 @@ export class NotebookEditorModel extends EditorModel implements INotebookEditorM async revert(options?: IRevertOptions | undefined): Promise { if (options?.soft) { await this._backupFileService.discardBackup(this.resource); + this.setDirty(false); return; }