Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Execution API #116416

Merged
merged 34 commits into from Mar 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
1251a3b
Remove NotebookEditorEdit API
roblourens Mar 19, 2021
e3d5b40
Execution API notes
roblourens Feb 11, 2021
e7b47bd
dts updates
roblourens Feb 12, 2021
8267ee6
Execution API updates
roblourens Feb 15, 2021
fdddfef
Move executionOrder, remove runStates from metadata
roblourens Mar 4, 2021
5d4be76
Begin implementing NotebookCellExecutionTask
roblourens Mar 10, 2021
062aa37
Implement cancellation for new cell execute api
roblourens Mar 11, 2021
69e37ee
Implement NotebookCellOutputEdit
roblourens Mar 12, 2021
14bf6da
Queue edits, update UI for Pending
roblourens Mar 12, 2021
03f8cc9
Handle cell execution metadata split, not all metadata exposed to ext…
roblourens Mar 13, 2021
556287e
Tweak "pending" ui
roblourens Mar 13, 2021
055de55
Remove test deleted upstream
roblourens Mar 15, 2021
345609d
Remove WorkspaceEdit-like API
roblourens Mar 15, 2021
2f29777
Tooltip for Pending state
roblourens Mar 16, 2021
98de6a9
Implement partial metadata edits
roblourens Mar 16, 2021
b1b32a3
Allow cancelling Pending cell
roblourens Mar 16, 2021
6f06f83
Implement previousResult
roblourens Mar 16, 2021
77e11c8
Fix smoke test extension
roblourens Mar 16, 2021
417faf9
Restore run state when clause
roblourens Mar 16, 2021
9a307fb
Clear last run info on end, if not set
roblourens Mar 16, 2021
8f7d01b
Clear executionOrder when task is created
roblourens Mar 17, 2021
f672255
Don't hold on to stale cell index
roblourens Mar 17, 2021
29aabe4
Tweaks to output API, this commit has some TODOs
roblourens Mar 18, 2021
8a038f6
Tweaks to edits
roblourens Mar 19, 2021
2feb770
Tweak
roblourens Mar 19, 2021
de75356
Implement global cancel for kernels with interrupt
roblourens Mar 19, 2021
7f0b512
Remove document-level runState metadata internally
roblourens Mar 19, 2021
28535cc
API jsdocs
roblourens Mar 20, 2021
c982571
Tweaks
roblourens Mar 20, 2021
7024a90
Fix test
roblourens Mar 22, 2021
d43faba
Revert "Remove NotebookEditorEdit API"
roblourens Mar 22, 2021
2cb1ba8
Fix test for NotebookEditorEdit
roblourens Mar 22, 2021
d49b868
previousResult => latestExecutionSummary
roblourens Mar 22, 2021
b105656
interrupt should not take cell range
roblourens Mar 22, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
455 changes: 353 additions & 102 deletions extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts

Large diffs are not rendered by default.

30 changes: 10 additions & 20 deletions extensions/vscode-notebook-tests/src/extension.ts
Expand Up @@ -62,32 +62,22 @@ export function activate(context: vscode.ExtensionContext): any {
}));

