Skip to content

Commit b3f7f26

Browse files
committed
Update cell output and metadata using Edit API
1 parent d24e2fa commit b3f7f26

File tree

9 files changed

+142
-219
lines changed

9 files changed

+142
-219
lines changed

src/client/datascience/jupyter/kernels/cellExecution.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@
66
import { nbformat } from '@jupyterlab/coreutils';
77
import type { KernelMessage } from '@jupyterlab/services/lib/kernel/messages';
88
import { CancellationToken, CellOutputKind, CellStreamOutput, NotebookCell, NotebookCellRunState } from 'vscode';
9+
import type { NotebookEditor as VSCNotebookEditor } from '../../../../../types/vscode-proposed';
910
import { concatMultilineString, formatStreamText } from '../../../../datascience-ui/common';
10-
import { IApplicationShell } from '../../../common/application/types';
11+
import { IApplicationShell, IVSCodeNotebook } from '../../../common/application/types';
1112
import { traceInfo, traceWarning } from '../../../common/logger';
1213
import { RefBool } from '../../../common/refBool';
1314
import { createDeferred } from '../../../common/utils/async';
@@ -41,12 +42,14 @@ export class CellExecutionFactory {
4142
private readonly contentProvider: INotebookContentProvider,
4243
private readonly errorHandler: IDataScienceErrorHandler,
4344
private readonly editorProvider: INotebookEditorProvider,
44-
private readonly appShell: IApplicationShell
45+
private readonly appShell: IApplicationShell,
46+
private readonly vscNotebook: IVSCodeNotebook
4547
) {}
4648

