Skip to content

Commit 176f9c3

Browse files
authored
Update NotebookModel with changes from VS Code Notebook UI (#12048)
For #10496 Update our model when user edits VS Code cells
1 parent 6abd30e commit 176f9c3

33 files changed

+1309
-496
lines changed

.vscode/launch.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,14 +195,16 @@
195195
"request": "launch",
196196
"runtimeExecutable": "${execPath}",
197197
"args": [
198-
"${workspaceFolder}/src/test",
198+
"${workspaceFolder}/src/test/datascience",
199199
"--disable-extensions",
200200
"--enable-proposed-api",
201201
"--extensionDevelopmentPath=${workspaceFolder}",
202202
"--extensionTestsPath=${workspaceFolder}/out/test"
203203
],
204204
"env": {
205205
"VSC_PYTHON_CI_TEST_GREP": "", // Modify this to run a subset of the single workspace tests
206+
"VSC_PYTHON_CI_TEST_INVERT_GREP": "", // Initialize this to invert the grep (exclude tests with value defined in grep).
207+
206208
"VSC_PYTHON_LOAD_EXPERIMENTS_FROM_FILE": "true",
207209
"TEST_FILES_SUFFIX": "ds.test"
208210
},

.vscode/tasks.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,10 @@
3333
"kind": "build",
3434
"isDefault": true
3535
},
36-
"problemMatcher": []
36+
"problemMatcher": [
37+
"$tsc-watch",
38+
"$ts-checker-webpack-watch"
39+
]
3740
},
3841
{
3942
"label": "Run Unit Tests",
@@ -48,7 +51,9 @@
4851
"label": "Inject DS WebBrowser UI",
4952
"type": "shell",
5053
"command": "node",
51-
"args": ["build/debug/replaceWithWebBrowserPanel.js"],
54+
"args": [
55+
"build/debug/replaceWithWebBrowserPanel.js"
56+
],
5257
"problemMatcher": []
5358
}
5459
]

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2998,7 +2998,7 @@
29982998
"test:cover:report": "nyc --nycrc-path build/.nycrc report --reporter=text --reporter=html --reporter=text-summary --reporter=cobertura",
29992999
"testDebugger": "node ./out/test/testBootstrap.js ./out/test/debuggerTest.js",
30003000
"testSingleWorkspace": "node ./out/test/testBootstrap.js ./out/test/standardTest.js",
3001-
"testDataScience": "cross-env VSC_PYTHON_CI_TEST_VSC_CHANNEL=insiders TEST_FILES_SUFFIX=ds.test node ./out/test/testBootstrap.js ./out/test/standardTest.js",
3001+
"testDataScience": "cross-env CODE_TESTS_WORKSPACE=src/test/datascience VSC_PYTHON_CI_TEST_VSC_CHANNEL=insiders TEST_FILES_SUFFIX=ds.test node ./out/test/testBootstrap.js ./out/test/standardTest.js",
30023002
"testMultiWorkspace": "node ./out/test/testBootstrap.js ./out/test/multiRootTest.js",
30033003
"testPerformance": "node ./out/test/testBootstrap.js ./out/test/performanceTest.js",
30043004
"testSmoke": "node ./out/test/smokeTest.js",

