diff --git a/extensions/vscode-notebook-tests/src/notebook.test.ts b/extensions/vscode-notebook-tests/src/notebook.test.ts index 106e05af0ed..8dba54b5396 100644 --- a/extensions/vscode-notebook-tests/src/notebook.test.ts +++ b/extensions/vscode-notebook-tests/src/notebook.test.ts @@ -319,6 +319,70 @@ suite('notebook working copy', () => { await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); }); + + test('multiple tabs: dirty + clean', async function () { + const resource = vscode.Uri.parse(join(vscode.workspace.rootPath || '', './first.vsctestnb')); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + + await waitFor(500); + await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); + assert.equal(vscode.notebook.activeNotebookEditor!.selection?.source, ''); + + await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove'); + await vscode.commands.executeCommand('default:type', { text: 'var abc = 0;' }); + + const secondResource = vscode.Uri.parse(join(vscode.workspace.rootPath || '', './second.vsctestnb')); + await vscode.commands.executeCommand('vscode.openWith', secondResource, 'notebookCoreTest'); + await waitFor(500); + await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); + + // make sure that the previous dirty editor is still restored in the extension host and no data loss + assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true); + assert.equal(vscode.notebook.activeNotebookEditor?.selection !== undefined, true); + assert.deepEqual(vscode.notebook.activeNotebookEditor?.document.cells[1], vscode.notebook.activeNotebookEditor?.selection); + assert.deepEqual(vscode.notebook.activeNotebookEditor?.document.cells.length, 3); + assert.equal(vscode.notebook.activeNotebookEditor?.selection?.source, 'var abc = 0;'); + + await vscode.commands.executeCommand('workbench.action.files.save'); + await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); + }); + + test('multiple tabs: two dirty tabs and switching', async function () { + const resource = vscode.Uri.parse(join(vscode.workspace.rootPath || '', './first.vsctestnb')); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + + await waitFor(500); + await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); + assert.equal(vscode.notebook.activeNotebookEditor!.selection?.source, ''); + + await vscode.commands.executeCommand('notebook.cell.insertCodeCellAbove'); + await vscode.commands.executeCommand('default:type', { text: 'var abc = 0;' }); + + const secondResource = vscode.Uri.parse(join(vscode.workspace.rootPath || '', './second.vsctestnb')); + await vscode.commands.executeCommand('vscode.openWith', secondResource, 'notebookCoreTest'); + await waitFor(500); + await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); + assert.equal(vscode.notebook.activeNotebookEditor!.selection?.source, ''); + + // switch to the first editor + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true); + assert.equal(vscode.notebook.activeNotebookEditor?.selection !== undefined, true); + assert.deepEqual(vscode.notebook.activeNotebookEditor?.document.cells[1], vscode.notebook.activeNotebookEditor?.selection); + assert.deepEqual(vscode.notebook.activeNotebookEditor?.document.cells.length, 3); + assert.equal(vscode.notebook.activeNotebookEditor?.selection?.source, 'var abc = 0;'); + + // switch to the second editor + await vscode.commands.executeCommand('vscode.openWith', secondResource, 'notebookCoreTest'); + assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true); + assert.equal(vscode.notebook.activeNotebookEditor?.selection !== undefined, true); + assert.deepEqual(vscode.notebook.activeNotebookEditor?.document.cells[1], vscode.notebook.activeNotebookEditor?.selection); + assert.deepEqual(vscode.notebook.activeNotebookEditor?.document.cells.length, 2); + assert.equal(vscode.notebook.activeNotebookEditor?.selection?.source, ''); + + await vscode.commands.executeCommand('workbench.action.files.saveAll'); + await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + }); }); suite('metadata', () => { @@ -344,6 +408,25 @@ suite('regression', () => { assert.equal(vscode.notebook.activeNotebookEditor!.selection?.source, ''); assert.equal(vscode.notebook.activeNotebookEditor!.selection?.language, 'typescript'); }); + + test('#97830, #97764. Support switch to other editor types', async function () { + const resource = vscode.Uri.parse(join(vscode.workspace.rootPath || '', './empty.vsctestnb')); + await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); + + await waitFor(500); + await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); + await vscode.commands.executeCommand('default:type', { text: 'var abc = 0;' }); + + assert.equal(vscode.notebook.activeNotebookEditor !== undefined, true, 'notebook first'); + assert.equal(vscode.notebook.activeNotebookEditor!.selection?.source, 'var abc = 0;'); + assert.equal(vscode.notebook.activeNotebookEditor!.selection?.language, 'typescript'); + + await vscode.commands.executeCommand('vscode.openWith', resource, 'default'); + assert.equal(vscode.window.activeTextEditor?.document.uri.path, resource.path); + + await vscode.commands.executeCommand('workbench.action.files.saveAll'); + await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + }); }); suite('webview resource uri', () => { diff --git a/extensions/vscode-notebook-tests/test/second.vsctestnb b/extensions/vscode-notebook-tests/test/second.vsctestnb new file mode 100644 index 00000000000..a4a092d8349 --- /dev/null +++ b/extensions/vscode-notebook-tests/test/second.vsctestnb @@ -0,0 +1,4 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 4a6a8bad9ed..e65d517c152 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -92,6 +92,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor protected readonly _contributions: { [key: string]: INotebookEditorContribution; }; private scrollBeyondLastLine: boolean; private readonly memento: Memento; + private _isDisposed: boolean = false; constructor( @@ -747,6 +748,10 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor let r: () => void; DOM.scheduleAtNextAnimationFrame(() => { + if (this._isDisposed) { + return; + } + relayout(cell, height); r(); }); @@ -1226,6 +1231,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor //#endregion dispose() { + this._isDisposed = true; const keys = Object.keys(this._contributions); for (let i = 0, len = keys.length; i < len; i++) { const contributionId = keys[i];