Merge branch 'notebook/dev' into main

This commit is contained in:
Rob Lourens 2021-05-28 14:42:20 -07:00
commit 887ff91a14
20 changed files with 719 additions and 132 deletions

196
.vscode/searches/api-todos.code-search vendored Normal file
View file

@ -0,0 +1,196 @@
# Query: todo@API
# Flags: WordMatch OpenEditors
# Including: */vscode.proposed.d.ts
# ContextLines: 1
56 results - 1 file
src/vs/vscode.proposed.d.ts:
1098 */
1099: // todo@API should this be called `notebookType` or `notebookKind`
1100 readonly viewType: string;
1159
1160: // todo@API jsdoc
1161 export class NotebookCellMetadata {
1165 */
1166: // todo@API decouple from metadata? extract as dedicated field or inside an options object and leave metadata purely for extensions?
1167 readonly inputCollapsed?: boolean;
1171 */
1172: // todo@API decouple from metadata? extract as dedicated field or inside an options object and leave metadata purely for extensions?
1173 readonly outputCollapsed?: boolean;
1215 */
1216: // todo@API use duration instead of start/end?
1217 readonly startTime?: number;
1224
1225: // todo@API jsdoc
1226: // todo@API remove this and use simple {}?
1227 export class NotebookDocumentMetadata {
1290 */
1291: // todo@API document which mime types are supported out of the box and
1292 // which are considered secure
1365
1366: //todo@API remove in favour of NotebookCellOutput#metadata
1367 metadata?: { [key: string]: any };
1383 */
1384: //todo@API - add sugar function to add more outputs
1385 export class NotebookCellOutput {
1407
1408: //todo@API have this OR NotebookCellOutputItem#metadata but not both? Preference for this.
1409 metadata?: { [key: string]: any };
1471 * @param languageId The language identifier of the source value.
1472: * @param outputs //TODO@API remove from ctor?
1473: * @param metadata //TODO@API remove from ctor?
1474: * @param executionSummary //TODO@API remove from ctor?
1475 */
1558 */
1559: // todo@API ...NotebookDocument... or just ...Notebook... just like...Cell... above
1560 transientDocumentMetadata?: { [K in keyof NotebookDocumentMetadata]?: boolean };
1617
1618: // todo@api jsdoc
1619: // todo@api Inline unless we can come up with more (future) properties
1620 export interface NotebookCellExecuteStartContext {
1627
1628: // todo@api jsdoc
1629: // todo@api Inline unless we can come up with more (future) properties
1630 export interface NotebookCellExecuteEndContext {
1634 */
1635: // todo@api undefined === false, so by default cells execution fails?
1636 success?: boolean;
1672
1673: // todo@API inline context object?
1674 start(context?: NotebookCellExecuteStartContext): void;
1675
1676: // todo@API inline context object?
1677 end(result?: NotebookCellExecuteEndContext): void;
1751 */
1752: // todo@api rename to notebookType?
1753 readonly viewType: string;
1790 */
1791: // todo@API rename to supportsExecutionOrder, usesExecutionOrder
1792 hasExecutionOrder?: boolean;
1849
1850: // todo@API allow add, not remove
1851 readonly rendererScripts: NotebookRendererScript[];
1870
1871: //todo@API validate this works
1872 asWebviewUri(localResource: Uri): Uri;
1899 */
1900: // todo@api remove? use cell.notebook instead?
1901 readonly document: NotebookDocument;
1910 */
1911: // todo@API rename to state?
1912 readonly executionState: NotebookCellExecutionState;
1930
1931: // todo@api jsdoc
1932 export class NotebookCellStatusBarItem {
1942
1943: // todo@api jsdoc
1944 export interface NotebookCellStatusBarItemProvider {
1952 */
1953: // todo@API rename to ...NotebookCell...
1954 provideCellStatusBarItems(cell: NotebookCell, token: CancellationToken): ProviderResult<NotebookCellStatusBarItem[]>;
1965 */
1966: // todo@api rename to notebooks?
1967: // todo@api what should be in this namespace? should notebookDocuments and friends be in the workspace namespace?
1968 export namespace notebook {
2035 * @param handler
2036: * @param rendererScripts todo@API should renderer scripts come later?
2037 */
2042 */
2043: // todo@API this is an event that is fired for a property that cells don't have and that makes me wonder
2044 // how a correct consumer work, e.g the consumer could have been late and missed an event?
2046
2047: // todo@api jsdoc
2048 export function registerNotebookCellStatusBarItemProvider(notebookType: string, provider: NotebookCellStatusBarItemProvider): Disposable;
2118 readonly start: number;
2119: // todo@API end? Use NotebookCellRange instead?
2120 readonly deletedCount: number;
2121: // todo@API removedCells, deletedCells?
2122 readonly deletedItems: NotebookCell[];
2123: // todo@API addedCells, insertedCells, newCells?
2124 readonly items: NotebookCell[];
2183
2184: // todo@API add onDidChangeNotebookCellOutputs
2185 export const onDidChangeCellOutputs: Event<NotebookCellOutputsChangeEvent>;
2186
2187: // todo@API add onDidChangeNotebookCellMetadata
2188 export const onDidChangeCellMetadata: Event<NotebookCellMetadataChangeEvent>;
2206
2207: // todo@API add NotebookEdit-type which handles all these cases?
2208 // export class NotebookEdit {
2223 export interface WorkspaceEdit {
2224: // todo@API add NotebookEdit-type which handles all these cases?
2225 replaceNotebookMetadata(uri: Uri, value: NotebookDocumentMetadata): void;
2285 */
2286: // todo@API really needed? we didn't find a user here
2287 export function createConcatTextDocument(notebook: NotebookDocument, selector?: DocumentSelector): NotebookConcatTextDocument;
2339
2340: // todo@API use openNotebookDOCUMENT to align with openCustomDocument etc?
2341: // todo@API rename to NotebookDocumentContentProvider
2342 export interface NotebookContentProvider {
2352
2353: // todo@API use NotebookData instead
2354 saveNotebook(document: NotebookDocument, token: CancellationToken): Thenable<void>;
2355
2356: // todo@API use NotebookData instead
2357 saveNotebookAs(targetResource: Uri, document: NotebookDocument, token: CancellationToken): Thenable<void>;
2358
2359: // todo@API use NotebookData instead
2360 backupNotebook(document: NotebookDocument, context: NotebookDocumentBackupContext, token: CancellationToken): Thenable<NotebookDocumentBackup>;
2364
2365: // TODO@api use NotebookDocumentFilter instead of just notebookType:string?
2366: // TODO@API options duplicates the more powerful variant on NotebookContentProvider
2367 export function registerNotebookContentProvider(notebookType: string, provider: NotebookContentProvider, options?: NotebookDocumentContentOptions): Disposable;
2647
2648: // todo@API Split between Inlay- and OverlayHints (InlayHint are for a position, OverlayHints for a non-empty range)
2649: // todo@API add "mini-markdown" for links and styles
2650 // (done) remove description
2699
2700: // todo@API make range first argument
2701 constructor(text: string, position: Position, kind?: InlayHintKind);
3564
3565: // TODO@API must be a class
3566 export interface OpenEditorInfo {
3574
3575: // todo@API proper event type
3576 export const onDidChangeOpenEditors: Event<void>;

View file

@ -475,10 +475,11 @@ suite('Notebook API tests', function () {
const secondCell = vscode.window.activeNotebookEditor!.document.cellAt(1);
assert.strictEqual(secondCell!.outputs.length, 1);
assert.deepStrictEqual(secondCell!.outputs[0].metadata, { testOutputMetadata: true });
assert.strictEqual(secondCell!.outputs[0].outputs.length, 1);
assert.strictEqual(secondCell!.outputs[0].outputs[0].mime, 'text/plain');
assert.strictEqual(new TextDecoder().decode(secondCell!.outputs[0].outputs[0].data), 'Hello World');
assert.deepStrictEqual(secondCell!.outputs[0].outputs[0].metadata, { testOutputItemMetadata: true });
assert.strictEqual((<any>secondCell!.outputs[0]).outputs.length, 1); //todo@jrieken will FAIL once the backwards compatibility is gone
assert.strictEqual(secondCell!.outputs[0].items.length, 1);
assert.strictEqual(secondCell!.outputs[0].items[0].mime, 'text/plain');
assert.strictEqual(new TextDecoder().decode(secondCell!.outputs[0].items[0].data), 'Hello World');
assert.deepStrictEqual(secondCell!.outputs[0].items[0].metadata, { testOutputItemMetadata: true });
assert.strictEqual(secondCell!.executionSummary?.executionOrder, 5);
assert.strictEqual(secondCell!.executionSummary?.success, true);
@ -746,9 +747,9 @@ suite('Notebook API tests', function () {
await vscode.commands.executeCommand('notebook.cell.execute');
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(new TextDecoder().decode(cell.outputs[0].outputs[0].data), 'my output');
assert.strictEqual(cell.outputs[0].items.length, 1);
assert.strictEqual(cell.outputs[0].items[0].mime, 'text/plain');
assert.deepStrictEqual(new TextDecoder().decode(cell.outputs[0].items[0].data), 'my output');
});
await withEvent<vscode.NotebookCellOutputsChangeEvent>(vscode.notebook.onDidChangeCellOutputs, async (event) => {
@ -756,9 +757,9 @@ suite('Notebook API tests', function () {
await vscode.commands.executeCommand('notebook.cell.execute');
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(new TextDecoder().decode(cell.outputs[0].outputs[0].data), 'my second output');
assert.strictEqual(cell.outputs[0].items.length, 1);
assert.strictEqual(cell.outputs[0].items[0].mime, 'text/plain');
assert.deepStrictEqual(new TextDecoder().decode(cell.outputs[0].items[0].data), 'my second output');
});
});
@ -796,9 +797,9 @@ suite('Notebook API tests', function () {
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(new TextDecoder().decode(cell.outputs[0].outputs[0].data), 'Canceled');
assert.strictEqual(cell.outputs[0].items.length, 1);
assert.strictEqual(cell.outputs[0].items[0].mime, 'text/plain');
assert.deepStrictEqual(new TextDecoder().decode(cell.outputs[0].items[0].data), 'Canceled');
});
cancelableKernel.controller.dispose();
@ -838,9 +839,9 @@ suite('Notebook API tests', function () {
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(new TextDecoder().decode(cell.outputs[0].outputs[0].data), 'Interrupted');
assert.strictEqual(cell.outputs[0].items.length, 1);
assert.strictEqual(cell.outputs[0].items[0].mime, 'text/plain');
assert.deepStrictEqual(new TextDecoder().decode(cell.outputs[0].items[0].data), 'Interrupted');
});
interruptableKernel.controller.dispose();
@ -1184,7 +1185,7 @@ suite('Notebook API tests', function () {
vscode.NotebookCellOutputItem.text('Some output', 'text/plain', undefined)
])]);
assert.strictEqual(cell.notebook.cellAt(0).outputs.length, 1);
assert.deepStrictEqual(new TextDecoder().decode(cell.notebook.cellAt(0).outputs[0].outputs[0].data), 'Some output');
assert.deepStrictEqual(new TextDecoder().decode(cell.notebook.cellAt(0).outputs[0].items[0].data), 'Some output');
task.end({});
called = true;
}

View file

@ -37,3 +37,10 @@
line-height: 16px;
margin-left: -4px;
}
.monaco-dropdown-with-primary > .dropdown-action-container > .monaco-dropdown > .dropdown-label > .action-label {
display: block;
background-size: 16px;
background-position: center center;
background-repeat: no-repeat;
}

View file

@ -554,3 +554,37 @@ export function mapFind<T, R>(array: Iterable<T>, mapFn: (value: T) => R | undef
return undefined;
}
/**
* Like Math.min with a delegate, and returns the winning index
*/
export function minIndex<T>(array: readonly T[], fn: (value: T) => number): number {
let minValue = Number.MAX_SAFE_INTEGER;
let minIdx = 0;
array.forEach((value, i) => {
const thisValue = fn(value);
if (thisValue < minValue) {
minValue = thisValue;
minIdx = i;
}
});
return minIdx;
}
/**
* Like Math.max with a delegate, and returns the winning index
*/
export function maxIndex<T>(array: readonly T[], fn: (value: T) => number): number {
let minValue = Number.MIN_SAFE_INTEGER;
let maxIdx = 0;
array.forEach((value, i) => {
const thisValue = fn(value);
if (thisValue > minValue) {
minValue = thisValue;
maxIdx = i;
}
});
return maxIdx;
}

View file

@ -295,4 +295,20 @@ suite('Arrays', () => {
remove();
assert.strictEqual(array.length, 0);
});
test('minIndex', () => {
const array = ['a', 'b', 'c'];
assert.strictEqual(arrays.minIndex(array, value => array.indexOf(value)), 0);
assert.strictEqual(arrays.minIndex(array, value => -array.indexOf(value)), 2);
assert.strictEqual(arrays.minIndex(array, _value => 0), 0);
assert.strictEqual(arrays.minIndex(array, value => value === 'b' ? 0 : 5), 1);
});
test('maxIndex', () => {
const array = ['a', 'b', 'c'];
assert.strictEqual(arrays.maxIndex(array, value => array.indexOf(value)), 2);
assert.strictEqual(arrays.maxIndex(array, value => -array.indexOf(value)), 0);
assert.strictEqual(arrays.maxIndex(array, _value => 0), 0);
assert.strictEqual(arrays.maxIndex(array, value => value === 'b' ? 5 : 0), 1);
});
});

View file

@ -4,11 +4,12 @@
*--------------------------------------------------------------------------------------------*/
import { IContextMenuProvider } from 'vs/base/browser/contextmenu';
import * as DOM from 'vs/base/browser/dom';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { ActionViewItem, BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems';
import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem';
import { IAction } from 'vs/base/common/actions';
import * as DOM from 'vs/base/browser/dom';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { Event } from 'vs/base/common/event';
import { KeyCode } from 'vs/base/common/keyCodes';
import { MenuItemAction } from 'vs/platform/actions/common/actions';
import { MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
@ -21,6 +22,10 @@ export class DropdownWithPrimaryActionViewItem extends BaseActionViewItem {
private _container: HTMLElement | null = null;
private _dropdownContainer: HTMLElement | null = null;
get onDidChangeDropdownVisibility(): Event<boolean> {
return this._dropdown.onDidChangeVisibility;
}
constructor(
primaryAction: MenuItemAction,
dropdownAction: IAction,
@ -33,10 +38,17 @@ export class DropdownWithPrimaryActionViewItem extends BaseActionViewItem {
super(null, primaryAction);
this._primaryAction = new MenuEntryActionViewItem(primaryAction, _keybindingService, _notificationService);
this._dropdown = new DropdownMenuActionViewItem(dropdownAction, dropdownMenuActions, this._contextMenuProvider, {
menuAsChild: true
menuAsChild: true,
classNames: ['codicon', 'codicon-chevron-down']
});
}
override setActionContext(newContext: unknown): void {
super.setActionContext(newContext);
this._primaryAction.setActionContext(newContext);
this._dropdown.setActionContext(newContext);
}
override render(container: HTMLElement): void {
this._container = container;
super.render(this._container);

View file

@ -1090,11 +1090,12 @@ declare module 'vscode' {
* saved on disk and therefore the `scheme` must be checked before trying to access the underlying file or siblings on disk.
*
* @see {@link FileSystemProvider}
* @see {@link TextDocumentContentProvider}
*/
readonly uri: Uri;
// todo@API should we really expose this?
/**
* The type of notebook.
*/
// todo@API should this be called `notebookType` or `notebookKind`
readonly viewType: string;
@ -1148,24 +1149,27 @@ declare module 'vscode' {
getCells(range?: NotebookRange): NotebookCell[];
/**
* Save the document. The saving will be handled by the corresponding content provider
* Save the document. The saving will be handled by the corresponding {@link NotebookSerializer serializer}.
*
* @return A promise that will resolve to true when the document
* has been saved. If the file was not dirty or the save failed,
* will return false.
* has been saved. Will return false if the file was not dirty or when save failed.
*/
save(): Thenable<boolean>;
}
// todo@API jsdoc
export class NotebookCellMetadata {
/**
* Whether a code cell's editor is collapsed
*/
// todo@API decouple from metadata? extract as dedicated field or inside an options object and leave metadata purely for extensions?
readonly inputCollapsed?: boolean;
/**
* Whether a code cell's outputs are collapsed
*/
// todo@API decouple from metadata? extract as dedicated field or inside an options object and leave metadata purely for extensions?
readonly outputCollapsed?: boolean;
/**
@ -1191,13 +1195,35 @@ declare module 'vscode' {
with(change: { inputCollapsed?: boolean | null, outputCollapsed?: boolean | null, [key: string]: any }): NotebookCellMetadata;
}
/**
* The summary of a notebook cell execution.
*/
export interface NotebookCellExecutionSummary {
/**
* The order in which the execution happened.
*/
readonly executionOrder?: number;
/**
* If the exclusive finished successfully.
*/
readonly success?: boolean;
/**
* The unix timestamp at which execution started.
*/
// todo@API use duration instead of start/end?
readonly startTime?: number;
/**
* The unix timestamp at which execution ended.
*/
readonly endTime?: number;
}
// todo@API jsdoc
// todo@API remove this and use simple {}?
export class NotebookDocumentMetadata {
/**
* Additional attributes of the document metadata.
@ -1220,7 +1246,7 @@ declare module 'vscode' {
}
/**
* A notebook range represents on ordered pair of two cell indicies.
* A notebook range represents an ordered pair of two cell indicies.
* It is guaranteed that start is less than or equal to end.
*/
export class NotebookRange {
@ -1337,7 +1363,7 @@ declare module 'vscode' {
*/
data: Uint8Array;
//todo@API
//todo@API remove in favour of NotebookCellOutput#metadata
metadata?: { [key: string]: any };
/**
@ -1377,14 +1403,26 @@ declare module 'vscode' {
* ])
* ```
*/
//todo@API rename to items
outputs: NotebookCellOutputItem[];
items: NotebookCellOutputItem[];
//todo@API
//todo@API have this OR NotebookCellOutputItem#metadata but not both? Preference for this.
metadata?: { [key: string]: any };
/**
* Create new notebook output.
*
* @param outputs Notebook output items.
* @param metadata Optional metadata.
*/
constructor(outputs: NotebookCellOutputItem[], metadata?: { [key: string]: any });
/**
* Create new notebook output.
*
* @param outputs Notebook output items.
* @param id Identifier of this output.
* @param metadata Optional metadata.
*/
constructor(outputs: NotebookCellOutputItem[], id: string, metadata?: { [key: string]: any });
}
@ -1431,9 +1469,9 @@ declare module 'vscode' {
* @param kind The kind.
* @param value The source value.
* @param languageId The language identifier of the source value.
* @param outputs //TODO@API remove ctor?
* @param metadata //TODO@API remove ctor?
* @param executionSummary //TODO@API remove ctor?
* @param outputs //TODO@API remove from ctor?
* @param metadata //TODO@API remove from ctor?
* @param executionSummary //TODO@API remove from ctor?
*/
constructor(kind: NotebookCellKind, value: string, languageId: string, outputs?: NotebookCellOutput[], metadata?: NotebookCellMetadata, executionSummary?: NotebookCellExecutionSummary);
}
@ -1495,6 +1533,12 @@ declare module 'vscode' {
serializeNotebook(data: NotebookData, token: CancellationToken): Uint8Array | Thenable<Uint8Array>;
}
/**
* Notebook content options define what parts of a notebook are persisted. Note
*
* For instance, a notebook serializer can opt-out of saving outputs and in that case the editor doesn't mark a
* notebooks as {@link NotebookDocument.isDirty dirty} when its output has changed.
*/
export interface NotebookDocumentContentOptions {
/**
* Controls if outputs change will trigger notebook document content change and if it will be used in the diff editor
@ -1512,9 +1556,13 @@ declare module 'vscode' {
* Controls if a document metadata property change will trigger notebook document content change and if it will be used in the diff editor
* Default to false. If the content provider doesn't persisit a metadata property in the file document, it should be set to true.
*/
// todo@API ...NotebookDocument... or just ...Notebook... just like...Cell... above
transientDocumentMetadata?: { [K in keyof NotebookDocumentMetadata]?: boolean };
}
/**
* A callback that is invoked by the editor whenever cell execution has been triggered.
*/
export interface NotebookExecuteHandler {
/**
* @param cells The notebook cells to execute.
@ -1524,12 +1572,28 @@ declare module 'vscode' {
(this: NotebookController, cells: NotebookCell[], notebook: NotebookDocument, controller: NotebookController): void | Thenable<void>
}
/**
* Notebook controller affinity for notebook documents.
*
* @see {@link NotebookController.updateNotebookAffinity}
*/
export enum NotebookControllerAffinity {
/**
* Default affinity.
*/
Default = 1,
/**
* A controller is preferred for a notebook.
*/
Preferred = 2
}
/**
* Represents a script that is loaded into the notebook renderer before rendering output. This allows
* to provide and share functionality for notebook markup and notebook output renderers.
*/
export class NotebookRendererScript {
/**
@ -1551,6 +1615,8 @@ declare module 'vscode' {
constructor(uri: Uri, provides?: string | string[]);
}
// todo@api jsdoc
// todo@api Inline unless we can come up with more (future) properties
export interface NotebookCellExecuteStartContext {
/**
* The time that execution began, in milliseconds in the Unix epoch. Used to drive the clock
@ -1559,11 +1625,14 @@ declare module 'vscode' {
startTime?: number;
}
// todo@api jsdoc
// todo@api Inline unless we can come up with more (future) properties
export interface NotebookCellExecuteEndContext {
/**
* If true, a green check is shown on the cell status bar.
* If false, a red X is shown.
*/
// todo@api undefined === false, so by default cells execution fails?
success?: boolean;
/**
@ -1572,18 +1641,13 @@ declare module 'vscode' {
endTime?: number;
}
// todo@API jsdoc slightly outdated: kernel, notebook.createNotebookCellExecution
/**
* A NotebookCellExecution is how the kernel modifies a notebook cell as it is executing. When
* {@link notebook.createNotebookCellExecution `createNotebookCellExecution`} 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.
* A NotebookCellExecution is how {@link NotebookController notebook controller} modify a notebook cell as
* it is executing.
*
* All outputs methods operate on this NotebookCellExecution'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.
* When a cell execution object is created, the cell enters the {@link NotebookCellExecutionState.Pending `Pending`} state.
* When {@link NotebookCellExecution.start `start(...)`} is called on the execution task, it enters the {@link NotebookCellExecutionState.Executing `Executing`} state. When
* {@link NotebookCellExecution.end `end(...)`} is called, it enters the {@link NotebookCellExecutionState.Idle `Idle`} state.
*/
export interface NotebookCellExecution {
@ -1601,9 +1665,6 @@ declare module 'vscode' {
*/
readonly token: CancellationToken;
//todo@API remove? use cell.notebook instead?
readonly document: NotebookDocument;
/**
* Set and unset the order of this cell execution.
*/
@ -1611,18 +1672,70 @@ declare module 'vscode' {
// todo@API inline context object?
start(context?: NotebookCellExecuteStartContext): void;
// todo@API inline context object?
end(result?: NotebookCellExecuteEndContext): void;
// todo@API use object instead of index? FF
clearOutput(cellIndex?: number): Thenable<void>;
appendOutput(out: NotebookCellOutput | NotebookCellOutput[], cellIndex?: number): Thenable<void>;
replaceOutput(out: NotebookCellOutput | NotebookCellOutput[], cellIndex?: number): Thenable<void>;
// todo@API use object instead of index?
appendOutputItems(items: NotebookCellOutputItem | NotebookCellOutputItem[], outputId: string): Thenable<void>;
replaceOutputItems(items: NotebookCellOutputItem | NotebookCellOutputItem[], outputId: string): Thenable<void>;
/**
* Clears the output of the cell that is executing or of another cell that is affected by this execution.
*
* @param cell Cell for which output is cleared. Defaults to the {@link NotebookCellExecution.cell cell} of
* this execution.
* @return A thenable that resolves when the operation finished.
*/
clearOutput(cell?: NotebookCell): Thenable<void>;
/**
* Replace the output of the cell that is executing or of another cell that is affected by this execution.
*
* @param out Output that replaces the current output.
* @param cell Cell for which output is cleared. Defaults to the {@link NotebookCellExecution.cell cell} of
* this execution.
* @return A thenable that resolves when the operation finished.
*/
replaceOutput(out: NotebookCellOutput | NotebookCellOutput[], cell?: NotebookCell): Thenable<void>;
/**
* Append to the output of the cell that is executing or to another cell that is affected by this execution.
*
* @param out Output that is appended to the current output.
* @param cell Cell for which output is cleared. Defaults to the {@link NotebookCellExecution.cell cell} of
* this execution.
* @return A thenable that resolves when the operation finished.
*/
appendOutput(out: NotebookCellOutput | NotebookCellOutput[], cell?: NotebookCell): Thenable<void>;
/**
* Replace all output items of existing cell output.
*
* @param items Output items that replace the items of existing output.
* @param output Output object or the identifier of one.
* @return A thenable that resolves when the operation finished.
*/
replaceOutputItems(items: NotebookCellOutputItem | NotebookCellOutputItem[], output: NotebookCellOutput | string): Thenable<void>;
/**
* Append output items to existing cell output.
*
* @param items Output items that are append to existing output.
* @param output Output object or the identifier of one.
* @return A thenable that resolves when the operation finished.
*/
appendOutputItems(items: NotebookCellOutputItem | NotebookCellOutputItem[], output: NotebookCellOutput | string): Thenable<void>;
}
/**
* A notebook controller represents an entity that can execute notebook cells. This is often referred to as a kernel.
*
* There can be multiple controllers and the editor will let users choose which controller to use for a certain notebook. The
* {@link NotebookController.viewType `viewType`}-property defines for what kind of notebooks a controller is for and
* the {@link NotebookController.updateNotebookAffinity `updateNotebookAffinity`}-function allows controllers to set a preference
* for specific notebooks.
*
* When a cell is being run the editor will invoke the {@link NotebookController.executeHandler `executeHandler`} and a controller
* is expected to create and finalize a {@link NotebookCellExecution notebook cell execution}. However, controllers are also free
* to create executions by themselves.
*/
export interface NotebookController {
/**
@ -1636,6 +1749,7 @@ declare module 'vscode' {
/**
* The notebook view type this controller is for.
*/
// todo@api rename to notebookType?
readonly viewType: string;
/**
@ -1689,8 +1803,8 @@ declare module 'vscode' {
* By default cell execution is canceled via {@link NotebookCellExecution.token tokens}. Cancellation
* tokens require that a controller can keep track of its execution so that it can cancel a specific execution at a later
* point. Not all scenarios allow for that, eg. REPL-style controllers often work by interrupting whatever is currently
* running. For those cases the {@link NotebookInterruptHandler interrupt handler} exists - it can be thought of as the
* equivalent of `SIGINT` or `Control+C` in terminals.
* running. For those cases the interrupt handler exists - it can be thought of as the equivalent of `SIGINT`
* or `Control+C` in terminals.
*
* _Note_ that supporting {@link NotebookCellExecution.token cancellation tokens} is preferred and that interrupt handlers should
* only be used when tokens cannot be supported.
@ -1711,7 +1825,7 @@ declare module 'vscode' {
/**
* A controller can set affinities for specific notebook documents. This allows a controller
* to be more important for some notebooks.
* to be presented more prominent for some notebooks.
*
* @param notebook The notebook for which a priority is set.
* @param affinity A controller affinity
@ -1721,8 +1835,11 @@ declare module 'vscode' {
/**
* Create a cell execution task.
*
* _Note_ that there can only be one execution per cell at a time and that an error is thrown if
* a cell execution is created while another is still active.
*
* This should be used in response to the {@link NotebookController.executeHandler execution handler}
* being calleed or when cell execution has been started else, e.g when a cell was already
* being called or when cell execution has been started else, e.g when a cell was already
* executing or when cell execution was triggered from another source.
*
* @param cell The notebook cell for which to create the execution.
@ -1734,7 +1851,8 @@ declare module 'vscode' {
readonly rendererScripts: NotebookRendererScript[];
/**
* An event that fires when a renderer (see `preloads`) has send a message to the controller.
* An event that fires when a {@link NotebookController.rendererScripts renderer script} has send a message to
* the controller.
*/
readonly onDidReceiveMessage: Event<{ editor: NotebookEditor, message: any }>;
@ -1754,18 +1872,43 @@ declare module 'vscode' {
asWebviewUri(localResource: Uri): Uri;
}
/**
* The execution state of a notebook cell.
*/
export enum NotebookCellExecutionState {
/**
* The cell is idle.
*/
Idle = 1,
/**
* Execution for the cell is pending.
*/
Pending = 2,
/**
* The cell is currently executing.
*/
Executing = 3,
}
/**
* An event describing a cell execution state change.
*/
export interface NotebookCellExecutionStateChangeEvent {
/**
* The {@link NotebookDocument notebook document} for which the cell execution state has changed.
*/
// todo@api remove? use cell.notebook instead?
readonly document: NotebookDocument;
/**
* The {@link NotebookCell cell} for which the execution state has changed.
*/
readonly cell: NotebookCell;
/**
* The new execution state of the cell.
*/
// todo@API rename to state?
readonly executionState: NotebookCellExecutionState;
}
@ -1785,6 +1928,7 @@ declare module 'vscode' {
Right = 2
}
// todo@api jsdoc
export class NotebookCellStatusBarItem {
text: string;
alignment: NotebookCellStatusBarAlignment;
@ -1796,6 +1940,7 @@ declare module 'vscode' {
constructor(text: string, alignment: NotebookCellStatusBarAlignment, command?: string | Command, tooltip?: string, priority?: number, accessibilityInformation?: AccessibilityInformation);
}
// todo@api jsdoc
export interface NotebookCellStatusBarItemProvider {
/**
* Implement and fire this event to signal that statusbar items have changed. The provide method will be called again.
@ -1805,9 +1950,21 @@ declare module 'vscode' {
/**
* The provider will be called when the cell scrolls into view, when its content, outputs, language, or metadata change, and when it changes execution state.
*/
// todo@API rename to ...NotebookCell...
provideCellStatusBarItems(cell: NotebookCell, token: CancellationToken): ProviderResult<NotebookCellStatusBarItem[]>;
}
/**
* Namespace for notebooks.
*
* The notebooks functionality is composed of three loosly coupled components:
*
* 1. {@link NotebookSerializer} enable the editor to open and save notebooks
* 2. {@link NotebookController} own the execution of notebooks, e.g they create output from code cells.
* 3. NotebookRenderer present notebook output in the editor. They run in a separate context.
*/
// todo@api rename to notebooks?
// todo@api what should be in this namespace? should notebookDocuments and friends be in the workspace namespace?
export namespace notebook {
/**
@ -1876,14 +2033,18 @@ declare module 'vscode' {
* @param viewType A notebook view type for which this controller is for.
* @param label The label of the controller
* @param handler
* @param rendererScripts
* @param rendererScripts todo@API should renderer scripts come later?
*/
export function createNotebookController(id: string, viewType: string, label: string, handler?: NotebookExecuteHandler, rendererScripts?: NotebookRendererScript[]): NotebookController;
// todo@API what is this used for?
// todo@API qualify cell, ...NotebookCell...
/**
* An {@link Event} which fires when the execution state of a cell has changed.
*/
// todo@API this is an event that is fired for a property that cells don't have and that makes me wonder
// how a correct consumer work, e.g the consumer could have been late and missed an event?
export const onDidChangeNotebookCellExecutionState: Event<NotebookCellExecutionStateChangeEvent>;
// todo@api jsdoc
export function registerNotebookCellStatusBarItemProvider(notebookType: string, provider: NotebookCellStatusBarItemProvider): Disposable;
}

View file

@ -44,7 +44,7 @@ export class ExtHostCell {
};
}
private _outputs: extHostTypes.NotebookCellOutput[];
private _outputs: vscode.NotebookCellOutput[];
private _metadata: extHostTypes.NotebookCellMetadata;
private _previousResult: vscode.NotebookCellExecutionSummary | undefined;
@ -102,9 +102,9 @@ export class ExtHostCell {
const output = this._outputs.find(op => op.id === outputId);
if (output) {
if (!append) {
output.outputs.length = 0;
output.items.length = 0;
}
output.outputs.push(...newItems);
output.items.push(...newItems);
}
}

View file

@ -381,38 +381,53 @@ class NotebookCellExecutionTask extends Disposable {
this.applyEdits([edit]);
}
private cellIndexToHandle(cellIndex: number | undefined): number {
if (typeof cellIndex !== 'number') {
return this._cell.handle;
private cellIndexToHandle(cellOrCellIndex: vscode.NotebookCell | number | undefined): number {
let cell: ExtHostCell | undefined = this._cell;
if (typeof cellOrCellIndex === 'number') {
// todo@jrieken remove support for number shortly
cell = this._document.getCellFromIndex(cellOrCellIndex);
} else if (cellOrCellIndex) {
cell = this._document.getCellFromApiCell(cellOrCellIndex);
}
const cell = this._document.getCellFromIndex(cellIndex);
if (!cell) {
throw new Error('INVALID cell index');
throw new Error('INVALID cell');
}
return cell.handle;
}
private validateAndConvertOutputs(items: vscode.NotebookCellOutput[]): IOutputDto[] {
return items.map(output => {
const newOutput = NotebookCellOutput.ensureUniqueMimeTypes(output.outputs, true);
if (newOutput === output.outputs) {
const newOutput = NotebookCellOutput.ensureUniqueMimeTypes(output.items, true);
if (newOutput === output.items) {
return extHostTypeConverters.NotebookCellOutput.from(output);
}
return extHostTypeConverters.NotebookCellOutput.from({
outputs: newOutput,
items: newOutput,
id: output.id,
metadata: output.metadata
});
});
}
private async updateOutputs(outputs: vscode.NotebookCellOutput | vscode.NotebookCellOutput[], cell: vscode.NotebookCell | number | undefined, append: boolean): Promise<void> {
const handle = this.cellIndexToHandle(cell);
const outputDtos = this.validateAndConvertOutputs(asArray(outputs));
return this.applyEditSoon({ editType: CellEditType.Output, handle, append, outputs: outputDtos });
}
private async updateOutputItems(items: vscode.NotebookCellOutputItem | vscode.NotebookCellOutputItem[], outputOrOutputId: vscode.NotebookCellOutput | string, append: boolean): Promise<void> {
if (NotebookCellOutput.isNotebookCellOutput(outputOrOutputId)) {
outputOrOutputId = outputOrOutputId.id;
}
items = NotebookCellOutput.ensureUniqueMimeTypes(asArray(items), true);
return this.applyEditSoon({ editType: CellEditType.OutputItems, items: items.map(extHostTypeConverters.NotebookCellOutputItem.from), outputId: outputOrOutputId, append });
}
asApiObject(): vscode.NotebookCellExecution {
const that = this;
return Object.freeze(<vscode.NotebookCellExecution>{
const result: vscode.NotebookCellExecution = {
get token() { return that._tokenSource.token; },
get document() { return that._document.apiNotebook; },
get cell() { return that._cell.apiCell; },
get executionOrder() { return that._executionOrder; },
set executionOrder(v: number | undefined) {
that._executionOrder = v;
@ -450,37 +465,32 @@ class NotebookCellExecutionTask extends Disposable {
});
},
clearOutput(cellIndex?: number): Thenable<void> {
clearOutput(cell?: vscode.NotebookCell | number): Thenable<void> {
that.verifyStateForOutput();
return this.replaceOutput([], cellIndex);
return that.updateOutputs([], cell, false);
},
async appendOutput(outputs: vscode.NotebookCellOutput | vscode.NotebookCellOutput[], cellIndex?: number): Promise<void> {
async appendOutput(outputs: vscode.NotebookCellOutput | vscode.NotebookCellOutput[], cell?: vscode.NotebookCell | number): Promise<void> {
that.verifyStateForOutput();
const handle = that.cellIndexToHandle(cellIndex);
const outputDtos = that.validateAndConvertOutputs(asArray(outputs));
return that.applyEditSoon({ editType: CellEditType.Output, handle, append: true, outputs: outputDtos });
return that.updateOutputs(outputs, cell, true);
},
async replaceOutput(outputs: vscode.NotebookCellOutput | vscode.NotebookCellOutput[], cellIndex?: number): Promise<void> {
async replaceOutput(outputs: vscode.NotebookCellOutput | vscode.NotebookCellOutput[], cell?: vscode.NotebookCell | number): Promise<void> {
that.verifyStateForOutput();
const handle = that.cellIndexToHandle(cellIndex);
const outputDtos = that.validateAndConvertOutputs(asArray(outputs));
return that.applyEditSoon({ editType: CellEditType.Output, handle, outputs: outputDtos });
return that.updateOutputs(outputs, cell, false);
},
async appendOutputItems(items: vscode.NotebookCellOutputItem | vscode.NotebookCellOutputItem[], outputId: string): Promise<void> {
async appendOutputItems(items: vscode.NotebookCellOutputItem | vscode.NotebookCellOutputItem[], output: vscode.NotebookCellOutput | string): Promise<void> {
that.verifyStateForOutput();
items = NotebookCellOutput.ensureUniqueMimeTypes(asArray(items), true);
return that.applyEditSoon({ editType: CellEditType.OutputItems, append: true, items: items.map(extHostTypeConverters.NotebookCellOutputItem.from), outputId });
that.updateOutputItems(items, output, true);
},
async replaceOutputItems(items: vscode.NotebookCellOutputItem | vscode.NotebookCellOutputItem[], outputId: string): Promise<void> {
async replaceOutputItems(items: vscode.NotebookCellOutputItem | vscode.NotebookCellOutputItem[], output: vscode.NotebookCellOutput | string): Promise<void> {
that.verifyStateForOutput();
items = NotebookCellOutput.ensureUniqueMimeTypes(asArray(items), true);
return that.applyEditSoon({ editType: CellEditType.OutputItems, items: items.map(extHostTypeConverters.NotebookCellOutputItem.from), outputId });
that.updateOutputItems(items, output, false);
}
});
};
return Object.freeze(result);
}
}

View file

@ -1547,10 +1547,10 @@ export namespace NotebookCellOutputItem {
}
export namespace NotebookCellOutput {
export function from(output: types.NotebookCellOutput): notebooks.IOutputDto {
export function from(output: vscode.NotebookCellOutput): notebooks.IOutputDto {
return {
outputId: output.id,
outputs: output.outputs.map(NotebookCellOutputItem.from),
outputs: output.items.map(NotebookCellOutputItem.from),
metadata: output.metadata
};
}

View file

@ -9,7 +9,7 @@ import { IRelativePattern } from 'vs/base/common/glob';
import { isMarkdownString, MarkdownString as BaseMarkdownString } from 'vs/base/common/htmlContent';
import { ReadonlyMapView, ResourceMap } from 'vs/base/common/map';
import { normalizeMimeType } from 'vs/base/common/mime';
import { isStringArray } from 'vs/base/common/types';
import { isArray, isStringArray } from 'vs/base/common/types';
import { URI } from 'vs/base/common/uri';
import { generateUuid } from 'vs/base/common/uuid';
import { FileSystemProviderErrorCode, markAsFileSystemProviderError } from 'vs/platform/files/common/files';
@ -3075,11 +3075,11 @@ export class NotebookCellData {
kind: NotebookCellKind;
value: string;
languageId: string;
outputs?: NotebookCellOutput[];
outputs?: vscode.NotebookCellOutput[];
metadata?: NotebookCellMetadata;
executionSummary?: vscode.NotebookCellExecutionSummary;
constructor(kind: NotebookCellKind, value: string, languageId: string, outputs?: NotebookCellOutput[], metadata?: NotebookCellMetadata, executionSummary?: vscode.NotebookCellExecutionSummary) {
constructor(kind: NotebookCellKind, value: string, languageId: string, outputs?: vscode.NotebookCellOutput[], metadata?: NotebookCellMetadata, executionSummary?: vscode.NotebookCellExecutionSummary) {
this.kind = kind;
this.value = value;
this.languageId = languageId;
@ -3164,6 +3164,16 @@ export class NotebookCellOutputItem {
export class NotebookCellOutput {
static isNotebookCellOutput(candidate: any): candidate is vscode.NotebookCellOutput {
if (candidate instanceof NotebookCellOutput) {
return true;
}
if (!candidate || typeof candidate !== 'object') {
return false;
}
return typeof (<NotebookCellOutput>candidate).id === 'string' && isArray((<NotebookCellOutput>candidate).items);
}
static ensureUniqueMimeTypes(items: NotebookCellOutputItem[], warn: boolean = false): NotebookCellOutputItem[] {
const seen = new Set<string>();
const removeIdx = new Set<number>();
@ -3187,15 +3197,18 @@ export class NotebookCellOutput {
}
id: string;
outputs: NotebookCellOutputItem[];
items: NotebookCellOutputItem[];
metadata?: Record<string, any>;
get outputs() { return this.items; }
set outputs(value) { this.items = value; }
constructor(
outputs: NotebookCellOutputItem[],
items: NotebookCellOutputItem[],
idOrMetadata?: string | Record<string, any>,
metadata?: Record<string, any>
) {
this.outputs = NotebookCellOutput.ensureUniqueMimeTypes(outputs, true);
this.items = NotebookCellOutput.ensureUniqueMimeTypes(items, true);
if (typeof idOrMetadata === 'string') {
this.id = idOrMetadata;
this.metadata = metadata;

View file

@ -34,7 +34,7 @@ import { WorkbenchActionExecutedClassification, WorkbenchActionExecutedEvent } f
import { NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService';
import { Iterable } from 'vs/base/common/iterator';
import { flatten } from 'vs/base/common/arrays';
import { flatten, maxIndex, minIndex } from 'vs/base/common/arrays';
import { Codicon } from 'vs/base/common/codicons';
// Kernel Command
@ -62,6 +62,8 @@ const DELETE_CELL_COMMAND_ID = 'notebook.cell.delete';
const CANCEL_CELL_COMMAND_ID = 'notebook.cell.cancelExecution';
const EXECUTE_CELL_SELECT_BELOW = 'notebook.cell.executeAndSelectBelow';
const EXECUTE_CELL_INSERT_BELOW = 'notebook.cell.executeAndInsertBelow';
const EXECUTE_CELL_AND_BELOW = 'notebook.cell.executeCellAndBelow';
const EXECUTE_CELLS_ABOVE = 'notebook.cell.executeCellsAbove';
const CLEAR_CELL_OUTPUTS_COMMAND_ID = 'notebook.cell.clearOutputs';
const CENTER_ACTIVE_CELL = 'notebook.centerActiveCell';
@ -410,6 +412,74 @@ function parseMultiCellExecutionArgs(accessor: ServicesAccessor, ...args: any[])
return context;
}
registerAction2(class ExecuteAboveCells extends NotebookMultiCellAction<INotebookActionContext> {
constructor() {
super({
id: EXECUTE_CELLS_ABOVE,
precondition: executeCellCondition,
title: localize('notebookActions.executeAbove', "Execute Above Cells"),
menu: {
id: MenuId.NotebookCellExecute,
when: executeCellCondition
},
icon: icons.executeAboveIcon
});
}
parseArgs(accessor: ServicesAccessor, ...args: any[]): INotebookActionContext | undefined {
return parseMultiCellExecutionArgs(accessor, ...args);
}
async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise<void> {
let endCellIdx: number | undefined = undefined;
if (context.ui && context.cell) {
endCellIdx = context.notebookEditor.viewModel.getCellIndex(context.cell);
} else if (context.selectedCells) {
endCellIdx = maxIndex(context.selectedCells, cell => context.notebookEditor.viewModel.getCellIndex(cell));
}
if (typeof endCellIdx === 'number') {
const range = { start: 0, end: endCellIdx };
const cells = context.notebookEditor.viewModel.getCells(range);
context.notebookEditor.executeNotebookCells(cells);
}
}
});
registerAction2(class ExecuteCellAndBelow extends NotebookMultiCellAction<INotebookActionContext> {
constructor() {
super({
id: EXECUTE_CELL_AND_BELOW,
precondition: executeCellCondition,
title: localize('notebookActions.executeBelow', "Execute Cell and Below"),
menu: {
id: MenuId.NotebookCellExecute,
when: executeCellCondition,
},
icon: icons.executeBelowIcon
});
}
parseArgs(accessor: ServicesAccessor, ...args: any[]): INotebookActionContext | undefined {
return parseMultiCellExecutionArgs(accessor, ...args);
}
async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise<void> {
let startCellIdx: number | undefined = undefined;
if (context.ui && context.cell) {
startCellIdx = context.notebookEditor.viewModel.getCellIndex(context.cell);
} else if (context.selectedCells) {
startCellIdx = minIndex(context.selectedCells, cell => context.notebookEditor.viewModel.getCellIndex(cell));
}
if (typeof startCellIdx === 'number') {
const range = { start: startCellIdx, end: context.notebookEditor.viewModel.viewCells.length };
const cells = context.notebookEditor.viewModel.getCells(range);
context.notebookEditor.executeNotebookCells(cells);
}
}
});
registerAction2(class ExecuteCell extends NotebookMultiCellAction<INotebookActionContext> {
constructor() {
super({

View file

@ -498,7 +498,7 @@
height: initial;
}
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .run-button-container .monaco-toolbar .codicon {
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .run-button-container .monaco-toolbar .action-item:not(.monaco-dropdown-with-primary) .codicon {
padding: 6px;
}
@ -508,6 +508,7 @@
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row:hover .run-button-container .monaco-toolbar,
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.focused .run-button-container .monaco-toolbar,
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-run-toolbar-dropdown-active .run-button-container .monaco-toolbar,
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-output-hover .run-button-container .monaco-toolbar {
visibility: visible;
}

View file

@ -30,7 +30,7 @@ import { NotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookEd
import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/common/notebookEditorInput';
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
import { NotebookService } from 'vs/workbench/contrib/notebook/browser/notebookServiceImpl';
import { CellKind, CellToolbarLocKey, CellToolbarVisibility, CellUri, DisplayOrderKey, UndoRedoPerCell, ExperimentalUseMarkdownRenderer, getCellUndoRedoComparisonKey, IResolvedNotebookEditorModel, NotebookDocumentBackupData, NotebookTextDiffEditorPreview, NotebookWorkingCopyTypeIdentifier, ShowCellStatusBarKey, CompactView, FocusIndicator, InsertToolbarPosition, GlobalToolbar, ConsolidatedOutputButton, ShowFoldingControls, DragAndDropEnabled, ShowCellStatusBarAfterExecuteKey, NotebookCellEditorOptionsCustomizations } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CellKind, CellToolbarLocKey, CellToolbarVisibility, CellUri, DisplayOrderKey, UndoRedoPerCell, ExperimentalUseMarkdownRenderer, getCellUndoRedoComparisonKey, IResolvedNotebookEditorModel, NotebookDocumentBackupData, NotebookTextDiffEditorPreview, NotebookWorkingCopyTypeIdentifier, ShowCellStatusBarKey, CompactView, FocusIndicator, InsertToolbarPosition, GlobalToolbar, ConsolidatedOutputButton, ShowFoldingControls, DragAndDropEnabled, ShowCellStatusBarAfterExecuteKey, NotebookCellEditorOptionsCustomizations, ConsolidatedRunButton } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo';
import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService';
@ -694,6 +694,12 @@ configurationRegistry.registerConfiguration({
default: true,
tags: ['notebookLayout']
},
[ConsolidatedRunButton]: {
description: nls.localize('notebook.consolidatedRunButton.description', "Control whether extra actions are shown in a dropdown next to the run button."),
type: 'boolean',
default: false,
tags: ['notebookLayout']
},
[NotebookCellEditorOptionsCustomizations]: editorOptionsCustomizationSchema
}
});

View file

@ -11,6 +11,8 @@ export const configureKernelIcon = registerIcon('notebook-kernel-configure', Cod
export const selectKernelIcon = registerIcon('notebook-kernel-select', Codicon.serverEnvironment, localize('selectKernelIcon', 'Configure icon to select a kernel in notebook editors.'));
export const executeIcon = registerIcon('notebook-execute', Codicon.play, localize('executeIcon', 'Icon to execute in notebook editors.'));
export const executeAboveIcon = registerIcon('notebook-execute-above', Codicon.runAbove, localize('executeAboveIcon', 'Icon to execute above cells in notebook editors.'));
export const executeBelowIcon = registerIcon('notebook-execute-below', Codicon.runBelow, localize('executeBelowIcon', 'Icon to execute below cells in notebook editors.'));
export const stopIcon = registerIcon('notebook-stop', Codicon.primitiveSquare, localize('stopIcon', 'Icon to stop an execution in notebook editors.'));
export const deleteCellIcon = registerIcon('notebook-delete-cell', Codicon.trash, localize('deleteCellIcon', 'Icon to delete a cell in notebook editors.'));
export const executeAllIcon = registerIcon('notebook-execute-all', Codicon.runAll, localize('executeAllIcon', 'Icon to execute all cells in notebook editors.'));

View file

@ -192,7 +192,7 @@ export interface ICreationRequestMessage {
type: 'html';
content:
| { type: RenderOutputType.Html; htmlContent: string }
| { type: RenderOutputType.Extension; outputId: string; valueBytes: Uint8Array, metadata: unknown; mimeType: string };
| { type: RenderOutputType.Extension; outputId: string; valueBytes: Uint8Array, metadata: unknown; metadata2: unknown, mimeType: string };
cellId: string;
outputId: string;
cellTop: number;
@ -1515,6 +1515,7 @@ var requirejs = (function() {
mimeType: content.mimeType,
valueBytes: new Uint8Array(outputDto?.valueBytes ?? []),
metadata: outputDto?.metadata,
metadata2: output.metadata
},
};
} else {

View file

@ -3,16 +3,16 @@
* 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';
import { domEvent } from 'vs/base/browser/event';
import * as aria from 'vs/base/browser/ui/aria/aria';
import { renderIcon } from 'vs/base/browser/ui/iconLabel/iconLabels';
import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar';
import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar';
import { IAction } from 'vs/base/common/actions';
import { Action, IAction } from 'vs/base/common/actions';
import * as Codicons from 'vs/base/common/codicons';
import { Color } from 'vs/base/common/color';
import { combinedDisposable, Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import * as platform from 'vs/base/common/platform';
@ -26,6 +26,7 @@ import { ITextModel } from 'vs/editor/common/model';
import * as modes from 'vs/editor/common/modes';
import { tokenizeLineToHTML } from 'vs/editor/common/modes/textToHtmlTokenizer';
import { localize } from 'vs/nls';
import { DropdownWithPrimaryActionViewItem } from 'vs/platform/actions/browser/dropdownWithPrimaryActionViewItem';
import { createActionViewItem, createAndFillInActionBarActions, MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
import { IMenu, MenuItemAction } from 'vs/platform/actions/common/actions';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
@ -35,10 +36,15 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { syncing } from 'vs/platform/theme/common/iconRegistry';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
import { DeleteCellAction, INotebookActionContext, INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions';
import { BaseCellRenderTemplate, CellEditState, CodeCellLayoutInfo, CodeCellRenderTemplate, EXPAND_CELL_INPUT_COMMAND_ID, ICellViewModel, INotebookEditor, isCodeCellRenderTemplate, MarkdownCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { errorStateIcon, successStateIcon, unfoldIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons';
import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellActionView';
import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys';
import { CellDragAndDropController, DRAGGING_CLASS } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellDnd';
import { CellEditorOptions } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellEditorOptions';
import { CellMenus } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellMenus';
import { CellEditorStatusBar } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets';
import { CodeCell } from 'vs/workbench/contrib/notebook/browser/view/renderers/codeCell';
@ -46,12 +52,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, NotebookCellExecutionState, NotebookCellInternalMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellActionView';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
import { errorStateIcon, successStateIcon, unfoldIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons';
import { syncing } from 'vs/platform/theme/common/iconRegistry';
import { CellEditorOptions } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellEditorOptions';
import { CellEditType, CellKind, NotebookCellExecutionState, NotebookCellInternalMetadata, NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { NotebookOptions } from 'vs/workbench/contrib/notebook/common/notebookOptions';
const $ = DOM.$;
@ -94,11 +95,11 @@ abstract class AbstractCellRenderer {
protected readonly notebookEditor: INotebookEditor,
protected readonly contextMenuService: IContextMenuService,
configurationService: IConfigurationService,
private readonly keybindingService: IKeybindingService,
private readonly notificationService: INotificationService,
protected readonly keybindingService: IKeybindingService,
protected readonly notificationService: INotificationService,
protected readonly contextKeyServiceProvider: (container: HTMLElement) => IContextKeyService,
language: string,
protected dndController: CellDragAndDropController | undefined,
protected dndController: CellDragAndDropController | undefined
) {
this.editorOptions = new CellEditorOptions(notebookEditor.notebookOptions, configurationService, language);
this.cellMenus = this.instantiationService.createInstance(CellMenus);
@ -700,7 +701,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende
const cellContainer = DOM.append(container, $('.cell.code'));
const runButtonContainer = DOM.append(cellContainer, $('.run-button-container'));
const runToolbar = disposables.add(this.setupRunToolbar(runButtonContainer, contextKeyService, disposables));
const runToolbar = this.setupRunToolbar(runButtonContainer, container, contextKeyService, disposables);
const executionOrderLabel = DOM.append(cellContainer, $('div.execution-count-label'));
const editorPart = DOM.append(cellContainer, $('.cell-editor-part'));
@ -831,18 +832,59 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende
return combinedDisposable(dragHandleListener, collapsedPartListener);
}
private setupRunToolbar(runButtonContainer: HTMLElement, contextKeyService: IContextKeyService, disposables: DisposableStore): ToolBar {
const runToolbar = this.createToolbar(runButtonContainer);
const runMenu = this.cellMenus.getCellExecuteMenu(contextKeyService);
const update = () => {
const actions = this.getCellToolbarActions(runMenu);
runToolbar.setActions(actions.primary, actions.secondary);
};
disposables.add(runMenu);
disposables.add(runMenu.onDidChange(() => {
update();
private createRunCellToolbar(container: HTMLElement, cellContainer: HTMLElement, contextKeyService: IContextKeyService, disposables: DisposableStore): ToolBar {
const actionViewItemDisposables = disposables.add(new DisposableStore());
const dropdownAction = disposables.add(new Action('notebook.moreRunActions', localize('notebook.moreRunActionsLabel', "More..."), 'codicon-chevron-down', true));
const toolbar = disposables.add(new ToolBar(container, this.contextMenuService, {
getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id),
actionViewItemProvider: _action => {
actionViewItemDisposables.clear();
const actions = this.getCellToolbarActions(this.cellMenus.getCellExecuteMenu(contextKeyService));
const primary = actions.primary[0];
if (!(primary instanceof MenuItemAction)) {
return undefined;
}
if (!actions.secondary.length) {
return undefined;
}
if (!this.notebookEditor.notebookOptions.getLayoutConfiguration().consolidatedRunButton) {
return undefined;
}
const item = new DropdownWithPrimaryActionViewItem(
primary,
dropdownAction,
actions.secondary,
'notebook-cell-run-toolbar',
this.contextMenuService,
this.keybindingService,
this.notificationService);
actionViewItemDisposables.add(item.onDidChangeDropdownVisibility(visible => {
cellContainer.classList.toggle('cell-run-toolbar-dropdown-active', visible);
}));
return item;
},
renderDropdownAsChildElement: true
}));
update();
return toolbar;
}
private setupRunToolbar(runButtonContainer: HTMLElement, cellContainer: HTMLElement, contextKeyService: IContextKeyService, disposables: DisposableStore): ToolBar {
const menu = this.cellMenus.getCellExecuteMenu(contextKeyService);
const runToolbar = this.createRunCellToolbar(runButtonContainer, cellContainer, contextKeyService, disposables);
const updateActions = () => {
const actions = this.getCellToolbarActions(this.cellMenus.getCellExecuteMenu(contextKeyService));
runToolbar.setActions(actions.primary);
};
updateActions();
disposables.add(menu.onDidChange(updateActions));
disposables.add(this.notebookEditor.notebookOptions.onDidChangeOptions(updateActions));
return runToolbar;
}

View file

@ -464,6 +464,7 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re
mime: string;
metadata: unknown;
metadata2: unknown;
text(): string;
json(): any;
@ -643,6 +644,7 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re
element: outputNode,
mime: content.mimeType,
metadata: content.metadata,
metadata2: content.metadata2,
data() {
return content.valueBytes;
},
@ -1036,6 +1038,7 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re
element,
mime: 'text/markdown',
metadata: undefined,
metadata2: undefined,
outputId: undefined,
text() { return content; },
json() { return undefined; },

View file

@ -180,12 +180,12 @@ export interface IOutputItemDto {
export interface IOutputDto {
outputs: IOutputItemDto[];
outputId: string;
metadata?: Record<string, unknown>;
metadata?: Record<string, any>;
}
export interface ICellOutput {
outputs: IOutputItemDto[];
// metadata?: NotebookCellOutsputMetadata;
metadata?: Record<string, any>;
outputId: string;
onDidChangeData: Event<void>;
replaceData(items: IOutputItemDto[]): void;
@ -921,6 +921,7 @@ export const ConsolidatedOutputButton = 'notebook.consolidatedOutputButton';
export const ShowFoldingControls = 'notebook.showFoldingControls';
export const DragAndDropEnabled = 'notebook.dragAndDropEnabled';
export const NotebookCellEditorOptionsCustomizations = 'notebook.editorOptionsCustomizations';
export const ConsolidatedRunButton = 'notebook.consolidatedRunButton';
export const enum CellStatusbarAlignment {
Left = 1,

View file

@ -6,7 +6,7 @@
import { Emitter } from 'vs/base/common/event';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IConfigurationChangeEvent, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { CellToolbarLocKey, CellToolbarVisibility, CompactView, ConsolidatedOutputButton, DragAndDropEnabled, ExperimentalInsertToolbarAlignment, FocusIndicator, GlobalToolbar, InsertToolbarPosition, NotebookCellEditorOptionsCustomizations, ShowCellStatusBarAfterExecuteKey, ShowCellStatusBarKey, ShowFoldingControls } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CellToolbarLocKey, CellToolbarVisibility, CompactView, ConsolidatedOutputButton, ConsolidatedRunButton, DragAndDropEnabled, ExperimentalInsertToolbarAlignment, FocusIndicator, GlobalToolbar, InsertToolbarPosition, NotebookCellEditorOptionsCustomizations, ShowCellStatusBarAfterExecuteKey, ShowCellStatusBarKey, ShowFoldingControls } from 'vs/workbench/contrib/notebook/common/notebookCommon';
const SCROLLABLE_ELEMENT_PADDING_TOP = 18;
@ -54,6 +54,7 @@ export interface NotebookLayoutConfiguration {
insertToolbarAlignment: 'left' | 'center';
globalToolbar: boolean;
consolidatedOutputButton: boolean;
consolidatedRunButton: boolean;
showFoldingControls: 'always' | 'mouseover';
dragAndDropEnabled: boolean;
fontSize: number;
@ -74,6 +75,7 @@ interface NotebookOptionsChangeEvent {
globalToolbar?: boolean;
showFoldingControls?: boolean;
consolidatedOutputButton?: boolean;
consolidatedRunButton?: boolean;
dragAndDropEnabled?: boolean;
fontSize?: boolean;
editorOptionsCustomizations?: boolean;
@ -110,6 +112,7 @@ export class NotebookOptions {
const showCellStatusBarAfterExecute = this.configurationService.getValue<boolean>(ShowCellStatusBarAfterExecuteKey);
const globalToolbar = this.configurationService.getValue<boolean | undefined>(GlobalToolbar) ?? false;
const consolidatedOutputButton = this.configurationService.getValue<boolean | undefined>(ConsolidatedOutputButton) ?? true;
const consolidatedRunButton = this.configurationService.getValue<boolean | undefined>(ConsolidatedRunButton) ?? false;
const dragAndDropEnabled = this.configurationService.getValue<boolean | undefined>(DragAndDropEnabled) ?? true;
const cellToolbarLocation = this.configurationService.getValue<string | { [key: string]: string }>(CellToolbarLocKey) ?? { 'default': 'right' };
const cellToolbarInteraction = this.configurationService.getValue<string>(CellToolbarVisibility);
@ -142,6 +145,7 @@ export class NotebookOptions {
showCellStatusBarAfterExecute,
globalToolbar,
consolidatedOutputButton,
consolidatedRunButton,
dragAndDropEnabled,
cellToolbarLocation,
cellToolbarInteraction,
@ -177,6 +181,7 @@ export class NotebookOptions {
const insertToolbarAlignment = e.affectsConfiguration(ExperimentalInsertToolbarAlignment);
const globalToolbar = e.affectsConfiguration(GlobalToolbar);
const consolidatedOutputButton = e.affectsConfiguration(ConsolidatedOutputButton);
const consolidatedRunButton = e.affectsConfiguration(ConsolidatedRunButton);
const showFoldingControls = e.affectsConfiguration(ShowFoldingControls);
const dragAndDropEnabled = e.affectsConfiguration(DragAndDropEnabled);
const fontSize = e.affectsConfiguration('editor.fontSize');
@ -193,6 +198,7 @@ export class NotebookOptions {
&& !insertToolbarAlignment
&& !globalToolbar
&& !consolidatedOutputButton
&& !consolidatedRunButton
&& !showFoldingControls
&& !dragAndDropEnabled
&& !fontSize
@ -246,6 +252,10 @@ export class NotebookOptions {
configuration.consolidatedOutputButton = this.configurationService.getValue<boolean>(ConsolidatedOutputButton) ?? true;
}
if (consolidatedRunButton) {
configuration.consolidatedRunButton = this.configurationService.getValue<boolean>(ConsolidatedRunButton) ?? true;
}
if (showFoldingControls) {
configuration.showFoldingControls = this._computeShowFoldingControlsOption();
}
@ -277,6 +287,7 @@ export class NotebookOptions {
globalToolbar,
showFoldingControls,
consolidatedOutputButton,
consolidatedRunButton,
dragAndDropEnabled,
fontSize,
editorOptionsCustomizations