diff --git a/extensions/markdown-language-features/notebook/index.ts b/extensions/markdown-language-features/notebook/index.ts index cecde581a9f..5ae7c219763 100644 --- a/extensions/markdown-language-features/notebook/index.ts +++ b/extensions/markdown-language-features/notebook/index.ts @@ -12,7 +12,7 @@ export function activate() { return { renderCell: (_id: string, context: { element: HTMLElement, value: string, text(): string }) => { - const rendered = markdownIt.render(context.value || context.text()); // todo@jrieken remove .value-usage + const rendered = markdownIt.render(context.text()); context.element.innerHTML = rendered; // Insert styles into markdown preview shadow dom so that they are applied diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts index 98b9ac8c12b..793dc37db5f 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts @@ -56,7 +56,7 @@ class Kernel { } protected async _runCell(cell: vscode.NotebookCell) { - const task = this.controller.createNotebookCellExecutionTask(cell); + const task = this.controller.createNotebookCellExecution(cell); task.start(); task.executionOrder = 1; if (cell.notebook.uri.path.endsWith('customRenderer.vsctestnb')) { @@ -180,7 +180,7 @@ suite('Notebook API tests', function () { } override async _runCell(cell: vscode.NotebookCell) { - const task = this.controller.createNotebookCellExecutionTask(cell); + const task = this.controller.createNotebookCellExecution(cell); task.start(); await task.replaceOutput([new vscode.NotebookCellOutput([ vscode.NotebookCellOutputItem.text('my second output', 'text/plain', undefined) @@ -772,7 +772,7 @@ suite('Notebook API tests', function () { override async _execute(cells: vscode.NotebookCell[]) { for (const cell of cells) { - const task = this.controller.createNotebookCellExecutionTask(cell); + const task = this.controller.createNotebookCellExecution(cell); task.start(); task.token.onCancellationRequested(async () => { await task.replaceOutput([new vscode.NotebookCellOutput([ @@ -812,10 +812,10 @@ suite('Notebook API tests', function () { this.controller.interruptHandler = this.interrupt.bind(this); } - private _task: vscode.NotebookCellExecutionTask | undefined; + private _task: vscode.NotebookCellExecution | undefined; override async _execute(cells: vscode.NotebookCell[]) { - this._task = this.controller.createNotebookCellExecutionTask(cells[0]); + this._task = this.controller.createNotebookCellExecution(cells[0]); this._task.start(); } @@ -1178,7 +1178,7 @@ suite('Notebook API tests', function () { override async _execute(cells: vscode.NotebookCell[]) { const [cell] = cells; - const task = this.controller.createNotebookCellExecutionTask(cell); + const task = this.controller.createNotebookCellExecution(cell); task.start(); await task.replaceOutput([new vscode.NotebookCellOutput([ vscode.NotebookCellOutputItem.text('Some output', 'text/plain', undefined) diff --git a/extensions/vscode-notebook-tests/src/extension.ts b/extensions/vscode-notebook-tests/src/extension.ts index 935968c0b4e..ae4fb948438 100644 --- a/extensions/vscode-notebook-tests/src/extension.ts +++ b/extensions/vscode-notebook-tests/src/extension.ts @@ -66,7 +66,7 @@ export function activate(context: vscode.ExtensionContext): any { controller.executeHandler = (cells) => { for (const cell of cells) { - const task = controller.createNotebookCellExecutionTask(cell); + const task = controller.createNotebookCellExecution(cell); task.start(); task.replaceOutput([new vscode.NotebookCellOutput([ vscode.NotebookCellOutputItem.text('test output', 'text/html', undefined) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index a9c410a5bd4..b1f98b56245 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1316,11 +1316,6 @@ declare module 'vscode' { */ data: Uint8Array; - /** - * @deprecated - */ - value: unknown; - //todo@API metadata?: { [key: string]: any }; @@ -1508,31 +1503,25 @@ declare module 'vscode' { (this: NotebookController, cells: NotebookCell[], notebook: NotebookDocument, controller: NotebookController): void | Thenable } - export interface NotebookInterruptHandler { - /** - * @param notebook The notebook for which the interrupt handler is being called. - */ - (this: NotebookController, notebook: NotebookDocument): void | Thenable; - } - export enum NotebookControllerAffinity { Default = 1, Preferred = 2 } - // todo@API this is called Controller - export class NotebookKernelPreload { + + export class NotebookRendererScript { + /** * APIs that the preload provides to the renderer. These are matched * against the `dependencies` and `optionalDependencies` arrays in the * notebook renderer contribution point. */ - readonly provides: string[]; + provides: string[]; /** * URI for the file to preload */ - readonly uri: Uri; + uri: Uri; /** * @param uri URI for the file to preload @@ -1562,31 +1551,53 @@ declare module 'vscode' { endTime?: number; } - // todo@API jsdoc slightly outdated: kernel, notebook.createNotebookCellExecutionTask + // todo@API jsdoc slightly outdated: kernel, notebook.createNotebookCellExecution /** - * A NotebookCellExecutionTask is how the kernel modifies a notebook cell as it is executing. When - * {@link notebook.createNotebookCellExecutionTask `createNotebookCellExecutionTask`} is called, the cell + * 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. * - * All outputs methods operate on this NotebookCellExecutionTask's cell by default. They optionally take + * 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. */ - export interface NotebookCellExecutionTask { - readonly document: NotebookDocument; + export interface NotebookCellExecution { + + /** + * The {@link NotebookCell cell} for which this execution has been created. + */ readonly cell: NotebookCell; + + /** + * A cancellation token which will be triggered when the cell execution is canceled + * from the UI. + * + * _Note_ that the cancellation token will not be triggered when the {@link NotebookController controller} + * that created this execution uses an {@link NotebookController.interruptHandler interrupt-handler}. + */ readonly token: CancellationToken; - start(context?: NotebookCellExecuteStartContext): void; + //todo@API remove? use cell.notebook instead? + readonly document: NotebookDocument; + + /** + * Set and unset the order of this cell execution. + */ executionOrder: number | undefined; + + // 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; appendOutput(out: NotebookCellOutput | NotebookCellOutput[], cellIndex?: number): Thenable; replaceOutput(out: NotebookCellOutput | NotebookCellOutput[], cellIndex?: number): Thenable; + // todo@API use object instead of index? appendOutputItems(items: NotebookCellOutputItem | NotebookCellOutputItem[], outputId: string): Thenable; replaceOutputItems(items: NotebookCellOutputItem | NotebookCellOutputItem[], outputId: string): Thenable; } @@ -1647,16 +1658,23 @@ declare module 'vscode' { /** * The execute handler is invoked when the run gestures in the UI are selected, e.g Run Cell, Run All, - * Run Selection etc. + * Run Selection etc. The execute handler is responsible for creating and managing {@link NotebookCellExecution execution}-objects. */ executeHandler: NotebookExecuteHandler; /** - * The interrupt handler is invoked the interrupt all execution. This is contrary to cancellation (available via - * [`NotebookCellExecutionTask#token`](NotebookCellExecutionTask#token)) and should only be used when - * execution-level cancellation is supported + * Optional interrupt handler. + * + * 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. + * + * _Note_ that supporting {@link NotebookCellExecution.token cancellation tokens} is preferred and that interrupt handlers should + * only be used when tokens cannot be supported. */ - interruptHandler?: NotebookInterruptHandler + interruptHandler?: (this: NotebookController, notebook: NotebookDocument) => void | Thenable; /** * Dispose and free associated resources. @@ -1689,13 +1707,10 @@ declare module 'vscode' { * @param cell The notebook cell for which to create the execution. * @returns A notebook cell execution. */ - // todo@API rename to NotebookCellExecution - createNotebookCellExecutionTask(cell: NotebookCell): NotebookCellExecutionTask; + createNotebookCellExecution(cell: NotebookCell): NotebookCellExecution; - // todo@API find a better name than "preloads" // todo@API allow add, not remove - // ipc - readonly preloads: NotebookKernelPreload[]; + readonly rendererScripts: NotebookRendererScript[]; /** * An event that fires when a renderer (see `preloads`) has send a message to the controller. @@ -1840,9 +1855,9 @@ 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 preloads + * @param rendererScripts */ - export function createNotebookController(id: string, viewType: string, label: string, handler?: NotebookExecuteHandler, preloads?: NotebookKernelPreload[]): NotebookController; + 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... diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index f1e5df0ebbd..24ec26272fa 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1275,7 +1275,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I NotebookCellMetadata: extHostTypes.NotebookCellMetadata, NotebookCellData: extHostTypes.NotebookCellData, NotebookData: extHostTypes.NotebookData, - NotebookKernelPreload: extHostTypes.NotebookKernelPreload, + NotebookRendererScript: extHostTypes.NotebookRendererScript, NotebookCellStatusBarAlignment: extHostTypes.NotebookCellStatusBarAlignment, NotebookEditorRevealType: extHostTypes.NotebookEditorRevealType, NotebookCellOutput: extHostTypes.NotebookCellOutput, diff --git a/src/vs/workbench/api/common/extHostNotebookKernels.ts b/src/vs/workbench/api/common/extHostNotebookKernels.ts index 2464bd06ad7..9cf31f438e7 100644 --- a/src/vs/workbench/api/common/extHostNotebookKernels.ts +++ b/src/vs/workbench/api/common/extHostNotebookKernels.ts @@ -26,7 +26,7 @@ interface IKernelData { extensionId: ExtensionIdentifier, controller: vscode.NotebookController; onDidChangeSelection: Emitter<{ selected: boolean; notebook: vscode.NotebookDocument; }>; - onDidReceiveMessage: Emitter<{ editor: vscode.NotebookEditor, message: any }>; + onDidReceiveMessage: Emitter<{ editor: vscode.NotebookEditor, message: any; }>; associatedNotebooks: ResourceMap; } @@ -47,7 +47,7 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { this._proxy = _mainContext.getProxy(MainContext.MainThreadNotebookKernels); } - createNotebookController(extension: IExtensionDescription, id: string, viewType: string, label: string, handler?: vscode.NotebookExecuteHandler, preloads?: vscode.NotebookKernelPreload[]): vscode.NotebookController { + createNotebookController(extension: IExtensionDescription, id: string, viewType: string, label: string, handler?: vscode.NotebookExecuteHandler, preloads?: vscode.NotebookRendererScript[]): vscode.NotebookController { for (let data of this._kernelData.values()) { if (data.controller.id === id && ExtensionIdentifier.equals(extension.identifier, data.extensionId)) { @@ -66,8 +66,8 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { let isDisposed = false; const commandDisposables = new DisposableStore(); - const onDidChangeSelection = new Emitter<{ selected: boolean, notebook: vscode.NotebookDocument }>(); - const onDidReceiveMessage = new Emitter<{ editor: vscode.NotebookEditor, message: any }>(); + const onDidChangeSelection = new Emitter<{ selected: boolean, notebook: vscode.NotebookDocument; }>(); + const onDidReceiveMessage = new Emitter<{ editor: vscode.NotebookEditor, message: any; }>(); const data: INotebookKernelDto2 = { id: `${extension.identifier.value}/${id}`, @@ -75,12 +75,12 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { extensionId: extension.identifier, extensionLocation: extension.extensionLocation, label: label || extension.identifier.value, - preloads: preloads ? preloads.map(extHostTypeConverters.NotebookKernelPreload.from) : [] + preloads: preloads ? preloads.map(extHostTypeConverters.NotebookRendererScript.from) : [] }; // let _executeHandler: vscode.NotebookExecuteHandler = handler ?? _defaultExecutHandler; - let _interruptHandler: vscode.NotebookInterruptHandler | undefined; + let _interruptHandler: ((this: vscode.NotebookController, notebook: vscode.NotebookDocument) => void | Thenable) | undefined; // todo@jrieken the selector needs to be massaged this._proxy.$addKernel(handle, data).catch(err => { @@ -147,8 +147,8 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { data.hasExecutionOrder = value; _update(); }, - get preloads() { - return data.preloads ? data.preloads.map(extHostTypeConverters.NotebookKernelPreload.to) : []; + get rendererScripts() { + return data.preloads ? data.preloads.map(extHostTypeConverters.NotebookRendererScript.to) : []; }, get executeHandler() { return _executeHandler; @@ -164,7 +164,7 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { data.supportsInterrupt = Boolean(value); _update(); }, - createNotebookCellExecutionTask(cell) { + createNotebookCellExecution(cell) { if (isDisposed) { throw new Error('notebook controller is DISPOSED'); } @@ -259,16 +259,19 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { // extension can dispose kernels in the meantime return; } + + // cancel or interrupt depends on the controller. When an interrupt handler is used we + // don't trigger the cancelation token of executions. const document = this._extHostNotebook.getNotebookDocument(URI.revive(uri)); if (obj.controller.interruptHandler) { await obj.controller.interruptHandler.call(obj.controller, document.apiNotebook); - } - // we do both? interrupt and cancellation or should we be selective? - for (let cellHandle of handles) { - const cell = document.getCell(cellHandle); - if (cell) { - this._activeExecutions.get(cell.uri)?.cancel(); + } else { + for (let cellHandle of handles) { + const cell = document.getCell(cellHandle); + if (cell) { + this._activeExecutions.get(cell.uri)?.cancel(); + } } } } @@ -290,7 +293,7 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { // --- - _createNotebookCellExecution(cell: vscode.NotebookCell): vscode.NotebookCellExecutionTask { + _createNotebookCellExecution(cell: vscode.NotebookCell): vscode.NotebookCellExecution { if (cell.index < 0) { throw new Error('CANNOT execute cell that has been REMOVED from notebook'); } @@ -403,9 +406,9 @@ class NotebookCellExecutionTask extends Disposable { }); } - asApiObject(): vscode.NotebookCellExecutionTask { + asApiObject(): vscode.NotebookCellExecution { const that = this; - return Object.freeze({ + return Object.freeze({ get token() { return that._tokenSource.token; }, get document() { return that._document.apiNotebook; }, get cell() { return that._cell.apiCell; }, diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 9cb1fd3f72f..2b011cbb0f0 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -1534,30 +1534,15 @@ export namespace NotebookCellData { export namespace NotebookCellOutputItem { export function from(item: types.NotebookCellOutputItem): notebooks.IOutputItemDto { - let value: unknown; - let valueBytes: number[] | undefined; - if (item.data instanceof Uint8Array) { - //todo@jrieken this HACKY and SLOW... hoist VSBuffer instead - valueBytes = Array.from(item.data); - } else { - value = item.value; - } return { metadata: item.metadata, mime: item.mime, - value, - valueBytes, + valueBytes: Array.from(item.data), //todo@jrieken this HACKY and SLOW... hoist VSBuffer instead }; } export function to(item: notebooks.IOutputItemDto): types.NotebookCellOutputItem { - let value: Uint8Array | any; - if (Array.isArray(item.valueBytes)) { - value = new Uint8Array(item.valueBytes); - } else { - value = item.value; - } - return new types.NotebookCellOutputItem(value, item.mime, item.metadata); + return new types.NotebookCellOutputItem(new Uint8Array(item.valueBytes), item.mime, item.metadata); } } @@ -1684,18 +1669,15 @@ export namespace NotebookDocumentContentOptions { } } -export namespace NotebookKernelPreload { - export function from(preload: vscode.NotebookKernelPreload): { uri: UriComponents; provides: string[] } { +export namespace NotebookRendererScript { + export function from(preload: vscode.NotebookRendererScript): { uri: UriComponents; provides: string[] } { return { uri: preload.uri, - // todo@connor4312: the conditional here can be removed after a migration period - provides: typeof preload.provides === 'string' - ? [preload.provides] - : preload.provides ?? [] + provides: preload.provides }; } - export function to(preload: { uri: UriComponents; provides: string[] }): vscode.NotebookKernelPreload { - return new types.NotebookKernelPreload(URI.revive(preload.uri), preload.provides); + export function to(preload: { uri: UriComponents; provides: string[] }): vscode.NotebookRendererScript { + return new types.NotebookRendererScript(URI.revive(preload.uri), preload.provides); } } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index cb45ef3fce5..ece6bab4537 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { coalesceInPlace, equals } from 'vs/base/common/arrays'; +import { asArray, coalesceInPlace, equals } from 'vs/base/common/arrays'; import { illegalArgument } from 'vs/base/common/errors'; import { IRelativePattern } from 'vs/base/common/glob'; import { isMarkdownString, MarkdownString as BaseMarkdownString } from 'vs/base/common/htmlContent'; @@ -3112,7 +3112,8 @@ export class NotebookCellOutputItem { if (!obj) { return false; } - return typeof (obj).mime === 'string'; + return typeof (obj).mime === 'string' + && (obj).data instanceof Uint8Array; } static error(err: Error | { name: string, message?: string, stack?: string }, metadata?: { [key: string]: any }): NotebookCellOutputItem { @@ -3148,18 +3149,11 @@ export class NotebookCellOutputItem { return NotebookCellOutputItem.text(rawStr, mime, metadata); } - /** @deprecated */ - public value: Uint8Array | unknown; // JSON'able - constructor( public data: Uint8Array, public mime: string, public metadata?: { [key: string]: any } ) { - if (!(data instanceof Uint8Array)) { - this.value = data; - } - const mimeNormalized = normalizeMimeType(mime, true); if (!mimeNormalized) { throw new Error('INVALID mime type, must not be empty or falsy: ' + mime); @@ -3251,14 +3245,15 @@ export enum NotebookControllerAffinity { Preferred = 2 } -export class NotebookKernelPreload { - public readonly provides: string[]; +export class NotebookRendererScript { + + public provides: string[]; constructor( - public readonly uri: vscode.Uri, + public uri: vscode.Uri, provides: string | string[] = [] ) { - this.provides = typeof provides === 'string' ? [provides] : provides; + this.provides = asArray(provides); } } diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index 45dd563cb91..5114543cfa5 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -16,7 +16,7 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; import { ITextModelContentProvider, ITextModelService } from 'vs/editor/common/services/resolverService'; import * as nls from 'vs/nls'; -import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; +import { Extensions, IConfigurationPropertySchema, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -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 } 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 } 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'; @@ -43,7 +43,7 @@ import { NotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/brow import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/notebookEditorService'; import { NotebookEditorWidgetService } from 'vs/workbench/contrib/notebook/browser/notebookEditorServiceImpl'; import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; -import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema'; import { Event } from 'vs/base/common/event'; import { getFormatedMetadataJSON } from 'vs/workbench/contrib/notebook/browser/diff/diffElementViewModel'; import { NotebookModelResolverServiceImpl } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverServiceImpl'; @@ -84,6 +84,7 @@ import 'vs/workbench/contrib/notebook/browser/diff/notebookDiffActions'; // Output renderers registration import 'vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform'; +import { editorOptionsRegistry } from 'vs/editor/common/config/editorOptions'; /*--------------------------------------------------------------------------------------------- */ @@ -548,6 +549,44 @@ registerSingleton(INotebookEditorService, NotebookEditorWidgetService, true); registerSingleton(INotebookKernelService, NotebookKernelService, true); registerSingleton(INotebookRendererMessagingService, NotebookRendererMessagingService, true); +const schemas: IJSONSchemaMap = {}; +function isConfigurationPropertySchema(x: IConfigurationPropertySchema | { [path: string]: IConfigurationPropertySchema; }): x is IConfigurationPropertySchema { + return (typeof x.type !== 'undefined' || typeof x.anyOf !== 'undefined'); +} +for (const editorOption of editorOptionsRegistry) { + const schema = editorOption.schema; + if (schema) { + if (isConfigurationPropertySchema(schema)) { + schemas[`editor.${editorOption.name}`] = schema; + } else { + for (let key in schema) { + if (Object.hasOwnProperty.call(schema, key)) { + schemas[key] = schema[key]; + } + } + } + } +} + +const editorOptionsCustomizationSchema: IConfigurationPropertySchema = { + description: nls.localize('notebook.editorOptions.experimentalCustomization', 'Notebook Cell editor options customization.'), + default: {}, + allOf: [ + { + properties: schemas, + } + // , { + // patternProperties: { + // '^\\[.*\\]$': { + // type: 'object', + // default: {}, + // properties: schemas + // } + // } + // } + ] +}; + const configurationRegistry = Registry.as(Extensions.Configuration); configurationRegistry.registerConfiguration({ id: 'notebook', @@ -654,6 +693,7 @@ configurationRegistry.registerConfiguration({ type: 'boolean', default: true, tags: ['notebookLayout'] - } + }, + [NotebookCellEditorOptionsCustomizations]: editorOptionsCustomizationSchema } }); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 8025957acc6..377b81e5bd5 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -685,6 +685,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor .monaco-workbench .notebookOverlay .cell-list-top-cell-toolbar-container .monaco-toolbar .action-label, .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container .monaco-toolbar .action-label { padding: 0px !important; justify-content: center; + border-radius: 4px; }`); styleSheets.push(` @@ -692,7 +693,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor .monaco-workbench .notebookOverlay .cell-list-top-cell-toolbar-container, .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container { align-items: flex-start; justify-content: left; - margin: 0 16px 0 8px; + margin: 0 16px 0 ${8 + codeCellLeftMargin}px; }`); styleSheets.push(` @@ -1386,7 +1387,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor // no cached view state so we are rendering the first viewport // after above async call, we already get init height for markdown cells, we can update their offset let offset = 0; - const offsetUpdateRequests: { id: string, top: number }[] = []; + const offsetUpdateRequests: { id: string, top: number; }[] = []; const scrollBottom = Math.max(this._dimension?.height ?? 0, 1080); for (const cell of viewModel.viewCells) { if (cell.cellKind === CellKind.Markup) { @@ -1450,7 +1451,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor if (this._list) { state.scrollPosition = { left: this._list.scrollLeft, top: this._list.scrollTop }; - const cellHeights: { [key: number]: number } = {}; + const cellHeights: { [key: number]: number; } = {}; for (let i = 0; i < this.viewModel!.length; i++) { const elm = this.viewModel!.cellAt(i) as CellViewModel; if (elm.cellKind === CellKind.Code) { @@ -1476,7 +1477,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } // Save contribution view states - const contributionsState: { [key: string]: unknown } = {}; + const contributionsState: { [key: string]: unknown; } = {}; for (const [id, contribution] of this._contributions) { if (typeof contribution.saveViewState === 'function') { contributionsState[id] = contribution.saveViewState(); @@ -2463,7 +2464,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this._webview.removeInsets(removedItems); - const markdownUpdateItems: { id: string, top: number }[] = []; + const markdownUpdateItems: { id: string, top: number; }[] = []; for (const cellId of this._webview.markdownPreviewMapping.keys()) { const cell = this.viewModel?.viewCells.find(cell => cell.id === cellId); if (cell) { @@ -2503,21 +2504,21 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } } - markdownCellDragStart(cellId: string, event: { dragOffsetY: number }): void { + markdownCellDragStart(cellId: string, event: { dragOffsetY: number; }): void { const cell = this.getCellById(cellId); if (cell instanceof MarkdownCellViewModel) { this._dndController?.startExplicitDrag(cell, event.dragOffsetY); } } - markdownCellDrag(cellId: string, event: { dragOffsetY: number }): void { + markdownCellDrag(cellId: string, event: { dragOffsetY: number; }): void { const cell = this.getCellById(cellId); if (cell instanceof MarkdownCellViewModel) { this._dndController?.explicitDrag(cell, event.dragOffsetY); } } - markdownCellDrop(cellId: string, event: { dragOffsetY: number, ctrlKey: boolean, altKey: boolean }): void { + markdownCellDrop(cellId: string, event: { dragOffsetY: number, ctrlKey: boolean, altKey: boolean; }): void { const cell = this.getCellById(cellId); if (cell instanceof MarkdownCellViewModel) { this._dndController?.explicitDrop(cell, event); @@ -2573,7 +2574,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor super.dispose(); } - toJSON(): { notebookUri: URI | undefined } { + toJSON(): { notebookUri: URI | undefined; } { return { notebookUri: this.viewModel?.uri, }; diff --git a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts index 60c40a9e613..589f638644c 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts @@ -6,7 +6,6 @@ import * as DOM from 'vs/base/browser/dom'; import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { dirname } from 'vs/base/common/resources'; -import { isArray } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { MarkdownRenderer } from 'vs/editor/browser/core/markdownRenderer'; import { IEditorConstructionOptions } from 'vs/editor/browser/editorBrowser'; @@ -109,14 +108,7 @@ class JSONRendererContrib extends CodeRendererContrib { } override render(output: ICellOutputViewModel, items: IOutputItemDto[], container: HTMLElement): IRenderOutput { - const str = items.map(item => { - if (isArray(item.valueBytes)) { - return getStringValue(item); - } else { - return JSON.stringify(item.value, null, '\t'); - } - }).join(''); - + const str = items.map(getStringValue).join(''); return this._render(output, container, str, 'jsonc'); } } @@ -170,57 +162,6 @@ class StderrRendererContrib extends StreamRendererContrib { } } -/** @deprecated */ -class ErrorRendererContrib extends Disposable implements IOutputRendererContribution { - getType() { - return RenderOutputType.Mainframe; - } - - getMimetypes() { - return ['application/x.notebook.error-traceback']; - } - - constructor( - public notebookEditor: ICommonNotebookEditor, - @IThemeService private readonly themeService: IThemeService, - @IInstantiationService private readonly instantiationService: IInstantiationService, - - ) { - super(); - } - - render(output: ICellOutputViewModel, items: IOutputItemDto[], container: HTMLElement, notebookUri: URI): IRenderOutput { - const linkDetector = this.instantiationService.createInstance(LinkDetector); - items.forEach(item => { - const data: any = item.value; - const header = document.createElement('div'); - const headerMessage = data.ename && data.evalue - ? `${data.ename}: ${data.evalue}` - : data.ename || data.evalue; - if (headerMessage) { - header.innerText = headerMessage; - container.appendChild(header); - } - const traceback = document.createElement('pre'); - traceback.classList.add('traceback'); - if (data.traceback) { - for (let j = 0; j < data.traceback.length; j++) { - traceback.appendChild(handleANSIOutput(data.traceback[j], linkDetector, this.themeService, undefined)); - } - } - container.appendChild(traceback); - container.classList.add('error'); - return { type: RenderOutputType.Mainframe }; - - }); - - return { type: RenderOutputType.Mainframe }; - } - - _render() { - } -} - class JSErrorRendererContrib implements IOutputRendererContribution { constructor( @@ -381,17 +322,10 @@ class ImgRendererContrib extends Disposable implements IOutputRendererContributi for (let item of items) { - let src: string; - if (Array.isArray(item.valueBytes)) { - const bytes = new Uint8Array(item.valueBytes); - const blob = new Blob([bytes], { type: item.mime }); - src = URL.createObjectURL(blob); - disposable.add(toDisposable(() => URL.revokeObjectURL(src))); - } else { - // OLD - const imagedata = item.value; - src = `data:${item.mime};base64,${imagedata}`; - } + const bytes = new Uint8Array(item.valueBytes); + const blob = new Blob([bytes], { type: item.mime }); + const src = URL.createObjectURL(blob); + disposable.add(toDisposable(() => URL.revokeObjectURL(src))); const image = document.createElement('img'); image.src = src; @@ -414,17 +348,11 @@ OutputRendererRegistry.registerOutputTransform(CodeRendererContrib); OutputRendererRegistry.registerOutputTransform(JSErrorRendererContrib); OutputRendererRegistry.registerOutputTransform(StreamRendererContrib); OutputRendererRegistry.registerOutputTransform(StderrRendererContrib); -OutputRendererRegistry.registerOutputTransform(ErrorRendererContrib); // --- utils --- function getStringValue(item: IOutputItemDto): string { - if (Array.isArray(item.valueBytes)) { - // todo@jrieken NOT proper, should be VSBuffer - return new TextDecoder().decode(new Uint8Array(item.valueBytes)); - } else { - // "old" world - return Array.isArray(item.value) ? item.value.join('') : String(item.value); - } + // todo@jrieken NOT proper, should be VSBuffer + return new TextDecoder().decode(new Uint8Array(item.valueBytes)); } function getOutputSimpleEditorOptions(): IEditorConstructionOptions { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index 6c31eb26680..febd9112487 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -192,7 +192,7 @@ export interface ICreationRequestMessage { type: 'html'; content: | { type: RenderOutputType.Html; htmlContent: string } - | { type: RenderOutputType.Extension; outputId: string; value: unknown; valueBytes: Uint8Array, metadata: unknown; mimeType: string }; + | { type: RenderOutputType.Extension; outputId: string; valueBytes: Uint8Array, metadata: unknown; mimeType: string }; cellId: string; outputId: string; cellTop: number; @@ -1513,7 +1513,6 @@ var requirejs = (function() { type: RenderOutputType.Extension, outputId: output.outputId, mimeType: content.mimeType, - value: outputDto?.value, valueBytes: new Uint8Array(outputDto?.valueBytes ?? []), metadata: outputDto?.metadata, }, diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellEditorOptions.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellEditorOptions.ts index bf7dc140594..8b38e913b72 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellEditorOptions.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellEditorOptions.ts @@ -47,41 +47,50 @@ export class CellEditorOptions extends Disposable { private readonly _onDidChange = new Emitter(); readonly onDidChange: Event = this._onDidChange.event; - constructor(readonly notebookOptions: NotebookOptions, readonly configurationService: IConfigurationService, language: string) { + constructor(readonly notebookOptions: NotebookOptions, readonly configurationService: IConfigurationService, readonly language: string) { super(); this._register(configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration('editor') || e.affectsConfiguration('notebook')) { - this._value = computeEditorOptions(); + this._value = this._computeEditorOptions(); this._onDidChange.fire(this.value); } })); this._register(notebookOptions.onDidChangeOptions(e => { - if (e.cellStatusBarVisibility || e.editorTopPadding) { - this._value = computeEditorOptions(); + if (e.cellStatusBarVisibility || e.editorTopPadding || e.editorOptionsCustomizations) { + this._value = this._computeEditorOptions(); this._onDidChange.fire(this.value); } })); - const computeEditorOptions = () => { - const editorPadding = this.notebookOptions.computeEditorPadding(); - const renderLiNumbers = configurationService.getValue<'on' | 'off'>('notebook.lineNumbers') === 'on'; - const lineNumbers: LineNumbersType = renderLiNumbers ? 'on' : 'off'; - const editorOptions = deepClone(configurationService.getValue('editor', { overrideIdentifier: language })); - const computed = { - ...editorOptions, - ...CellEditorOptions.fixedEditorOptions, - ...{ padding: editorPadding, lineNumbers }, - }; + this._value = this._computeEditorOptions(); + } - if (!computed.folding) { - computed.lineDecorationsWidth = 16; + private _computeEditorOptions() { + const editorPadding = this.notebookOptions.computeEditorPadding(); + const renderLiNumbers = this.configurationService.getValue<'on' | 'off'>('notebook.lineNumbers') === 'on'; + const lineNumbers: LineNumbersType = renderLiNumbers ? 'on' : 'off'; + const editorOptions = deepClone(this.configurationService.getValue('editor', { overrideIdentifier: this.language })); + const editorOptionsOverrideRaw = this.notebookOptions.getLayoutConfiguration().editorOptionsCustomizations ?? {}; + let editorOptionsOverride: { [key: string]: any } = {}; + for (let key in editorOptionsOverrideRaw) { + if (key.indexOf('editor.') === 0) { + editorOptionsOverride[key.substr(7)] = editorOptionsOverrideRaw[key]; } - - return computed; + } + const computed = { + ...editorOptions, + ...CellEditorOptions.fixedEditorOptions, + ... { lineNumbers }, + ...editorOptionsOverride, + ...{ padding: editorPadding } }; - this._value = computeEditorOptions(); + if (!computed.folding) { + computed.lineDecorationsWidth = 16; + } + + return computed; } override dispose(): void { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts index ff86aa25c5f..d3ce5beb2cd 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -463,7 +463,6 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re outputId?: string; mime: string; - value: unknown; metadata: unknown; text(): string; @@ -643,15 +642,13 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re rendererApi.renderCell(outputId, { element: outputNode, mime: content.mimeType, - value: content.value, metadata: content.metadata, data() { return content.valueBytes; }, bytes() { return this.data(); }, text() { - return new TextDecoder().decode(content.valueBytes) - || String(content.value); //todo@jrieken remove this once `value` is gone! + return new TextDecoder().decode(content.valueBytes); }, json() { return JSON.parse(this.text()); @@ -1037,7 +1034,6 @@ async function webviewPreloads(style: PreloadStyles, options: PreloadOptions, re markdownRenderers[0].api?.renderCell(id, { element, - value: content, mime: 'text/markdown', metadata: undefined, outputId: undefined, diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index de639c6c2a9..96973ca5a9a 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -173,8 +173,7 @@ export interface IOrderedMimeType { export interface IOutputItemDto { readonly mime: string; - readonly value: unknown; - readonly valueBytes?: number[]; + readonly valueBytes: number[]; readonly metadata?: Record; } @@ -603,7 +602,6 @@ const _mimeTypeInfo = new Map([ ['application/x.notebook.stdout', { alwaysSecure: true, supportedByCore: true, mergeable: true }], ['application/x.notebook.stderr', { alwaysSecure: true, supportedByCore: true, mergeable: true }], ['application/x.notebook.stream', { alwaysSecure: true, supportedByCore: true, mergeable: true }], // deprecated - ['application/x.notebook.error-traceback', { alwaysSecure: true, supportedByCore: true }], // deprecated ]); export function mimeTypeIsAlwaysSecure(mimeType: string): boolean { @@ -922,6 +920,7 @@ export const UndoRedoPerCell = 'notebook.undoRedoPerCell'; export const ConsolidatedOutputButton = 'notebook.consolidatedOutputButton'; export const ShowFoldingControls = 'notebook.showFoldingControls'; export const DragAndDropEnabled = 'notebook.dragAndDropEnabled'; +export const NotebookCellEditorOptionsCustomizations = 'notebook.editorOptionsCustomizations'; export const enum CellStatusbarAlignment { Left = 1, diff --git a/src/vs/workbench/contrib/notebook/common/notebookOptions.ts b/src/vs/workbench/contrib/notebook/common/notebookOptions.ts index be3c9320288..58f43fec488 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookOptions.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookOptions.ts @@ -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, ShowCellStatusBarAfterExecuteKey, ShowCellStatusBarKey, ShowFoldingControls } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellToolbarLocKey, CellToolbarVisibility, CompactView, ConsolidatedOutputButton, DragAndDropEnabled, ExperimentalInsertToolbarAlignment, FocusIndicator, GlobalToolbar, InsertToolbarPosition, NotebookCellEditorOptionsCustomizations, ShowCellStatusBarAfterExecuteKey, ShowCellStatusBarKey, ShowFoldingControls } from 'vs/workbench/contrib/notebook/common/notebookCommon'; const SCROLLABLE_ELEMENT_PADDING_TOP = 18; @@ -58,6 +58,7 @@ export interface NotebookLayoutConfiguration { dragAndDropEnabled: boolean; fontSize: number; focusIndicatorLeftMargin: number; + editorOptionsCustomizations: any | undefined; } interface NotebookOptionsChangeEvent { @@ -75,6 +76,7 @@ interface NotebookOptionsChangeEvent { consolidatedOutputButton?: boolean; dragAndDropEnabled?: boolean; fontSize?: boolean; + editorOptionsCustomizations?: boolean; } const defaultConfigConstants = { @@ -118,6 +120,7 @@ export class NotebookOptions { const showFoldingControls = this._computeShowFoldingControlsOption(); // const { bottomToolbarGap, bottomToolbarHeight } = this._computeBottomToolbarDimensions(compactView, insertToolbarPosition, insertToolbarAlignment); const fontSize = this.configurationService.getValue('editor.fontSize'); + const editorOptionsCustomizations = this.configurationService.getValue(NotebookCellEditorOptionsCustomizations); this._disposables = []; this._layoutConfiguration = { @@ -147,7 +150,8 @@ export class NotebookOptions { insertToolbarPosition, insertToolbarAlignment, showFoldingControls, - fontSize + fontSize, + editorOptionsCustomizations }; this._disposables.push(this.configurationService.onDidChangeConfiguration(e => { @@ -176,6 +180,7 @@ export class NotebookOptions { const showFoldingControls = e.affectsConfiguration(ShowFoldingControls); const dragAndDropEnabled = e.affectsConfiguration(DragAndDropEnabled); const fontSize = e.affectsConfiguration('editor.fontSize'); + const editorOptionsCustomizations = e.affectsConfiguration(NotebookCellEditorOptionsCustomizations); if ( !cellStatusBarVisibility @@ -190,7 +195,8 @@ export class NotebookOptions { && !consolidatedOutputButton && !showFoldingControls && !dragAndDropEnabled - && !fontSize) { + && !fontSize + && !editorOptionsCustomizations) { return; } @@ -252,6 +258,10 @@ export class NotebookOptions { configuration.fontSize = this.configurationService.getValue('editor.fontSize'); } + if (editorOptionsCustomizations) { + configuration.editorOptionsCustomizations = this.configurationService.getValue(NotebookCellEditorOptionsCustomizations); + } + this._layoutConfiguration = Object.freeze(configuration); // trigger event @@ -268,7 +278,8 @@ export class NotebookOptions { showFoldingControls, consolidatedOutputButton, dragAndDropEnabled, - fontSize: fontSize + fontSize, + editorOptionsCustomizations }); } diff --git a/src/vs/workbench/contrib/notebook/test/notebookDiff.test.ts b/src/vs/workbench/contrib/notebook/test/notebookDiff.test.ts index 724ff468ed0..3e612b388c9 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookDiff.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookDiff.test.ts @@ -14,9 +14,9 @@ suite('NotebookCommon', () => { test('diff different source', async () => { await withTestNotebookDiffModel([ - ['x', 'javascript', CellKind.Code, [{ outputId: 'someOtherId', outputs: [{ mime: 'text/plain', value: '3' }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 3 }], + ['x', 'javascript', CellKind.Code, [{ outputId: 'someOtherId', outputs: [{ mime: 'text/plain', valueBytes: [3] }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 3 }], ], [ - ['y', 'javascript', CellKind.Code, [{ outputId: 'someOtherId', outputs: [{ mime: 'text/plain', value: '3' }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 3 }], + ['y', 'javascript', CellKind.Code, [{ outputId: 'someOtherId', outputs: [{ mime: 'text/plain', valueBytes: [3] }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 3 }], ], (model, accessor) => { const diff = new LcsDiff(new CellSequence(model.original.notebook), new CellSequence(model.modified.notebook)); const diffResult = diff.ComputeDiff(false); @@ -44,10 +44,10 @@ suite('NotebookCommon', () => { test('diff different output', async () => { await withTestNotebookDiffModel([ - ['x', 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: 'text/plain', value: '5' }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 5 }], + ['x', 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: 'text/plain', valueBytes: [5] }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 5 }], ['', 'javascript', CellKind.Code, [], {}] ], [ - ['x', 'javascript', CellKind.Code, [{ outputId: 'someOtherId', outputs: [{ mime: 'text/plain', value: '3' }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 3 }], + ['x', 'javascript', CellKind.Code, [{ outputId: 'someOtherId', outputs: [{ mime: 'text/plain', valueBytes: [3] }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 3 }], ['', 'javascript', CellKind.Code, [], {}] ], (model, accessor) => { const diff = new LcsDiff(new CellSequence(model.original.notebook), new CellSequence(model.modified.notebook)); @@ -145,12 +145,12 @@ suite('NotebookCommon', () => { test('diff foo/foe', async () => { await withTestNotebookDiffModel([ - [['def foe(x, y):\n', ' return x + y\n', 'foe(3, 2)'].join(''), 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: 'text/plain', value: '6' }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 5 }], - [['def foo(x, y):\n', ' return x * y\n', 'foo(1, 2)'].join(''), 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: 'text/plain', value: '2' }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 6 }], + [['def foe(x, y):\n', ' return x + y\n', 'foe(3, 2)'].join(''), 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: 'text/plain', valueBytes: [6] }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 5 }], + [['def foo(x, y):\n', ' return x * y\n', 'foo(1, 2)'].join(''), 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: 'text/plain', valueBytes: [2] }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 6 }], ['', 'javascript', CellKind.Code, [], {}] ], [ - [['def foo(x, y):\n', ' return x * y\n', 'foo(1, 2)'].join(''), 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: 'text/plain', value: '6' }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 5 }], - [['def foe(x, y):\n', ' return x + y\n', 'foe(3, 2)'].join(''), 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: 'text/plain', value: '2' }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 6 }], + [['def foo(x, y):\n', ' return x * y\n', 'foo(1, 2)'].join(''), 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: 'text/plain', valueBytes: [6] }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 5 }], + [['def foe(x, y):\n', ' return x + y\n', 'foe(3, 2)'].join(''), 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: 'text/plain', valueBytes: [2] }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 6 }], ['', 'javascript', CellKind.Code, [], {}] ], (model, accessor) => { const diff = new LcsDiff(new CellSequence(model.original.notebook), new CellSequence(model.modified.notebook)); @@ -272,13 +272,13 @@ suite('NotebookCommon', () => { await withTestNotebookDiffModel([ ['# Description', 'markdown', CellKind.Markup, [], { custom: { metadata: {} } }], ['x = 3', 'javascript', CellKind.Code, [], { custom: { metadata: { collapsed: true } }, executionOrder: 1 }], - ['x', 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: 'text/plain', value: '3' }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 1 }], + ['x', 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: 'text/plain', valueBytes: [3] }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 1 }], ['x', 'javascript', CellKind.Code, [], { custom: { metadata: { collapsed: false } } }] ], [ ['# Description', 'markdown', CellKind.Markup, [], { custom: { metadata: {} } }], ['x = 3', 'javascript', CellKind.Code, [], { custom: { metadata: { collapsed: true } }, executionOrder: 1 }], ['x', 'javascript', CellKind.Code, [], { custom: { metadata: { collapsed: false } } }], - ['x', 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: 'text/plain', value: '3' }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 1 }] + ['x', 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: 'text/plain', valueBytes: [3] }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 1 }] ], async (model) => { const diff = new LcsDiff(new CellSequence(model.original.notebook), new CellSequence(model.modified.notebook)); const diffResult = diff.ComputeDiff(false); @@ -305,18 +305,18 @@ suite('NotebookCommon', () => { await withTestNotebookDiffModel([ ['# Description', 'markdown', CellKind.Markup, [], { custom: { metadata: {} } }], ['x = 3', 'javascript', CellKind.Code, [], { custom: { metadata: { collapsed: true } }, executionOrder: 1 }], - ['x', 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: 'text/plain', value: '3' }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 1 }], + ['x', 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: 'text/plain', valueBytes: [3] }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 1 }], ['x', 'javascript', CellKind.Code, [], { custom: { metadata: { collapsed: false } } }], ['x = 5', 'javascript', CellKind.Code, [], {}], ['x', 'javascript', CellKind.Code, [], {}], - ['x', 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: 'text/plain', value: '5' }] }], {}], + ['x', 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: 'text/plain', valueBytes: [5] }] }], {}], ], [ ['# Description', 'markdown', CellKind.Markup, [], { custom: { metadata: {} } }], ['x = 3', 'javascript', CellKind.Code, [], { custom: { metadata: { collapsed: true } }, executionOrder: 1 }], ['x', 'javascript', CellKind.Code, [], { custom: { metadata: { collapsed: false } } }], - ['x', 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: 'text/plain', value: '3' }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 1 }], + ['x', 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: 'text/plain', valueBytes: [3] }] }], { custom: { metadata: { collapsed: false } }, executionOrder: 1 }], ['x = 5', 'javascript', CellKind.Code, [], {}], - ['x', 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: 'text/plain', value: '5' }] }], {}], + ['x', 'javascript', CellKind.Code, [{ outputId: 'someId', outputs: [{ mime: 'text/plain', valueBytes: [5] }] }], {}], ['x', 'javascript', CellKind.Code, [], {}], ], async (model) => { const diff = new LcsDiff(new CellSequence(model.original.notebook), new CellSequence(model.modified.notebook)); diff --git a/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts b/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts index 59bb65360de..3e799efe395 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts @@ -10,6 +10,11 @@ import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { IModeService } from 'vs/editor/common/services/modeService'; suite('NotebookTextModel', () => { + + function valueBytesFromString(value: string) { + return Array.from(new TextEncoder().encode(value)); + } + const instantiationService = setupInstantiationService(); const modeService = instantiationService.get(IModeService); instantiationService.spy(IUndoRedoService, 'pushElement'); @@ -184,7 +189,7 @@ suite('NotebookTextModel', () => { editType: CellEditType.Output, outputs: [{ outputId: 'someId', - outputs: [{ mime: 'text/markdown', value: '_Hello_' }] + outputs: [{ mime: 'text/markdown', valueBytes: valueBytesFromString('_Hello_') }] }] }], true, undefined, () => undefined, undefined); @@ -198,7 +203,7 @@ suite('NotebookTextModel', () => { append: true, outputs: [{ outputId: 'someId2', - outputs: [{ mime: 'text/markdown', value: '_Hello2_' }] + outputs: [{ mime: 'text/markdown', valueBytes: valueBytesFromString('_Hello2_') }] }] }], true, undefined, () => undefined, undefined); @@ -214,7 +219,7 @@ suite('NotebookTextModel', () => { editType: CellEditType.Output, outputs: [{ outputId: 'someId3', - outputs: [{ mime: 'text/plain', value: 'Last, replaced output' }] + outputs: [{ mime: 'text/plain', valueBytes: valueBytesFromString('Last, replaced output') }] }] }], true, undefined, () => undefined, undefined); @@ -242,7 +247,7 @@ suite('NotebookTextModel', () => { append: true, outputs: [{ outputId: 'append1', - outputs: [{ mime: 'text/markdown', value: 'append 1' }] + outputs: [{ mime: 'text/markdown', valueBytes: valueBytesFromString('append 1') }] }] }, { @@ -251,7 +256,7 @@ suite('NotebookTextModel', () => { append: true, outputs: [{ outputId: 'append2', - outputs: [{ mime: 'text/markdown', value: 'append 2' }] + outputs: [{ mime: 'text/markdown', valueBytes: valueBytesFromString('append 2') }] }] } ], true, undefined, () => undefined, undefined); @@ -418,7 +423,7 @@ suite('NotebookTextModel', () => { const success1 = model.applyEdits( [{ editType: CellEditType.Output, index: 0, outputs: [ - { outputId: 'out1', outputs: [{ mime: 'application/x.notebook.stream', value: 1 }] } + { outputId: 'out1', outputs: [{ mime: 'application/x.notebook.stream', valueBytes: [1] }] } ], append: false }], true, undefined, () => undefined, undefined, false @@ -430,7 +435,7 @@ suite('NotebookTextModel', () => { const success2 = model.applyEdits( [{ editType: CellEditType.Output, index: 0, outputs: [ - { outputId: 'out2', outputs: [{ mime: 'application/x.notebook.stream', value: 1 }] } + { outputId: 'out2', outputs: [{ mime: 'application/x.notebook.stream', valueBytes: [1] }] } ], append: true }], true, undefined, () => undefined, undefined, false @@ -457,7 +462,7 @@ suite('NotebookTextModel', () => { const success = model.applyEdits( [{ editType: CellEditType.Output, index: 0, outputs: [ - { outputId: 'out1', outputs: [{ mime: 'application/x.notebook.stream', value: 1 }] } + { outputId: 'out1', outputs: [{ mime: 'application/x.notebook.stream', valueBytes: [1] }] } ], append: false }], true, undefined, () => undefined, undefined, false @@ -563,14 +568,14 @@ suite('NotebookTextModel', () => { test('Destructive sorting in _doApplyEdits #121994', async function () { await withTestNotebook([ - ['var a = 1;', 'javascript', CellKind.Code, [{ outputId: 'i42', outputs: [{ mime: 'm/ime', value: 'test' }] }], {}] + ['var a = 1;', 'javascript', CellKind.Code, [{ outputId: 'i42', outputs: [{ mime: 'm/ime', valueBytes: valueBytesFromString('test') }] }], {}] ], async (editor) => { const notebook = editor.viewModel.notebookDocument; assert.strictEqual(notebook.cells[0].outputs.length, 1); assert.strictEqual(notebook.cells[0].outputs[0].outputs.length, 1); - assert.strictEqual(notebook.cells[0].outputs[0].outputs[0].value, 'test'); + assert.deepStrictEqual(notebook.cells[0].outputs[0].outputs[0].valueBytes, valueBytesFromString('test')); const edits: ICellEditOperation[] = [ { @@ -579,7 +584,7 @@ suite('NotebookTextModel', () => { { editType: CellEditType.Output, handle: 0, append: true, outputs: [{ outputId: 'newOutput', - outputs: [{ mime: 'text/plain', value: 'cba' }, { mime: 'application/foo', value: 'cba' }] + outputs: [{ mime: 'text/plain', valueBytes: valueBytesFromString('cba') }, { mime: 'application/foo', valueBytes: valueBytesFromString('cba') }] }] } ]; @@ -593,9 +598,9 @@ suite('NotebookTextModel', () => { test('Destructive sorting in _doApplyEdits #121994. cell splice between output changes', async function () { await withTestNotebook([ - ['var a = 1;', 'javascript', CellKind.Code, [{ outputId: 'i42', outputs: [{ mime: 'm/ime', value: 'test' }] }], {}], - ['var b = 2;', 'javascript', CellKind.Code, [{ outputId: 'i43', outputs: [{ mime: 'm/ime', value: 'test' }] }], {}], - ['var c = 3;', 'javascript', CellKind.Code, [{ outputId: 'i44', outputs: [{ mime: 'm/ime', value: 'test' }] }], {}] + ['var a = 1;', 'javascript', CellKind.Code, [{ outputId: 'i42', outputs: [{ mime: 'm/ime', valueBytes: valueBytesFromString('test') }] }], {}], + ['var b = 2;', 'javascript', CellKind.Code, [{ outputId: 'i43', outputs: [{ mime: 'm/ime', valueBytes: valueBytesFromString('test') }] }], {}], + ['var c = 3;', 'javascript', CellKind.Code, [{ outputId: 'i44', outputs: [{ mime: 'm/ime', valueBytes: valueBytesFromString('test') }] }], {}] ], async (editor) => { const notebook = editor.viewModel.notebookDocument; @@ -609,7 +614,7 @@ suite('NotebookTextModel', () => { { editType: CellEditType.Output, index: 2, append: true, outputs: [{ outputId: 'newOutput', - outputs: [{ mime: 'text/plain', value: 'cba' }, { mime: 'application/foo', value: 'cba' }] + outputs: [{ mime: 'text/plain', valueBytes: valueBytesFromString('cba') }, { mime: 'application/foo', valueBytes: valueBytesFromString('cba') }] }] } ]; @@ -626,9 +631,9 @@ suite('NotebookTextModel', () => { test('Destructive sorting in _doApplyEdits #121994. cell splice between output changes 2', async function () { await withTestNotebook([ - ['var a = 1;', 'javascript', CellKind.Code, [{ outputId: 'i42', outputs: [{ mime: 'm/ime', value: 'test' }] }], {}], - ['var b = 2;', 'javascript', CellKind.Code, [{ outputId: 'i43', outputs: [{ mime: 'm/ime', value: 'test' }] }], {}], - ['var c = 3;', 'javascript', CellKind.Code, [{ outputId: 'i44', outputs: [{ mime: 'm/ime', value: 'test' }] }], {}] + ['var a = 1;', 'javascript', CellKind.Code, [{ outputId: 'i42', outputs: [{ mime: 'm/ime', valueBytes: valueBytesFromString('test') }] }], {}], + ['var b = 2;', 'javascript', CellKind.Code, [{ outputId: 'i43', outputs: [{ mime: 'm/ime', valueBytes: valueBytesFromString('test') }] }], {}], + ['var c = 3;', 'javascript', CellKind.Code, [{ outputId: 'i44', outputs: [{ mime: 'm/ime', valueBytes: valueBytesFromString('test') }] }], {}] ], async (editor) => { const notebook = editor.viewModel.notebookDocument; @@ -636,7 +641,7 @@ suite('NotebookTextModel', () => { { editType: CellEditType.Output, index: 1, append: true, outputs: [{ outputId: 'newOutput', - outputs: [{ mime: 'text/plain', value: 'cba' }, { mime: 'application/foo', value: 'cba' }] + outputs: [{ mime: 'text/plain', valueBytes: valueBytesFromString('cba') }, { mime: 'application/foo', valueBytes: valueBytesFromString('cba') }] }] }, { @@ -645,7 +650,7 @@ suite('NotebookTextModel', () => { { editType: CellEditType.Output, index: 1, append: true, outputs: [{ outputId: 'newOutput2', - outputs: [{ mime: 'text/plain', value: 'cba' }, { mime: 'application/foo', value: 'cba' }] + outputs: [{ mime: 'text/plain', valueBytes: valueBytesFromString('cba') }, { mime: 'application/foo', valueBytes: valueBytesFromString('cba') }] }] } ]; diff --git a/src/vs/workbench/test/browser/api/extHostNotebookKernel2.test.ts b/src/vs/workbench/test/browser/api/extHostNotebookKernel2.test.ts index 5bfdd1356c3..742118b48d0 100644 --- a/src/vs/workbench/test/browser/api/extHostNotebookKernel2.test.ts +++ b/src/vs/workbench/test/browser/api/extHostNotebookKernel2.test.ts @@ -165,28 +165,28 @@ suite('NotebookKernel', function () { assert.strictEqual(first.label, 'Far'); }); - test('execute - simple createNotebookCellExecutionTask', function () { + test('execute - simple createNotebookCellExecution', function () { const kernel = extHostNotebookKernels.createNotebookController(nullExtensionDescription, 'foo', '*', 'Foo'); extHostNotebookKernels.$acceptNotebookAssociation(0, notebook.uri, true); const cell1 = notebook.apiNotebook.cellAt(0); - const task = kernel.createNotebookCellExecutionTask(cell1); + const task = kernel.createNotebookCellExecution(cell1); task.start(); task.end(); }); - test('createNotebookCellExecutionTask, must be selected/associated', function () { + test('createNotebookCellExecution, must be selected/associated', function () { const kernel = extHostNotebookKernels.createNotebookController(nullExtensionDescription, 'foo', '*', 'Foo'); assert.throws(() => { - kernel.createNotebookCellExecutionTask(notebook.apiNotebook.cellAt(0)); + kernel.createNotebookCellExecution(notebook.apiNotebook.cellAt(0)); }); extHostNotebookKernels.$acceptNotebookAssociation(0, notebook.uri, true); - kernel.createNotebookCellExecutionTask(notebook.apiNotebook.cellAt(0)); + kernel.createNotebookCellExecution(notebook.apiNotebook.cellAt(0)); }); - test('createNotebookCellExecutionTask, cell must be alive', function () { + test('createNotebookCellExecution, cell must be alive', function () { const kernel = extHostNotebookKernels.createNotebookController(nullExtensionDescription, 'foo', '*', 'Foo'); const cell1 = notebook.apiNotebook.cellAt(0); @@ -203,7 +203,7 @@ suite('NotebookKernel', function () { assert.strictEqual(cell1.index, -1); assert.throws(() => { - kernel.createNotebookCellExecutionTask(cell1); + kernel.createNotebookCellExecution(cell1); }); }); @@ -218,15 +218,15 @@ suite('NotebookKernel', function () { const cell1 = notebook.apiNotebook.cellAt(0); - const task = kernel.createNotebookCellExecutionTask(cell1); + const task = kernel.createNotebookCellExecution(cell1); task.token.onCancellationRequested(() => tokenCancelCount += 1); await extHostNotebookKernels.$cancelCells(0, notebook.uri, [0]); assert.strictEqual(interruptCallCount, 1); - assert.strictEqual(tokenCancelCount, 1); + assert.strictEqual(tokenCancelCount, 0); await extHostNotebookKernels.$cancelCells(0, notebook.uri, [0]); assert.strictEqual(interruptCallCount, 2); - assert.strictEqual(tokenCancelCount, 1); + assert.strictEqual(tokenCancelCount, 0); }); }); diff --git a/src/vs/workbench/test/browser/api/extHostTypeConverter.test.ts b/src/vs/workbench/test/browser/api/extHostTypeConverter.test.ts index 324cdd3c071..9b9e68a2778 100644 --- a/src/vs/workbench/test/browser/api/extHostTypeConverter.test.ts +++ b/src/vs/workbench/test/browser/api/extHostTypeConverter.test.ts @@ -91,14 +91,12 @@ suite('ExtHostTypeConverter', function () { assert.strictEqual(dto.mime, 'foo/bar'); assert.strictEqual(dto.metadata, undefined); - assert.strictEqual(dto.value, undefined); assert.deepStrictEqual(dto.valueBytes, Array.from(new TextEncoder().encode('Hello'))); const item2 = NotebookCellOutputItem.to(dto); assert.strictEqual(item2.mime, item.mime); assert.strictEqual(item2.metadata, item.metadata); - assert.strictEqual(item2.value, item.value); assert.deepStrictEqual(item2.data, item.data); }); });