const kernel: vscode.NotebookKernel = {
id: 'notebookSmokeTest',
label: 'notebookSmokeTest',
isPreferred: true,
executeAllCells: async (_document: vscode.NotebookDocument) => {
const edit = new vscode.WorkspaceEdit();
for (let i = 0; i < _document.cells.length; i++) {
edit.replaceNotebookCellOutput(_document.uri, i, [new vscode.NotebookCellOutput([
new vscode.NotebookCellOutputItem('text/html', ['test output'], undefined)
])]);
}

await vscode.workspace.applyEdit(edit);
},
cancelAllCellsExecution: async () => { },
executeCell: async (_document: vscode.NotebookDocument, _cell: vscode.NotebookCell | undefined) => {
if (!_cell) {
_cell = _document.cells[0];
executeCellsRequest: async (document: vscode.NotebookDocument, ranges: vscode.NotebookCellRange[]) => {
const idx = ranges[0].start;
const task = vscode.notebook.createNotebookCellExecutionTask(document.uri, idx, 'notebookSmokeTest');
if (!task) {
return;
}

const edit = new vscode.WorkspaceEdit();
edit.replaceNotebookCellOutput(_document.uri, _cell.index, [new vscode.NotebookCellOutput([
task.start();
task.replaceOutput([new vscode.NotebookCellOutput([
new vscode.NotebookCellOutputItem('text/html', ['test output'], undefined)
])]);
await vscode.workspace.applyEdit(edit);
return;
},
cancelCellExecution: async () => { }
task.end({ success: true });
}
};

context.subscriptions.push(vscode.notebook.registerNotebookKernelProvider({ filenamePattern: '*.smoke-nb' }, {
Expand Down
152 changes: 99 additions & 53 deletions src/vs/vscode.proposed.d.ts
Expand Up @@ -963,18 +963,6 @@ declare module 'vscode' {
Code = 2
}

export enum NotebookCellRunState {
Running = 1,
Idle = 2,
Success = 3,
Error = 4
}

export enum NotebookRunState {
Running = 1,
Idle = 2
}

export class NotebookCellMetadata {
/**
* Controls whether a cell's editor is editable/readonly.
Expand Down Expand Up @@ -1003,14 +991,16 @@ declare module 'vscode' {

// run related API, will be removed
readonly hasExecutionOrder?: boolean;
readonly executionOrder?: number;
readonly runState?: NotebookCellRunState;
readonly runStartTime?: number;
readonly lastRunDuration?: number;

constructor(editable?: boolean, breakpointMargin?: boolean, hasExecutionOrder?: boolean, executionOrder?: number, runState?: NotebookCellRunState, runStartTime?: number, statusMessage?: string, lastRunDuration?: number, inputCollapsed?: boolean, outputCollapsed?: boolean, custom?: Record<string, any>)
constructor(editable?: boolean, breakpointMargin?: boolean, hasExecutionOrder?: boolean, statusMessage?: string, lastRunDuration?: number, inputCollapsed?: boolean, outputCollapsed?: boolean, custom?: Record<string, any>)

with(change: { editable?: boolean | null, breakpointMargin?: boolean | null, hasExecutionOrder?: boolean | null, executionOrder?: number | null, runState?: NotebookCellRunState | null, runStartTime?: number | null, statusMessage?: string | null, lastRunDuration?: number | null, inputCollapsed?: boolean | null, outputCollapsed?: boolean | null, custom?: Record<string, any> | null, }): NotebookCellMetadata;
with(change: { editable?: boolean | null, breakpointMargin?: boolean | null, hasExecutionOrder?: boolean | null, statusMessage?: string | null, lastRunDuration?: number | null, inputCollapsed?: boolean | null, outputCollapsed?: boolean | null, custom?: Record<string, any> | null, }): NotebookCellMetadata;
}

export interface NotebookCellExecutionSummary {
executionOrder?: number;
success?: boolean;
duration?: number;
}

// todo@API support ids https://github.com/jupyter/enhancement-proposals/blob/master/62-cell-id/cell-id.md
Expand All @@ -1021,6 +1011,7 @@ declare module 'vscode' {
readonly document: TextDocument;
readonly metadata: NotebookCellMetadata
readonly outputs: ReadonlyArray<NotebookCellOutput>;
readonly latestExecutionSummary: NotebookCellExecutionSummary | undefined;
}

export class NotebookDocumentMetadata {
Expand Down Expand Up @@ -1048,12 +1039,9 @@ declare module 'vscode' {
// todo@API is this a kernel property?
readonly cellHasExecutionOrder: boolean;

// todo@API remove
readonly runState: NotebookRunState;
constructor(editable?: boolean, cellEditable?: boolean, cellHasExecutionOrder?: boolean, custom?: { [key: string]: any; }, trusted?: boolean);

constructor(editable?: boolean, cellEditable?: boolean, cellHasExecutionOrder?: boolean, custom?: { [key: string]: any; }, runState?: NotebookRunState, trusted?: boolean);

with(change: { editable?: boolean | null, cellEditable?: boolean | null, cellHasExecutionOrder?: boolean | null, custom?: { [key: string]: any; } | null, runState?: NotebookRunState | null, trusted?: boolean | null, }): NotebookDocumentMetadata
with(change: { editable?: boolean | null, cellEditable?: boolean | null, cellHasExecutionOrder?: boolean | null, custom?: { [key: string]: any; } | null, trusted?: boolean | null, }): NotebookDocumentMetadata
}

export interface NotebookDocumentContentOptions {
Expand Down Expand Up @@ -1227,6 +1215,12 @@ declare module 'vscode' {
readonly visibleRanges: ReadonlyArray<NotebookCellRange>;
}

export interface NotebookCellExecutionStateChangeEvent {
readonly document: NotebookDocument;
readonly cell: NotebookCell;
readonly executionState: NotebookCellExecutionState;
}

// todo@API support ids https://github.com/jupyter/enhancement-proposals/blob/master/62-cell-id/cell-id.md
export class NotebookCellData {
kind: NotebookCellKind;
Expand All @@ -1236,7 +1230,8 @@ declare module 'vscode' {
language: string;
outputs?: NotebookCellOutput[];
metadata?: NotebookCellMetadata;
constructor(kind: NotebookCellKind, source: string, language: string, outputs?: NotebookCellOutput[], metadata?: NotebookCellMetadata)
latestExecutionSummary?: NotebookCellExecutionSummary;
constructor(kind: NotebookCellKind, source: string, language: string, outputs?: NotebookCellOutput[], metadata?: NotebookCellMetadata, latestExecutionSummary?: NotebookCellExecutionSummary);
}

export class NotebookData {
Expand Down Expand Up @@ -1496,27 +1491,6 @@ declare module 'vscode' {

//#region https://github.com/microsoft/vscode/issues/106744, NotebookKernel

// todo@API use the NotebookCellExecution-object as a container to model and enforce
// the flow of a cell execution

// kernel -> execute_info
// ext -> createNotebookCellExecution(cell)
// kernel -> done
// exec.dispose();

// export interface NotebookCellExecution {
// dispose(): void;
// clearOutput(): void;
// appendOutput(out: NotebookCellOutput): void;
// replaceOutput(out: NotebookCellOutput): void;
// appendOutputItems(output:string, items: NotebookCellOutputItem[]):void;
// replaceOutputItems(output:string, items: NotebookCellOutputItem[]):void;
// }

// export function createNotebookCellExecution(cell: NotebookCell, startTime?: number): NotebookCellExecution;
// export const onDidStartNotebookCellExecution: Event<any>;
// export const onDidStopNotebookCellExecution: Event<any>;

export interface NotebookKernel {

// todo@API make this mandatory?
Expand All @@ -1541,14 +1515,86 @@ declare module 'vscode' {
// fired when properties like the supported languages etc change
// onDidChangeProperties?: Event<void>

// @roblourens
// todo@API change to `executeCells(document: NotebookDocument, cells: NotebookCellRange[], context:{isWholeNotebooke: boolean}, token: CancelationToken): void;`
// todo@API interrupt vs cancellation, https://github.com/microsoft/vscode/issues/106741
// interrupt?():void;
executeCell(document: NotebookDocument, cell: NotebookCell): void;
cancelCellExecution(document: NotebookDocument, cell: NotebookCell): void;
executeAllCells(document: NotebookDocument): void;
cancelAllCellsExecution(document: NotebookDocument): void;
/**
* A kernel can optionally implement this which will be called when any "cancel" button is clicked in the document.
*/
interrupt?(document: NotebookDocument): void;

/**
* Called when the user triggers execution of a cell by clicking the run button for a cell, multiple cells,
* or full notebook. The cell will be put into the Pending state when this method is called. If
* createNotebookCellExecutionTask has not been called by the time the promise returned by this method is
* resolved, the cell will be put back into the Idle state.
*/
executeCellsRequest(document: NotebookDocument, ranges: NotebookCellRange[]): Thenable<void>;
}

export interface NotebookCellExecuteStartContext {
// TODO@roblou are we concerned about clock issues with this absolute time?
/**
* The time that execution began, in milliseconds in the Unix epoch. Used to drive the clock
* that shows for how long a cell has been running. If not given, the clock won't be shown.
*/
startTime?: number;
}

export interface NotebookCellExecuteEndContext {
/**
* If true, a green check is shown on the cell status bar.
* If false, a red X is shown.
*/
success?: boolean;

/**
* The total execution time in milliseconds.
*/
duration?: number;
}

/**
* A NotebookCellExecutionTask is how the kernel modifies a notebook cell as it is executing. When
* [`createNotebookCellExecutionTask`](#notebook.createNotebookCellExecutionTask) is called, the cell
* enters the Pending state. When `start()` is called on the execution task, it enters the Executing state. When
* `end()` is called, it enters the Idle state. While in the Executing state, cell outputs can be
* modified with the methods on the run task.
*
* All outputs methods operate on this NotebookCellExecutionTask's cell by default. They optionally take
* a cellIndex parameter that allows them to modify the outputs of other cells. `appendOutputItems` and
* `replaceOutputItems` operate on the output with the given ID, which can be an output on any cell. They
* all resolve once the output edit has been applied.
*/
export interface NotebookCellExecutionTask {
readonly document: NotebookDocument;
readonly cell: NotebookCell;

start(context?: NotebookCellExecuteStartContext): void;
executionOrder: number | undefined;
end(result?: NotebookCellExecuteEndContext): void;
readonly token: CancellationToken;

clearOutput(cellIndex?: number): Thenable<void>;
appendOutput(out: NotebookCellOutput[], cellIndex?: number): Thenable<void>;
replaceOutput(out: NotebookCellOutput[], cellIndex?: number): Thenable<void>;
appendOutputItems(items: NotebookCellOutputItem[], outputId: string): Thenable<void>;
replaceOutputItems(items: NotebookCellOutputItem[], outputId: string): Thenable<void>;
}

export enum NotebookCellExecutionState {
Idle = 1,
Pending = 2,
Executing = 3,
}

export namespace notebook {
/**
* Creates a [`NotebookCellExecutionTask`](#NotebookCellExecutionTask). Should only be called by a kernel. Returns undefined unless requested by the active kernel.
* @param uri The [uri](#Uri) of the notebook document.
* @param index The index of the cell.
* @param kernelId The id of the kernel requesting this run task. If this kernel is not the current active kernel, `undefined` is returned.
*/
export function createNotebookCellExecutionTask(uri: Uri, index: number, kernelId: string): NotebookCellExecutionTask | undefined;

export const onDidChangeCellExecutionState: Event<NotebookCellExecutionStateChangeEvent>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this, we're planning on using this in another extension (Gather).

}

export type NotebookFilenamePattern = GlobPattern | { include: GlobPattern; exclude: GlobPattern; };
Expand Down
24 changes: 17 additions & 7 deletions src/vs/workbench/api/browser/mainThreadNotebook.ts
Expand Up @@ -24,7 +24,7 @@ import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/no
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService';
import { ICellEditOperation, ICellRange, IMainCellDto, INotebookDecorationRenderOptions, INotebookDocumentFilter, INotebookExclusiveDocumentFilter, INotebookKernel, NotebookCellsChangeType, NotebookDataDto, TransientMetadata, TransientOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { ICellEditOperation, ICellRange, IImmediateCellEditOperation, IMainCellDto, INotebookDecorationRenderOptions, INotebookDocumentFilter, INotebookExclusiveDocumentFilter, INotebookKernel, NotebookCellsChangeType, NotebookDataDto, TransientMetadata, TransientOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService';
import { IMainNotebookController, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
Expand Down Expand Up @@ -165,6 +165,15 @@ export class MainThreadNotebooks implements MainThreadNotebookShape {
return textModel.applyEdits(cellEdits, true, undefined, () => undefined, undefined);
}

async $applyEdits(resource: UriComponents, cellEdits: IImmediateCellEditOperation[], computeUndoRedo = true): Promise<void> {
const textModel = this._notebookService.getNotebookTextModel(URI.from(resource));
if (!textModel) {
throw new Error(`Can't apply edits to unknown notebook model: ${resource}`);
}

textModel.applyEdits(cellEdits, true, undefined, () => undefined, undefined, computeUndoRedo);
}

private _registerListeners(): void {

// forward changes to dirty state
Expand Down Expand Up @@ -497,17 +506,18 @@ export class MainThreadNotebooks implements MainThreadNotebookShape {
isPreferred: dto.isPreferred,
preloads: dto.preloads?.map(u => URI.revive(u)),
supportedLanguages: dto.supportedLanguages,
implementsInterrupt: dto.implementsInterrupt,
resolve: (uri: URI, editorId: string, token: CancellationToken): Promise<void> => {
this._logService.debug('MainthreadNotebooks.resolveNotebookKernel', uri.path, dto.friendlyId);
return this._proxy.$resolveNotebookKernel(handle, editorId, uri, dto.friendlyId, token);
},
executeNotebookCell: (uri: URI, cellHandle: number | undefined): Promise<void> => {
this._logService.debug('MainthreadNotebooks.executeNotebookCell', uri.path, dto.friendlyId, cellHandle);
return this._proxy.$executeNotebookKernelFromProvider(handle, uri, dto.friendlyId, cellHandle);
executeNotebookCellsRequest: (uri: URI, cellRanges: ICellRange[]): Promise<void> => {
this._logService.debug('MainthreadNotebooks.executeNotebookCell', uri.path, dto.friendlyId, cellRanges);
return this._proxy.$executeNotebookKernelFromProvider(handle, uri, dto.friendlyId, cellRanges);
},
cancelNotebookCell: (uri: URI, cellHandle: number | undefined): Promise<void> => {
this._logService.debug('MainthreadNotebooks.cancelNotebookCell', uri.path, dto.friendlyId, cellHandle);
return this._proxy.$cancelNotebookKernelFromProvider(handle, uri, dto.friendlyId, cellHandle);
cancelNotebookCellExecution: (uri: URI, cellRanges: ICellRange[]): Promise<void> => {
this._logService.debug('MainthreadNotebooks.cancelNotebookCellExecution', uri.path, dto.friendlyId, cellRanges);
return this._proxy.$cancelNotebookCellExecution(handle, uri, dto.friendlyId, cellRanges);
}
});
}
Expand Down
11 changes: 9 additions & 2 deletions src/vs/workbench/api/common/extHost.api.impl.ts
Expand Up @@ -1078,6 +1078,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
checkProposedApiEnabled(extension);
return extHostNotebook.onDidChangeNotebookCells(listener, thisArgs, disposables);
},
onDidChangeCellExecutionState(listener, thisArgs?, disposables?) {
checkProposedApiEnabled(extension);
return extHostNotebook.onDidChangeNotebookCellExecutionState(listener, thisArgs, disposables);
},
onDidChangeCellOutputs(listener, thisArgs?, disposables?) {
checkProposedApiEnabled(extension);
return extHostNotebook.onDidChangeCellOutputs(listener, thisArgs, disposables);
Expand All @@ -1093,6 +1097,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
createCellStatusBarItem(cell: vscode.NotebookCell, alignment?: vscode.NotebookCellStatusBarAlignment, priority?: number): vscode.NotebookCellStatusBarItem {
checkProposedApiEnabled(extension);
return extHostNotebook.createNotebookCellStatusBarItemInternal(cell, alignment, priority);
},
createNotebookCellExecutionTask(uri: vscode.Uri, index: number, kernelId: string): vscode.NotebookCellExecutionTask | undefined {
checkProposedApiEnabled(extension);
return extHostNotebook.createNotebookCellExecution(uri, index, kernelId);
}
};

Expand Down Expand Up @@ -1231,12 +1239,11 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
TimelineItem: extHostTypes.TimelineItem,
NotebookCellRange: extHostTypes.NotebookCellRange,
NotebookCellKind: extHostTypes.NotebookCellKind,
NotebookCellRunState: extHostTypes.NotebookCellRunState,
NotebookCellExecutionState: extHostTypes.NotebookCellExecutionState,
NotebookDocumentMetadata: extHostTypes.NotebookDocumentMetadata,
NotebookCellMetadata: extHostTypes.NotebookCellMetadata,
NotebookCellData: extHostTypes.NotebookCellData,
NotebookData: extHostTypes.NotebookData,
NotebookRunState: extHostTypes.NotebookRunState,
NotebookCellStatusBarAlignment: extHostTypes.NotebookCellStatusBarAlignment,
NotebookEditorRevealType: extHostTypes.NotebookEditorRevealType,
NotebookCellOutput: extHostTypes.NotebookCellOutput,
Expand Down