src/client/common/application/notebook.ts

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { inject, injectable } from 'inversify';
55
import { Disposable, Event, EventEmitter, GlobPattern, TextDocument, window } from 'vscode';
66
import type {
77
notebook,
8+
NotebookCellsChangeEvent as VSCNotebookCellsChangeEvent,
89
NotebookContentProvider,
910
NotebookDocument,
1011
NotebookEditor,
@@ -17,19 +18,15 @@ import { IDisposableRegistry } from '../types';
1718
import {
1819
IVSCodeNotebook,
1920
NotebookCellLanguageChangeEvent,
20-
NotebookCellMoveEvent,
2121
NotebookCellOutputsChangeEvent,
2222
NotebookCellsChangeEvent
2323
} from './types';
2424

2525
@injectable()
2626
export class VSCodeNotebook implements IVSCodeNotebook {
27-
private readonly _onDidChangeNotebookDocument = new EventEmitter<
28-
| NotebookCellsChangeEvent
29-
| NotebookCellMoveEvent
30-
| NotebookCellOutputsChangeEvent
31-
| NotebookCellLanguageChangeEvent
32-
>();
27+
public get onDidChangeActiveNotebookEditor(): Event<NotebookEditor | undefined> {
28+
return this.notebook.onDidChangeActiveNotebookEditor;
29+
}
3330
public get onDidOpenNotebookDocument(): Event<NotebookDocument> {
3431
return this.notebook.onDidOpenNotebookDocument;
3532
}
@@ -40,17 +37,8 @@ export class VSCodeNotebook implements IVSCodeNotebook {
4037
return this.notebook.visibleNotebookEditors;
4138
}
4239
public get onDidChangeNotebookDocument(): Event<
43-
| NotebookCellsChangeEvent
44-
| NotebookCellMoveEvent
45-
| NotebookCellOutputsChangeEvent
46-
| NotebookCellLanguageChangeEvent
40+
NotebookCellsChangeEvent | NotebookCellOutputsChangeEvent | NotebookCellLanguageChangeEvent
4741
> {
48-
// Temporarily disabled as API is not yet
49-
// Bogus if, to satisyf compiler and ensure addEventHandlers method is used.
50-
if (process.env.SOME_BOGUS_ENV_VAR) {
51-
this.addEventHandlers();
52-
}
53-
5442
return this._onDidChangeNotebookDocument.event;
5543
}
5644
public get activeNotebookEditor(): NotebookEditor | undefined {
@@ -72,19 +60,27 @@ export class VSCodeNotebook implements IVSCodeNotebook {
7260
}
7361
return this.notebook.activeNotebookEditor;
7462
}
75-
private addedEventHandlers?: boolean;
76-
private _notebook?: typeof notebook;
7763
private get notebook() {
7864
if (!this._notebook) {
7965
// tslint:disable-next-line: no-require-imports
8066
this._notebook = require('vscode').notebook;
8167
}
8268
return this._notebook!;
8369
}
70+
private readonly _onDidChangeNotebookDocument = new EventEmitter<
71+
NotebookCellsChangeEvent | NotebookCellOutputsChangeEvent | NotebookCellLanguageChangeEvent
72+
>();
73+
private addedEventHandlers?: boolean;
74+
private _notebook?: typeof notebook;
75+
private readonly handledCellChanges = new WeakSet<VSCNotebookCellsChangeEvent>();
8476
constructor(
8577
@inject(UseProposedApi) private readonly useProposedApi: boolean,
8678
@inject(IDisposableRegistry) private readonly disposables: IDisposableRegistry
87-
) {}
79+
) {
80+
if (this.useProposedApi) {
81+
this.addEventHandlers();
82+
}
83+
}
8884
public registerNotebookContentProvider(notebookType: string, provider: NotebookContentProvider): Disposable {
8985
return this.notebook.registerNotebookContentProvider(notebookType, provider);
9086
}
@@ -110,6 +106,7 @@ export class VSCodeNotebook implements IVSCodeNotebook {
110106
if (this.addedEventHandlers) {
111107
return;
112108
}
109+
this.addedEventHandlers = true;
113110
this.disposables.push(
114111
...[
115112
this.notebook.onDidChangeCellLanguage((e) =>
@@ -118,12 +115,13 @@ export class VSCodeNotebook implements IVSCodeNotebook {
118115
this.notebook.onDidChangeCellOutputs((e) =>
119116
this._onDidChangeNotebookDocument.fire({ ...e, type: 'changeCellOutputs' })
120117
),
121-
this.notebook.onDidChangeNotebookCells((e) =>
122-
this._onDidChangeNotebookDocument.fire({ ...e, type: 'changeCells' })
123-
),
124-
this.notebook.onDidMoveNotebookCell((e) =>
125-
this._onDidChangeNotebookDocument.fire({ ...e, type: 'moveCell' })
126-
)
118+
this.notebook.onDidChangeNotebookCells((e) => {
119+
if (this.handledCellChanges.has(e)) {
120+
return;
121+
}
122+
this.handledCellChanges.add(e);
123+
this._onDidChangeNotebookDocument.fire({ ...e, type: 'changeCells' });
124+
})
127125
]
128126
);
129127
}

src/client/common/application/types.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ import {
6060
} from 'vscode';
6161
import type {
6262
NotebookCellLanguageChangeEvent as VSCNotebookCellLanguageChangeEvent,
63-
NotebookCellMoveEvent as VSCNotebookCellMoveEvent,
6463
NotebookCellOutputsChangeEvent as VSCNotebookCellOutputsChangeEvent,
6564
NotebookCellsChangeEvent as VSCNotebookCellsChangeEvent,
6665
NotebookContentProvider,
@@ -1456,20 +1455,16 @@ export interface IClipboard {
14561455
}
14571456

14581457
export type NotebookCellsChangeEvent = { type: 'changeCells' } & VSCNotebookCellsChangeEvent;
1459-
export type NotebookCellMoveEvent = { type: 'moveCell' } & VSCNotebookCellMoveEvent;
14601458
export type NotebookCellOutputsChangeEvent = { type: 'changeCellOutputs' } & VSCNotebookCellOutputsChangeEvent;
14611459
export type NotebookCellLanguageChangeEvent = { type: 'changeCellLanguage' } & VSCNotebookCellLanguageChangeEvent;
14621460

14631461
export const IVSCodeNotebook = Symbol('IVSCodeNotebook');
14641462
export interface IVSCodeNotebook {
14651463
readonly onDidOpenNotebookDocument: Event<NotebookDocument>;
14661464
readonly onDidCloseNotebookDocument: Event<NotebookDocument>;
1467-
1465+
readonly onDidChangeActiveNotebookEditor: Event<NotebookEditor | undefined>;
14681466
readonly onDidChangeNotebookDocument: Event<
1469-
| NotebookCellsChangeEvent
1470-
| NotebookCellMoveEvent
1471-
| NotebookCellOutputsChangeEvent
1472-
| NotebookCellLanguageChangeEvent
1467+
NotebookCellsChangeEvent | NotebookCellOutputsChangeEvent | NotebookCellLanguageChangeEvent
14731468
>;
14741469
readonly notebookEditors: Readonly<NotebookEditor[]>;
14751470
readonly activeNotebookEditor: NotebookEditor | undefined;

src/client/datascience/interactive-common/interactiveWindowTypes.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,11 @@ export interface INotebookModelModifyChange extends INotebookModelChange {
363363
newCells: ICell[];
364364
oldCells: ICell[];
365365
}
366+
export interface INotebookModelCellExecutionCountChange extends INotebookModelChange {
367+
kind: 'updateCellExecutionCount';
368+
cellId: string;
369+
executionCount?: number;
370+
}
366371

367372
export interface INotebookModelClearChange extends INotebookModelChange {
368373
kind: 'clear';
@@ -478,7 +483,8 @@ export type NotebookModelChange =
478483
| INotebookModelAddChange
479484
| INotebookModelEditChange
480485
| INotebookModelVersionChange
481-
| INotebookModelChangeTypeChange;
486+
| INotebookModelChangeTypeChange
487+
| INotebookModelCellExecutionCountChange;
482488

483489
export interface IRunByLine {
484490
cell: ICell;

src/client/datascience/interactive-ipynb/nativeEditorStorage.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,9 @@ export class NativeEditorNotebookModel implements INotebookModel {
208208
case 'swap':
209209
changed = this.swapCells(change.firstCellId, change.secondCellId);
210210
break;
211+
case 'updateCellExecutionCount':
212+
changed = this.updateCellExecutionCount(change.cellId, change.executionCount);
213+
break;
211214
case 'version':
212215
changed = this.updateVersionInfo(change.interpreter, change.kernelSpec);
213216
break;
@@ -325,6 +328,15 @@ export class NativeEditorNotebookModel implements INotebookModel {
325328
return false;
326329
}
327330

331+
private updateCellExecutionCount(cellId: string, executionCount?: number) {
332+
const index = this.cells.findIndex((v) => v.id === cellId);
333+
if (index >= 0) {
334+
this._state.cells[index].data.execution_count = (executionCount || 0) > 0 ? executionCount : null;
335+
return true;
336+
}
337+
return false;
338+
}
339+
328340
private modifyCells(cells: ICell[]): boolean {
329341
// Update these cells in our list
330342
cells.forEach((c) => {
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
'use strict';
5+
6+
/**
7+
* When executing cells, VSCode doesn't perform a save operation.
8+
* However in Jupyter Lab (with auto save on), even when you execute cells auto save kicks in.
9+
* Solution:
10+
* - If notebook isn't saved after a cell is executed, then we can save manually.
11+
* - We need to take the auto save delay into account (if any, for throttling).
12+
* - Save only if user has executed (if execution count changes or cell output changes).
13+
* - When adding/deleting/editing cells, VSC automatically performs a save.
14+
*
15+
* Hence, we only need to handle auto save when executing cell.s
16+
*/
17+
// @injectable()
18+
// export class AutoSaveService implements IExtensionSingleActivationService {}

0 commit comments

Comments
 (0)