move NotebookCellExecutionTask into extHostNotebookKernels, fyi @roblourens

This commit is contained in:
Johannes Rieken
2021-05-07 14:27:07 +02:00
parent f9b5f8f624
commit fa378cd02a
2 changed files with 223 additions and 225 deletions

View File

@@ -3,14 +3,13 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { timeout } from 'vs/base/common/async';
import { VSBuffer } from 'vs/base/common/buffer';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Emitter, Event } from 'vs/base/common/event';
import { IRelativePattern } from 'vs/base/common/glob';
import { hash } from 'vs/base/common/hash';
import { IdGenerator } from 'vs/base/common/idGenerator';
import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { ResourceMap } from 'vs/base/common/map';
import { isFalsyOrWhitespace } from 'vs/base/common/strings';
import { assertIsDefined } from 'vs/base/common/types';
@@ -25,7 +24,7 @@ import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocum
import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths';
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
import { CellEditType, IImmediateCellEditOperation, INotebookExclusiveDocumentFilter, INotebookContributionData, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookDataDto, NullablePartialNotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { INotebookExclusiveDocumentFilter, INotebookContributionData, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookDataDto } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import type * as vscode from 'vscode';
import { ExtHostCell, ExtHostNotebookDocument } from './extHostNotebookDocument';
import { ExtHostNotebookEditor } from './extHostNotebookEditor';
@@ -103,8 +102,6 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
private _onDidChangeVisibleNotebookEditors = new Emitter<vscode.NotebookEditor[]>();
onDidChangeVisibleNotebookEditors = this._onDidChangeVisibleNotebookEditors.event;
private _activeExecutions = new ResourceMap<NotebookCellExecutionTask>();
private _statusBarCache = new Cache<IDisposable>('NotebookCellStatusBarCache');
constructor(
@@ -380,11 +377,6 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
return VSBuffer.wrap(bytes);
}
cancelOneNotebookCellExecution(cell: ExtHostCell): void {
const execution = this._activeExecutions.get(cell.uri);
execution?.cancel();
}
// --- open, save, saveAs, backup
async $openNotebook(viewType: string, uri: UriComponents, backupId: string | undefined, untitledDocumentData: VSBuffer | undefined, token: CancellationToken): Promise<NotebookDataDto> {
@@ -642,204 +634,4 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
this._onDidChangeActiveNotebookEditor.fire(this._activeNotebookEditor?.apiEditor);
}
}
createNotebookCellExecution(cell: ExtHostCell): vscode.NotebookCellExecutionTask | undefined {
// TODO@roblou also validate kernelId, once kernel has moved from editor to document
if (this._activeExecutions.has(cell.uri)) {
throw new Error(`duplicate execution for ${cell.uri}`);
}
const execution = new NotebookCellExecutionTask(cell.notebook, cell, this._notebookDocumentsProxy);
this._activeExecutions.set(cell.uri, execution);
const listener = execution.onDidChangeState(() => {
if (execution.state === NotebookCellExecutionTaskState.Resolved) {
execution.dispose();
listener.dispose();
this._activeExecutions.delete(cell.uri);
}
});
return execution.asApiObject();
}
}
enum NotebookCellExecutionTaskState {
Init,
Started,
Resolved
}
class NotebookCellExecutionTask extends Disposable {
private _onDidChangeState = new Emitter<void>();
readonly onDidChangeState = this._onDidChangeState.event;
private _state = NotebookCellExecutionTaskState.Init;
get state(): NotebookCellExecutionTaskState { return this._state; }
private readonly _tokenSource = this._register(new CancellationTokenSource());
private readonly _collector: TimeoutBasedCollector<IImmediateCellEditOperation>;
private _executionOrder: number | undefined;
constructor(
private readonly _document: ExtHostNotebookDocument,
private readonly _cell: ExtHostCell,
private readonly _proxy: MainThreadNotebookDocumentsShape) {
super();
this._collector = new TimeoutBasedCollector(10, edits => this.applyEdits(edits));
this._executionOrder = _cell.internalMetadata.executionOrder;
this.mixinMetadata({
runState: extHostTypes.NotebookCellExecutionState.Pending,
executionOrder: null
});
}
cancel(): void {
this._tokenSource.cancel();
}
private async applyEditSoon(edit: IImmediateCellEditOperation): Promise<void> {
await this._collector.addItem(edit);
}
private async applyEdits(edits: IImmediateCellEditOperation[]): Promise<void> {
return this._proxy.$applyEdits(this._document.uri, edits, false);
}
private verifyStateForOutput() {
if (this._state === NotebookCellExecutionTaskState.Init) {
throw new Error('Must call start before modifying cell output');
}
if (this._state === NotebookCellExecutionTaskState.Resolved) {
throw new Error('Cannot modify cell output after calling resolve');
}
}
private mixinMetadata(mixinMetadata: NullablePartialNotebookCellMetadata) {
const edit: IImmediateCellEditOperation = { editType: CellEditType.PartialMetadata, handle: this._cell.handle, metadata: mixinMetadata };
this.applyEdits([edit]);
}
private cellIndexToHandle(cellIndex: number | undefined): number | undefined {
const cell = typeof cellIndex === 'number' ? this._document.getCellFromIndex(cellIndex) : this._cell;
if (!cell) {
return;
}
return cell.handle;
}
asApiObject(): vscode.NotebookCellExecutionTask {
const that = this;
return Object.freeze(<vscode.NotebookCellExecutionTask>{
get document() { return that._document.apiNotebook; },
get cell() { return that._cell.apiCell; },
get executionOrder() { return that._executionOrder; },
set executionOrder(v: number | undefined) {
that._executionOrder = v;
that.mixinMetadata({
executionOrder: v
});
},
start(context?: vscode.NotebookCellExecuteStartContext): void {
if (that._state === NotebookCellExecutionTaskState.Resolved || that._state === NotebookCellExecutionTaskState.Started) {
throw new Error('Cannot call start again');
}
that._state = NotebookCellExecutionTaskState.Started;
that._onDidChangeState.fire();
that.mixinMetadata({
runState: extHostTypes.NotebookCellExecutionState.Executing,
runStartTime: context?.startTime ?? null
});
},
end(result?: vscode.NotebookCellExecuteEndContext): void {
if (that._state === NotebookCellExecutionTaskState.Resolved) {
throw new Error('Cannot call resolve twice');
}
that._state = NotebookCellExecutionTaskState.Resolved;
that._onDidChangeState.fire();
that.mixinMetadata({
runState: extHostTypes.NotebookCellExecutionState.Idle,
lastRunSuccess: result?.success ?? null,
runEndTime: result?.endTime ?? null,
});
},
clearOutput(cellIndex?: number): Thenable<void> {
that.verifyStateForOutput();
return this.replaceOutput([], cellIndex);
},
async appendOutput(outputs: vscode.NotebookCellOutput | vscode.NotebookCellOutput[], cellIndex?: number): Promise<void> {
that.verifyStateForOutput();
const handle = that.cellIndexToHandle(cellIndex);
if (typeof handle !== 'number') {
return;
}
outputs = Array.isArray(outputs) ? outputs : [outputs];
return that.applyEditSoon({ editType: CellEditType.Output, handle, append: true, outputs: outputs.map(typeConverters.NotebookCellOutput.from) });
},
async replaceOutput(outputs: vscode.NotebookCellOutput | vscode.NotebookCellOutput[], cellIndex?: number): Promise<void> {
that.verifyStateForOutput();
const handle = that.cellIndexToHandle(cellIndex);
if (typeof handle !== 'number') {
return;
}
outputs = Array.isArray(outputs) ? outputs : [outputs];
return that.applyEditSoon({ editType: CellEditType.Output, handle, outputs: outputs.map(typeConverters.NotebookCellOutput.from) });
},
async appendOutputItems(items: vscode.NotebookCellOutputItem | vscode.NotebookCellOutputItem[], outputId: string): Promise<void> {
that.verifyStateForOutput();
items = Array.isArray(items) ? items : [items];
return that.applyEditSoon({ editType: CellEditType.OutputItems, append: true, items: items.map(typeConverters.NotebookCellOutputItem.from), outputId });
},
async replaceOutputItems(items: vscode.NotebookCellOutputItem | vscode.NotebookCellOutputItem[], outputId: string): Promise<void> {
that.verifyStateForOutput();
items = Array.isArray(items) ? items : [items];
return that.applyEditSoon({ editType: CellEditType.OutputItems, items: items.map(typeConverters.NotebookCellOutputItem.from), outputId });
},
token: that._tokenSource.token
});
}
}
class TimeoutBasedCollector<T> {
private batch: T[] = [];
private waitPromise: Promise<void> | undefined;
constructor(
private readonly delay: number,
private readonly callback: (items: T[]) => Promise<void>) { }
addItem(item: T): Promise<void> {
this.batch.push(item);
if (!this.waitPromise) {
this.waitPromise = timeout(this.delay).then(() => {
this.waitPromise = undefined;
const batch = this.batch;
this.batch = [];
return this.callback(batch);
});
}
return this.waitPromise;
}
}