4749
public create(cell: NotebookCell) {
4850
// tslint:disable-next-line: no-use-before-declare
4951
return CellExecution.fromCell(
52+
this.vscNotebook.notebookEditors.find((e) => e.document === cell.notebook)!,
5053
cell,
5154
this.contentProvider,
5255
this.errorHandler,
@@ -87,6 +90,7 @@ export class CellExecution {
8790
private _completed?: boolean;
8891

8992
private constructor(
93+
public readonly editor: VSCNotebookEditor,
9094
public readonly cell: NotebookCell,
9195
private readonly contentProvider: INotebookContentProvider,
9296
private readonly errorHandler: IDataScienceErrorHandler,
@@ -98,19 +102,20 @@ export class CellExecution {
98102
}
99103

100104
public static fromCell(
105+
editor: VSCNotebookEditor,
101106
cell: NotebookCell,
102107
contentProvider: INotebookContentProvider,
103108
errorHandler: IDataScienceErrorHandler,
104109
editorProvider: INotebookEditorProvider,
105110
appService: IApplicationShell
106111
) {
107-
return new CellExecution(cell, contentProvider, errorHandler, editorProvider, appService);
112+
return new CellExecution(editor, cell, contentProvider, errorHandler, editorProvider, appService);
108113
}
109114

110115
public start(kernelPromise: Promise<IKernel>, notebook: INotebook) {
111116
this.started = true;
112117
// Ensure we clear the cell state and trigger a change.
113-
clearCellForExecution(this.cell);
118+
clearCellForExecution(this.editor, this.cell);
114119
this.cell.metadata.runStartTime = new Date().getTime();
115120
this.stopWatch.reset();
116121
// Changes to metadata must be saved in ipynb, hence mark doc has dirty.
@@ -147,7 +152,7 @@ export class CellExecution {
147152
private completedWithErrors(error: Partial<Error>) {
148153
this.sendPerceivedCellExecute();
149154
this.cell.metadata.lastRunDuration = this.stopWatch.elapsedTime;
150-
updateCellWithErrorStatus(this.cell, error);
155+
updateCellWithErrorStatus(this.editor, this.cell, error);
151156
this.contentProvider.notifyChangesToDocument(this.cell.notebook);
152157
this.errorHandler.handleError((error as unknown) as Error).ignoreErrors();
153158

@@ -167,7 +172,7 @@ export class CellExecution {
167172

168173
this.cell.metadata.statusMessage = '';
169174
this.cell.metadata.lastRunDuration = this.stopWatch.elapsedTime;
170-
updateCellExecutionTimes(this.cell, {
175+
updateCellExecutionTimes(this.editor, this.cell, {
171176
startTime: this.cell.metadata.runStartTime,
172177
duration: this.cell.metadata.lastRunDuration
173178
});
@@ -351,7 +356,7 @@ export class CellExecution {
351356

352357
// Set execution count, all messages should have it
353358
if ('execution_count' in msg.content && typeof msg.content.execution_count === 'number') {
354-
if (updateCellExecutionCount(this.cell, msg.content.execution_count)) {
359+
if (updateCellExecutionCount(this.editor, this.cell, msg.content.execution_count)) {
355360
shouldUpdate = true;
356361
}
357362
}
@@ -442,7 +447,7 @@ export class CellExecution {
442447

443448
private handleExecuteInput(msg: KernelMessage.IExecuteInputMsg, _clearState: RefBool) {
444449
if (msg.content.execution_count) {
445-
updateCellExecutionCount(this.cell, msg.content.execution_count);
450+
updateCellExecutionCount(this.editor, this.cell, msg.content.execution_count);
446451
}
447452
}
448453

@@ -518,7 +523,7 @@ export class CellExecution {
518523

519524
// Set execution count, all messages should have it
520525
if ('execution_count' in msg.content && typeof msg.content.execution_count === 'number') {
521-
updateCellExecutionCount(this.cell, msg.content.execution_count);
526+
updateCellExecutionCount(this.editor, this.cell, msg.content.execution_count);
522527
}
523528

524529
// Send this event.

src/client/datascience/jupyter/kernels/kernel.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
Uri
1818
} from 'vscode';
1919
import { ServerStatus } from '../../../../datascience-ui/interactive-common/mainState';
20-
import { IApplicationShell, ICommandManager } from '../../../common/application/types';
20+
import { IApplicationShell, ICommandManager, IVSCodeNotebook } from '../../../common/application/types';
2121
import { traceError } from '../../../common/logger';
2222
import { IDisposableRegistry } from '../../../common/types';
2323
import { createDeferred, Deferred } from '../../../common/utils/async';
@@ -86,7 +86,8 @@ export class Kernel implements IKernel {
8686
editorProvider: INotebookEditorProvider,
8787
private readonly kernelProvider: IKernelProvider,
8888
private readonly kernelSelectionUsage: IKernelSelectionUsage,
89-
appShell: IApplicationShell
89+
appShell: IApplicationShell,
90+
vscNotebook: IVSCodeNotebook
9091
) {
9192
this.kernelExecution = new KernelExecution(
9293
kernelProvider,
@@ -96,7 +97,8 @@ export class Kernel implements IKernel {
9697
contentProvider,
9798
editorProvider,
9899
kernelSelectionUsage,
99-
appShell
100+
appShell,
101+
vscNotebook
100102
);
101103
}
102104
public async executeCell(cell: NotebookCell): Promise<void> {

src/client/datascience/jupyter/kernels/kernelExecution.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import { KernelMessage } from '@jupyterlab/services';
77
import { NotebookCell, NotebookCellRunState, NotebookDocument } from 'vscode';
8-
import { IApplicationShell, ICommandManager } from '../../../common/application/types';
8+
import { IApplicationShell, ICommandManager, IVSCodeNotebook } from '../../../common/application/types';
99
import { IDisposable } from '../../../common/types';
1010
import { noop } from '../../../common/utils/misc';
1111
import { IInterpreterService } from '../../../interpreter/contracts';
@@ -43,9 +43,16 @@ export class KernelExecution implements IDisposable {
4343
private readonly contentProvider: INotebookContentProvider,
4444
editorProvider: INotebookEditorProvider,
4545
readonly kernelSelectionUsage: IKernelSelectionUsage,
46-
readonly appShell: IApplicationShell
46+
readonly appShell: IApplicationShell,
47+
readonly vscNotebook: IVSCodeNotebook
4748
) {
48-
this.executionFactory = new CellExecutionFactory(this.contentProvider, errorHandler, editorProvider, appShell);
49+
this.executionFactory = new CellExecutionFactory(
50+
this.contentProvider,
51+
errorHandler,
52+
editorProvider,
53+
appShell,
54+
vscNotebook
55+
);
4956
}
5057

5158
@captureTelemetry(Telemetry.ExecuteNativeCell, undefined, true)
@@ -163,8 +170,11 @@ export class KernelExecution implements IDisposable {
163170
private onIoPubMessage(document: NotebookDocument, msg: KernelMessage.IIOPubMessage) {
164171
// tslint:disable-next-line:no-require-imports
165172
const jupyterLab = require('@jupyterlab/services') as typeof import('@jupyterlab/services');
166-
if (jupyterLab.KernelMessage.isUpdateDisplayDataMsg(msg) && handleUpdateDisplayDataMessage(msg, document)) {
167-
this.contentProvider.notifyChangesToDocument(document);
173+
const editor = this.vscNotebook.notebookEditors.find((e) => e.document === document);
174+
if (jupyterLab.KernelMessage.isUpdateDisplayDataMsg(msg) && editor) {
175+
if (handleUpdateDisplayDataMessage(msg, editor)) {
176+
this.contentProvider.notifyChangesToDocument(document);
177+
}
168178
}
169179
}
170180

src/client/datascience/jupyter/kernels/kernelProvider.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import * as fastDeepEqual from 'fast-deep-equal';
77
import { inject, injectable } from 'inversify';
88
import { Uri } from 'vscode';
9-
import { IApplicationShell, ICommandManager } from '../../../common/application/types';
9+
import { IApplicationShell, ICommandManager, IVSCodeNotebook } from '../../../common/application/types';
1010
import { traceInfo, traceWarning } from '../../../common/logger';
1111
import { IAsyncDisposableRegistry, IConfigurationService, IDisposableRegistry } from '../../../common/types';
1212
import { IInterpreterService } from '../../../interpreter/contracts';
@@ -30,7 +30,8 @@ export class KernelProvider implements IKernelProvider {
3030
@inject(INotebookContentProvider) private readonly contentProvider: INotebookContentProvider,
3131
@inject(INotebookEditorProvider) private readonly editorProvider: INotebookEditorProvider,
3232
@inject(KernelSelector) private readonly kernelSelectionUsage: IKernelSelectionUsage,
33-
@inject(IApplicationShell) private readonly appShell: IApplicationShell
33+
@inject(IApplicationShell) private readonly appShell: IApplicationShell,
34+
@inject(IVSCodeNotebook) private readonly vscNotebook: IVSCodeNotebook
3435
) {}
3536
public get(uri: Uri): IKernel | undefined {
3637
return this.kernelsByUri.get(uri.toString())?.kernel;
@@ -57,7 +58,8 @@ export class KernelProvider implements IKernelProvider {
5758
this.editorProvider,
5859
this,
5960
this.kernelSelectionUsage,
60-
this.appShell
61+
this.appShell,
62+
this.vscNotebook
6163
);
6264
this.asyncDisposables.push(kernel);
6365
this.kernelsByUri.set(uri.toString(), { options, kernel });

src/client/datascience/notebook/helpers/executionHelpers.ts

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import type { nbformat } from '@jupyterlab/coreutils';
77
import type { KernelMessage } from '@jupyterlab/services';
88
import * as fastDeepEqual from 'fast-deep-equal';
9-
import { NotebookCell, NotebookCellRunState, NotebookDocument } from 'vscode';
9+
import type { NotebookCell, NotebookEditor } from '../../../../../types/vscode-proposed';
1010
import { createErrorOutput } from '../../../../datascience-ui/common/cellFactory';
1111
import { createIOutputFromCellOutputs, createVSCCellOutputsFromOutputs, translateErrorOutput } from './helpers';
1212
// tslint:disable-next-line: no-var-requires no-require-imports
@@ -20,8 +20,9 @@ const vscodeNotebookEnums = require('vscode') as typeof import('vscode-proposed'
2020
*/
2121
export function handleUpdateDisplayDataMessage(
2222
msg: KernelMessage.IUpdateDisplayDataMsg,
23-
document: NotebookDocument
23+
editor: NotebookEditor
2424
): boolean {
25+
const document = editor.document;
2526
// Find any cells that have this same display_id
2627
return (
2728
document.cells.filter((cellToCheck, index) => {
@@ -56,7 +57,7 @@ export function handleUpdateDisplayDataMessage(
5657
}
5758

5859
const vscCell = document.cells[index];
59-
updateCellOutput(vscCell, changedOutputs);
60+
updateCellOutput(editor, vscCell, changedOutputs);
6061
return true;
6162
}).length > 0
6263
);
@@ -65,17 +66,24 @@ export function handleUpdateDisplayDataMessage(
6566
/**
6667
* Updates the VSC cell with the error output.
6768
*/
68-
export function updateCellWithErrorStatus(cell: NotebookCell, ex: Partial<Error>) {
69+
export function updateCellWithErrorStatus(editor: NotebookEditor, cell: NotebookCell, ex: Partial<Error>) {
70+
editor.edit((edit) => {
71+
const cellIndex = editor.document.cells.indexOf(cell);
72+
edit.replaceMetadata(cellIndex, { ...cell.metadata, runState: vscodeNotebookEnums.NotebookCellRunState.Error });
73+
edit.replaceOutput(cellIndex, [translateErrorOutput(createErrorOutput(ex))]);
74+
});
6975
cell.outputs = [translateErrorOutput(createErrorOutput(ex))];
70-
cell.metadata.runState = NotebookCellRunState.Error;
7176
}
7277

7378
/**
7479
* @returns {boolean} Returns `true` if execution count has changed.
7580
*/
76-
export function updateCellExecutionCount(vscCell: NotebookCell, executionCount: number): boolean {
77-
if (vscCell.metadata.executionOrder !== executionCount && executionCount) {
78-
vscCell.metadata.executionOrder = executionCount;
81+
export function updateCellExecutionCount(editor: NotebookEditor, cell: NotebookCell, executionCount: number): boolean {
82+
if (cell.metadata.executionOrder !== executionCount && executionCount) {
83+
editor.edit((edit) => {
84+
const cellIndex = editor.document.cells.indexOf(cell);
85+
edit.replaceMetadata(cellIndex, { ...cell.metadata, executionOrder: executionCount });
86+
});
7987
return true;
8088
}
8189
return false;
@@ -87,7 +95,11 @@ export function updateCellExecutionCount(vscCell: NotebookCell, executionCount:
8795
* Here we update both the VSCode Cell as well as our ICell (cell in our INotebookModel).
8896
* @returns {(boolean | undefined)} Returns `true` if output has changed.
8997
*/
90-
export function updateCellOutput(vscCell: NotebookCell, outputs: nbformat.IOutput[]): boolean | undefined {
98+
export function updateCellOutput(
99+
editor: NotebookEditor,
100+
vscCell: NotebookCell,
101+
outputs: nbformat.IOutput[]
102+
): boolean | undefined {
91103
const newOutput = createVSCCellOutputsFromOutputs(outputs);
92104
// If there was no output and still no output, then nothing to do.
93105
if (vscCell.outputs.length === 0 && newOutput.length === 0) {
@@ -98,6 +110,6 @@ export function updateCellOutput(vscCell: NotebookCell, outputs: nbformat.IOutpu
98110
if (vscCell.outputs.length === newOutput.length && fastDeepEqual(vscCell.outputs, newOutput)) {
99111
return;
100112
}
101-
vscCell.outputs = newOutput;
113+
editor.edit((edit) => edit.replaceOutput(0, newOutput));
102114
return true;
103115
}

0 commit comments

Comments
 (0)