Implement Execution API (#116416)
Implement new execution task API #106744 Fix #105847
This commit is contained in:
parent
5a0fe1f6a8
commit
7b96cc4c8b
|
@ -53,79 +53,106 @@ async function withEvent<T>(event: vscode.Event<T>, callback: (e: Promise<T>) =>
|
|||
await callback(e);
|
||||
}
|
||||
|
||||
const kernel1: vscode.NotebookKernel = {
|
||||
id: 'mainKernel',
|
||||
label: 'Notebook Test Kernel',
|
||||
isPreferred: true,
|
||||
supportedLanguages: ['typescript', 'javascript'],
|
||||
executeAllCells: async (_document: vscode.NotebookDocument) => {
|
||||
const edit = new vscode.WorkspaceEdit();
|
||||
const kernel1 = new class implements vscode.NotebookKernel {
|
||||
readonly id = 'mainKernel';
|
||||
readonly label = 'Notebook Test Kernel';
|
||||
readonly isPreferred = true;
|
||||
readonly supportedLanguages = ['typescript', 'javascript'];
|
||||
|
||||
edit.replaceNotebookCellOutput(_document.uri, 0, [new vscode.NotebookCellOutput([
|
||||
new vscode.NotebookCellOutputItem('text/plain', ['my output'], undefined)
|
||||
])]);
|
||||
return vscode.workspace.applyEdit(edit);
|
||||
},
|
||||
cancelAllCellsExecution: async (_document: vscode.NotebookDocument) => { },
|
||||
executeCell: async (document: vscode.NotebookDocument, cell: vscode.NotebookCell | undefined) => {
|
||||
if (!cell) {
|
||||
cell = document.cells[0];
|
||||
async executeCellsRequest(document: vscode.NotebookDocument, ranges: vscode.NotebookCellRange[]) {
|
||||
if (ranges.length > 1 || ranges[0].start + 1 < ranges[0].end) {
|
||||
// Keeping same behavior... if the full notebook is executed, just execute the first cell
|
||||
const task = vscode.notebook.createNotebookCellExecutionTask(document.uri, 0, 'mainKernel');
|
||||
if (!task) {
|
||||
return;
|
||||
}
|
||||
|
||||
task.start();
|
||||
await task.replaceOutput([new vscode.NotebookCellOutput([
|
||||
new vscode.NotebookCellOutputItem('text/plain', ['my output'], undefined)
|
||||
])]);
|
||||
task.end({ success: true });
|
||||
return;
|
||||
}
|
||||
|
||||
for (let range of ranges) {
|
||||
for (let i = range.start; i < range.end; i++) {
|
||||
await this.runCell(document, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async runCell(document: vscode.NotebookDocument, idx: number) {
|
||||
const task = vscode.notebook.createNotebookCellExecutionTask(document.uri, idx, 'mainKernel');
|
||||
if (!task) {
|
||||
return;
|
||||
}
|
||||
|
||||
task.start();
|
||||
task.executionOrder = 1;
|
||||
if (document.uri.path.endsWith('customRenderer.vsctestnb')) {
|
||||
const edit = new vscode.WorkspaceEdit();
|
||||
edit.replaceNotebookCellOutput(document.uri, cell.index, [new vscode.NotebookCellOutput([
|
||||
await task.replaceOutput([new vscode.NotebookCellOutput([
|
||||
new vscode.NotebookCellOutputItem('text/custom', ['test'], undefined)
|
||||
])]);
|
||||
|
||||
return vscode.workspace.applyEdit(edit);
|
||||
return;
|
||||
}
|
||||
|
||||
const edit = new vscode.WorkspaceEdit();
|
||||
// const previousOutputs = cell.outputs;
|
||||
edit.replaceNotebookCellOutput(document.uri, cell.index, [new vscode.NotebookCellOutput([
|
||||
await task.replaceOutput([new vscode.NotebookCellOutput([
|
||||
new vscode.NotebookCellOutputItem('text/plain', ['my output'], undefined)
|
||||
])]);
|
||||
|
||||
return vscode.workspace.applyEdit(edit);
|
||||
},
|
||||
cancelCellExecution: async (_document: vscode.NotebookDocument, _cell: vscode.NotebookCell) => { }
|
||||
task.end({ success: true });
|
||||
}
|
||||
};
|
||||
|
||||
const kernel2: vscode.NotebookKernel = {
|
||||
id: 'secondaryKernel',
|
||||
label: 'Notebook Secondary Test Kernel',
|
||||
isPreferred: false,
|
||||
supportedLanguages: ['typescript', 'javascript'],
|
||||
executeAllCells: async (_document: vscode.NotebookDocument) => {
|
||||
const edit = new vscode.WorkspaceEdit();
|
||||
edit.replaceNotebookCellOutput(_document.uri, 0, [new vscode.NotebookCellOutput([
|
||||
new vscode.NotebookCellOutputItem('text/plain', ['my second output'], undefined)
|
||||
])]);
|
||||
const kernel2 = new class implements vscode.NotebookKernel {
|
||||
readonly id = 'secondaryKernel';
|
||||
readonly label = 'Notebook Secondary Test Kernel';
|
||||
readonly isPreferred = false;
|
||||
readonly supportedLanguages = ['typescript', 'javascript'];
|
||||
|
||||
return vscode.workspace.applyEdit(edit);
|
||||
},
|
||||
cancelAllCellsExecution: async (_document: vscode.NotebookDocument) => { },
|
||||
executeCell: async (document: vscode.NotebookDocument, cell: vscode.NotebookCell | undefined) => {
|
||||
if (!cell) {
|
||||
cell = document.cells[0];
|
||||
}
|
||||
async executeCellsRequest(document: vscode.NotebookDocument, ranges: vscode.NotebookCellRange[]) {
|
||||
if (ranges.length > 1 || ranges[0].start + 1 < ranges[0].end) {
|
||||
// Keeping same behavior... if the full notebook is executed, just execute the first cell
|
||||
const task = vscode.notebook.createNotebookCellExecutionTask(document.uri, 0, 'secondaryKernel');
|
||||
if (!task) {
|
||||
return;
|
||||
}
|
||||
|
||||
const edit = new vscode.WorkspaceEdit();
|
||||
|
||||
if (document.uri.path.endsWith('customRenderer.vsctestnb')) {
|
||||
edit.replaceNotebookCellOutput(document.uri, cell.index, [new vscode.NotebookCellOutput([
|
||||
new vscode.NotebookCellOutputItem('text/custom', ['test 2'], undefined)
|
||||
])]);
|
||||
} else {
|
||||
edit.replaceNotebookCellOutput(document.uri, cell.index, [new vscode.NotebookCellOutput([
|
||||
task.start();
|
||||
await task.replaceOutput([new vscode.NotebookCellOutput([
|
||||
new vscode.NotebookCellOutputItem('text/plain', ['my second output'], undefined)
|
||||
])]);
|
||||
task.end({ success: true });
|
||||
return;
|
||||
}
|
||||
|
||||
return vscode.workspace.applyEdit(edit);
|
||||
},
|
||||
cancelCellExecution: async (_document: vscode.NotebookDocument, _cell: vscode.NotebookCell) => { }
|
||||
for (let range of ranges) {
|
||||
for (let i = range.start; i < range.end; i++) {
|
||||
await this.runCell(document, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async runCell(document: vscode.NotebookDocument, idx: number) {
|
||||
const task = vscode.notebook.createNotebookCellExecutionTask(document.uri, idx, 'mainKernel');
|
||||
if (!task) {
|
||||
return;
|
||||
}
|
||||
|
||||
task.start();
|
||||
if (document.uri.path.endsWith('customRenderer.vsctestnb')) {
|
||||
task.replaceOutput([new vscode.NotebookCellOutput([
|
||||
new vscode.NotebookCellOutputItem('text/custom', ['test 2'], undefined)
|
||||
])]);
|
||||
task.end({ success: true });
|
||||
return;
|
||||
}
|
||||
|
||||
await task.replaceOutput([new vscode.NotebookCellOutput([
|
||||
new vscode.NotebookCellOutputItem('text/plain', ['my second output'], undefined)
|
||||
])]);
|
||||
task.end({ success: true });
|
||||
}
|
||||
};
|
||||
|
||||
class KernelProvider implements vscode.NotebookKernelProvider {
|
||||
|
@ -133,7 +160,12 @@ class KernelProvider implements vscode.NotebookKernelProvider {
|
|||
onDidChangeKernels = this._onDidChangeKernels.event;
|
||||
|
||||
private _hasKernels = true;
|
||||
private readonly _kernels = [kernel1, kernel2];
|
||||
private readonly _kernels: vscode.NotebookKernel[] = [kernel1, kernel2];
|
||||
|
||||
addKernel(kernel: vscode.NotebookKernel): void {
|
||||
this._kernels.push(kernel);
|
||||
this._onDidChangeKernels.fire(undefined);
|
||||
}
|
||||
|
||||
provideKernels(): vscode.ProviderResult<vscode.NotebookKernel[]> {
|
||||
return this._hasKernels ? this._kernels : [];
|
||||
|
@ -144,11 +176,13 @@ class KernelProvider implements vscode.NotebookKernelProvider {
|
|||
this._onDidChangeKernels.fire(undefined);
|
||||
}
|
||||
}
|
||||
let currentKernerProvider: KernelProvider;
|
||||
|
||||
let currentKernelProvider: KernelProvider;
|
||||
|
||||
suite('Notebook API tests', function () {
|
||||
|
||||
const disposables: vscode.Disposable[] = [];
|
||||
const testDisposables: vscode.Disposable[] = [];
|
||||
const suiteDisposables: vscode.Disposable[] = [];
|
||||
|
||||
suiteTeardown(async function () {
|
||||
|
||||
|
@ -157,12 +191,12 @@ suite('Notebook API tests', function () {
|
|||
await revertAllDirty();
|
||||
await closeAllEditors();
|
||||
|
||||
disposeAll(disposables);
|
||||
disposables.length = 0;
|
||||
disposeAll(suiteDisposables);
|
||||
suiteDisposables.length = 0;
|
||||
});
|
||||
|
||||
suiteSetup(function () {
|
||||
disposables.push(vscode.notebook.registerNotebookContentProvider('notebookCoreTest', {
|
||||
suiteDisposables.push(vscode.notebook.registerNotebookContentProvider('notebookCoreTest', {
|
||||
openNotebook: async (_resource: vscode.Uri): Promise<vscode.NotebookData> => {
|
||||
if (/.*empty\-.*\.vsctestnb$/.test(_resource.path)) {
|
||||
return {
|
||||
|
@ -191,6 +225,7 @@ suite('Notebook API tests', function () {
|
|||
],
|
||||
{ testOutputMetadata: true })
|
||||
],
|
||||
previousResult: { executionOrder: 5, success: true },
|
||||
metadata: new vscode.NotebookCellMetadata().with({ custom: { testCellMetadata: 456 } })
|
||||
}
|
||||
]
|
||||
|
@ -213,9 +248,16 @@ suite('Notebook API tests', function () {
|
|||
};
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
currentKernerProvider = new KernelProvider();
|
||||
disposables.push(vscode.notebook.registerNotebookKernelProvider({ filenamePattern: '*.vsctestnb' }, currentKernerProvider));
|
||||
setup(() => {
|
||||
currentKernelProvider = new KernelProvider();
|
||||
testDisposables.push(vscode.notebook.registerNotebookKernelProvider({ filenamePattern: '*.vsctestnb' }, currentKernelProvider));
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
disposeAll(testDisposables);
|
||||
testDisposables.length = 0;
|
||||
});
|
||||
|
||||
test('shared document in notebook editors', async function () {
|
||||
|
@ -391,12 +433,11 @@ suite('Notebook API tests', function () {
|
|||
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
|
||||
|
||||
await vscode.window.activeNotebookEditor!.edit(editBuilder => {
|
||||
editBuilder.replaceCellMetadata(0, new vscode.NotebookCellMetadata().with({ inputCollapsed: true, executionOrder: 17 }));
|
||||
editBuilder.replaceCellMetadata(0, new vscode.NotebookCellMetadata().with({ inputCollapsed: true }));
|
||||
});
|
||||
|
||||
const document = vscode.window.activeNotebookEditor?.document!;
|
||||
assert.strictEqual(document.cells.length, 2);
|
||||
assert.strictEqual(document.cells[0].metadata.executionOrder, 17);
|
||||
assert.strictEqual(document.cells[0].metadata.inputCollapsed, true);
|
||||
|
||||
assert.strictEqual(document.isDirty, true);
|
||||
|
@ -410,12 +451,11 @@ suite('Notebook API tests', function () {
|
|||
const event = asPromise<vscode.NotebookCellMetadataChangeEvent>(vscode.notebook.onDidChangeCellMetadata);
|
||||
|
||||
await vscode.window.activeNotebookEditor!.edit(editBuilder => {
|
||||
editBuilder.replaceCellMetadata(0, new vscode.NotebookCellMetadata().with({ inputCollapsed: true, executionOrder: 17 }));
|
||||
editBuilder.replaceCellMetadata(0, new vscode.NotebookCellMetadata().with({ inputCollapsed: true }));
|
||||
});
|
||||
|
||||
const data = await event;
|
||||
assert.strictEqual(data.document, vscode.window.activeNotebookEditor?.document);
|
||||
assert.strictEqual(data.cell.metadata.executionOrder, 17);
|
||||
assert.strictEqual(data.cell.metadata.inputCollapsed, true);
|
||||
|
||||
assert.strictEqual(data.document.isDirty, true);
|
||||
|
@ -499,6 +539,8 @@ suite('Notebook API tests', function () {
|
|||
assert.strictEqual(secondCell!.outputs[0].outputs[0].mime, 'text/plain');
|
||||
assert.strictEqual(secondCell!.outputs[0].outputs[0].value, 'Hello World');
|
||||
assert.deepStrictEqual(secondCell!.outputs[0].outputs[0].metadata, { testOutputItemMetadata: true });
|
||||
assert.strictEqual(secondCell!.previousResult?.executionOrder, 5);
|
||||
assert.strictEqual(secondCell!.previousResult?.success, true);
|
||||
|
||||
await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow');
|
||||
assert.strictEqual(vscode.window.activeNotebookEditor!.selection?.document.getText(), '');
|
||||
|
@ -637,11 +679,11 @@ suite('Notebook API tests', function () {
|
|||
const cell = editor.document.cells[0];
|
||||
assert.strictEqual(cell.outputs.length, 0);
|
||||
|
||||
currentKernerProvider.setHasKernels(false);
|
||||
currentKernelProvider.setHasKernels(false);
|
||||
await vscode.commands.executeCommand('notebook.execute');
|
||||
assert.strictEqual(cell.outputs.length, 0, 'should not execute'); // not runnable, didn't work
|
||||
|
||||
currentKernerProvider.setHasKernels(true);
|
||||
currentKernelProvider.setHasKernels(true);
|
||||
|
||||
await withEvent<vscode.NotebookCellOutputsChangeEvent>(vscode.notebook.onDidChangeCellOutputs, async (event) => {
|
||||
await vscode.commands.executeCommand('notebook.execute');
|
||||
|
@ -737,26 +779,158 @@ suite('Notebook API tests', function () {
|
|||
const editor = vscode.window.activeNotebookEditor!;
|
||||
const cell = editor.document.cells[0];
|
||||
|
||||
await vscode.commands.executeCommand('notebook.cell.execute');
|
||||
assert.strictEqual(cell.outputs.length, 1, 'should execute'); // runnable, it worked
|
||||
assert.strictEqual(cell.outputs[0].outputs.length, 1);
|
||||
assert.strictEqual(cell.outputs[0].outputs[0].mime, 'text/plain');
|
||||
assert.deepStrictEqual(cell.outputs[0].outputs[0].value, [
|
||||
'my output'
|
||||
]);
|
||||
vscode.commands.executeCommand('notebook.cell.execute');
|
||||
await withEvent<vscode.NotebookCellOutputsChangeEvent>(vscode.notebook.onDidChangeCellOutputs, async (event) => {
|
||||
await event;
|
||||
assert.strictEqual(cell.outputs.length, 1, 'should execute'); // runnable, it worked
|
||||
assert.strictEqual(cell.outputs[0].outputs.length, 1);
|
||||
assert.strictEqual(cell.outputs[0].outputs[0].mime, 'text/plain');
|
||||
assert.deepStrictEqual(cell.outputs[0].outputs[0].value, [
|
||||
'my output'
|
||||
]);
|
||||
});
|
||||
|
||||
await vscode.commands.executeCommand('notebook.selectKernel', { extension: 'vscode.vscode-api-tests', id: 'secondaryKernel' });
|
||||
await vscode.commands.executeCommand('notebook.cell.execute');
|
||||
assert.strictEqual(cell.outputs.length, 1, 'should execute'); // runnable, it worked
|
||||
assert.strictEqual(cell.outputs[0].outputs.length, 1);
|
||||
assert.strictEqual(cell.outputs[0].outputs[0].mime, 'text/plain');
|
||||
assert.deepStrictEqual(cell.outputs[0].outputs[0].value, [
|
||||
'my second output'
|
||||
]);
|
||||
vscode.commands.executeCommand('notebook.cell.execute');
|
||||
await withEvent<vscode.NotebookCellOutputsChangeEvent>(vscode.notebook.onDidChangeCellOutputs, async (event) => {
|
||||
await event;
|
||||
assert.strictEqual(cell.outputs.length, 1, 'should execute'); // runnable, it worked
|
||||
assert.strictEqual(cell.outputs[0].outputs.length, 1);
|
||||
assert.strictEqual(cell.outputs[0].outputs[0].mime, 'text/plain');
|
||||
assert.deepStrictEqual(cell.outputs[0].outputs[0].value, [
|
||||
'my second output'
|
||||
]);
|
||||
});
|
||||
|
||||
await saveAllFilesAndCloseAll(undefined);
|
||||
});
|
||||
// });
|
||||
|
||||
test('set outputs on cancel', async () => {
|
||||
const cancelableKernel = new class implements vscode.NotebookKernel {
|
||||
readonly id = 'cancelableKernel';
|
||||
readonly label = 'Notebook Cancelable Test Kernel';
|
||||
readonly isPreferred = false;
|
||||
readonly supportedLanguages = ['typescript', 'javascript'];
|
||||
|
||||
async executeCellsRequest(document: vscode.NotebookDocument, ranges: vscode.NotebookCellRange[]) {
|
||||
const idx = ranges[0].start;
|
||||
|
||||
const task = vscode.notebook.createNotebookCellExecutionTask(document.uri, idx, 'cancelableKernel');
|
||||
if (!task) {
|
||||
return;
|
||||
}
|
||||
|
||||
task.start();
|
||||
task.token.onCancellationRequested(async () => {
|
||||
await task.replaceOutput([new vscode.NotebookCellOutput([
|
||||
new vscode.NotebookCellOutputItem('text/plain', ['Canceled'], undefined)
|
||||
])]);
|
||||
task.end({});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
currentKernelProvider.addKernel(cancelableKernel);
|
||||
const resource = await createRandomFile('', undefined, '.vsctestnb');
|
||||
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
|
||||
const editor = vscode.window.activeNotebookEditor!;
|
||||
const cell = editor.document.cells[0];
|
||||
|
||||
await vscode.commands.executeCommand('notebook.selectKernel', { extension: 'vscode.vscode-api-tests', id: cancelableKernel.id });
|
||||
await withEvent<vscode.NotebookCellOutputsChangeEvent>(vscode.notebook.onDidChangeCellOutputs, async (event) => {
|
||||
await vscode.commands.executeCommand('notebook.cell.execute');
|
||||
await vscode.commands.executeCommand('notebook.cell.cancelExecution');
|
||||
await event;
|
||||
assert.strictEqual(cell.outputs.length, 1, 'should execute'); // runnable, it worked
|
||||
assert.strictEqual(cell.outputs[0].outputs.length, 1);
|
||||
assert.strictEqual(cell.outputs[0].outputs[0].mime, 'text/plain');
|
||||
assert.deepStrictEqual(cell.outputs[0].outputs[0].value, [
|
||||
'Canceled'
|
||||
]);
|
||||
});
|
||||
|
||||
await saveAllFilesAndCloseAll(undefined);
|
||||
});
|
||||
|
||||
test('set outputs on interrupt', async () => {
|
||||
const interruptableKernel = new class implements vscode.NotebookKernel {
|
||||
readonly id = 'interruptableKernel';
|
||||
readonly label = 'Notebook Interruptable Test Kernel';
|
||||
readonly isPreferred = false;
|
||||
readonly supportedLanguages = ['typescript', 'javascript'];
|
||||
|
||||
private _task: vscode.NotebookCellExecutionTask | undefined;
|
||||
|
||||
async executeCellsRequest(document: vscode.NotebookDocument, ranges: vscode.NotebookCellRange[]) {
|
||||
const idx = ranges[0].start;
|
||||
|
||||
this._task = vscode.notebook.createNotebookCellExecutionTask(document.uri, idx, 'interruptableKernel');
|
||||
if (!this._task) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._task.start();
|
||||
}
|
||||
|
||||
async interrupt(_document: vscode.NotebookDocument, _ranges: vscode.NotebookCellRange[]) {
|
||||
await this._task!.replaceOutput([new vscode.NotebookCellOutput([
|
||||
new vscode.NotebookCellOutputItem('text/plain', ['Interrupted'], undefined)
|
||||
])]);
|
||||
this._task!.end({});
|
||||
}
|
||||
};
|
||||
|
||||
currentKernelProvider.addKernel(interruptableKernel);
|
||||
const resource = await createRandomFile('', undefined, '.vsctestnb');
|
||||
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
|
||||
const editor = vscode.window.activeNotebookEditor!;
|
||||
const cell = editor.document.cells[0];
|
||||
|
||||
await vscode.commands.executeCommand('notebook.selectKernel', { extension: 'vscode.vscode-api-tests', id: interruptableKernel.id });
|
||||
await withEvent<vscode.NotebookCellOutputsChangeEvent>(vscode.notebook.onDidChangeCellOutputs, async (event) => {
|
||||
await vscode.commands.executeCommand('notebook.cell.execute');
|
||||
await vscode.commands.executeCommand('notebook.cell.cancelExecution');
|
||||
await event;
|
||||
assert.strictEqual(cell.outputs.length, 1, 'should execute'); // runnable, it worked
|
||||
assert.strictEqual(cell.outputs[0].outputs.length, 1);
|
||||
assert.strictEqual(cell.outputs[0].outputs[0].mime, 'text/plain');
|
||||
assert.deepStrictEqual(cell.outputs[0].outputs[0].value, [
|
||||
'Interrupted'
|
||||
]);
|
||||
});
|
||||
|
||||
await saveAllFilesAndCloseAll(undefined);
|
||||
});
|
||||
|
||||
test('onDidChangeCellExecutionState is fired', async () => {
|
||||
const resource = await createRandomFile('', undefined, '.vsctestnb');
|
||||
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
|
||||
const editor = vscode.window.activeNotebookEditor!;
|
||||
const cell = editor.document.cells[0];
|
||||
|
||||
vscode.commands.executeCommand('notebook.cell.execute');
|
||||
let eventCount = 0;
|
||||
let resolve: () => void;
|
||||
const p = new Promise<void>(r => resolve = r);
|
||||
const listener = vscode.notebook.onDidChangeCellExecutionState(e => {
|
||||
if (eventCount === 0) {
|
||||
assert.strictEqual(e.executionState, vscode.NotebookCellExecutionState.Pending, 'should be set to Pending');
|
||||
} else if (eventCount === 1) {
|
||||
assert.strictEqual(e.executionState, vscode.NotebookCellExecutionState.Executing, 'should be set to Executing');
|
||||
assert.strictEqual(cell.outputs.length, 0, 'no outputs yet: ' + JSON.stringify(cell.outputs[0]));
|
||||
} else if (eventCount === 2) {
|
||||
assert.strictEqual(e.executionState, vscode.NotebookCellExecutionState.Idle, 'should be set to Idle');
|
||||
assert.strictEqual(cell.outputs.length, 1, 'should have an output');
|
||||
resolve();
|
||||
}
|
||||
|
||||
eventCount++;
|
||||
});
|
||||
|
||||
await p;
|
||||
listener.dispose();
|
||||
await saveAllFilesAndCloseAll(undefined);
|
||||
});
|
||||
|
||||
// suite('notebook dirty state', () => {
|
||||
test('notebook open', async function () {
|
||||
|
@ -1094,7 +1268,6 @@ suite('Notebook API tests', function () {
|
|||
await saveAllFilesAndCloseAll(resource);
|
||||
});
|
||||
|
||||
|
||||
test('#116808, active kernel should not be undefined', async function () {
|
||||
const resource = await createRandomFile('', undefined, '.vsctestnb');
|
||||
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
|
||||
|
@ -1108,32 +1281,110 @@ suite('Notebook API tests', function () {
|
|||
await saveAllFilesAndCloseAll(resource);
|
||||
});
|
||||
|
||||
test('Numeric metadata should get updated correctly', async function () {
|
||||
test('Output changes are applied once the promise resolves', async function () {
|
||||
const verifyOutputSyncKernel = new class implements vscode.NotebookKernel {
|
||||
readonly id = 'verifyOutputSyncKernel';
|
||||
readonly label = '';
|
||||
readonly isPreferred = false;
|
||||
readonly supportedLanguages = ['typescript', 'javascript'];
|
||||
|
||||
async executeCellsRequest(document: vscode.NotebookDocument, ranges: vscode.NotebookCellRange[]) {
|
||||
const idx = ranges[0].start;
|
||||
|
||||
const task = vscode.notebook.createNotebookCellExecutionTask(document.uri, idx, this.id);
|
||||
if (!task) {
|
||||
return;
|
||||
}
|
||||
|
||||
task.start();
|
||||
await task.replaceOutput([new vscode.NotebookCellOutput([
|
||||
new vscode.NotebookCellOutputItem('text/plain', ['Some output'], undefined)
|
||||
])]);
|
||||
assert.strictEqual(document.cells[0].outputs.length, 1);
|
||||
assert.deepStrictEqual(document.cells[0].outputs[0].outputs[0].value, ['Some output']);
|
||||
task.end({});
|
||||
}
|
||||
};
|
||||
|
||||
currentKernelProvider.addKernel(verifyOutputSyncKernel);
|
||||
|
||||
const resource = await createRandomFile('', undefined, '.vsctestnb');
|
||||
const document = await vscode.notebook.openNotebookDocument(resource);
|
||||
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
|
||||
await vscode.commands.executeCommand('notebook.selectKernel', { extension: 'vscode.vscode-api-tests', id: verifyOutputSyncKernel.id });
|
||||
await vscode.commands.executeCommand('notebook.cell.execute');
|
||||
|
||||
const edit = new vscode.WorkspaceEdit();
|
||||
const runStartTime = Date.now();
|
||||
const lastRunDuration = Date.now() + 1000;
|
||||
const runState = vscode.NotebookCellRunState.Success;
|
||||
const executionOrder = 1234;
|
||||
const metadata = document.cells[0].metadata.with({
|
||||
...document.cells[0].metadata,
|
||||
runStartTime,
|
||||
runState,
|
||||
lastRunDuration,
|
||||
executionOrder
|
||||
});
|
||||
edit.replaceNotebookCellMetadata(document.uri, 0, metadata);
|
||||
await vscode.workspace.applyEdit(edit);
|
||||
|
||||
assert.strictEqual(document.cells[0].metadata.runStartTime, runStartTime);
|
||||
assert.strictEqual(document.cells[0].metadata.lastRunDuration, lastRunDuration);
|
||||
assert.strictEqual(document.cells[0].metadata.executionOrder, executionOrder);
|
||||
assert.strictEqual(document.cells[0].metadata.runState, vscode.NotebookCellRunState.Success);
|
||||
await saveAllFilesAndCloseAll(undefined);
|
||||
});
|
||||
|
||||
test('previousResult', async () => {
|
||||
const resource = await createRandomFile('', undefined, '.vsctestnb');
|
||||
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
|
||||
const editor = vscode.window.activeNotebookEditor!;
|
||||
const cell = editor.document.cells[0];
|
||||
|
||||
assert.strictEqual(cell.previousResult?.success, undefined);
|
||||
assert.strictEqual(cell.previousResult?.executionOrder, undefined);
|
||||
await vscode.commands.executeCommand('notebook.cell.execute');
|
||||
assert.strictEqual(cell.outputs.length, 1, 'should execute');
|
||||
assert.ok(cell.previousResult);
|
||||
assert.strictEqual(cell.previousResult!.success, true);
|
||||
assert.strictEqual(typeof cell.previousResult!.executionOrder, 'number');
|
||||
|
||||
await saveAllFilesAndCloseAll(undefined);
|
||||
});
|
||||
|
||||
test('initialize previousResult', async () => {
|
||||
const resource = await createRandomFile('', undefined, '.vsctestnb');
|
||||
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
|
||||
const editor = vscode.window.activeNotebookEditor!;
|
||||
const cell = editor.document.cells[0];
|
||||
|
||||
assert.strictEqual(cell.previousResult?.success, undefined);
|
||||
assert.strictEqual(cell.previousResult?.executionOrder, undefined);
|
||||
await vscode.commands.executeCommand('notebook.cell.execute');
|
||||
assert.strictEqual(cell.outputs.length, 1, 'should execute');
|
||||
assert.ok(cell.previousResult);
|
||||
assert.strictEqual(cell.previousResult!.success, true);
|
||||
assert.strictEqual(typeof cell.previousResult!.executionOrder, 'number');
|
||||
|
||||
await saveAllFilesAndCloseAll(undefined);
|
||||
});
|
||||
|
||||
test('Throws errors for invalid execution tasks', async function () {
|
||||
let missedError: string | undefined;
|
||||
|
||||
const invalidKernel = new class implements vscode.NotebookKernel {
|
||||
readonly id = 'invalidKernel';
|
||||
readonly label = '';
|
||||
readonly isPreferred = false;
|
||||
readonly supportedLanguages = ['typescript', 'javascript'];
|
||||
|
||||
async executeCellsRequest(document: vscode.NotebookDocument, _ranges: vscode.NotebookCellRange[]) {
|
||||
try {
|
||||
vscode.notebook.createNotebookCellExecutionTask(document.uri, 1000, this.id);
|
||||
missedError = 'Expected to throw for invalid index';
|
||||
return;
|
||||
} catch (e) { }
|
||||
|
||||
try {
|
||||
vscode.notebook.createNotebookCellExecutionTask(vscode.Uri.file('slkdf'), 0, this.id);
|
||||
missedError = 'Expected to throw for invalid uri';
|
||||
return;
|
||||
} catch (e) { }
|
||||
}
|
||||
};
|
||||
|
||||
currentKernelProvider.addKernel(invalidKernel);
|
||||
|
||||
const resource = await createRandomFile('', undefined, '.vsctestnb');
|
||||
await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest');
|
||||
await vscode.commands.executeCommand('notebook.selectKernel', { extension: 'vscode.vscode-api-tests', id: invalidKernel.id });
|
||||
await vscode.commands.executeCommand('notebook.cell.execute');
|
||||
|
||||
assert.strictEqual(missedError, undefined, missedError);
|
||||
|
||||
await saveAllFilesAndCloseAll(undefined);
|
||||
});
|
||||
|
||||
// });
|
||||
|
||||
|
|
|
@ -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)
|
||||
])]);
|
||||
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;
|
||||
}
|
||||
|
||||
await vscode.workspace.applyEdit(edit);
|
||||
},
|
||||
cancelAllCellsExecution: async () => { },
|
||||
executeCell: async (_document: vscode.NotebookDocument, _cell: vscode.NotebookCell | undefined) => {
|
||||
if (!_cell) {
|
||||
_cell = _document.cells[0];
|
||||
}
|
||||
|
||||
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' }, {
|
||||
|
|
152
src/vs/vscode.proposed.d.ts
vendored
152
src/vs/vscode.proposed.d.ts
vendored
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -1021,6 +1011,7 @@ declare module 'vscode' {
|
|||
readonly document: TextDocument;
|
||||
readonly metadata: NotebookCellMetadata
|
||||
readonly outputs: ReadonlyArray<NotebookCellOutput>;
|
||||
readonly latestExecutionSummary: NotebookCellExecutionSummary | undefined;
|
||||
}
|
||||
|
||||
export class NotebookDocumentMetadata {
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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?
|
||||
|
@ -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>;
|
||||
}
|
||||
|
||||
export type NotebookFilenamePattern = GlobPattern | { include: GlobPattern; exclude: GlobPattern; };
|
||||
|
|
|
@ -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';
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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,
|
||||
|
|
|
@ -50,7 +50,7 @@ import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService';
|
|||
import { TunnelCreationOptions, TunnelProviderFeatures, TunnelOptions, ProvidedPortAttributes } from 'vs/platform/remote/common/tunnel';
|
||||
import { Timeline, TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor, InternalTimelineOptions } from 'vs/workbench/contrib/timeline/common/timeline';
|
||||
import { revive } from 'vs/base/common/marshalling';
|
||||
import { NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEventDto, NotebookDataDto, IMainCellDto, INotebookDocumentFilter, TransientMetadata, INotebookCellStatusBarEntry, ICellRange, INotebookDecorationRenderOptions, INotebookExclusiveDocumentFilter, IOutputDto, TransientOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEventDto, NotebookDataDto, IMainCellDto, INotebookDocumentFilter, TransientMetadata, INotebookCellStatusBarEntry, ICellRange, INotebookDecorationRenderOptions, INotebookExclusiveDocumentFilter, IOutputDto, TransientOptions, IImmediateCellEditOperation } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { CallHierarchyItem } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
|
||||
import { Dto } from 'vs/base/common/types';
|
||||
import { DebugConfigurationProviderTriggerKind, WorkspaceTrustState } from 'vs/workbench/api/common/extHostTypes';
|
||||
|
@ -849,6 +849,7 @@ export interface MainThreadNotebookShape extends IDisposable {
|
|||
$onNotebookKernelChange(handle: number, uri: UriComponents | undefined): void;
|
||||
$trySaveDocument(uri: UriComponents): Promise<boolean>;
|
||||
$tryApplyEdits(viewType: string, resource: UriComponents, modelVersionId: number, edits: ICellEditOperation[]): Promise<boolean>;
|
||||
$applyEdits(resource: UriComponents, edits: IImmediateCellEditOperation[], computeUndoRedo?: boolean): Promise<void>;
|
||||
$postMessage(editorId: string, forRendererId: string | undefined, value: any): Promise<boolean>;
|
||||
$setStatusBarEntry(id: number, statusBarEntry: INotebookCellStatusBarEntryDto): Promise<void>;
|
||||
$tryOpenDocument(uriComponents: UriComponents): Promise<UriComponents>;
|
||||
|
@ -1857,6 +1858,7 @@ export interface INotebookKernelInfoDto2 {
|
|||
isPreferred?: boolean;
|
||||
preloads?: UriComponents[];
|
||||
supportedLanguages?: string[]
|
||||
implementsInterrupt?: boolean;
|
||||
}
|
||||
|
||||
export interface ExtHostNotebookShape {
|
||||
|
@ -1864,8 +1866,8 @@ export interface ExtHostNotebookShape {
|
|||
$acceptNotebookActiveKernelChange(event: { uri: UriComponents, providerHandle: number | undefined, kernelFriendlyId: string | undefined }): void;
|
||||
$provideNotebookKernels(handle: number, uri: UriComponents, token: CancellationToken): Promise<INotebookKernelInfoDto2[]>;
|
||||
$resolveNotebookKernel(handle: number, editorId: string, uri: UriComponents, kernelId: string, token: CancellationToken): Promise<void>;
|
||||
$executeNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellHandle: number | undefined): Promise<void>;
|
||||
$cancelNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellHandle: number | undefined): Promise<void>;
|
||||
$executeNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellRanges: ICellRange[]): Promise<void>;
|
||||
$cancelNotebookCellExecution(handle: number, uri: UriComponents, kernelId: string, cellRange: ICellRange[]): Promise<void>;
|
||||
$onDidReceiveMessage(editorId: string, rendererId: string | undefined, message: unknown): void;
|
||||
|
||||
$openNotebook(viewType: string, uri: UriComponents, backupId: string | undefined, untitledDocumentData: VSBuffer | undefined, token: CancellationToken): Promise<NotebookDataDto>;
|
||||
|
|
|
@ -17,7 +17,7 @@ import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePa
|
|||
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
|
||||
import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview';
|
||||
import { CellStatusbarAlignment, CellUri, INotebookCellStatusBarEntry, INotebookExclusiveDocumentFilter, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookDataDto, TransientOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { CellEditType, CellStatusbarAlignment, CellUri, ICellRange, INotebookCellStatusBarEntry, INotebookExclusiveDocumentFilter, NotebookCellMetadata, NotebookCellExecutionState, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookDataDto, TransientOptions, NullablePartialNotebookCellMetadata, IImmediateCellEditOperation } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import * as vscode from 'vscode';
|
||||
import { ResourceMap } from 'vs/base/common/map';
|
||||
import { ExtHostCell, ExtHostNotebookDocument } from './extHostNotebookDocument';
|
||||
|
@ -125,7 +125,8 @@ export class ExtHostNotebookKernelProviderAdapter extends Disposable {
|
|||
detail: kernel.detail,
|
||||
isPreferred: kernel.isPreferred,
|
||||
preloads: kernel.preloads,
|
||||
supportedLanguages: kernel.supportedLanguages
|
||||
supportedLanguages: kernel.supportedLanguages,
|
||||
implementsInterrupt: !!kernel.interrupt
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -151,42 +152,25 @@ export class ExtHostNotebookKernelProviderAdapter extends Disposable {
|
|||
}
|
||||
}
|
||||
|
||||
async executeNotebook(kernelId: string, document: ExtHostNotebookDocument, cell: ExtHostCell | undefined) {
|
||||
async executeNotebook(kernelId: string, document: ExtHostNotebookDocument, cellRange: ICellRange[]): Promise<void> {
|
||||
const kernel = this._friendlyIdToKernel.get(document.uri)?.get(kernelId);
|
||||
|
||||
if (!kernel) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cell) {
|
||||
return withToken(token => (kernel.executeCell as any)(document.notebookDocument, cell.cell, token));
|
||||
} else {
|
||||
return withToken(token => (kernel.executeAllCells as any)(document.notebookDocument, token));
|
||||
}
|
||||
const extCellRange = cellRange.map(c => typeConverters.NotebookCellRange.to(c));
|
||||
return kernel.executeCellsRequest(document.notebookDocument, extCellRange);
|
||||
}
|
||||
|
||||
async cancelNotebook(kernelId: string, document: ExtHostNotebookDocument, cell: ExtHostCell | undefined) {
|
||||
async interruptNotebookExecution(kernelId: string, document: ExtHostNotebookDocument): Promise<void> {
|
||||
const kernel = this._friendlyIdToKernel.get(document.uri)?.get(kernelId);
|
||||
|
||||
if (!kernel) {
|
||||
if (!kernel || !kernel.interrupt) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cell) {
|
||||
return kernel.cancelCellExecution(document.notebookDocument, cell.cell);
|
||||
} else {
|
||||
return kernel.cancelAllCellsExecution(document.notebookDocument);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO@roblou remove 'token' passed to all execute APIs once extensions are updated
|
||||
async function withToken(cb: (token: CancellationToken) => any) {
|
||||
const source = new CancellationTokenSource();
|
||||
try {
|
||||
await cb(source.token);
|
||||
} finally {
|
||||
source.dispose();
|
||||
return kernel.interrupt(document.notebookDocument);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -239,6 +223,8 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
|
|||
readonly onDidChangeCellMetadata = this._onDidChangeCellMetadata.event;
|
||||
private readonly _onDidChangeActiveNotebookEditor = new Emitter<vscode.NotebookEditor | undefined>();
|
||||
readonly onDidChangeActiveNotebookEditor = this._onDidChangeActiveNotebookEditor.event;
|
||||
private readonly _onDidChangeCellExecutionState = new Emitter<vscode.NotebookCellExecutionStateChangeEvent>();
|
||||
readonly onDidChangeNotebookCellExecutionState = this._onDidChangeCellExecutionState.event;
|
||||
|
||||
private _activeNotebookEditor: ExtHostNotebookEditor | undefined;
|
||||
get activeNotebookEditor(): vscode.NotebookEditor | undefined {
|
||||
|
@ -260,6 +246,8 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
|
|||
private _onDidChangeVisibleNotebookEditors = new Emitter<vscode.NotebookEditor[]>();
|
||||
onDidChangeVisibleNotebookEditors = this._onDidChangeVisibleNotebookEditors.event;
|
||||
|
||||
private _activeExecutions = new ResourceMap<NotebookCellExecutionTask>();
|
||||
|
||||
constructor(
|
||||
mainContext: IMainContext,
|
||||
commands: ExtHostCommands,
|
||||
|
@ -481,19 +469,9 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
|
|||
await provider.provider.resolveNotebook(document.notebookDocument, webComm.contentProviderComm);
|
||||
}
|
||||
|
||||
async $executeNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellHandle: number | undefined): Promise<void> {
|
||||
async $executeNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellRange: ICellRange[]): Promise<void> {
|
||||
await this._withAdapter(handle, uri, async (adapter, document) => {
|
||||
const cell = cellHandle !== undefined ? document.getCell(cellHandle) : undefined;
|
||||
|
||||
return adapter.executeNotebook(kernelId, document, cell);
|
||||
});
|
||||
}
|
||||
|
||||
async $cancelNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellHandle: number | undefined): Promise<void> {
|
||||
await this._withAdapter(handle, uri, async (adapter, document) => {
|
||||
const cell = cellHandle !== undefined ? document.getCell(cellHandle) : undefined;
|
||||
|
||||
return adapter.cancelNotebook(kernelId, document, cell);
|
||||
return adapter.executeNotebook(kernelId, document, cellRange);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -540,6 +518,31 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
|
|||
return VSBuffer.wrap(bytes);
|
||||
}
|
||||
|
||||
async $cancelNotebookCellExecution(handle: number, uri: UriComponents, kernelId: string, cellRange: ICellRange[]): Promise<void> {
|
||||
await this._withAdapter(handle, uri, async (adapter, document) => {
|
||||
return adapter.interruptNotebookExecution(kernelId, document);
|
||||
});
|
||||
|
||||
const document = this._documents.get(URI.revive(uri));
|
||||
if (!document) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let range of cellRange) {
|
||||
for (let i = range.start; i < range.end; i++) {
|
||||
const cell = document.getCellFromIndex(i);
|
||||
if (cell) {
|
||||
this.cancelOneNotebookCellExecution(cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private 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> {
|
||||
|
@ -729,6 +732,9 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
|
|||
emitCellMetadataChange(event: vscode.NotebookCellMetadataChangeEvent): void {
|
||||
that._onDidChangeCellMetadata.fire(event);
|
||||
},
|
||||
emitCellExecutionStateChange(event: vscode.NotebookCellExecutionStateChangeEvent): void {
|
||||
that._onDidChangeCellExecutionState.fire(event);
|
||||
}
|
||||
},
|
||||
viewType,
|
||||
modelData.metadata ? typeConverters.NotebookDocumentMetadata.to(modelData.metadata) : new extHostTypes.NotebookDocumentMetadata(),
|
||||
|
@ -837,6 +843,35 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
|
|||
|
||||
return statusBarItem;
|
||||
}
|
||||
|
||||
createNotebookCellExecution(docUri: vscode.Uri, index: number, kernelId: string): vscode.NotebookCellExecutionTask | undefined {
|
||||
const document = this.lookupNotebookDocument(docUri);
|
||||
if (!document) {
|
||||
throw new Error(`Invalid cell uri/index: ${docUri}, ${index}`);
|
||||
}
|
||||
|
||||
const cell = document.getCellFromIndex(index);
|
||||
if (!cell) {
|
||||
throw new Error(`Invalid cell uri/index: ${docUri}, ${index}`);
|
||||
}
|
||||
|
||||
// TODO@roblou also validate kernelId, once kernel has moved from editor to document
|
||||
if (this._activeExecutions.has(cell.uri)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const execution = new NotebookCellExecutionTask(docUri, document, cell, this._proxy);
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
export class NotebookCellStatusBarItemInternal extends Disposable {
|
||||
|
@ -1013,3 +1048,153 @@ function createNotebookCellStatusBarApiItem(internalItem: NotebookCellStatusBarI
|
|||
dispose() { internalItem.dispose(); }
|
||||
});
|
||||
}
|
||||
|
||||
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: CancellationTokenSource;
|
||||
|
||||
private _executionOrder: number | undefined;
|
||||
|
||||
constructor(
|
||||
private readonly _uri: vscode.Uri,
|
||||
private readonly _document: ExtHostNotebookDocument,
|
||||
private readonly _cell: ExtHostCell,
|
||||
private readonly _proxy: MainThreadNotebookShape) {
|
||||
super();
|
||||
this._tokenSource = this._register(new CancellationTokenSource());
|
||||
|
||||
this._executionOrder = _cell.internalMetadata.executionOrder;
|
||||
this.mixinMetadata({
|
||||
runState: NotebookCellExecutionState.Pending,
|
||||
lastRunDuration: null,
|
||||
executionOrder: null
|
||||
});
|
||||
}
|
||||
|
||||
cancel(): void {
|
||||
this._tokenSource.cancel();
|
||||
}
|
||||
|
||||
private async applyEdits(edits: IImmediateCellEditOperation[]): Promise<void> {
|
||||
return this._proxy.$applyEdits(this._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 edits: IImmediateCellEditOperation[] = [
|
||||
{ editType: CellEditType.PartialMetadata, handle: this._cell.handle, metadata: mixinMetadata }
|
||||
];
|
||||
this.applyEdits(edits);
|
||||
}
|
||||
|
||||
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.notebookDocument; },
|
||||
get cell() { return that._cell.cell; },
|
||||
|
||||
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: NotebookCellExecutionState.Executing,
|
||||
runStartTime: context?.startTime
|
||||
});
|
||||
},
|
||||
|
||||
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: NotebookCellExecutionState.Idle,
|
||||
lastRunSuccess: result?.success ?? null,
|
||||
lastRunDuration: result?.duration ?? null,
|
||||
});
|
||||
},
|
||||
|
||||
clearOutput(cellIndex?: number): Thenable<void> {
|
||||
that.verifyStateForOutput();
|
||||
return this.replaceOutput([], cellIndex);
|
||||
},
|
||||
|
||||
async appendOutput(outputs: vscode.NotebookCellOutput[], cellIndex?: number): Promise<void> {
|
||||
that.verifyStateForOutput();
|
||||
const handle = that.cellIndexToHandle(cellIndex);
|
||||
if (typeof handle !== 'number') {
|
||||
return;
|
||||
}
|
||||
|
||||
return that.applyEdits([{ editType: CellEditType.Output, handle, append: true, outputs: outputs.map(typeConverters.NotebookCellOutput.from) }]);
|
||||
},
|
||||
|
||||
async replaceOutput(outputs: vscode.NotebookCellOutput[], cellIndex?: number): Promise<void> {
|
||||
that.verifyStateForOutput();
|
||||
const handle = that.cellIndexToHandle(cellIndex);
|
||||
if (typeof handle !== 'number') {
|
||||
return;
|
||||
}
|
||||
|
||||
return that.applyEdits([{ editType: CellEditType.Output, handle, outputs: outputs.map(typeConverters.NotebookCellOutput.from) }]);
|
||||
},
|
||||
|
||||
async appendOutputItems(items: vscode.NotebookCellOutputItem[], outputId: string): Promise<void> {
|
||||
that.verifyStateForOutput();
|
||||
return that.applyEdits([{ editType: CellEditType.OutputItems, append: true, items: items.map(typeConverters.NotebookCellOutputItem.from), outputId }]);
|
||||
},
|
||||
|
||||
async replaceOutputItems(items: vscode.NotebookCellOutputItem[], outputId: string): Promise<void> {
|
||||
that.verifyStateForOutput();
|
||||
return that.applyEdits([{ editType: CellEditType.OutputItems, items: items.map(typeConverters.NotebookCellOutputItem.from), outputId }]);
|
||||
},
|
||||
|
||||
token: that._tokenSource.token
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Disposable, DisposableStore, dispose } from 'vs/base/common/lifecycle';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { deepFreeze } from 'vs/base/common/objects';
|
||||
import { deepFreeze, equals } from 'vs/base/common/objects';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { CellKind, INotebookDocumentPropertiesChangeData, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
|
||||
|
@ -51,7 +51,9 @@ export class ExtHostCell {
|
|||
|
||||
private _outputs: extHostTypes.NotebookCellOutput[];
|
||||
private _metadata: extHostTypes.NotebookCellMetadata;
|
||||
private _previousResult: vscode.NotebookCellExecutionSummary | undefined;
|
||||
|
||||
private _internalMetadata: NotebookCellMetadata;
|
||||
readonly handle: number;
|
||||
readonly uri: URI;
|
||||
readonly cellKind: CellKind;
|
||||
|
@ -67,7 +69,9 @@ export class ExtHostCell {
|
|||
this.uri = URI.revive(_cellData.uri);
|
||||
this.cellKind = _cellData.cellKind;
|
||||
this._outputs = _cellData.outputs.map(extHostTypeConverters.NotebookCellOutput.to);
|
||||
this._metadata = extHostTypeConverters.NotebookCellMetadata.to(_cellData.metadata ?? {});
|
||||
this._internalMetadata = _cellData.metadata ?? {};
|
||||
this._metadata = extHostTypeConverters.NotebookCellMetadata.to(this._internalMetadata);
|
||||
this._previousResult = extHostTypeConverters.NotebookCellPreviousExecutionResult.to(this._internalMetadata);
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
@ -75,6 +79,10 @@ export class ExtHostCell {
|
|||
this._onDidDispose.dispose();
|
||||
}
|
||||
|
||||
get internalMetadata(): NotebookCellMetadata {
|
||||
return this._internalMetadata;
|
||||
}
|
||||
|
||||
get cell(): vscode.NotebookCell {
|
||||
if (!this._cell) {
|
||||
const that = this;
|
||||
|
@ -89,6 +97,7 @@ export class ExtHostCell {
|
|||
document: data.document,
|
||||
get outputs() { return that._outputs.slice(0); },
|
||||
get metadata() { return that._metadata; },
|
||||
get latestExecutionSummary() { return that._previousResult; }
|
||||
});
|
||||
}
|
||||
return this._cell;
|
||||
|
@ -110,7 +119,9 @@ export class ExtHostCell {
|
|||
}
|
||||
|
||||
setMetadata(newMetadata: NotebookCellMetadata): void {
|
||||
this._internalMetadata = newMetadata;
|
||||
this._metadata = extHostTypeConverters.NotebookCellMetadata.to(newMetadata);
|
||||
this._previousResult = extHostTypeConverters.NotebookCellPreviousExecutionResult.to(newMetadata);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -118,6 +129,7 @@ export interface INotebookEventEmitter {
|
|||
emitModelChange(events: vscode.NotebookCellsChangeEvent): void;
|
||||
emitCellOutputsChange(event: vscode.NotebookCellOutputsChangeEvent): void;
|
||||
emitCellMetadataChange(event: vscode.NotebookCellMetadataChangeEvent): void;
|
||||
emitCellExecutionStateChange(event: vscode.NotebookCellExecutionStateChangeEvent): void;
|
||||
}
|
||||
|
||||
|
||||
|
@ -308,10 +320,22 @@ export class ExtHostNotebookDocument extends Disposable {
|
|||
}
|
||||
}
|
||||
|
||||
private _changeCellMetadata(index: number, newMetadata: NotebookCellMetadata | undefined): void {
|
||||
private _changeCellMetadata(index: number, newMetadata: NotebookCellMetadata): void {
|
||||
const cell = this._cells[index];
|
||||
cell.setMetadata(newMetadata || {});
|
||||
this._emitter.emitCellMetadataChange(deepFreeze({ document: this.notebookDocument, cell: cell.cell }));
|
||||
|
||||
const originalInternalMetadata = cell.internalMetadata;
|
||||
const originalExtMetadata = cell.cell.metadata;
|
||||
cell.setMetadata(newMetadata);
|
||||
const newExtMetadata = cell.cell.metadata;
|
||||
|
||||
if (!equals(originalExtMetadata, newExtMetadata)) {
|
||||
this._emitter.emitCellMetadataChange(deepFreeze({ document: this.notebookDocument, cell: cell.cell }));
|
||||
}
|
||||
|
||||
if (originalInternalMetadata.runState !== newMetadata.runState) {
|
||||
const executionState = newMetadata.runState ?? extHostTypes.NotebookCellExecutionState.Idle;
|
||||
this._emitter.emitCellExecutionStateChange(deepFreeze({ document: this.notebookDocument, cell: cell.cell, executionState }));
|
||||
}
|
||||
}
|
||||
|
||||
getCellFromIndex(index: number): ExtHostCell | undefined {
|
||||
|
|
|
@ -204,9 +204,9 @@ export class ExtHostNotebookEditor {
|
|||
const prevIndex = compressedEditsIndex;
|
||||
const prev = compressedEdits[prevIndex];
|
||||
|
||||
if (prev.editType === CellEditType.Replace && editData.cellEdits[i].editType === CellEditType.Replace) {
|
||||
const edit = editData.cellEdits[i];
|
||||
if ((edit.editType !== CellEditType.DocumentMetadata) && prev.index === edit.index) {
|
||||
const edit = editData.cellEdits[i];
|
||||
if (prev.editType === CellEditType.Replace && edit.editType === CellEditType.Replace) {
|
||||
if (prev.index === edit.index) {
|
||||
prev.cells.push(...(editData.cellEdits[i] as ICellReplaceEdit).cells);
|
||||
prev.count += (editData.cellEdits[i] as ICellReplaceEdit).count;
|
||||
continue;
|
||||
|
|
|
@ -568,7 +568,7 @@ export namespace WorkspaceEdit {
|
|||
metadata: entry.metadata,
|
||||
resource: entry.uri,
|
||||
edit: {
|
||||
editType: notebooks.CellEditType.Metadata,
|
||||
editType: notebooks.CellEditType.PartialMetadata,
|
||||
index: entry.index,
|
||||
metadata: entry.newMetadata
|
||||
}
|
||||
|
@ -594,7 +594,6 @@ export namespace WorkspaceEdit {
|
|||
resource: entry.uri,
|
||||
edit: {
|
||||
editType: notebooks.CellEditType.OutputItems,
|
||||
index: entry.index,
|
||||
outputId: entry.outputId,
|
||||
items: entry.newOutputItems?.map(NotebookCellOutputItem.from) || [],
|
||||
append: entry.append
|
||||
|
@ -1420,7 +1419,7 @@ export namespace NotebookCellRange {
|
|||
export namespace NotebookCellMetadata {
|
||||
|
||||
export function to(data: notebooks.NotebookCellMetadata): types.NotebookCellMetadata {
|
||||
return new types.NotebookCellMetadata(data.editable, data.breakpointMargin, data.hasExecutionOrder, data.executionOrder, data.runState, data.runStartTime, data.statusMessage, data.lastRunDuration, data.inputCollapsed, data.outputCollapsed, data.custom);
|
||||
return new types.NotebookCellMetadata(data.editable, data.breakpointMargin, data.hasExecutionOrder, data.statusMessage, data.inputCollapsed, data.outputCollapsed, data.custom);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1431,9 +1430,26 @@ export namespace NotebookDocumentMetadata {
|
|||
}
|
||||
|
||||
export function to(data: notebooks.NotebookDocumentMetadata): types.NotebookDocumentMetadata {
|
||||
return new types.NotebookDocumentMetadata(data.editable, data.cellEditable, data.cellHasExecutionOrder, data.custom, data.runState, data.trusted);
|
||||
return new types.NotebookDocumentMetadata(data.editable, data.cellEditable, data.cellHasExecutionOrder, data.custom, data.trusted);
|
||||
}
|
||||
}
|
||||
|
||||
export namespace NotebookCellPreviousExecutionResult {
|
||||
export function to(data: notebooks.NotebookCellMetadata): vscode.NotebookCellExecutionSummary {
|
||||
return {
|
||||
duration: data.lastRunDuration,
|
||||
executionOrder: data.executionOrder,
|
||||
success: data.lastRunSuccess
|
||||
};
|
||||
}
|
||||
|
||||
export function from(data: vscode.NotebookCellExecutionSummary): Partial<notebooks.NotebookCellMetadata> {
|
||||
return {
|
||||
lastRunSuccess: data.success,
|
||||
lastRunDuration: data.duration,
|
||||
executionOrder: data.executionOrder
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export namespace NotebookCellKind {
|
||||
|
@ -1465,7 +1481,10 @@ export namespace NotebookCellData {
|
|||
cellKind: NotebookCellKind.from(data.kind),
|
||||
language: data.language,
|
||||
source: data.source,
|
||||
metadata: data.metadata,
|
||||
metadata: {
|
||||
...data.metadata,
|
||||
...NotebookCellPreviousExecutionResult.from(data.latestExecutionSummary ?? {})
|
||||
},
|
||||
outputs: data.outputs ? data.outputs.map(NotebookCellOutput.from) : []
|
||||
};
|
||||
}
|
||||
|
|
|
@ -707,7 +707,7 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit {
|
|||
}
|
||||
|
||||
replaceNotebookCellMetadata(uri: URI, index: number, cellMetadata: vscode.NotebookCellMetadata, metadata?: vscode.WorkspaceEditEntryMetadata): void {
|
||||
this._edits.push({ _type: FileEditType.Cell, metadata, uri, edit: { editType: CellEditType.Metadata, index, metadata: cellMetadata } });
|
||||
this._edits.push({ _type: FileEditType.Cell, metadata, uri, edit: { editType: CellEditType.PartialMetadata, index, metadata: cellMetadata } });
|
||||
}
|
||||
|
||||
// --- text
|
||||
|
@ -2930,11 +2930,7 @@ export class NotebookCellMetadata {
|
|||
readonly editable?: boolean,
|
||||
readonly breakpointMargin?: boolean,
|
||||
readonly hasExecutionOrder?: boolean,
|
||||
readonly executionOrder?: number,
|
||||
readonly runState?: NotebookCellRunState,
|
||||
readonly runStartTime?: number,
|
||||
readonly statusMessage?: string,
|
||||
readonly lastRunDuration?: number,
|
||||
readonly inputCollapsed?: boolean,
|
||||
readonly outputCollapsed?: boolean,
|
||||
readonly custom?: Record<string, any>,
|
||||
|
@ -2944,17 +2940,13 @@ export class NotebookCellMetadata {
|
|||
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 {
|
||||
|
||||
let { editable, breakpointMargin, hasExecutionOrder, executionOrder, runState, runStartTime, statusMessage, lastRunDuration, inputCollapsed, outputCollapsed, custom } = change;
|
||||
let { editable, breakpointMargin, hasExecutionOrder, statusMessage, inputCollapsed, outputCollapsed, custom } = change;
|
||||
|
||||
if (editable === undefined) {
|
||||
editable = this.editable;
|
||||
|
@ -2971,31 +2963,11 @@ export class NotebookCellMetadata {
|
|||
} else if (hasExecutionOrder === null) {
|
||||
hasExecutionOrder = undefined;
|
||||
}
|
||||
if (executionOrder === undefined) {
|
||||
executionOrder = this.executionOrder;
|
||||
} else if (executionOrder === null) {
|
||||
executionOrder = undefined;
|
||||
}
|
||||
if (runState === undefined) {
|
||||
runState = this.runState;
|
||||
} else if (runState === null) {
|
||||
runState = undefined;
|
||||
}
|
||||
if (runStartTime === undefined) {
|
||||
runStartTime = this.runStartTime;
|
||||
} else if (runStartTime === null) {
|
||||
runStartTime = undefined;
|
||||
}
|
||||
if (statusMessage === undefined) {
|
||||
statusMessage = this.statusMessage;
|
||||
} else if (statusMessage === null) {
|
||||
statusMessage = undefined;
|
||||
}
|
||||
if (lastRunDuration === undefined) {
|
||||
lastRunDuration = this.lastRunDuration;
|
||||
} else if (lastRunDuration === null) {
|
||||
lastRunDuration = undefined;
|
||||
}
|
||||
if (inputCollapsed === undefined) {
|
||||
inputCollapsed = this.inputCollapsed;
|
||||
} else if (inputCollapsed === null) {
|
||||
|
@ -3015,11 +2987,7 @@ export class NotebookCellMetadata {
|
|||
if (editable === this.editable &&
|
||||
breakpointMargin === this.breakpointMargin &&
|
||||
hasExecutionOrder === this.hasExecutionOrder &&
|
||||
executionOrder === this.executionOrder &&
|
||||
runState === this.runState &&
|
||||
runStartTime === this.runStartTime &&
|
||||
statusMessage === this.statusMessage &&
|
||||
lastRunDuration === this.lastRunDuration &&
|
||||
inputCollapsed === this.inputCollapsed &&
|
||||
outputCollapsed === this.outputCollapsed &&
|
||||
custom === this.custom
|
||||
|
@ -3031,11 +2999,7 @@ export class NotebookCellMetadata {
|
|||
editable,
|
||||
breakpointMargin,
|
||||
hasExecutionOrder,
|
||||
executionOrder,
|
||||
runState,
|
||||
runStartTime,
|
||||
statusMessage,
|
||||
lastRunDuration,
|
||||
inputCollapsed,
|
||||
outputCollapsed,
|
||||
custom,
|
||||
|
@ -3050,7 +3014,6 @@ export class NotebookDocumentMetadata {
|
|||
readonly cellEditable: boolean = true,
|
||||
readonly cellHasExecutionOrder: boolean = true,
|
||||
readonly custom: { [key: string]: any; } = {},
|
||||
readonly runState: NotebookRunState = NotebookRunState.Idle,
|
||||
readonly trusted: boolean = true,
|
||||
) { }
|
||||
|
||||
|
@ -3059,11 +3022,10 @@ export class NotebookDocumentMetadata {
|
|||
cellEditable?: boolean | null,
|
||||
cellHasExecutionOrder?: boolean | null,
|
||||
custom?: { [key: string]: any; } | null,
|
||||
runState?: NotebookRunState | null,
|
||||
trusted?: boolean | null,
|
||||
}): NotebookDocumentMetadata {
|
||||
|
||||
let { editable, cellEditable, cellHasExecutionOrder, custom, runState, trusted } = change;
|
||||
let { editable, cellEditable, cellHasExecutionOrder, custom, trusted } = change;
|
||||
|
||||
if (editable === undefined) {
|
||||
editable = this.editable;
|
||||
|
@ -3085,11 +3047,6 @@ export class NotebookDocumentMetadata {
|
|||
} else if (custom === null) {
|
||||
custom = undefined;
|
||||
}
|
||||
if (runState === undefined) {
|
||||
runState = this.runState;
|
||||
} else if (runState === null) {
|
||||
runState = undefined;
|
||||
}
|
||||
if (trusted === undefined) {
|
||||
trusted = this.trusted;
|
||||
} else if (trusted === null) {
|
||||
|
@ -3100,7 +3057,6 @@ export class NotebookDocumentMetadata {
|
|||
cellEditable === this.cellEditable &&
|
||||
cellHasExecutionOrder === this.cellHasExecutionOrder &&
|
||||
custom === this.custom &&
|
||||
runState === this.runState &&
|
||||
trusted === this.trusted
|
||||
) {
|
||||
return this;
|
||||
|
@ -3112,7 +3068,6 @@ export class NotebookDocumentMetadata {
|
|||
cellEditable,
|
||||
cellHasExecutionOrder,
|
||||
custom,
|
||||
runState,
|
||||
trusted
|
||||
);
|
||||
}
|
||||
|
@ -3125,13 +3080,15 @@ export class NotebookCellData {
|
|||
language: string;
|
||||
outputs?: NotebookCellOutput[];
|
||||
metadata?: NotebookCellMetadata;
|
||||
lastExecutionSummary?: vscode.NotebookCellExecutionSummary;
|
||||
|
||||
constructor(kind: NotebookCellKind, source: string, language: string, outputs?: NotebookCellOutput[], metadata?: NotebookCellMetadata) {
|
||||
constructor(kind: NotebookCellKind, source: string, language: string, outputs?: NotebookCellOutput[], metadata?: NotebookCellMetadata, lastExecutionSummary?: vscode.NotebookCellExecutionSummary) {
|
||||
this.kind = kind;
|
||||
this.source = source;
|
||||
this.language = language;
|
||||
this.outputs = outputs ?? [];
|
||||
this.metadata = metadata;
|
||||
this.lastExecutionSummary = lastExecutionSummary;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3187,16 +3144,10 @@ export enum NotebookCellKind {
|
|||
Code = 2
|
||||
}
|
||||
|
||||
export enum NotebookCellRunState {
|
||||
Running = 1,
|
||||
Idle = 2,
|
||||
Success = 3,
|
||||
Error = 4
|
||||
}
|
||||
|
||||
export enum NotebookRunState {
|
||||
Running = 1,
|
||||
Idle = 2
|
||||
export enum NotebookCellExecutionState {
|
||||
Idle = 1,
|
||||
Pending = 2,
|
||||
Executing = 3,
|
||||
}
|
||||
|
||||
export enum NotebookCellStatusBarAlignment {
|
||||
|
|
|
@ -19,9 +19,9 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation
|
|||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { IQuickInputService, IQuickPickItem, QuickPickInput } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { CATEGORIES } from 'vs/workbench/common/actions';
|
||||
import { BaseCellRenderTemplate, CellEditState, CellFocusMode, EXECUTE_CELL_COMMAND_ID, EXPAND_CELL_CONTENT_COMMAND_ID, getNotebookEditorFromEditorPane, IActiveNotebookEditor, ICellViewModel, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_CELL_INPUT_COLLAPSED, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_OUTPUT_COLLAPSED, NOTEBOOK_CELL_RUN_STATE, NOTEBOOK_CELL_TYPE, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_KERNEL_COUNT, NOTEBOOK_OUTPUT_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { BaseCellRenderTemplate, CellEditState, CellFocusMode, EXECUTE_CELL_COMMAND_ID, EXPAND_CELL_CONTENT_COMMAND_ID, getNotebookEditorFromEditorPane, IActiveNotebookEditor, ICellViewModel, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_CELL_INPUT_COLLAPSED, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_OUTPUT_COLLAPSED, NOTEBOOK_CELL_EXECUTION_STATE, NOTEBOOK_CELL_TYPE, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_KERNEL_COUNT, NOTEBOOK_OUTPUT_FOCUSED, NOTEBOOK_INTERRUPTIBLE_KERNEL, NOTEBOOK_HAS_RUNNING_CELL } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
|
||||
import { CellEditType, CellKind, ICellEditOperation, ICellRange, INotebookDocumentFilter, isDocumentExcludePattern, NotebookCellMetadata, NotebookCellRunState, NOTEBOOK_EDITOR_CURSOR_BOUNDARY, SelectionStateType, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { CellEditType, CellKind, ICellEditOperation, ICellRange, INotebookDocumentFilter, isDocumentExcludePattern, NotebookCellMetadata, NotebookCellExecutionState, NOTEBOOK_EDITOR_CURSOR_BOUNDARY, TransientMetadata, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
|
@ -240,7 +240,11 @@ export abstract class NotebookCellAction<T = INotebookCellActionContext> extends
|
|||
|
||||
const executeCellCondition = ContextKeyExpr.or(
|
||||
ContextKeyExpr.and(
|
||||
ContextKeyExpr.notEquals(NOTEBOOK_CELL_RUN_STATE.key, NotebookCellRunState[NotebookCellRunState.Running]),
|
||||
ContextKeyExpr.or(
|
||||
ContextKeyExpr.equals(NOTEBOOK_CELL_EXECUTION_STATE.key, 'idle'),
|
||||
ContextKeyExpr.equals(NOTEBOOK_CELL_EXECUTION_STATE.key, 'succeeded'),
|
||||
ContextKeyExpr.equals(NOTEBOOK_CELL_EXECUTION_STATE.key, 'failed'),
|
||||
),
|
||||
ContextKeyExpr.greater(NOTEBOOK_KERNEL_COUNT.key, 0)),
|
||||
NOTEBOOK_CELL_TYPE.isEqualTo('markdown'));
|
||||
|
||||
|
@ -344,16 +348,21 @@ registerAction2(class ExecuteCell extends NotebookCellAction<ICellRange> {
|
|||
}
|
||||
});
|
||||
|
||||
const cellCancelCondition = ContextKeyExpr.or(
|
||||
ContextKeyExpr.equals(NOTEBOOK_CELL_EXECUTION_STATE.key, 'executing'),
|
||||
ContextKeyExpr.equals(NOTEBOOK_CELL_EXECUTION_STATE.key, 'pending'),
|
||||
);
|
||||
|
||||
registerAction2(class CancelExecuteCell extends NotebookCellAction<ICellRange> {
|
||||
constructor() {
|
||||
super({
|
||||
id: CANCEL_CELL_COMMAND_ID,
|
||||
precondition: ContextKeyExpr.equals(NOTEBOOK_CELL_RUN_STATE.key, NotebookCellRunState[NotebookCellRunState.Running]),
|
||||
precondition: cellCancelCondition,
|
||||
title: localize('notebookActions.cancel', "Stop Cell Execution"),
|
||||
icon: icons.stopIcon,
|
||||
menu: {
|
||||
id: MenuId.NotebookCellExecute,
|
||||
when: ContextKeyExpr.equals(NOTEBOOK_CELL_RUN_STATE.key, NotebookCellRunState[NotebookCellRunState.Running]),
|
||||
when: cellCancelCondition,
|
||||
group: 'inline'
|
||||
},
|
||||
description: {
|
||||
|
@ -618,7 +627,7 @@ MenuRegistry.appendMenuItem(MenuId.NotebookToolbar, {
|
|||
},
|
||||
order: -1,
|
||||
group: 'navigation',
|
||||
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK.toNegated(), executeNotebookCondition)
|
||||
when: ContextKeyExpr.and(executeNotebookCondition, ContextKeyExpr.or(NOTEBOOK_INTERRUPTIBLE_KERNEL.toNegated(), NOTEBOOK_HAS_RUNNING_CELL.toNegated()))
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.NotebookToolbar, {
|
||||
|
@ -629,7 +638,7 @@ MenuRegistry.appendMenuItem(MenuId.NotebookToolbar, {
|
|||
},
|
||||
order: -1,
|
||||
group: 'navigation',
|
||||
when: ContextKeyExpr.and(NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK)
|
||||
when: ContextKeyExpr.and(NOTEBOOK_HAS_RUNNING_CELL, NOTEBOOK_INTERRUPTIBLE_KERNEL)
|
||||
});
|
||||
|
||||
registerAction2(class extends NotebookCellAction {
|
||||
|
@ -681,7 +690,7 @@ registerAction2(class extends NotebookCellAction {
|
|||
});
|
||||
|
||||
async function runCell(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise<void> {
|
||||
if (context.cell.metadata?.runState === NotebookCellRunState.Running) {
|
||||
if (context.cell.metadata?.runState === NotebookCellExecutionState.Executing) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1300,11 +1309,11 @@ registerAction2(class extends NotebookCellAction {
|
|||
|
||||
editor.viewModel.notebookDocument.applyEdits([{ editType: CellEditType.Output, index, outputs: [] }], true, undefined, () => undefined, undefined);
|
||||
|
||||
if (context.cell.metadata && context.cell.metadata?.runState !== NotebookCellRunState.Running) {
|
||||
if (context.cell.metadata && context.cell.metadata?.runState !== NotebookCellExecutionState.Executing) {
|
||||
context.notebookEditor.viewModel.notebookDocument.applyEdits([{
|
||||
editType: CellEditType.Metadata, index, metadata: {
|
||||
...context.cell.metadata,
|
||||
runState: NotebookCellRunState.Idle,
|
||||
runState: NotebookCellExecutionState.Idle,
|
||||
runStartTime: undefined,
|
||||
lastRunDuration: undefined,
|
||||
statusMessage: undefined,
|
||||
|
@ -1515,11 +1524,11 @@ registerAction2(class extends NotebookAction {
|
|||
})), true, undefined, () => undefined, undefined);
|
||||
|
||||
const clearExecutionMetadataEdits = editor.viewModel.notebookDocument.cells.map((cell, index) => {
|
||||
if (cell.metadata && cell.metadata?.runState !== NotebookCellRunState.Running) {
|
||||
if (cell.metadata && cell.metadata?.runState !== NotebookCellExecutionState.Executing) {
|
||||
return {
|
||||
editType: CellEditType.Metadata, index, metadata: {
|
||||
...cell.metadata,
|
||||
runState: NotebookCellRunState.Idle,
|
||||
runState: NotebookCellExecutionState.Idle,
|
||||
runStartTime: undefined,
|
||||
lastRunDuration: undefined,
|
||||
statusMessage: undefined,
|
||||
|
|
|
@ -383,7 +383,6 @@ abstract class AbstractElementRenderer extends Disposable {
|
|||
|
||||
private _applySanitizedMetadataChanges(currentMetadata: NotebookCellMetadata, newMetadata: any) {
|
||||
let result: { [key: string]: any } = {};
|
||||
let newLangauge: string | undefined = undefined;
|
||||
try {
|
||||
const newMetadataObj = JSON.parse(newMetadata);
|
||||
const keys = new Set([...Object.keys(newMetadataObj)]);
|
||||
|
@ -428,25 +427,11 @@ abstract class AbstractElementRenderer extends Disposable {
|
|||
}
|
||||
break;
|
||||
default:
|
||||
if (key === 'language') {
|
||||
newLangauge = newMetadataObj[key];
|
||||
}
|
||||
result[key] = newMetadataObj[key];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (newLangauge !== undefined && newLangauge !== this.cell.modified!.language) {
|
||||
const index = this.notebookEditor.textModel!.cells.indexOf(this.cell.modified!.textModel);
|
||||
this.notebookEditor.textModel!.applyEdits(
|
||||
[{ editType: CellEditType.CellLanguage, index, language: newLangauge }],
|
||||
true,
|
||||
undefined,
|
||||
() => undefined,
|
||||
undefined
|
||||
);
|
||||
}
|
||||
|
||||
const index = this.notebookEditor.textModel!.cells.indexOf(this.cell.modified!.textModel);
|
||||
|
||||
if (index < 0) {
|
||||
|
|
|
@ -43,7 +43,7 @@ export const NOTEBOOK_EDITOR_OPEN = new RawContextKey<boolean>('notebookEditorOp
|
|||
export const NOTEBOOK_CELL_LIST_FOCUSED = new RawContextKey<boolean>('notebookCellListFocused', false);
|
||||
export const NOTEBOOK_OUTPUT_FOCUSED = new RawContextKey<boolean>('notebookOutputFocused', false);
|
||||
export const NOTEBOOK_EDITOR_EDITABLE = new RawContextKey<boolean>('notebookEditable', true);
|
||||
export const NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK = new RawContextKey<boolean>('notebookExecuting', false);
|
||||
export const NOTEBOOK_HAS_RUNNING_CELL = new RawContextKey<boolean>('notebookHasRunningCell', false);
|
||||
|
||||
// Diff Editor Keys
|
||||
export const IN_NOTEBOOK_TEXT_DIFF_EDITOR = new RawContextKey<boolean>('isInNotebookTextDiffEditor', false);
|
||||
|
@ -55,13 +55,15 @@ export const NOTEBOOK_CELL_EDITABLE = new RawContextKey<boolean>('notebookCellEd
|
|||
export const NOTEBOOK_CELL_FOCUSED = new RawContextKey<boolean>('notebookCellFocused', false); // bool
|
||||
export const NOTEBOOK_CELL_EDITOR_FOCUSED = new RawContextKey<boolean>('notebookCellEditorFocused', false); // bool
|
||||
export const NOTEBOOK_CELL_MARKDOWN_EDIT_MODE = new RawContextKey<boolean>('notebookCellMarkdownEditMode', false); // bool
|
||||
export const NOTEBOOK_CELL_RUN_STATE = new RawContextKey<string>('notebookCellRunState', undefined); // Idle, Running
|
||||
export type NotebookCellExecutionStateContext = 'idle' | 'pending' | 'executing' | 'succeeded' | 'failed';
|
||||
export const NOTEBOOK_CELL_EXECUTION_STATE = new RawContextKey<NotebookCellExecutionStateContext>('notebookCellExecutionState', undefined);
|
||||
export const NOTEBOOK_CELL_HAS_OUTPUTS = new RawContextKey<boolean>('notebookCellHasOutputs', false); // bool
|
||||
export const NOTEBOOK_CELL_INPUT_COLLAPSED = new RawContextKey<boolean>('notebookCellInputIsCollapsed', false); // bool
|
||||
export const NOTEBOOK_CELL_OUTPUT_COLLAPSED = new RawContextKey<boolean>('notebookCellOutputIsCollapsed', false); // bool
|
||||
// Kernels
|
||||
export const NOTEBOOK_HAS_MULTIPLE_KERNELS = new RawContextKey<boolean>('notebookHasMultipleKernels', false);
|
||||
export const NOTEBOOK_KERNEL_COUNT = new RawContextKey<number>('notebookKernelCount', 0);
|
||||
export const NOTEBOOK_INTERRUPTIBLE_KERNEL = new RawContextKey<boolean>('notebookInterruptibleKernel', false);
|
||||
|
||||
//#endregion
|
||||
|
||||
|
|
|
@ -8,24 +8,25 @@ import { IQuickInputService, IQuickPickItem, QuickPickInput } from 'vs/platform/
|
|||
import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { Disposable, DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
import { Memento } from 'vs/workbench/common/memento';
|
||||
import { ICellViewModel, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_HAS_MULTIPLE_KERNELS, NOTEBOOK_KERNEL_COUNT } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { ICellViewModel, NOTEBOOK_HAS_MULTIPLE_KERNELS, NOTEBOOK_HAS_RUNNING_CELL, NOTEBOOK_INTERRUPTIBLE_KERNEL, NOTEBOOK_KERNEL_COUNT } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { configureKernelIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons';
|
||||
import { NotebookKernelProviderAssociation, NotebookKernelProviderAssociations, notebookKernelProviderAssociationsSettingId } from 'vs/workbench/contrib/notebook/browser/notebookKernelAssociation';
|
||||
import { NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
|
||||
import { CellKind, INotebookKernel, NotebookCellRunState, NotebookRunState } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { CellViewModel, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
|
||||
import { cellIndexesToRanges, CellKind, ICellRange, INotebookKernel, NotebookCellExecutionState } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider';
|
||||
|
||||
const NotebookEditorActiveKernelCache = 'workbench.editor.notebook.activeKernel';
|
||||
|
||||
export interface IKernelManagerDelegate {
|
||||
viewModel: NotebookViewModel | undefined;
|
||||
onDidChangeViewModel: Event<void>;
|
||||
getId(): string;
|
||||
getContributedNotebookProviders(resource?: URI): readonly NotebookProviderInfo[];
|
||||
getContributedNotebookProvider(viewType: string): NotebookProviderInfo | undefined;
|
||||
|
@ -46,9 +47,14 @@ export class NotebookEditorKernelManager extends Disposable {
|
|||
private _contributedKernelsComputePromise: CancelablePromise<INotebookKernel[]> | null = null;
|
||||
private _initialKernelComputationDone: boolean = false;
|
||||
|
||||
private readonly _notebookExecuting: IContextKey<boolean>;
|
||||
private readonly _notebookHasMultipleKernels: IContextKey<boolean>;
|
||||
private readonly _notebookKernelCount: IContextKey<number>;
|
||||
private readonly _interruptibleKernel: IContextKey<boolean>;
|
||||
private readonly _someCellRunning: IContextKey<boolean>;
|
||||
|
||||
private _cellStateListeners: IDisposable[] = [];
|
||||
private _executionCount = 0;
|
||||
private _viewModelDisposables: DisposableStore;
|
||||
|
||||
get activeKernel() {
|
||||
return this._activeKernel;
|
||||
|
@ -67,6 +73,8 @@ export class NotebookEditorKernelManager extends Disposable {
|
|||
return;
|
||||
}
|
||||
|
||||
this._interruptibleKernel.set(!!kernel?.implementsInterrupt);
|
||||
|
||||
this._activeKernel = kernel;
|
||||
this._activeKernelResolvePromise = undefined;
|
||||
|
||||
|
@ -104,9 +112,47 @@ export class NotebookEditorKernelManager extends Disposable {
|
|||
|
||||
this._activeKernelMemento = new Memento(NotebookEditorActiveKernelCache, storageService);
|
||||
|
||||
this._notebookExecuting = NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK.bindTo(contextKeyService);
|
||||
this._notebookHasMultipleKernels = NOTEBOOK_HAS_MULTIPLE_KERNELS.bindTo(contextKeyService);
|
||||
this._notebookKernelCount = NOTEBOOK_KERNEL_COUNT.bindTo(contextKeyService);
|
||||
this._interruptibleKernel = NOTEBOOK_INTERRUPTIBLE_KERNEL.bindTo(contextKeyService);
|
||||
this._someCellRunning = NOTEBOOK_HAS_RUNNING_CELL.bindTo(contextKeyService);
|
||||
|
||||
this._viewModelDisposables = this._register(new DisposableStore());
|
||||
this._register(this._delegate.onDidChangeViewModel(() => {
|
||||
this._viewModelDisposables.clear();
|
||||
this.initCellListeners();
|
||||
}));
|
||||
}
|
||||
|
||||
private initCellListeners(): void {
|
||||
dispose(this._cellStateListeners);
|
||||
this._cellStateListeners = [];
|
||||
|
||||
if (!this._delegate.viewModel) {
|
||||
return;
|
||||
}
|
||||
|
||||
const addCellStateListener = (c: ICellViewModel) => {
|
||||
return (c as CellViewModel).onDidChangeState(() => {
|
||||
if (c.metadata?.runState === NotebookCellExecutionState.Pending) {
|
||||
this._executionCount++;
|
||||
} else if (c.metadata?.runState === NotebookCellExecutionState.Idle) {
|
||||
this._executionCount--;
|
||||
}
|
||||
|
||||
this._someCellRunning.set(this._executionCount > 0);
|
||||
});
|
||||
};
|
||||
|
||||
this._cellStateListeners = this._delegate.viewModel.viewCells.map(addCellStateListener);
|
||||
|
||||
this._viewModelDisposables.add(this._delegate.viewModel.onDidChangeViewCells(e => {
|
||||
e.splices.reverse().forEach(splice => {
|
||||
const [start, deleted, newCells] = splice;
|
||||
const deletedCells = this._cellStateListeners.splice(start, deleted, ...newCells.map(addCellStateListener));
|
||||
dispose(deletedCells);
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
public async setKernels(tokenSource: CancellationTokenSource) {
|
||||
|
@ -167,15 +213,6 @@ export class NotebookEditorKernelManager extends Disposable {
|
|||
return result;
|
||||
}
|
||||
|
||||
updateForMetadata(): void {
|
||||
if (!this._delegate.viewModel) {
|
||||
return;
|
||||
}
|
||||
|
||||
const notebookMetadata = this._delegate.viewModel.metadata;
|
||||
this._notebookExecuting.set(notebookMetadata.runState === NotebookRunState.Running);
|
||||
}
|
||||
|
||||
private async _setKernelsFromProviders(provider: NotebookProviderInfo, kernels: INotebookKernel[], tokenSource: CancellationTokenSource) {
|
||||
const rawAssociations = this._configurationService.getValue<NotebookKernelProviderAssociations>(notebookKernelProviderAssociationsSettingId) || [];
|
||||
const userSetKernelProvider = rawAssociations.filter(e => e.viewType === this._delegate.viewModel?.viewType)[0]?.kernelProvider;
|
||||
|
@ -366,12 +403,12 @@ export class NotebookEditorKernelManager extends Disposable {
|
|||
return;
|
||||
}
|
||||
|
||||
if (this._delegate.viewModel.metadata.runState !== NotebookRunState.Running) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this._ensureActiveKernel();
|
||||
await this._activeKernel?.cancelNotebookCell!(this._delegate.viewModel.uri, undefined);
|
||||
|
||||
const fullRange: ICellRange = {
|
||||
start: 0, end: this._delegate.viewModel.length
|
||||
};
|
||||
await this._activeKernel?.cancelNotebookCellExecution!(this._delegate.viewModel.uri, [fullRange]);
|
||||
}
|
||||
|
||||
async executeNotebook(): Promise<void> {
|
||||
|
@ -384,8 +421,11 @@ export class NotebookEditorKernelManager extends Disposable {
|
|||
return;
|
||||
}
|
||||
|
||||
const fullRange: ICellRange = {
|
||||
start: 0, end: this._delegate.viewModel.length
|
||||
};
|
||||
this._activeKernelExecuted = true;
|
||||
await this._activeKernel?.executeNotebookCell!(this._delegate.viewModel.uri, undefined);
|
||||
await this._activeKernel?.executeNotebookCellsRequest(this._delegate.viewModel.uri, [fullRange]);
|
||||
}
|
||||
|
||||
async cancelNotebookCellExecution(cell: ICellViewModel): Promise<void> {
|
||||
|
@ -398,12 +438,15 @@ export class NotebookEditorKernelManager extends Disposable {
|
|||
}
|
||||
|
||||
const metadata = cell.getEvaluatedMetadata(this._delegate.viewModel.metadata);
|
||||
if (metadata.runState !== NotebookCellRunState.Running) {
|
||||
if (metadata.runState === NotebookCellExecutionState.Idle) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this._ensureActiveKernel();
|
||||
await this._activeKernel?.cancelNotebookCell!(this._delegate.viewModel.uri, cell.handle);
|
||||
|
||||
const idx = this._delegate.viewModel.getCellIndex(cell);
|
||||
const ranges = cellIndexesToRanges([idx]);
|
||||
await this._activeKernel?.cancelNotebookCellExecution!(this._delegate.viewModel.uri, ranges);
|
||||
}
|
||||
|
||||
async executeNotebookCell(cell: ICellViewModel): Promise<void> {
|
||||
|
@ -416,8 +459,14 @@ export class NotebookEditorKernelManager extends Disposable {
|
|||
throw new Error('Cell is not executable: ' + cell.uri);
|
||||
}
|
||||
|
||||
if (!this.activeKernel) {
|
||||
return;
|
||||
}
|
||||
|
||||
const idx = this._delegate.viewModel.getCellIndex(cell);
|
||||
const range = cellIndexesToRanges([idx]);
|
||||
this._activeKernelExecuted = true;
|
||||
await this._activeKernel?.executeNotebookCell!(this._delegate.viewModel.uri, cell.handle);
|
||||
await this._activeKernel!.executeNotebookCellsRequest(this._delegate.viewModel.uri, range);
|
||||
}
|
||||
|
||||
private canExecuteNotebook(): boolean {
|
||||
|
|
|
@ -351,6 +351,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
|
|||
this._kernelManger = instantiationService.createInstance(NotebookEditorKernelManager, <IKernelManagerDelegate>{
|
||||
getId() { return that.getId(); },
|
||||
loadKernelPreloads: that._loadKernelPreloads.bind(that),
|
||||
onDidChangeViewModel: that.onDidChangeModel,
|
||||
get viewModel() { return that.viewModel; },
|
||||
getContributedNotebookProviders(resource?: URI) {
|
||||
return that.notebookService.getContributedNotebookProviders(resource);
|
||||
|
@ -967,8 +968,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor
|
|||
this._editorEditable.set(!!notebookMetadata?.editable);
|
||||
this._overflowContainer.classList.toggle('notebook-editor-editable', !!notebookMetadata?.editable);
|
||||
this.getDomNode().classList.toggle('notebook-editor-editable', !!notebookMetadata?.editable);
|
||||
|
||||
this._kernelManger.updateForMetadata();
|
||||
}
|
||||
|
||||
private async _resolveWebview(): Promise<BackLayerWebView<ICommonCellInfo> | null> {
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { INotebookTextModel, NotebookCellRunState } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { NOTEBOOK_CELL_TYPE, NOTEBOOK_VIEW_TYPE, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_RUN_STATE, NOTEBOOK_CELL_HAS_OUTPUTS, CellViewModelStateChangeEvent, CellEditState, NOTEBOOK_CELL_INPUT_COLLAPSED, NOTEBOOK_CELL_OUTPUT_COLLAPSED, NOTEBOOK_CELL_FOCUSED, INotebookEditor, NOTEBOOK_CELL_EDITOR_FOCUSED, CellFocusMode } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { INotebookTextModel, NotebookCellExecutionState } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { NOTEBOOK_CELL_TYPE, NOTEBOOK_VIEW_TYPE, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_EXECUTION_STATE, NOTEBOOK_CELL_HAS_OUTPUTS, CellViewModelStateChangeEvent, CellEditState, NOTEBOOK_CELL_INPUT_COLLAPSED, NOTEBOOK_CELL_OUTPUT_COLLAPSED, NOTEBOOK_CELL_FOCUSED, INotebookEditor, NOTEBOOK_CELL_EDITOR_FOCUSED, CellFocusMode, NotebookCellExecutionStateContext } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel';
|
||||
import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel';
|
||||
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
|
@ -17,7 +17,7 @@ export class CellContextKeyManager extends Disposable {
|
|||
private cellEditable!: IContextKey<boolean>;
|
||||
private cellFocused!: IContextKey<boolean>;
|
||||
private cellEditorFocused!: IContextKey<boolean>;
|
||||
private cellRunState!: IContextKey<string>;
|
||||
private cellRunState!: IContextKey<NotebookCellExecutionStateContext>;
|
||||
private cellHasOutputs!: IContextKey<boolean>;
|
||||
private cellContentCollapsed!: IContextKey<boolean>;
|
||||
private cellOutputCollapsed!: IContextKey<boolean>;
|
||||
|
@ -41,7 +41,7 @@ export class CellContextKeyManager extends Disposable {
|
|||
this.cellFocused = NOTEBOOK_CELL_FOCUSED.bindTo(this.contextKeyService);
|
||||
this.cellEditorFocused = NOTEBOOK_CELL_EDITOR_FOCUSED.bindTo(this.contextKeyService);
|
||||
this.markdownEditMode = NOTEBOOK_CELL_MARKDOWN_EDIT_MODE.bindTo(this.contextKeyService);
|
||||
this.cellRunState = NOTEBOOK_CELL_RUN_STATE.bindTo(this.contextKeyService);
|
||||
this.cellRunState = NOTEBOOK_CELL_EXECUTION_STATE.bindTo(this.contextKeyService);
|
||||
this.cellHasOutputs = NOTEBOOK_CELL_HAS_OUTPUTS.bindTo(this.contextKeyService);
|
||||
this.cellContentCollapsed = NOTEBOOK_CELL_INPUT_COLLAPSED.bindTo(this.contextKeyService);
|
||||
this.cellOutputCollapsed = NOTEBOOK_CELL_OUTPUT_COLLAPSED.bindTo(this.contextKeyService);
|
||||
|
@ -115,8 +115,20 @@ export class CellContextKeyManager extends Disposable {
|
|||
const metadata = this.element.getEvaluatedMetadata(this.notebookTextModel.metadata);
|
||||
this.cellEditable.set(!!metadata.editable);
|
||||
|
||||
const runState = metadata.runState ?? NotebookCellRunState.Idle;
|
||||
this.cellRunState.set(NotebookCellRunState[runState]);
|
||||
const runState = metadata.runState ?? NotebookCellExecutionState.Idle;
|
||||
if (runState === NotebookCellExecutionState.Idle) {
|
||||
if (metadata.lastRunSuccess === true) {
|
||||
this.cellRunState.set('succeeded');
|
||||
} else if (metadata.lastRunSuccess === false) {
|
||||
this.cellRunState.set('failed');
|
||||
} else {
|
||||
this.cellRunState.set('idle');
|
||||
}
|
||||
} else if (runState === NotebookCellExecutionState.Executing) {
|
||||
this.cellRunState.set('executing');
|
||||
} else if (runState === NotebookCellExecutionState.Pending) {
|
||||
this.cellRunState.set('pending');
|
||||
}
|
||||
}
|
||||
|
||||
private updateForEditState() {
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as Codicons from 'vs/base/common/codicons';
|
||||
import { getPixelRatio, getZoomLevel } from 'vs/base/browser/browser';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import * as aria from 'vs/base/browser/ui/aria/aria';
|
||||
|
@ -48,7 +49,7 @@ import { StatefulMarkdownCell } from 'vs/workbench/contrib/notebook/browser/view
|
|||
import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel';
|
||||
import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel';
|
||||
import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
|
||||
import { CellEditType, CellKind, NotebookCellMetadata, NotebookCellRunState, NotebookCellsChangeType, ShowCellStatusBarKey } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { CellEditType, CellKind, NotebookCellMetadata, NotebookCellExecutionState, NotebookCellsChangeType, ShowCellStatusBarKey } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { CodiconActionViewItem, createAndFillInActionBarActionsWithVerticalSeparators, VerticalSeparator, VerticalSeparatorViewItem } from './cellActionView';
|
||||
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
import { errorStateIcon, successStateIcon, unfoldIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons';
|
||||
|
@ -889,9 +890,9 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende
|
|||
}
|
||||
|
||||
return this.notebookEditor.viewModel.getCellIndex(element);
|
||||
});
|
||||
}, element.metadata?.lastRunSuccess);
|
||||
|
||||
if (metadata.runState === NotebookCellRunState.Running) {
|
||||
if (metadata.runState === NotebookCellExecutionState.Executing) {
|
||||
if (metadata.runStartTime) {
|
||||
templateData.elementDisposables.add(templateData.timer.start(metadata.runStartTime));
|
||||
} else {
|
||||
|
@ -907,7 +908,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende
|
|||
editorOptions.setGlyphMargin(metadata.breakpointMargin);
|
||||
}
|
||||
|
||||
if (metadata.runState === NotebookCellRunState.Running) {
|
||||
if (metadata.runState === NotebookCellExecutionState.Executing) {
|
||||
templateData.progressBar.infinite().show(500);
|
||||
} else {
|
||||
templateData.progressBar.hide();
|
||||
|
@ -1103,8 +1104,9 @@ export class RunStateRenderer {
|
|||
private static readonly MIN_SPINNER_TIME = 200;
|
||||
|
||||
private spinnerTimer: any | undefined;
|
||||
private pendingNewState: NotebookCellRunState | undefined;
|
||||
private lastRunState: NotebookCellRunState | undefined;
|
||||
private lastRunState: NotebookCellExecutionState | undefined;
|
||||
private pendingNewState: NotebookCellExecutionState | undefined;
|
||||
private pendingLastRunSuccess: boolean | undefined;
|
||||
|
||||
constructor(private readonly element: HTMLElement) {
|
||||
DOM.hide(element);
|
||||
|
@ -1117,41 +1119,51 @@ export class RunStateRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
renderState(runState: NotebookCellRunState = NotebookCellRunState.Idle, getCellIndex: () => number) {
|
||||
renderState(runState: NotebookCellExecutionState = NotebookCellExecutionState.Idle, getCellIndex: () => number, lastRunSuccess: boolean | undefined = undefined) {
|
||||
if (this.spinnerTimer) {
|
||||
this.pendingNewState = runState;
|
||||
this.pendingLastRunSuccess = lastRunSuccess;
|
||||
return;
|
||||
}
|
||||
|
||||
if (runState === NotebookCellRunState.Success) {
|
||||
let runStateTooltip: string | undefined;
|
||||
if (runState === NotebookCellExecutionState.Idle && lastRunSuccess) {
|
||||
aria.alert(`Code cell at ${getCellIndex()} finishes running successfully`);
|
||||
DOM.reset(this.element, renderIcon(successStateIcon));
|
||||
} else if (runState === NotebookCellRunState.Error) {
|
||||
} else if (runState === NotebookCellExecutionState.Idle && !lastRunSuccess) {
|
||||
aria.alert(`Code cell at ${getCellIndex()} finishes running with errors`);
|
||||
DOM.reset(this.element, renderIcon(errorStateIcon));
|
||||
} else if (runState === NotebookCellRunState.Running) {
|
||||
if (this.lastRunState !== NotebookCellRunState.Running) {
|
||||
} else if (runState === NotebookCellExecutionState.Executing) {
|
||||
runStateTooltip = localize('runStateExecuting', "Executing");
|
||||
if (this.lastRunState !== NotebookCellExecutionState.Executing) {
|
||||
aria.alert(`Code cell at ${getCellIndex()} starts running`);
|
||||
}
|
||||
DOM.reset(this.element, renderIcon(syncing));
|
||||
|
||||
this.spinnerTimer = setTimeout(() => {
|
||||
this.spinnerTimer = undefined;
|
||||
if (this.pendingNewState) {
|
||||
this.renderState(this.pendingNewState, getCellIndex);
|
||||
if (this.pendingNewState && this.pendingNewState !== runState) {
|
||||
this.renderState(this.pendingNewState, getCellIndex, this.pendingLastRunSuccess);
|
||||
this.pendingNewState = undefined;
|
||||
}
|
||||
}, RunStateRenderer.MIN_SPINNER_TIME);
|
||||
} else if (runState === NotebookCellExecutionState.Pending) {
|
||||
// Not spinning
|
||||
runStateTooltip = localize('runStatePending', "Pending");
|
||||
DOM.reset(this.element, renderIcon(Codicons.Codicon.sync));
|
||||
} else {
|
||||
this.element.innerText = '';
|
||||
}
|
||||
|
||||
if (runState === NotebookCellRunState.Idle) {
|
||||
if (runState === NotebookCellExecutionState.Idle && typeof lastRunSuccess !== 'boolean') {
|
||||
DOM.hide(this.element);
|
||||
} else {
|
||||
this.element.style.display = 'flex';
|
||||
}
|
||||
|
||||
if (runStateTooltip) {
|
||||
this.element.setAttribute('title', runStateTooltip);
|
||||
}
|
||||
|
||||
this.lastRunState = runState;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import { Emitter, Event } from 'vs/base/common/event';
|
|||
import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
|
||||
import { INotebookTextModel, NotebookCellOutputsSplice, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, CellEditType, CellUri, notebookDocumentMetadataDefaults, diff, NotebookCellsChangeType, ICellDto2, TransientOptions, NotebookTextModelChangedEvent, NotebookRawContentEvent, IOutputDto, ICellOutput, IOutputItemDto, ISelectionState } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { INotebookTextModel, NotebookCellOutputsSplice, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, CellEditType, CellUri, notebookDocumentMetadataDefaults, diff, NotebookCellsChangeType, ICellDto2, TransientOptions, NotebookTextModelChangedEvent, NotebookRawContentEvent, IOutputDto, ICellOutput, IOutputItemDto, ISelectionState, NullablePartialNotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { ITextSnapshot } from 'vs/editor/common/model';
|
||||
import { IUndoRedoService, UndoRedoElementType, IUndoRedoElement, IResourceUndoRedoElement, UndoRedoGroup, IWorkspaceUndoRedoElement } from 'vs/platform/undoRedo/common/undoRedo';
|
||||
import { MoveCellEdit, SpliceCellsEdit, CellMetadataEdit } from 'vs/workbench/contrib/notebook/common/model/cellEdit';
|
||||
|
@ -317,18 +317,40 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
|
|||
this._operationManager.pushStackElement(label, selectionState, undoRedoGroup, this.alternativeVersionId);
|
||||
}
|
||||
|
||||
private _getCellIndexByHandle(handle: number) {
|
||||
return this.cells.findIndex(c => c.handle === handle);
|
||||
}
|
||||
|
||||
private _getCellIndexWithOutputIdHandle(outputId: string) {
|
||||
return this.cells.findIndex(c => !!c.outputs.find(o => o.outputId === outputId));
|
||||
}
|
||||
|
||||
applyEdits(rawEdits: ICellEditOperation[], synchronous: boolean, beginSelectionState: ISelectionState | undefined, endSelectionsComputer: () => ISelectionState | undefined, undoRedoGroup: UndoRedoGroup | undefined, computeUndoRedo: boolean = true): boolean {
|
||||
|
||||
this._eventEmitter.beginDeferredEmit();
|
||||
this.pushStackElement('edit', beginSelectionState, undoRedoGroup);
|
||||
|
||||
const edits = rawEdits.map((edit, index) => {
|
||||
let cellIndex: number = -1;
|
||||
if ('index' in edit) {
|
||||
cellIndex = edit.index;
|
||||
} else if ('handle' in edit) {
|
||||
cellIndex = this._getCellIndexByHandle(edit.handle);
|
||||
this._assertIndex(cellIndex);
|
||||
} else if ('outputId' in edit) {
|
||||
cellIndex = this._getCellIndexWithOutputIdHandle(edit.outputId);
|
||||
this._assertIndex(cellIndex);
|
||||
} else if (edit.editType !== CellEditType.DocumentMetadata) {
|
||||
throw new Error('Invalid cell edit');
|
||||
}
|
||||
|
||||
return {
|
||||
edit,
|
||||
cellIndex,
|
||||
end:
|
||||
(edit.editType === CellEditType.DocumentMetadata)
|
||||
? undefined
|
||||
: (edit.editType === CellEditType.Replace ? edit.index + edit.count : edit.index),
|
||||
: (edit.editType === CellEditType.Replace ? edit.index + edit.count : cellIndex),
|
||||
originalIndex: index,
|
||||
};
|
||||
}).sort((a, b) => {
|
||||
|
@ -343,15 +365,15 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
|
|||
return b.end - a.end || b.originalIndex - a.originalIndex;
|
||||
});
|
||||
|
||||
for (const { edit } of edits) {
|
||||
for (const { edit, cellIndex } of edits) {
|
||||
switch (edit.editType) {
|
||||
case CellEditType.Replace:
|
||||
this._replaceCells(edit.index, edit.count, edit.cells, synchronous, computeUndoRedo);
|
||||
break;
|
||||
case CellEditType.Output:
|
||||
//TODO@jrieken,@rebornix no event, no undo stop (?)
|
||||
this._assertIndex(edit.index);
|
||||
const cell = this._cells[edit.index];
|
||||
this._assertIndex(cellIndex);
|
||||
const cell = this._cells[cellIndex];
|
||||
if (edit.append) {
|
||||
this._spliceNotebookCellOutputs(cell.handle, [[cell.outputs.length, 0, edit.outputs.map(op => new NotebookCellOutputTextModel(op))]], computeUndoRedo);
|
||||
} else {
|
||||
|
@ -360,8 +382,8 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
|
|||
break;
|
||||
case CellEditType.OutputItems:
|
||||
{
|
||||
this._assertIndex(edit.index);
|
||||
const cell = this._cells[edit.index];
|
||||
this._assertIndex(cellIndex);
|
||||
const cell = this._cells[cellIndex];
|
||||
if (edit.append) {
|
||||
this._appendNotebookCellOutputItems(cell.handle, edit.outputId, edit.items);
|
||||
} else {
|
||||
|
@ -374,6 +396,10 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
|
|||
this._assertIndex(edit.index);
|
||||
this._changeCellMetadata(this._cells[edit.index].handle, edit.metadata, computeUndoRedo);
|
||||
break;
|
||||
case CellEditType.PartialMetadata:
|
||||
this._assertIndex(cellIndex);
|
||||
this._changeCellMetadataPartial(this._cells[cellIndex].handle, edit.metadata, computeUndoRedo);
|
||||
break;
|
||||
case CellEditType.CellLanguage:
|
||||
this._assertIndex(edit.index);
|
||||
this._changeCellLanguage(this._cells[edit.index].handle, edit.language, computeUndoRedo);
|
||||
|
@ -501,16 +527,10 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
|
|||
}
|
||||
readonly label = 'Update Notebook Metadata';
|
||||
undo() {
|
||||
that._updateNotebookMetadata({
|
||||
...oldMetadata,
|
||||
runState: that.metadata.runState
|
||||
}, false);
|
||||
that._updateNotebookMetadata(oldMetadata, false);
|
||||
}
|
||||
redo() {
|
||||
that._updateNotebookMetadata({
|
||||
...metadata,
|
||||
runState: that.metadata.runState
|
||||
}, false);
|
||||
that._updateNotebookMetadata(metadata, false);
|
||||
}
|
||||
}(), undefined, undefined);
|
||||
}
|
||||
|
@ -623,9 +643,26 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
|
|||
return true;
|
||||
}
|
||||
|
||||
private _changeCellMetadata(handle: number, metadata: NotebookCellMetadata, computeUndoRedo: boolean) {
|
||||
const cell = this._cells.find(cell => cell.handle === handle);
|
||||
private _changeCellMetadataPartial(handle: number, metadata: NullablePartialNotebookCellMetadata, computeUndoRedo: boolean) {
|
||||
const cell = this._mapping.get(handle);
|
||||
if (!cell) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newMetadata: NotebookCellMetadata = {
|
||||
...cell.metadata
|
||||
};
|
||||
let k: keyof NullablePartialNotebookCellMetadata;
|
||||
for (k in metadata) {
|
||||
const value = metadata[k] ?? undefined;
|
||||
newMetadata[k] = value as any; // TS...
|
||||
}
|
||||
|
||||
return this._changeCellMetadata(handle, newMetadata, computeUndoRedo);
|
||||
}
|
||||
|
||||
private _changeCellMetadata(handle: number, metadata: NotebookCellMetadata, computeUndoRedo: boolean) {
|
||||
const cell = this._mapping.get(handle);
|
||||
if (!cell) {
|
||||
return;
|
||||
}
|
||||
|
@ -648,12 +685,11 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
|
|||
}
|
||||
}), undefined, undefined);
|
||||
}
|
||||
// should be deferred
|
||||
cell.metadata = metadata;
|
||||
} else {
|
||||
cell.metadata = metadata;
|
||||
}
|
||||
|
||||
// should be deferred
|
||||
cell.metadata = metadata;
|
||||
|
||||
this._eventEmitter.emit({ kind: NotebookCellsChangeType.ChangeCellMetadata, index: this._cells.indexOf(cell), metadata: cell.metadata, transient: !triggerDirtyChange }, true);
|
||||
}
|
||||
|
||||
|
|
|
@ -61,7 +61,6 @@ export const notebookDocumentMetadataDefaults: Required<NotebookDocumentMetadata
|
|||
cellEditable: true,
|
||||
cellHasExecutionOrder: true,
|
||||
custom: {},
|
||||
runState: NotebookRunState.Idle,
|
||||
trusted: true
|
||||
};
|
||||
|
||||
|
@ -70,15 +69,19 @@ export interface NotebookDocumentMetadata {
|
|||
cellEditable: boolean;
|
||||
cellHasExecutionOrder: boolean;
|
||||
custom?: { [key: string]: unknown };
|
||||
runState?: NotebookRunState;
|
||||
trusted: boolean;
|
||||
}
|
||||
|
||||
export enum NotebookCellRunState {
|
||||
Running = 1,
|
||||
Idle = 2,
|
||||
Success = 3,
|
||||
Error = 4
|
||||
export enum NotebookCellExecutionState {
|
||||
Idle = 1,
|
||||
Pending = 2,
|
||||
Executing = 3,
|
||||
}
|
||||
|
||||
export interface INotebookCellPreviousExecutionResult {
|
||||
executionOrder?: number;
|
||||
success?: boolean;
|
||||
duration?: number;
|
||||
}
|
||||
|
||||
export interface NotebookCellMetadata {
|
||||
|
@ -87,7 +90,8 @@ export interface NotebookCellMetadata {
|
|||
hasExecutionOrder?: boolean;
|
||||
executionOrder?: number;
|
||||
statusMessage?: string;
|
||||
runState?: NotebookCellRunState;
|
||||
lastRunSuccess?: boolean;
|
||||
runState?: NotebookCellExecutionState;
|
||||
runStartTime?: number;
|
||||
lastRunDuration?: number;
|
||||
inputCollapsed?: boolean;
|
||||
|
@ -269,12 +273,12 @@ export interface NotebookCellsChangeLanguageEvent {
|
|||
export interface NotebookCellsChangeMetadataEvent {
|
||||
readonly kind: NotebookCellsChangeType.ChangeCellMetadata;
|
||||
readonly index: number;
|
||||
readonly metadata: NotebookCellMetadata | undefined;
|
||||
readonly metadata: NotebookCellMetadata;
|
||||
}
|
||||
|
||||
export interface NotebookDocumentChangeMetadataEvent {
|
||||
readonly kind: NotebookCellsChangeType.ChangeDocumentMetadata;
|
||||
readonly metadata: NotebookDocumentMetadata | undefined;
|
||||
readonly metadata: NotebookDocumentMetadata;
|
||||
}
|
||||
|
||||
export interface NotebookDocumentUnknownChangeEvent {
|
||||
|
@ -326,7 +330,8 @@ export const enum CellEditType {
|
|||
Move = 7,
|
||||
Unknown = 8,
|
||||
CellContent = 9,
|
||||
OutputItems = 10
|
||||
OutputItems = 10,
|
||||
PartialMetadata = 11
|
||||
}
|
||||
|
||||
export interface ICellDto2 {
|
||||
|
@ -351,9 +356,15 @@ export interface ICellOutputEdit {
|
|||
append?: boolean
|
||||
}
|
||||
|
||||
export interface ICellOutputEditByHandle {
|
||||
editType: CellEditType.Output;
|
||||
handle: number;
|
||||
outputs: IOutputDto[];
|
||||
append?: boolean
|
||||
}
|
||||
|
||||
export interface ICellOutputItemEdit {
|
||||
editType: CellEditType.OutputItems;
|
||||
index: number;
|
||||
outputId: string;
|
||||
items: IOutputItemDto[];
|
||||
append?: boolean;
|
||||
|
@ -365,6 +376,21 @@ export interface ICellMetadataEdit {
|
|||
metadata: NotebookCellMetadata;
|
||||
}
|
||||
|
||||
export type NullablePartialNotebookCellMetadata = {
|
||||
[Key in keyof Partial<NotebookCellMetadata>]: NotebookCellMetadata[Key] | null
|
||||
};
|
||||
|
||||
export interface ICellPartialMetadataEdit {
|
||||
editType: CellEditType.PartialMetadata;
|
||||
index: number;
|
||||
metadata: Partial<NullablePartialNotebookCellMetadata>;
|
||||
}
|
||||
|
||||
export interface ICellPartialMetadataEditByHandle {
|
||||
editType: CellEditType.PartialMetadata;
|
||||
handle: number;
|
||||
metadata: Partial<NullablePartialNotebookCellMetadata>;
|
||||
}
|
||||
|
||||
export interface ICellLanguageEdit {
|
||||
editType: CellEditType.CellLanguage;
|
||||
|
@ -384,7 +410,8 @@ export interface ICellMoveEdit {
|
|||
newIdx: number;
|
||||
}
|
||||
|
||||
export type ICellEditOperation = ICellReplaceEdit | ICellOutputEdit | ICellMetadataEdit | ICellLanguageEdit | IDocumentMetadataEdit | ICellMoveEdit | ICellOutputItemEdit;
|
||||
export type IImmediateCellEditOperation = ICellOutputEditByHandle | ICellPartialMetadataEditByHandle | ICellOutputItemEdit;
|
||||
export type ICellEditOperation = IImmediateCellEditOperation | ICellReplaceEdit | ICellOutputEdit | ICellMetadataEdit | ICellPartialMetadataEdit | IDocumentMetadataEdit | ICellMoveEdit | ICellOutputItemEdit | ICellLanguageEdit;
|
||||
|
||||
export interface NotebookDataDto {
|
||||
readonly cells: ICellDto2[];
|
||||
|
@ -731,10 +758,11 @@ export interface INotebookKernel {
|
|||
isPreferred?: boolean;
|
||||
preloads?: URI[];
|
||||
supportedLanguages?: string[]
|
||||
implementsInterrupt?: boolean;
|
||||
|
||||
resolve(uri: URI, editorId: string, token: CancellationToken): Promise<void>;
|
||||
executeNotebookCell(uri: URI, handle: number | undefined): Promise<void>;
|
||||
cancelNotebookCell(uri: URI, handle: number | undefined): Promise<void>;
|
||||
executeNotebookCellsRequest(uri: URI, ranges: ICellRange[]): Promise<void>;
|
||||
cancelNotebookCellExecution(uri: URI, ranges: ICellRange[]): Promise<void>;
|
||||
}
|
||||
|
||||
export interface INotebookKernelProvider {
|
||||
|
|
|
@ -17,7 +17,7 @@ import { NOTEBOOK_KERNEL_COUNT } from 'vs/workbench/contrib/notebook/browser/not
|
|||
import { NotebookEditorKernelManager } from 'vs/workbench/contrib/notebook/browser/notebookEditorKernelManager';
|
||||
import { NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
|
||||
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
|
||||
import { CellKind, INotebookKernel, IOutputDto, NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { CellKind, ICellRange, INotebookKernel, IOutputDto, NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { setupInstantiationService, withTestNotebook as _withTestNotebook } from 'vs/workbench/contrib/notebook/test/testNotebookEditor';
|
||||
import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices';
|
||||
import { TestQuickInputService } from 'vs/workbench/test/browser/workbenchTestServices';
|
||||
|
@ -29,13 +29,15 @@ suite('NotebookEditorKernelManager', () => {
|
|||
instantiationService.stub(IQuickInputService, new TestQuickInputService());
|
||||
|
||||
const loadKernelPreloads = async () => { };
|
||||
const onDidChangeViewModel = () => { };
|
||||
const testDelegate = { loadKernelPreloads, onDidChangeViewModel };
|
||||
|
||||
async function withTestNotebook(cells: [string, string, CellKind, IOutputDto[], NotebookCellMetadata][], callback: (viewModel: NotebookViewModel, textModel: NotebookTextModel) => void | Promise<void>) {
|
||||
return _withTestNotebook(cells, (editor) => callback(editor.viewModel, editor.viewModel.notebookDocument));
|
||||
}
|
||||
|
||||
test('ctor', () => {
|
||||
instantiationService.createInstance(NotebookEditorKernelManager, {});
|
||||
instantiationService.createInstance(NotebookEditorKernelManager, testDelegate);
|
||||
const contextKeyService = instantiationService.get(IContextKeyService);
|
||||
|
||||
assert.strictEqual(contextKeyService.getContextKeyValue(NOTEBOOK_KERNEL_COUNT.key), 0);
|
||||
|
@ -45,7 +47,7 @@ suite('NotebookEditorKernelManager', () => {
|
|||
await withTestNotebook(
|
||||
[],
|
||||
async (viewModel) => {
|
||||
const kernelManager: NotebookEditorKernelManager = instantiationService.createInstance(NotebookEditorKernelManager, { viewModel, loadKernelPreloads });
|
||||
const kernelManager: NotebookEditorKernelManager = instantiationService.createInstance(NotebookEditorKernelManager, { ...testDelegate, ...{ viewModel } });
|
||||
|
||||
const cell = viewModel.createCell(1, 'var c = 3', 'javascript', CellKind.Code, {}, [], true);
|
||||
await assertThrowsAsync(async () => await kernelManager.executeNotebookCell(cell));
|
||||
|
@ -56,7 +58,7 @@ suite('NotebookEditorKernelManager', () => {
|
|||
await withTestNotebook(
|
||||
[],
|
||||
async (viewModel) => {
|
||||
const kernelManager: NotebookEditorKernelManager = instantiationService.createInstance(NotebookEditorKernelManager, { viewModel, loadKernelPreloads });
|
||||
const kernelManager: NotebookEditorKernelManager = instantiationService.createInstance(NotebookEditorKernelManager, { ...testDelegate, ...{ viewModel } });
|
||||
kernelManager.activeKernel = new TestNotebookKernel({ languages: ['testlang'] });
|
||||
|
||||
const cell = viewModel.createCell(1, 'var c = 3', 'javascript', CellKind.Code, {}, [], true);
|
||||
|
@ -68,10 +70,10 @@ suite('NotebookEditorKernelManager', () => {
|
|||
await withTestNotebook(
|
||||
[],
|
||||
async (viewModel) => {
|
||||
const kernelManager: NotebookEditorKernelManager = instantiationService.createInstance(NotebookEditorKernelManager, { viewModel, loadKernelPreloads });
|
||||
const kernelManager: NotebookEditorKernelManager = instantiationService.createInstance(NotebookEditorKernelManager, { ...testDelegate, ...{ viewModel } });
|
||||
const kernel = new TestNotebookKernel({ languages: ['javascript'] });
|
||||
const executeSpy = sinon.spy();
|
||||
kernel.executeNotebookCell = executeSpy;
|
||||
kernel.executeNotebookCellsRequest = executeSpy;
|
||||
kernelManager.activeKernel = kernel;
|
||||
|
||||
const cell = viewModel.createCell(0, 'var c = 3', 'javascript', CellKind.Code, {}, [], true);
|
||||
|
@ -94,10 +96,10 @@ class TestNotebookKernel implements INotebookKernel {
|
|||
preloads?: URI[] | undefined;
|
||||
supportedLanguages?: string[] | undefined;
|
||||
async resolve(uri: URI, editorId: string, token: CancellationToken): Promise<void> { }
|
||||
executeNotebookCell(uri: URI, handle: number | undefined): Promise<void> {
|
||||
executeNotebookCellsRequest(uri: URI, ranges: ICellRange[]): Promise<void> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
cancelNotebookCell(uri: URI, handle: number | undefined): Promise<void> {
|
||||
cancelNotebookCellExecution(): Promise<void> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
|
|
|
@ -253,6 +253,12 @@ suite('NotebookTextModel', () => {
|
|||
}], true, undefined, () => undefined, undefined);
|
||||
});
|
||||
|
||||
textModel.applyEdits([{
|
||||
index: 0,
|
||||
editType: CellEditType.Metadata,
|
||||
metadata: { executionOrder: 15 },
|
||||
}], true, undefined, () => undefined, undefined);
|
||||
|
||||
textModel.applyEdits([{
|
||||
index: 0,
|
||||
editType: CellEditType.Metadata,
|
||||
|
@ -261,6 +267,34 @@ suite('NotebookTextModel', () => {
|
|||
|
||||
assert.equal(textModel.cells.length, 1);
|
||||
assert.equal(textModel.cells[0].metadata?.editable, false);
|
||||
assert.equal(textModel.cells[0].metadata?.executionOrder, undefined);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test('partial metadata', async function () {
|
||||
await withTestNotebook(
|
||||
[
|
||||
['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }],
|
||||
],
|
||||
(editor) => {
|
||||
const textModel = editor.viewModel.notebookDocument;
|
||||
|
||||
textModel.applyEdits([{
|
||||
index: 0,
|
||||
editType: CellEditType.PartialMetadata,
|
||||
metadata: { executionOrder: 15 },
|
||||
}], true, undefined, () => undefined, undefined);
|
||||
|
||||
textModel.applyEdits([{
|
||||
index: 0,
|
||||
editType: CellEditType.PartialMetadata,
|
||||
metadata: { editable: false },
|
||||
}], true, undefined, () => undefined, undefined);
|
||||
|
||||
assert.strictEqual(textModel.cells.length, 1);
|
||||
assert.strictEqual(textModel.cells[0].metadata?.editable, false);
|
||||
assert.strictEqual(textModel.cells[0].metadata?.executionOrder, 15);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
|
@ -13,7 +13,7 @@ import { mock } from 'vs/base/test/common/mock';
|
|||
import { IModelAddedData, MainContext, MainThreadCommandsShape, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook';
|
||||
import { ExtHostNotebookDocument } from 'vs/workbench/api/common/extHostNotebookDocument';
|
||||
import { CellKind, CellUri, NotebookCellsChangeType } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { CellKind, CellUri, NotebookCellExecutionState, NotebookCellsChangeType } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
|
||||
import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
|
||||
|
@ -412,4 +412,30 @@ suite('NotebookCell#Document', function () {
|
|||
assert.strictEqual(first.document.languageId, 'fooLang');
|
||||
assert.ok(removedDoc === addedDoc);
|
||||
});
|
||||
|
||||
test('change cell execution state does not trigger onDidChangeMetadata event', async function () {
|
||||
let didFireOnDidChangeMetadata = false;
|
||||
let e = extHostNotebooks.onDidChangeCellMetadata(() => {
|
||||
didFireOnDidChangeMetadata = true;
|
||||
});
|
||||
|
||||
const changeExeState = Event.toPromise(extHostNotebooks.onDidChangeNotebookCellExecutionState);
|
||||
|
||||
extHostNotebooks.$acceptModelChanged(notebook.uri, {
|
||||
versionId: 12, rawEvents: [{
|
||||
kind: NotebookCellsChangeType.ChangeCellMetadata,
|
||||
index: 0,
|
||||
metadata: {
|
||||
...notebook.getCellFromIndex(0)?.internalMetadata,
|
||||
...{
|
||||
runState: NotebookCellExecutionState.Executing
|
||||
}
|
||||
}
|
||||
}]
|
||||
}, false);
|
||||
|
||||
await changeExeState;
|
||||
assert.strictEqual(didFireOnDidChangeMetadata, false);
|
||||
e.dispose();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -655,7 +655,6 @@ suite('ExtHostTypes', function () {
|
|||
assert.strictEqual(obj.cellHasExecutionOrder, notebookDocumentMetadataDefaults.cellHasExecutionOrder);
|
||||
assert.deepStrictEqual(obj.custom, notebookDocumentMetadataDefaults.custom);
|
||||
assert.strictEqual(obj.editable, notebookDocumentMetadataDefaults.editable);
|
||||
assert.strictEqual(obj.runState, notebookDocumentMetadataDefaults.runState);
|
||||
assert.strictEqual(obj.trusted, notebookDocumentMetadataDefaults.trusted);
|
||||
});
|
||||
|
||||
|
@ -683,22 +682,4 @@ suite('ExtHostTypes', function () {
|
|||
assert.strictEqual(newObj.custom, undefined);
|
||||
|
||||
});
|
||||
|
||||
test('Unable to reset executionOrder of cells #116956', function () {
|
||||
|
||||
let obj = new types.NotebookCellMetadata();
|
||||
assert.strictEqual(obj.executionOrder, undefined);
|
||||
|
||||
obj = obj.with({ executionOrder: 23 });
|
||||
assert.strictEqual(obj.executionOrder, 23);
|
||||
|
||||
obj = obj.with({ executionOrder: undefined });
|
||||
assert.strictEqual(obj.executionOrder, 23);
|
||||
|
||||
obj = obj.with({});
|
||||
assert.strictEqual(obj.executionOrder, 23);
|
||||
|
||||
obj = obj.with({ executionOrder: null });
|
||||
assert.strictEqual(obj.executionOrder, undefined);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue