mirror of
https://github.com/Microsoft/vscode
synced 2024-09-18 01:58:27 +00:00
NotebookExecutionStateService - createCellExecution returns an execution object. For #125668
This commit is contained in:
parent
62b0d5cb81
commit
07b7831746
|
@ -97,7 +97,6 @@ export namespace NotebookDto {
|
|||
if (data.editType === CellExecutionUpdateType.Output) {
|
||||
return {
|
||||
editType: data.editType,
|
||||
cellHandle: data.cellHandle,
|
||||
append: data.append,
|
||||
outputs: data.outputs.map(fromNotebookOutputDto)
|
||||
};
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { flatten, groupBy, isNonEmptyArray } from 'vs/base/common/arrays';
|
||||
import { flatten, isNonEmptyArray } from 'vs/base/common/arrays';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { combinedDisposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
|
@ -14,8 +14,7 @@ import { NotebookDto } from 'vs/workbench/api/browser/mainThreadNotebookDto';
|
|||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/notebookEditorService';
|
||||
import { CellUri } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService';
|
||||
import { INotebookCellExecution, INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService';
|
||||
import { INotebookKernel, INotebookKernelChangeEvent, INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService';
|
||||
import { SerializableObjectWithBuffers } from 'vs/workbench/services/extensions/common/proxyIdentifier';
|
||||
import { ExtHostContext, ExtHostNotebookKernelsShape, ICellExecuteUpdateDto, ICellExecutionCompleteDto, IExtHostContext, INotebookKernelDto2, MainContext, MainThreadNotebookKernelsShape } from '../common/extHost.protocol';
|
||||
|
@ -107,7 +106,7 @@ export class MainThreadNotebookKernels implements MainThreadNotebookKernelsShape
|
|||
private readonly _kernels = new Map<number, [kernel: MainThreadKernel, registraion: IDisposable]>();
|
||||
private readonly _proxy: ExtHostNotebookKernelsShape;
|
||||
|
||||
private readonly _executions = new Set<string>();
|
||||
private readonly _executions = new Map<number, INotebookCellExecution>();
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
|
@ -125,10 +124,7 @@ export class MainThreadNotebookKernels implements MainThreadNotebookKernelsShape
|
|||
this._disposables.add(toDisposable(() => {
|
||||
// EH shut down, complete all executions started by this EH
|
||||
this._executions.forEach(e => {
|
||||
const uri = CellUri.parse(URI.parse(e));
|
||||
if (uri) {
|
||||
this._notebookExecutionStateService.completeNotebookCellExecution(uri.notebook, uri.handle, { });
|
||||
}
|
||||
e.complete({ });
|
||||
});
|
||||
}));
|
||||
|
||||
|
@ -249,34 +245,34 @@ export class MainThreadNotebookKernels implements MainThreadNotebookKernelsShape
|
|||
|
||||
// --- execution
|
||||
|
||||
$addExecution(rawUri: UriComponents, cellHandle: number): void {
|
||||
$createExecution(handle: number, controllerId: string, rawUri: UriComponents, cellHandle: number): void {
|
||||
const uri = URI.revive(rawUri);
|
||||
this._notebookExecutionStateService.createNotebookCellExecution(uri, cellHandle);
|
||||
|
||||
const cellUri = CellUri.generateCellUri(uri, cellHandle, uri.scheme);
|
||||
this._executions.add(cellUri.toString());
|
||||
const execution = this._notebookExecutionStateService.createCellExecution(controllerId, uri, cellHandle);
|
||||
this._executions.set(handle, execution);
|
||||
}
|
||||
|
||||
$updateExecutions(data: SerializableObjectWithBuffers<ICellExecuteUpdateDto[]>): void {
|
||||
$updateExecution(handle: number, data: SerializableObjectWithBuffers<ICellExecuteUpdateDto[]>): void {
|
||||
const updates = data.value;
|
||||
const groupedUpdates = groupBy(updates, (a, b) => a.cellHandle - b.cellHandle);
|
||||
groupedUpdates.forEach(datas => {
|
||||
const first = datas[0];
|
||||
|
||||
try {
|
||||
const uri = URI.revive(first.uri);
|
||||
this._notebookExecutionStateService.updateNotebookCellExecution(uri, first.cellHandle, datas.map(NotebookDto.fromCellExecuteUpdateDto));
|
||||
} catch (e) {
|
||||
onUnexpectedError(e);
|
||||
try {
|
||||
const execution = this._executions.get(handle);
|
||||
if (execution) {
|
||||
execution.update(updates.map(NotebookDto.fromCellExecuteUpdateDto));
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
onUnexpectedError(e);
|
||||
}
|
||||
}
|
||||
|
||||
$completeExecution(rawUri: UriComponents, cellHandle: number, data: SerializableObjectWithBuffers<ICellExecutionCompleteDto>): void {
|
||||
const uri = URI.revive(rawUri);
|
||||
this._notebookExecutionStateService.completeNotebookCellExecution(uri, cellHandle, NotebookDto.fromCellExecuteCompleteDto(data.value));
|
||||
|
||||
const cellUri = CellUri.generateCellUri(uri, cellHandle, uri.scheme);
|
||||
this._executions.delete(cellUri.toString());
|
||||
$completeExecution(handle: number, data: SerializableObjectWithBuffers<ICellExecutionCompleteDto>): void {
|
||||
try {
|
||||
const execution = this._executions.get(handle);
|
||||
if (execution) {
|
||||
execution.complete(NotebookDto.fromCellExecuteCompleteDto(data.value));
|
||||
}
|
||||
} catch (e) {
|
||||
onUnexpectedError(e);
|
||||
} finally {
|
||||
this._executions.delete(handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -948,29 +948,21 @@ export interface INotebookKernelDto2 {
|
|||
|
||||
export interface ICellExecuteOutputEditDto {
|
||||
editType: CellExecutionUpdateType.Output;
|
||||
uri: UriComponents;
|
||||
cellHandle: number;
|
||||
append?: boolean;
|
||||
outputs: NotebookOutputDto[];
|
||||
}
|
||||
|
||||
export interface ICellExecuteOutputItemEditDto {
|
||||
editType: CellExecutionUpdateType.OutputItems;
|
||||
uri: UriComponents;
|
||||
cellHandle: number;
|
||||
append?: boolean;
|
||||
outputId: string;
|
||||
items: NotebookOutputItemDto[];
|
||||
}
|
||||
|
||||
export interface ICellExecutionStateUpdateDto extends ICellExecutionStateUpdate {
|
||||
uri: UriComponents;
|
||||
cellHandle: number;
|
||||
}
|
||||
|
||||
export interface ICellExecutionCompleteDto extends ICellExecutionComplete {
|
||||
uri: UriComponents;
|
||||
cellHandle: number;
|
||||
}
|
||||
|
||||
export type ICellExecuteUpdateDto = ICellExecuteOutputEditDto | ICellExecuteOutputItemEditDto | ICellExecutionStateUpdateDto;
|
||||
|
@ -982,9 +974,9 @@ export interface MainThreadNotebookKernelsShape extends IDisposable {
|
|||
$removeKernel(handle: number): void;
|
||||
$updateNotebookPriority(handle: number, uri: UriComponents, value: number | undefined): void;
|
||||
|
||||
$addExecution(uri: UriComponents, cellHandle: number): void;
|
||||
$updateExecutions(data: SerializableObjectWithBuffers<ICellExecuteUpdateDto[]>): void;
|
||||
$completeExecution(uri: UriComponents, cellHandle: number, data: SerializableObjectWithBuffers<ICellExecutionCompleteDto>): void;
|
||||
$createExecution(handle: number, controllerId: string, uri: UriComponents, cellHandle: number): void;
|
||||
$updateExecution(handle: number, data: SerializableObjectWithBuffers<ICellExecuteUpdateDto[]>): void;
|
||||
$completeExecution(handle: number, data: SerializableObjectWithBuffers<ICellExecutionCompleteDto>): void;
|
||||
}
|
||||
|
||||
export interface MainThreadNotebookRenderersShape extends IDisposable {
|
||||
|
|
|
@ -16,9 +16,9 @@ import { ExtHostNotebookKernelsShape, ICellExecuteUpdateDto, IMainContext, INote
|
|||
import { ApiCommand, ApiCommandArgument, ApiCommandResult, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
|
||||
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
|
||||
import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook';
|
||||
import { ExtHostCell, ExtHostNotebookDocument } from 'vs/workbench/api/common/extHostNotebookDocument';
|
||||
import { ExtHostCell } from 'vs/workbench/api/common/extHostNotebookDocument';
|
||||
import * as extHostTypeConverters from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { NotebookCellOutput, NotebookCellExecutionState as ExtHostNotebookCellExecutionState } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { NotebookCellExecutionState as ExtHostNotebookCellExecutionState, NotebookCellOutput } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { asWebviewUri } from 'vs/workbench/api/common/shared/webview';
|
||||
import { NotebookCellExecutionState } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { CellExecutionUpdateType } from 'vs/workbench/contrib/notebook/common/notebookExecutionService';
|
||||
|
@ -108,7 +108,7 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape {
|
|||
const onDidReceiveMessage = new Emitter<{ editor: vscode.NotebookEditor, message: any; }>();
|
||||
|
||||
const data: INotebookKernelDto2 = {
|
||||
id: `${extension.identifier.value}/${id}`,
|
||||
id: createKernelId(extension, id),
|
||||
notebookType: viewType,
|
||||
extensionId: extension.identifier,
|
||||
extensionLocation: extension.extensionLocation,
|
||||
|
@ -218,7 +218,7 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape {
|
|||
that._logService.trace(`NotebookController[${handle}] NOT associated to notebook, associated to THESE notebooks:`, Array.from(associatedNotebooks.keys()).map(u => u.toString()));
|
||||
throw new Error(`notebook controller is NOT associated to notebook: ${cell.notebook.uri.toString()}`);
|
||||
}
|
||||
return that._createNotebookCellExecution(cell);
|
||||
return that._createNotebookCellExecution(cell, createKernelId(extension, this.id));
|
||||
},
|
||||
dispose: () => {
|
||||
if (!isDisposed) {
|
||||
|
@ -348,7 +348,7 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape {
|
|||
|
||||
// ---
|
||||
|
||||
_createNotebookCellExecution(cell: vscode.NotebookCell): vscode.NotebookCellExecution {
|
||||
_createNotebookCellExecution(cell: vscode.NotebookCell, controllerId: string): vscode.NotebookCellExecution {
|
||||
if (cell.index < 0) {
|
||||
throw new Error('CANNOT execute cell that has been REMOVED from notebook');
|
||||
}
|
||||
|
@ -360,7 +360,7 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape {
|
|||
if (this._activeExecutions.has(cellObj.uri)) {
|
||||
throw new Error(`duplicate execution for ${cellObj.uri}`);
|
||||
}
|
||||
const execution = new NotebookCellExecutionTask(cellObj.notebook, cellObj, this._proxy);
|
||||
const execution = new NotebookCellExecutionTask(controllerId, cellObj, this._proxy);
|
||||
this._activeExecutions.set(cellObj.uri, execution);
|
||||
const listener = execution.onDidChangeState(() => {
|
||||
if (execution.state === NotebookCellExecutionTaskState.Resolved) {
|
||||
|
@ -381,6 +381,9 @@ enum NotebookCellExecutionTaskState {
|
|||
}
|
||||
|
||||
class NotebookCellExecutionTask extends Disposable {
|
||||
private static HANDLE = 0;
|
||||
private _handle = NotebookCellExecutionTask.HANDLE++;
|
||||
|
||||
private _onDidChangeState = new Emitter<void>();
|
||||
readonly onDidChangeState = this._onDidChangeState.event;
|
||||
|
||||
|
@ -394,7 +397,7 @@ class NotebookCellExecutionTask extends Disposable {
|
|||
private _executionOrder: number | undefined;
|
||||
|
||||
constructor(
|
||||
private readonly _document: ExtHostNotebookDocument,
|
||||
controllerId: string,
|
||||
private readonly _cell: ExtHostCell,
|
||||
private readonly _proxy: MainThreadNotebookKernelsShape
|
||||
) {
|
||||
|
@ -403,7 +406,7 @@ class NotebookCellExecutionTask extends Disposable {
|
|||
this._collector = new TimeoutBasedCollector(10, updates => this.update(updates));
|
||||
|
||||
this._executionOrder = _cell.internalMetadata.executionOrder;
|
||||
this._proxy.$addExecution(this._cell.notebook.uri, this._cell.handle);
|
||||
this._proxy.$createExecution(this._handle, controllerId, this._cell.notebook.uri, this._cell.handle);
|
||||
}
|
||||
|
||||
cancel(): void {
|
||||
|
@ -416,7 +419,7 @@ class NotebookCellExecutionTask extends Disposable {
|
|||
|
||||
private async update(update: ICellExecuteUpdateDto | ICellExecuteUpdateDto[]): Promise<void> {
|
||||
const updates = Array.isArray(update) ? update : [update];
|
||||
return this._proxy.$updateExecutions(new SerializableObjectWithBuffers(updates));
|
||||
return this._proxy.$updateExecution(this._handle, new SerializableObjectWithBuffers(updates));
|
||||
}
|
||||
|
||||
private verifyStateForOutput() {
|
||||
|
@ -429,17 +432,6 @@ class NotebookCellExecutionTask extends Disposable {
|
|||
}
|
||||
}
|
||||
|
||||
private cellIndexToHandle(cellOrCellIndex: vscode.NotebookCell | undefined): number {
|
||||
let cell: ExtHostCell | undefined = this._cell;
|
||||
if (cellOrCellIndex) {
|
||||
cell = this._document.getCellFromApiCell(cellOrCellIndex);
|
||||
}
|
||||
if (!cell) {
|
||||
throw new Error('INVALID cell');
|
||||
}
|
||||
return cell.handle;
|
||||
}
|
||||
|
||||
private validateAndConvertOutputs(items: vscode.NotebookCellOutput[]): NotebookOutputDto[] {
|
||||
return items.map(output => {
|
||||
const newOutput = NotebookCellOutput.ensureUniqueMimeTypes(output.items, true);
|
||||
|
@ -455,13 +447,10 @@ class NotebookCellExecutionTask extends Disposable {
|
|||
}
|
||||
|
||||
private async updateOutputs(outputs: vscode.NotebookCellOutput | vscode.NotebookCellOutput[], cell: vscode.NotebookCell | undefined, append: boolean): Promise<void> {
|
||||
const handle = this.cellIndexToHandle(cell);
|
||||
const outputDtos = this.validateAndConvertOutputs(asArray(outputs));
|
||||
return this.updateSoon(
|
||||
{
|
||||
editType: CellExecutionUpdateType.Output,
|
||||
uri: this._document.uri,
|
||||
cellHandle: handle,
|
||||
append,
|
||||
outputs: outputDtos
|
||||
});
|
||||
|
@ -471,8 +460,6 @@ class NotebookCellExecutionTask extends Disposable {
|
|||
items = NotebookCellOutput.ensureUniqueMimeTypes(asArray(items), true);
|
||||
return this.updateSoon({
|
||||
editType: CellExecutionUpdateType.OutputItems,
|
||||
uri: this._document.uri,
|
||||
cellHandle: this._cell.handle,
|
||||
items: items.map(extHostTypeConverters.NotebookCellOutputItem.from),
|
||||
outputId: output.id,
|
||||
append
|
||||
|
@ -489,8 +476,6 @@ class NotebookCellExecutionTask extends Disposable {
|
|||
that._executionOrder = v;
|
||||
that.update([{
|
||||
editType: CellExecutionUpdateType.ExecutionState,
|
||||
uri: that._document.uri,
|
||||
cellHandle: that._cell.handle,
|
||||
executionOrder: that._executionOrder
|
||||
}]);
|
||||
},
|
||||
|
@ -505,8 +490,6 @@ class NotebookCellExecutionTask extends Disposable {
|
|||
|
||||
that.update({
|
||||
editType: CellExecutionUpdateType.ExecutionState,
|
||||
uri: that._document.uri,
|
||||
cellHandle: that._cell.handle,
|
||||
runStartTime: startTime
|
||||
});
|
||||
},
|
||||
|
@ -523,9 +506,7 @@ class NotebookCellExecutionTask extends Disposable {
|
|||
// so we use updateSoon and immediately flush.
|
||||
that._collector.flush();
|
||||
|
||||
that._proxy.$completeExecution(that._document.uri, that._cell.handle, new SerializableObjectWithBuffers({
|
||||
uri: that._document.uri,
|
||||
cellHandle: that._cell.handle,
|
||||
that._proxy.$completeExecution(that._handle, new SerializableObjectWithBuffers({
|
||||
runEndTime: endTime,
|
||||
lastRunSuccess: success
|
||||
}));
|
||||
|
@ -601,3 +582,7 @@ class TimeoutBasedCollector<T> {
|
|||
.finally(() => deferred.complete());
|
||||
}
|
||||
}
|
||||
|
||||
function createKernelId(extension: IExtensionDescription, id: string): string {
|
||||
return `${extension.identifier.value}/${id}`;
|
||||
}
|
||||
|
|
|
@ -575,7 +575,7 @@ export class InteractiveEditor extends EditorPane {
|
|||
}
|
||||
|
||||
if (this.#lastCell) {
|
||||
const runState = this.#notebookExecutionStateService.getCellExecutionState(this.#lastCell.uri)?.state;
|
||||
const runState = this.#notebookExecutionStateService.getCellExecution(this.#lastCell.uri)?.state;
|
||||
if (runState === NotebookCellExecutionState.Executing) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -184,9 +184,9 @@ class NotebookCellPausing extends Disposable implements IWorkbenchContribution {
|
|||
private editIsPaused(cellUri: URI, isPaused: boolean) {
|
||||
const parsed = CellUri.parse(cellUri);
|
||||
if (parsed) {
|
||||
const exeState = this._notebookExecutionStateService.getCellExecutionState(cellUri);
|
||||
const exeState = this._notebookExecutionStateService.getCellExecution(cellUri);
|
||||
if (exeState && (exeState.isPaused !== isPaused || !exeState.didPause)) {
|
||||
this._notebookExecutionStateService.updateNotebookCellExecution(parsed.notebook, parsed.handle, [{
|
||||
exeState.update([{
|
||||
editType: CellExecutionUpdateType.ExecutionState,
|
||||
didPause: true,
|
||||
isPaused
|
||||
|
|
|
@ -15,7 +15,7 @@ import { cellStatusIconError, cellStatusIconSuccess } from 'vs/workbench/contrib
|
|||
import { errorStateIcon, executingStateIcon, pendingStateIcon, successStateIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons';
|
||||
import { CellViewModel, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
|
||||
import { CellStatusbarAlignment, INotebookCellStatusBarItem, NotebookCellExecutionState, NotebookCellInternalMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { ICellExecutionEntry, INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService';
|
||||
import { INotebookCellExecution, INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService';
|
||||
|
||||
export class NotebookStatusBarController extends Disposable {
|
||||
private readonly _visibleCells = new Map<number, IDisposable>();
|
||||
|
@ -111,7 +111,7 @@ class ExecutionStateCellStatusBarItem extends Disposable {
|
|||
* Returns undefined if there should be no change, and an empty array if all items should be removed.
|
||||
*/
|
||||
private _getItemsForCell(): INotebookCellStatusBarItem[] | undefined {
|
||||
const runState = this._executionStateService.getCellExecutionState(this._cell.uri);
|
||||
const runState = this._executionStateService.getCellExecution(this._cell.uri);
|
||||
if (this._currentExecutingStateTimer && !runState?.isPaused) {
|
||||
return;
|
||||
}
|
||||
|
@ -121,7 +121,7 @@ class ExecutionStateCellStatusBarItem extends Disposable {
|
|||
// Show the execution spinner for a minimum time
|
||||
if (runState?.state === NotebookCellExecutionState.Executing) {
|
||||
this._currentExecutingStateTimer = this._register(disposableTimeout(() => {
|
||||
const runState = this._executionStateService.getCellExecutionState(this._cell.uri);
|
||||
const runState = this._executionStateService.getCellExecution(this._cell.uri);
|
||||
this._currentExecutingStateTimer = undefined;
|
||||
if (runState?.state !== NotebookCellExecutionState.Executing) {
|
||||
this._update();
|
||||
|
@ -132,7 +132,7 @@ class ExecutionStateCellStatusBarItem extends Disposable {
|
|||
return item ? [item] : [];
|
||||
}
|
||||
|
||||
private _getItemForState(runState: ICellExecutionEntry | undefined, internalMetadata: NotebookCellInternalMetadata): INotebookCellStatusBarItem | undefined {
|
||||
private _getItemForState(runState: INotebookCellExecution | undefined, internalMetadata: NotebookCellInternalMetadata): INotebookCellStatusBarItem | undefined {
|
||||
const state = runState?.state;
|
||||
const { lastRunSuccess } = internalMetadata;
|
||||
if (!state && lastRunSuccess) {
|
||||
|
@ -212,7 +212,7 @@ class TimerCellStatusBarItem extends Disposable {
|
|||
|
||||
private async _update() {
|
||||
let item: INotebookCellStatusBarItem | undefined;
|
||||
const runState = this._executionStateService.getCellExecutionState(this._cell.uri);
|
||||
const runState = this._executionStateService.getCellExecution(this._cell.uri);
|
||||
const state = runState?.state;
|
||||
if (runState?.didPause) {
|
||||
item = undefined;
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { INotebookEditor, INotebookEditorContribution } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { registerNotebookContribution } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions';
|
||||
import { INotebookExecutionService } from 'vs/workbench/contrib/notebook/common/notebookExecutionService';
|
||||
import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService';
|
||||
import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService';
|
||||
|
||||
export class ExecutionContrib extends Disposable implements INotebookEditorContribution {
|
||||
static id: string = 'workbench.notebook.executionContrib';
|
||||
|
||||
constructor(
|
||||
private readonly _notebookEditor: INotebookEditor,
|
||||
@INotebookExecutionStateService private readonly _notebookExecutionStateService: INotebookExecutionStateService,
|
||||
@INotebookExecutionService private readonly _notebookExecutionService: INotebookExecutionService,
|
||||
@ILogService private readonly _logService: ILogService,
|
||||
@INotebookKernelService private readonly _notebookKernelService: INotebookKernelService,
|
||||
) {
|
||||
super();
|
||||
|
||||
this._register(this._notebookKernelService.onDidChangeSelectedNotebooks(e => {
|
||||
if (e.newKernel && this._notebookEditor.textModel?.uri.toString() === e.notebook.toString()) {
|
||||
this.cancelAll();
|
||||
this._notebookExecutionStateService.forceCancelNotebookExecutions(e.notebook);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private cancelAll(): void {
|
||||
this._logService.debug(`ExecutionContrib#cancelAll`);
|
||||
const exes = this._notebookExecutionStateService.getCellExecutionStatesForNotebook(this._notebookEditor.textModel!.uri);
|
||||
this._notebookExecutionService.cancelNotebookCellHandles(this._notebookEditor.textModel!, exes.map(exe => exe.cellHandle));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
registerNotebookContribution(ExecutionContrib.id, ExecutionContrib);
|
|
@ -8,7 +8,7 @@ import { Disposable } from 'vs/base/common/lifecycle';
|
|||
import { INotebookEditor, INotebookEditorContribution } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { registerNotebookContribution } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions';
|
||||
import { NotebookCellExecutionState } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { ICellExecutionEntry, INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService';
|
||||
import { INotebookCellExecution, INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService';
|
||||
|
||||
export class ExecutionEditorProgressController extends Disposable implements INotebookEditorContribution {
|
||||
static id: string = 'workbench.notebook.executionEditorProgress';
|
||||
|
@ -40,7 +40,7 @@ export class ExecutionEditorProgressController extends Disposable implements INo
|
|||
|
||||
const executing = this._notebookExecutionStateService.getCellExecutionStatesForNotebook(this._notebookEditor.textModel?.uri)
|
||||
.filter(exe => exe.state === NotebookCellExecutionState.Executing);
|
||||
const executionIsVisible = (exe: ICellExecutionEntry) => {
|
||||
const executionIsVisible = (exe: INotebookCellExecution) => {
|
||||
for (const range of this._notebookEditor.visibleRanges) {
|
||||
for (const cell of this._notebookEditor.getCellsInRange(range)) {
|
||||
if (cell.handle === exe.cellHandle) {
|
||||
|
|
|
@ -447,7 +447,7 @@ export class NotebookCellOutline extends Disposable implements IOutline<OutlineE
|
|||
preview = localize('empty', "empty cell");
|
||||
}
|
||||
|
||||
const exeState = !isMarkdown && this._notebookExecutionStateService.getCellExecutionState(cell.uri);
|
||||
const exeState = !isMarkdown && this._notebookExecutionStateService.getCellExecution(cell.uri);
|
||||
entries.push(new OutlineEntry(entries.length, 7, cell, preview, !!exeState, exeState ? exeState.isPaused : false));
|
||||
}
|
||||
|
||||
|
|
|
@ -213,7 +213,7 @@ registerAction2(class ClearCellOutputsAction extends NotebookCellAction {
|
|||
|
||||
editor.textModel.applyEdits([{ editType: CellEditType.Output, index, outputs: [] }], true, undefined, () => undefined, undefined);
|
||||
|
||||
const runState = notebookExecutionStateService.getCellExecutionState(context.cell.uri)?.state;
|
||||
const runState = notebookExecutionStateService.getCellExecution(context.cell.uri)?.state;
|
||||
if (runState !== NotebookCellExecutionState.Executing) {
|
||||
context.notebookEditor.textModel.applyEdits([{
|
||||
editType: CellEditType.PartialInternalMetadata, index, internalMetadata: {
|
||||
|
@ -272,7 +272,7 @@ registerAction2(class ClearAllCellOutputsAction extends NotebookAction {
|
|||
})), true, undefined, () => undefined, undefined);
|
||||
|
||||
const clearExecutionMetadataEdits = editor.textModel.cells.map((cell, index) => {
|
||||
const runState = notebookExecutionStateService.getCellExecutionState(cell.uri)?.state;
|
||||
const runState = notebookExecutionStateService.getCellExecution(cell.uri)?.state;
|
||||
if (runState !== NotebookCellExecutionState.Executing) {
|
||||
return {
|
||||
editType: CellEditType.PartialInternalMetadata, index, internalMetadata: {
|
||||
|
|
|
@ -89,6 +89,7 @@ import 'vs/workbench/contrib/notebook/browser/contrib/troubleshoot/layout';
|
|||
import 'vs/workbench/contrib/notebook/browser/contrib/codeRenderer/codeRenderer';
|
||||
import 'vs/workbench/contrib/notebook/browser/contrib/breakpoints/notebookBreakpoints';
|
||||
import 'vs/workbench/contrib/notebook/browser/contrib/execute/executionEditorProgress';
|
||||
import 'vs/workbench/contrib/notebook/browser/contrib/execute/execution';
|
||||
|
||||
// Diff Editor Contribution
|
||||
import 'vs/workbench/contrib/notebook/browser/diff/notebookDiffActions';
|
||||
|
|
|
@ -47,7 +47,7 @@ export class NotebookExecutionService implements INotebookExecutionService {
|
|||
|
||||
const cellHandles: number[] = [];
|
||||
for (const cell of cellsArr) {
|
||||
const cellExe = this._notebookExecutionStateService.getCellExecutionState(cell.uri);
|
||||
const cellExe = this._notebookExecutionStateService.getCellExecution(cell.uri);
|
||||
if (cell.cellKind !== CellKind.Code || !!cellExe) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { combinedDisposable, Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ResourceMap } from 'vs/base/common/map';
|
||||
import { isEqual } from 'vs/base/common/resources';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
@ -13,110 +13,135 @@ import { ILogService } from 'vs/platform/log/common/log';
|
|||
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
|
||||
import { CellEditType, CellUri, ICellEditOperation, NotebookCellExecutionState, NotebookCellInternalMetadata, NotebookTextModelWillAddRemoveEvent } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { CellExecutionUpdateType, INotebookExecutionService } from 'vs/workbench/contrib/notebook/common/notebookExecutionService';
|
||||
import { ICellExecuteUpdate, ICellExecutionComplete, ICellExecutionEntry, ICellExecutionStateChangedEvent, ICellExecutionStateUpdate, INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService';
|
||||
import { ICellExecuteUpdate, ICellExecutionComplete, ICellExecutionStateChangedEvent, ICellExecutionStateUpdate, INotebookCellExecution, INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService';
|
||||
import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService';
|
||||
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
|
||||
|
||||
export class NotebookExecutionStateService extends Disposable implements INotebookExecutionStateService {
|
||||
declare _serviceBrand: undefined;
|
||||
|
||||
private readonly _executions = new ResourceMap<NotebookExecution>();
|
||||
private readonly _executions = new ResourceMap<Map<number, CellExecution>>();
|
||||
private readonly _notebookListeners = new ResourceMap<NotebookExecutionListeners>();
|
||||
private readonly _cellListeners = new ResourceMap<IDisposable>();
|
||||
|
||||
private readonly _onDidChangeCellExecution = new Emitter<ICellExecutionStateChangedEvent>();
|
||||
private readonly _onDidChangeCellExecution = this._register(new Emitter<ICellExecutionStateChangedEvent>());
|
||||
onDidChangeCellExecution = this._onDidChangeCellExecution.event;
|
||||
|
||||
constructor(
|
||||
@INotebookKernelService private readonly _notebookKernelService: INotebookKernelService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@ILogService private readonly _logService: ILogService,
|
||||
@INotebookService private readonly _notebookService: INotebookService,
|
||||
) {
|
||||
super();
|
||||
|
||||
this._register(this._notebookKernelService.onDidChangeSelectedNotebooks(e => {
|
||||
if (e.newKernel) {
|
||||
const notebookExecution = this._executions.get(e.notebook);
|
||||
if (notebookExecution) {
|
||||
notebookExecution.cancelAll();
|
||||
this.checkNotebookExecutionEmpty(e.notebook);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
getCellExecutionState(cellUri: URI): ICellExecutionEntry | undefined {
|
||||
forceCancelNotebookExecutions(notebookUri: URI): void {
|
||||
const notebookExecutions = this._executions.get(notebookUri);
|
||||
if (!notebookExecutions) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const exe of notebookExecutions.values()) {
|
||||
this._onCellExecutionDidComplete(notebookUri, exe.cellHandle, exe);
|
||||
}
|
||||
}
|
||||
|
||||
getCellExecution(cellUri: URI): INotebookCellExecution | undefined {
|
||||
const parsed = CellUri.parse(cellUri);
|
||||
if (!parsed) {
|
||||
throw new Error(`Not a cell URI: ${cellUri}`);
|
||||
}
|
||||
|
||||
const exe = this._executions.get(parsed.notebook);
|
||||
if (exe) {
|
||||
return exe.getCellExecution(parsed.handle);
|
||||
const exeMap = this._executions.get(parsed.notebook);
|
||||
if (exeMap) {
|
||||
return exeMap.get(parsed.handle);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getCellExecutionStatesForNotebook(notebook: URI): ICellExecutionEntry[] {
|
||||
const exe = this._executions.get(notebook);
|
||||
return (exe?.getCellExecutions() ?? []);
|
||||
getCellExecutionStatesForNotebook(notebook: URI): INotebookCellExecution[] {
|
||||
const exeMap = this._executions.get(notebook);
|
||||
return exeMap ? Array.from(exeMap.values()) : [];
|
||||
}
|
||||
|
||||
createNotebookCellExecution(notebook: URI, cellHandle: number): void {
|
||||
let notebookExecution = this._executions.get(notebook);
|
||||
if (!notebookExecution) {
|
||||
notebookExecution = this._instantiationService.createInstance(NotebookExecution, notebook);
|
||||
this._executions.set(notebook, notebookExecution);
|
||||
}
|
||||
|
||||
const exe = notebookExecution.addExecution(cellHandle);
|
||||
this._onDidChangeCellExecution.fire(new NotebookExecutionEvent(notebook, cellHandle, exe));
|
||||
private _onCellExecutionDidChange(notebookUri: URI, cellHandle: number, exe: CellExecution): void {
|
||||
this._onDidChangeCellExecution.fire(new NotebookExecutionEvent(notebookUri, cellHandle, exe));
|
||||
}
|
||||
|
||||
updateNotebookCellExecution(notebook: URI, cellHandle: number, updates: ICellExecuteUpdate[]): void {
|
||||
const notebookExecution = this._executions.get(notebook);
|
||||
if (!notebookExecution) {
|
||||
this._logService.error(`notebook execution not found for ${notebook}`);
|
||||
private _onCellExecutionDidComplete(notebookUri: URI, cellHandle: number, exe: CellExecution): void {
|
||||
const notebookExecutions = this._executions.get(notebookUri);
|
||||
if (!notebookExecutions) {
|
||||
this._logService.debug(`NotebookExecutionStateService#_onCellExecutionDidComplete - unknown notebook ${notebookUri.toString()}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const exe = notebookExecution.updateExecution(cellHandle, updates);
|
||||
if (exe) {
|
||||
this._onDidChangeCellExecution.fire(new NotebookExecutionEvent(notebook, cellHandle, exe));
|
||||
exe.dispose();
|
||||
const cellUri = CellUri.generate(notebookUri, cellHandle);
|
||||
this._cellListeners.get(cellUri)?.dispose();
|
||||
this._cellListeners.delete(cellUri);
|
||||
notebookExecutions.delete(cellHandle);
|
||||
if (notebookExecutions.size === 0) {
|
||||
this._executions.delete(notebookUri);
|
||||
this._notebookListeners.get(notebookUri)?.dispose();
|
||||
this._notebookListeners.delete(notebookUri);
|
||||
}
|
||||
|
||||
this._onDidChangeCellExecution.fire(new NotebookExecutionEvent(notebookUri, cellHandle));
|
||||
}
|
||||
|
||||
completeNotebookCellExecution(notebook: URI, cellHandle: number, complete: ICellExecutionComplete): void {
|
||||
const notebookExecution = this._executions.get(notebook);
|
||||
if (!notebookExecution) {
|
||||
this._logService.error(`notebook execution not found for ${notebook}`);
|
||||
return;
|
||||
createCellExecution(controllerId: string, notebookUri: URI, cellHandle: number): INotebookCellExecution {
|
||||
const notebook = this._notebookService.getNotebookTextModel(notebookUri);
|
||||
if (!notebook) {
|
||||
throw new Error(`Notebook not found: ${notebookUri.toString()}`);
|
||||
}
|
||||
|
||||
const exe = notebookExecution.completeExecution(cellHandle, complete);
|
||||
if (exe) {
|
||||
this.checkNotebookExecutionEmpty(notebook);
|
||||
this._onDidChangeCellExecution.fire(new NotebookExecutionEvent(notebook, cellHandle));
|
||||
const kernel = this._notebookKernelService.getMatchingKernel(notebook);
|
||||
if (!kernel.selected || kernel.selected.id !== controllerId) {
|
||||
throw new Error(`Kernel is not selected: ${kernel.selected?.id} !== ${controllerId}`);
|
||||
}
|
||||
|
||||
let notebookExecutionMap = this._executions.get(notebookUri);
|
||||
if (!notebookExecutionMap) {
|
||||
const listeners = this._instantiationService.createInstance(NotebookExecutionListeners, notebookUri);
|
||||
this._notebookListeners.set(notebookUri, listeners);
|
||||
|
||||
notebookExecutionMap = new Map<number, CellExecution>();
|
||||
this._executions.set(notebookUri, notebookExecutionMap);
|
||||
}
|
||||
|
||||
let exe = notebookExecutionMap.get(cellHandle);
|
||||
if (!exe) {
|
||||
exe = this._createNotebookCellExecution(notebook, cellHandle);
|
||||
notebookExecutionMap.set(cellHandle, exe);
|
||||
}
|
||||
|
||||
return exe;
|
||||
}
|
||||
|
||||
private checkNotebookExecutionEmpty(notebook: URI): void {
|
||||
const notebookExecution = this._executions.get(notebook);
|
||||
if (!notebookExecution) {
|
||||
return;
|
||||
}
|
||||
private _createNotebookCellExecution(notebook: NotebookTextModel, cellHandle: number): CellExecution {
|
||||
const notebookUri = notebook.uri;
|
||||
const exe: CellExecution = this._instantiationService.createInstance(CellExecution, cellHandle, notebook);
|
||||
const disposable = combinedDisposable(
|
||||
exe.onDidUpdate(() => this._onCellExecutionDidChange(notebookUri, cellHandle, exe)),
|
||||
exe.onDidComplete(() => this._onCellExecutionDidComplete(notebookUri, cellHandle, exe)));
|
||||
this._cellListeners.set(CellUri.generate(notebookUri, cellHandle), disposable);
|
||||
|
||||
if (notebookExecution.isEmpty()) {
|
||||
this._logService.debug(`NotebookExecution#dispose ${notebook.toString()}`);
|
||||
notebookExecution.dispose();
|
||||
this._executions.delete(notebook);
|
||||
}
|
||||
this._onDidChangeCellExecution.fire(new NotebookExecutionEvent(notebookUri, cellHandle, exe));
|
||||
return exe;
|
||||
}
|
||||
|
||||
override dispose(): void {
|
||||
super.dispose();
|
||||
this._executions.forEach(e => e.dispose());
|
||||
this._executions.forEach(executionMap => {
|
||||
executionMap.forEach(execution => execution.dispose());
|
||||
executionMap.clear();
|
||||
});
|
||||
this._executions.clear();
|
||||
|
||||
this._cellListeners.forEach(disposable => disposable.dispose());
|
||||
this._notebookListeners.forEach(disposable => disposable.dispose());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -137,16 +162,14 @@ class NotebookExecutionEvent implements ICellExecutionStateChangedEvent {
|
|||
}
|
||||
}
|
||||
|
||||
class NotebookExecution extends Disposable {
|
||||
class NotebookExecutionListeners extends Disposable {
|
||||
private readonly _notebookModel: NotebookTextModel;
|
||||
|
||||
private readonly _cellExecutions = new Map<number, CellExecution>();
|
||||
|
||||
constructor(
|
||||
notebook: URI,
|
||||
@INotebookService private readonly _notebookService: INotebookService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@INotebookExecutionService private readonly _notebookExecutionService: INotebookExecutionService,
|
||||
@INotebookExecutionStateService private readonly _notebookExecutionStateService: INotebookExecutionStateService,
|
||||
@ILogService private readonly _logService: ILogService,
|
||||
) {
|
||||
super();
|
||||
|
@ -162,66 +185,10 @@ class NotebookExecution extends Disposable {
|
|||
this._register(this._notebookModel.onWillDispose(() => this.onWillDisposeDocument()));
|
||||
}
|
||||
|
||||
getCellExecution(cellHandle: number): CellExecution | undefined {
|
||||
return this._cellExecutions.get(cellHandle);
|
||||
}
|
||||
|
||||
getCellExecutions(): CellExecution[] {
|
||||
return Array.from(this._cellExecutions.values());
|
||||
}
|
||||
|
||||
private getCellLog(cellHandle: number): string {
|
||||
return `${this._notebookModel.uri.toString()}, ${cellHandle}`;
|
||||
}
|
||||
|
||||
isEmpty(): boolean {
|
||||
return this._cellExecutions.size === 0;
|
||||
}
|
||||
|
||||
cancelAll(): void {
|
||||
this._logService.debug(`NotebookExecution#cancelAll`);
|
||||
this._notebookExecutionService.cancelNotebookCellHandles(this._notebookModel, this._cellExecutions.keys());
|
||||
}
|
||||
|
||||
addExecution(cellHandle: number): CellExecution {
|
||||
this._logService.debug(`NotebookExecution#addExecution ${this.getCellLog(cellHandle)}`);
|
||||
const execution = this._instantiationService.createInstance(CellExecution, cellHandle, this._notebookModel);
|
||||
this._cellExecutions.set(cellHandle, execution);
|
||||
return execution;
|
||||
}
|
||||
|
||||
updateExecution(cellHandle: number, updates: ICellExecuteUpdate[]): CellExecution | undefined {
|
||||
this.logUpdates(cellHandle, updates);
|
||||
const execution = this._cellExecutions.get(cellHandle);
|
||||
if (!execution) {
|
||||
this._logService.error(`no execution for cell ${cellHandle}`);
|
||||
return;
|
||||
}
|
||||
|
||||
execution.update(updates);
|
||||
return execution;
|
||||
}
|
||||
|
||||
private logUpdates(cellHandle: number, updates: ICellExecuteUpdate[]): void {
|
||||
const updateTypes = updates.map(u => CellExecutionUpdateType[u.editType]).join(', ');
|
||||
this._logService.debug(`NotebookExecution#updateExecution ${this.getCellLog(cellHandle)}, [${updateTypes}]`);
|
||||
}
|
||||
|
||||
completeExecution(cellHandle: number, complete: ICellExecutionComplete): CellExecution | undefined {
|
||||
this._logService.debug(`NotebookExecution#completeExecution ${this.getCellLog(cellHandle)}`);
|
||||
|
||||
const execution = this._cellExecutions.get(cellHandle);
|
||||
if (!execution) {
|
||||
this._logService.error(`no execution for cell ${cellHandle}`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
execution.complete(complete);
|
||||
return execution;
|
||||
} finally {
|
||||
this._cellExecutions.delete(cellHandle);
|
||||
}
|
||||
private cancelAll(): void {
|
||||
this._logService.debug(`NotebookExecutionListeners#cancelAll`);
|
||||
const exes = this._notebookExecutionStateService.getCellExecutionStatesForNotebook(this._notebookModel.uri);
|
||||
this._notebookExecutionService.cancelNotebookCellHandles(this._notebookModel, exes.map(exe => exe.cellHandle));
|
||||
}
|
||||
|
||||
private onWillDisposeDocument(): void {
|
||||
|
@ -230,7 +197,8 @@ class NotebookExecution extends Disposable {
|
|||
}
|
||||
|
||||
private onWillAddRemoveCells(e: NotebookTextModelWillAddRemoveEvent): void {
|
||||
const handles = new Set(this._cellExecutions.keys());
|
||||
const notebookExes = this._notebookExecutionStateService.getCellExecutionStatesForNotebook(this._notebookModel.uri);
|
||||
const handles = new Set(notebookExes.map(exe => exe.cellHandle));
|
||||
const myDeletedHandles = new Set<number>();
|
||||
e.rawEvent.changes.forEach(([start, deleteCount]) => {
|
||||
if (deleteCount) {
|
||||
|
@ -256,7 +224,7 @@ function updateToEdit(update: ICellExecuteUpdate, cellHandle: number): ICellEdit
|
|||
if (update.editType === CellExecutionUpdateType.Output) {
|
||||
return {
|
||||
editType: CellEditType.Output,
|
||||
handle: update.cellHandle,
|
||||
handle: cellHandle,
|
||||
append: update.append,
|
||||
outputs: update.outputs,
|
||||
};
|
||||
|
@ -268,7 +236,7 @@ function updateToEdit(update: ICellExecuteUpdate, cellHandle: number): ICellEdit
|
|||
outputId: update.outputId
|
||||
};
|
||||
} else if (update.editType === CellExecutionUpdateType.ExecutionState) {
|
||||
const newInternalMetadata: Partial<NotebookCellInternalMetadata> = { };
|
||||
const newInternalMetadata: Partial<NotebookCellInternalMetadata> = {};
|
||||
if (typeof update.executionOrder !== 'undefined') {
|
||||
newInternalMetadata.executionOrder = update.executionOrder;
|
||||
}
|
||||
|
@ -285,7 +253,13 @@ function updateToEdit(update: ICellExecuteUpdate, cellHandle: number): ICellEdit
|
|||
throw new Error('Unknown cell update type');
|
||||
}
|
||||
|
||||
class CellExecution implements ICellExecutionEntry {
|
||||
class CellExecution extends Disposable implements INotebookCellExecution {
|
||||
private readonly _onDidUpdate = this._register(new Emitter<void>());
|
||||
readonly onDidUpdate = this._onDidUpdate.event;
|
||||
|
||||
private readonly _onDidComplete = this._register(new Emitter<void>());
|
||||
readonly onDidComplete = this._onDidComplete.event;
|
||||
|
||||
private _state: NotebookCellExecutionState = NotebookCellExecutionState.Pending;
|
||||
get state() {
|
||||
return this._state;
|
||||
|
@ -310,6 +284,7 @@ class CellExecution implements ICellExecutionEntry {
|
|||
private readonly _notebookModel: NotebookTextModel,
|
||||
@ILogService private readonly _logService: ILogService,
|
||||
) {
|
||||
super();
|
||||
const startExecuteEdit: ICellEditOperation = {
|
||||
editType: CellEditType.PartialInternalMetadata,
|
||||
handle: this.cellHandle,
|
||||
|
@ -323,7 +298,17 @@ class CellExecution implements ICellExecutionEntry {
|
|||
this._applyExecutionEdits([startExecuteEdit]);
|
||||
}
|
||||
|
||||
private getCellLog(cellHandle: number): string {
|
||||
return `${this._notebookModel.uri.toString()}, ${cellHandle}`;
|
||||
}
|
||||
|
||||
private logUpdates(cellHandle: number, updates: ICellExecuteUpdate[]): void {
|
||||
const updateTypes = updates.map(u => CellExecutionUpdateType[u.editType]).join(', ');
|
||||
this._logService.debug(`NotebookExecution#updateExecution ${this.getCellLog(cellHandle)}, [${updateTypes}]`);
|
||||
}
|
||||
|
||||
update(updates: ICellExecuteUpdate[]): void {
|
||||
this.logUpdates(this.cellHandle, updates);
|
||||
if (updates.some(u => u.editType === CellExecutionUpdateType.ExecutionState)) {
|
||||
this._state = NotebookCellExecutionState.Executing;
|
||||
}
|
||||
|
@ -337,27 +322,35 @@ class CellExecution implements ICellExecutionEntry {
|
|||
this._isPaused = (lastIsPausedUpdate as ICellExecutionStateUpdate).isPaused!;
|
||||
}
|
||||
|
||||
const edits = updates.map(update => updateToEdit(update, this.cellHandle));
|
||||
this._applyExecutionEdits(edits);
|
||||
const cellModel = this._notebookModel.cells.find(c => c.handle === this.cellHandle);
|
||||
if (!cellModel) {
|
||||
this._logService.debug(`CellExecution#update, updating cell not in notebook: ${this._notebookModel.uri.toString()}, ${this.cellHandle}`);
|
||||
} else {
|
||||
const edits = updates.map(update => updateToEdit(update, this.cellHandle));
|
||||
this._applyExecutionEdits(edits);
|
||||
}
|
||||
|
||||
this._onDidUpdate.fire();
|
||||
}
|
||||
|
||||
complete(completionData: ICellExecutionComplete): void {
|
||||
const cellModel = this._notebookModel.cells.find(c => c.handle === this.cellHandle);
|
||||
if (!cellModel) {
|
||||
this._logService.debug(`CellExecution#complete, updating cell not in notebook: ${this._notebookModel.uri.toString()}, ${this.cellHandle}`);
|
||||
return;
|
||||
this._logService.debug(`CellExecution#complete, completing cell not in notebook: ${this._notebookModel.uri.toString()}, ${this.cellHandle}`);
|
||||
} else {
|
||||
const edit: ICellEditOperation = {
|
||||
editType: CellEditType.PartialInternalMetadata,
|
||||
handle: this.cellHandle,
|
||||
internalMetadata: {
|
||||
lastRunSuccess: completionData.lastRunSuccess,
|
||||
runStartTime: this._didPause ? null : cellModel.internalMetadata.runStartTime,
|
||||
runEndTime: this._didPause ? null : completionData.runEndTime,
|
||||
}
|
||||
};
|
||||
this._applyExecutionEdits([edit]);
|
||||
}
|
||||
|
||||
const edit: ICellEditOperation = {
|
||||
editType: CellEditType.PartialInternalMetadata,
|
||||
handle: this.cellHandle,
|
||||
internalMetadata: {
|
||||
lastRunSuccess: completionData.lastRunSuccess,
|
||||
runStartTime: this._didPause ? null : cellModel.internalMetadata.runStartTime,
|
||||
runEndTime: this._didPause ? null : completionData.runEndTime,
|
||||
}
|
||||
};
|
||||
this._applyExecutionEdits([edit]);
|
||||
this._onDidComplete.fire();
|
||||
}
|
||||
|
||||
private _applyExecutionEdits(edits: ICellEditOperation[]): void {
|
||||
|
|
|
@ -127,7 +127,7 @@ export class CellContextKeyManager extends Disposable {
|
|||
const internalMetadata = this.element.internalMetadata;
|
||||
this.cellEditable.set(!this.notebookEditor.isReadOnly);
|
||||
|
||||
const exeState = this._notebookExecutionStateService.getCellExecutionState(this.element.uri);
|
||||
const exeState = this._notebookExecutionStateService.getCellExecution(this.element.uri);
|
||||
if (this.element instanceof MarkupCellViewModel) {
|
||||
this.cellRunState.reset();
|
||||
this.cellExecuting.reset();
|
||||
|
|
|
@ -48,7 +48,7 @@ export class CellProgressBar extends CellPart {
|
|||
}
|
||||
|
||||
if (e.inputCollapsedChanged) {
|
||||
const exeState = this._notebookExecutionStateService.getCellExecutionState(element.uri);
|
||||
const exeState = this._notebookExecutionStateService.getCellExecution(element.uri);
|
||||
if (element.isInputCollapsed) {
|
||||
this._progressBar.hide();
|
||||
if (exeState?.state === NotebookCellExecutionState.Executing) {
|
||||
|
@ -64,7 +64,7 @@ export class CellProgressBar extends CellPart {
|
|||
}
|
||||
|
||||
private _updateForExecutionState(element: ICellViewModel, e?: ICellExecutionStateChangedEvent): void {
|
||||
const exeState = e?.changed ?? this._notebookExecutionStateService.getCellExecutionState(element.uri);
|
||||
const exeState = e?.changed ?? this._notebookExecutionStateService.getCellExecution(element.uri);
|
||||
const progressBar = element.isInputCollapsed ? this._collapsedProgressBar : this._progressBar;
|
||||
if (exeState?.state === NotebookCellExecutionState.Executing && !exeState.isPaused) {
|
||||
showProgressBar(progressBar);
|
||||
|
|
|
@ -15,7 +15,6 @@ export enum CellExecutionUpdateType {
|
|||
|
||||
export interface ICellExecuteOutputEdit {
|
||||
editType: CellExecutionUpdateType.Output;
|
||||
cellHandle: number;
|
||||
append?: boolean;
|
||||
outputs: IOutputDto[]
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ export interface ICellExecutionEntry {
|
|||
export interface ICellExecutionStateChangedEvent {
|
||||
notebook: URI;
|
||||
cellHandle: number;
|
||||
changed?: ICellExecutionEntry; // undefined -> execution was completed
|
||||
changed?: INotebookCellExecution; // undefined -> execution was completed
|
||||
affectsCell(cell: URI): boolean;
|
||||
affectsNotebook(notebook: URI): boolean;
|
||||
}
|
||||
|
@ -47,11 +47,19 @@ export interface INotebookExecutionStateService {
|
|||
|
||||
onDidChangeCellExecution: Event<ICellExecutionStateChangedEvent>;
|
||||
|
||||
getCellExecutionStatesForNotebook(notebook: URI): ICellExecutionEntry[];
|
||||
|
||||
getCellExecutionState(cellUri: URI): ICellExecutionEntry | undefined;
|
||||
|
||||
createNotebookCellExecution(notebook: URI, cellHandle: number): void;
|
||||
updateNotebookCellExecution(notebook: URI, cellHandle: number, updates: ICellExecuteUpdate[]): void;
|
||||
completeNotebookCellExecution(notebook: URI, cellHandle: number, complete: ICellExecutionComplete): void;
|
||||
forceCancelNotebookExecutions(notebookUri: URI): void
|
||||
getCellExecutionStatesForNotebook(notebook: URI): INotebookCellExecution[];
|
||||
getCellExecution(cellUri: URI): INotebookCellExecution | undefined;
|
||||
createCellExecution(controllerId: string, notebook: URI, cellHandle: number): INotebookCellExecution;
|
||||
}
|
||||
|
||||
export interface INotebookCellExecution {
|
||||
readonly notebook: URI;
|
||||
readonly cellHandle: number;
|
||||
readonly state: NotebookCellExecutionState;
|
||||
readonly didPause: boolean;
|
||||
readonly isPaused: boolean;
|
||||
|
||||
update(updates: ICellExecuteUpdate[]): void;
|
||||
complete(complete: ICellExecutionComplete): void;
|
||||
}
|
||||
|
|
|
@ -479,7 +479,7 @@ export class NotebookOptions extends Disposable {
|
|||
}
|
||||
|
||||
private statusBarIsVisible(internalMetadata: NotebookCellInternalMetadata, cellUri: URI): boolean {
|
||||
const exe = this.notebookExecutionStateService.getCellExecutionState(cellUri);
|
||||
const exe = this.notebookExecutionStateService.getCellExecution(cellUri);
|
||||
if (this._layoutConfiguration.showCellStatusBar === 'visible') {
|
||||
return true;
|
||||
} else if (this._layoutConfiguration.showCellStatusBar === 'visibleAfterExecute') {
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
@ -20,6 +19,7 @@ import { NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewMod
|
|||
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
|
||||
import { CellEditType, CellKind, IOutputDto, NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { INotebookExecutionService } from 'vs/workbench/contrib/notebook/common/notebookExecutionService';
|
||||
import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService';
|
||||
import { INotebookKernel, INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService';
|
||||
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
|
||||
import { setupInstantiationService, withTestNotebook as _withTestNotebook } from 'vs/workbench/contrib/notebook/test/browser/testNotebookEditor';
|
||||
|
@ -29,6 +29,7 @@ suite('NotebookExecutionStateService', () => {
|
|||
let instantiationService: TestInstantiationService;
|
||||
let kernelService: INotebookKernelService;
|
||||
let disposables: DisposableStore;
|
||||
let testNotebookModel: NotebookTextModel | undefined;
|
||||
|
||||
setup(function () {
|
||||
|
||||
|
@ -40,11 +41,15 @@ suite('NotebookExecutionStateService', () => {
|
|||
override onDidAddNotebookDocument = Event.None;
|
||||
override onWillRemoveNotebookDocument = Event.None;
|
||||
override getNotebookTextModels() { return []; }
|
||||
override getNotebookTextModel(uri: URI): NotebookTextModel | undefined {
|
||||
return testNotebookModel;
|
||||
}
|
||||
});
|
||||
|
||||
kernelService = instantiationService.createInstance(NotebookKernelService);
|
||||
instantiationService.set(INotebookKernelService, kernelService);
|
||||
instantiationService.set(INotebookExecutionService, instantiationService.createInstance(NotebookExecutionService));
|
||||
instantiationService.set(INotebookExecutionStateService, instantiationService.createInstance(NotebookExecutionStateService));
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
|
@ -55,13 +60,9 @@ suite('NotebookExecutionStateService', () => {
|
|||
return _withTestNotebook(cells, (editor, viewModel) => callback(viewModel, viewModel.notebookDocument));
|
||||
}
|
||||
|
||||
test('cancel execution when cell is deleted', async function () {
|
||||
test('cancel execution when cell is deleted', async function () { // TODO@roblou Should be a test for NotebookExecutionListeners, which can be a standalone contribution
|
||||
return withTestNotebook([], async viewModel => {
|
||||
instantiationService.stub(INotebookService, new class extends mock<INotebookService>() {
|
||||
override getNotebookTextModel(uri: URI): NotebookTextModel | undefined {
|
||||
return viewModel.notebookDocument;
|
||||
}
|
||||
});
|
||||
testNotebookModel = viewModel.notebookDocument;
|
||||
|
||||
let didCancel = false;
|
||||
const kernel = new class extends TestNotebookKernel {
|
||||
|
@ -76,19 +77,64 @@ suite('NotebookExecutionStateService', () => {
|
|||
}
|
||||
};
|
||||
kernelService.registerKernel(kernel);
|
||||
kernelService.selectKernelForNotebook(kernel, viewModel.notebookDocument);
|
||||
|
||||
const executionStateService: NotebookExecutionStateService = instantiationService.createInstance(NotebookExecutionStateService);
|
||||
const executionStateService: INotebookExecutionStateService = instantiationService.get(INotebookExecutionStateService);
|
||||
|
||||
const cell = insertCellAtIndex(viewModel, 0, 'var c = 3', 'javascript', CellKind.Code, {}, [], true, true);
|
||||
executionStateService.createNotebookCellExecution(viewModel.uri, cell.handle);
|
||||
executionStateService.createCellExecution(kernel.id, viewModel.uri, cell.handle);
|
||||
assert.strictEqual(didCancel, false);
|
||||
viewModel.notebookDocument.applyEdits([{
|
||||
editType: CellEditType.Replace, index: 0, count: 1, cells: []
|
||||
}], true, undefined, () => undefined, undefined, false);
|
||||
await timeout(0);
|
||||
assert.strictEqual(didCancel, true);
|
||||
});
|
||||
});
|
||||
|
||||
test('fires onDidChangeCellExecution when cell is completed while deleted', async function () {
|
||||
return withTestNotebook([], async viewModel => {
|
||||
testNotebookModel = viewModel.notebookDocument;
|
||||
|
||||
const kernel = new TestNotebookKernel();
|
||||
kernelService.registerKernel(kernel);
|
||||
kernelService.selectKernelForNotebook(kernel, viewModel.notebookDocument);
|
||||
|
||||
const executionStateService: INotebookExecutionStateService = instantiationService.get(INotebookExecutionStateService);
|
||||
const cell = insertCellAtIndex(viewModel, 0, 'var c = 3', 'javascript', CellKind.Code, {}, [], true, true);
|
||||
const exe = executionStateService.createCellExecution(kernel.id, viewModel.uri, cell.handle);
|
||||
|
||||
let didFire = false;
|
||||
disposables.add(executionStateService.onDidChangeCellExecution(e => {
|
||||
didFire = !e.changed;
|
||||
}));
|
||||
|
||||
viewModel.notebookDocument.applyEdits([{
|
||||
editType: CellEditType.Replace, index: 0, count: 1, cells: []
|
||||
}], true, undefined, () => undefined, undefined, false);
|
||||
exe.complete({});
|
||||
assert.strictEqual(didFire, true);
|
||||
});
|
||||
});
|
||||
|
||||
test('force-cancel works', async function () {
|
||||
return withTestNotebook([], async viewModel => {
|
||||
testNotebookModel = viewModel.notebookDocument;
|
||||
|
||||
const kernel = new TestNotebookKernel();
|
||||
kernelService.registerKernel(kernel);
|
||||
kernelService.selectKernelForNotebook(kernel, viewModel.notebookDocument);
|
||||
|
||||
const executionStateService: INotebookExecutionStateService = instantiationService.get(INotebookExecutionStateService);
|
||||
const cell = insertCellAtIndex(viewModel, 0, 'var c = 3', 'javascript', CellKind.Code, {}, [], true, true);
|
||||
executionStateService.createCellExecution(kernel.id, viewModel.uri, cell.handle);
|
||||
const exe = executionStateService.getCellExecution(cell.uri);
|
||||
assert.ok(exe);
|
||||
|
||||
executionStateService.forceCancelNotebookExecutions(viewModel.uri);
|
||||
const exe2 = executionStateService.getCellExecution(cell.uri);
|
||||
assert.strictEqual(exe2, undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
class TestNotebookKernel implements INotebookKernel {
|
||||
|
@ -110,7 +156,10 @@ class TestNotebookKernel implements INotebookKernel {
|
|||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
constructor(opts?: { languages: string[] }) {
|
||||
constructor(opts?: { languages?: string[], id?: string }) {
|
||||
this.supportedLanguages = opts?.languages ?? [PLAINTEXT_LANGUAGE_ID];
|
||||
if (opts?.id) {
|
||||
this.id = opts?.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/vie
|
|||
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
|
||||
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
|
||||
import { CellKind, CellUri, INotebookDiffEditorModel, INotebookEditorModel, INotebookSearchOptions, IOutputDto, IResolvedNotebookEditorModel, NotebookCellMetadata, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { ICellExecuteUpdate, ICellExecutionComplete, ICellExecutionEntry, ICellExecutionStateChangedEvent, INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService';
|
||||
import { ICellExecuteUpdate, ICellExecutionComplete, ICellExecutionStateChangedEvent, INotebookCellExecution, INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService';
|
||||
import { NotebookOptions } from 'vs/workbench/contrib/notebook/common/notebookOptions';
|
||||
import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange';
|
||||
import { TextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService';
|
||||
|
@ -416,15 +416,19 @@ class TestNotebookExecutionStateService implements INotebookExecutionStateServic
|
|||
|
||||
onDidChangeCellExecution = new Emitter<ICellExecutionStateChangedEvent>().event;
|
||||
|
||||
getCellExecutionStatesForNotebook(notebook: URI): ICellExecutionEntry[] {
|
||||
forceCancelNotebookExecutions(notebookUri: URI): void {
|
||||
}
|
||||
|
||||
getCellExecutionStatesForNotebook(notebook: URI): INotebookCellExecution[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
getCellExecutionState(cellUri: URI): ICellExecutionEntry | undefined {
|
||||
getCellExecution(cellUri: URI): INotebookCellExecution | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
createNotebookCellExecution(notebook: URI, cellHandle: number): void {
|
||||
createCellExecution(controllerId: string, notebook: URI, cellHandle: number): INotebookCellExecution {
|
||||
return undefined!;
|
||||
}
|
||||
|
||||
updateNotebookCellExecution(notebook: URI, cellHandle: number, updates: ICellExecuteUpdate[]): void {
|
||||
|
|
|
@ -6,11 +6,11 @@
|
|||
import * as assert from 'assert';
|
||||
import { Barrier } from 'vs/base/common/async';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
import { ICellExecuteUpdateDto, INotebookKernelDto2, MainContext, MainThreadCommandsShape, MainThreadNotebookDocumentsShape, MainThreadNotebookKernelsShape, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ICellExecuteUpdateDto, ICellExecutionCompleteDto, INotebookKernelDto2, MainContext, MainThreadCommandsShape, MainThreadNotebookDocumentsShape, MainThreadNotebookKernelsShape, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
|
||||
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
|
||||
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
|
@ -43,13 +43,17 @@ suite('NotebookKernel', function () {
|
|||
const kernelData = new Map<number, INotebookKernelDto2>();
|
||||
const disposables = new DisposableStore();
|
||||
|
||||
const cellExecuteCreate: { notebook: UriComponents, cell: number }[] = [];
|
||||
const cellExecuteUpdates: ICellExecuteUpdateDto[] = [];
|
||||
const cellExecuteComplete: ICellExecutionCompleteDto[] = [];
|
||||
|
||||
teardown(function () {
|
||||
disposables.clear();
|
||||
});
|
||||
setup(async function () {
|
||||
cellExecuteCreate.length = 0;
|
||||
cellExecuteUpdates.length = 0;
|
||||
cellExecuteComplete.length = 0;
|
||||
kernelData.clear();
|
||||
|
||||
rpcProtocol = new TestRPCProtocol();
|
||||
|
@ -67,9 +71,15 @@ suite('NotebookKernel', function () {
|
|||
assert.strictEqual(kernelData.has(handle), true);
|
||||
kernelData.set(handle, { ...kernelData.get(handle)!, ...data, });
|
||||
}
|
||||
override $updateExecutions(data: SerializableObjectWithBuffers<ICellExecuteUpdateDto[]>): void {
|
||||
override $createExecution(handle: number, controllerId: string, uri: UriComponents, cellHandle: number): void {
|
||||
cellExecuteCreate.push({ notebook: uri, cell: cellHandle });
|
||||
}
|
||||
override $updateExecution(handle: number, data: SerializableObjectWithBuffers<ICellExecuteUpdateDto[]>): void {
|
||||
cellExecuteUpdates.push(...data.value);
|
||||
}
|
||||
override $completeExecution(handle: number, data: SerializableObjectWithBuffers<ICellExecutionCompleteDto>): void {
|
||||
cellExecuteComplete.push(data.value);
|
||||
}
|
||||
});
|
||||
rpcProtocol.set(MainContext.MainThreadNotebookDocuments, new class extends mock<MainThreadNotebookDocumentsShape>() {
|
||||
|
||||
|
|
Loading…
Reference in a new issue