Merge branch 'notebook/dev' into main

This commit is contained in:
rebornix 2021-05-27 14:04:55 -07:00
commit 866ecdd45a
20 changed files with 252 additions and 271 deletions

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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<void>
}
export interface NotebookInterruptHandler {
/**
* @param notebook The notebook for which the interrupt handler is being called.
*/
(this: NotebookController, notebook: NotebookDocument): void | Thenable<void>;
}
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<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>;
}
@ -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<void>;
/**
* 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...

View file

@ -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,

View file

@ -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<boolean>;
}
@ -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<void>) | 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(<vscode.NotebookCellExecutionTask>{
return Object.freeze(<vscode.NotebookCellExecution>{
get token() { return that._tokenSource.token; },
get document() { return that._document.apiNotebook; },
get cell() { return that._cell.apiCell; },

View file

@ -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);
}
}

View file

@ -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 (<vscode.NotebookCellOutputItem>obj).mime === 'string';
return typeof (<vscode.NotebookCellOutputItem>obj).mime === 'string'
&& (<vscode.NotebookCellOutputItem>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);
}
}

View file

@ -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<IConfigurationRegistry>(Extensions.Configuration);
configurationRegistry.registerConfiguration({
id: 'notebook',
@ -654,6 +693,7 @@ configurationRegistry.registerConfiguration({
type: 'boolean',
default: true,
tags: ['notebookLayout']
}
},
[NotebookCellEditorOptionsCustomizations]: editorOptionsCustomizationSchema
}
});

View file

@ -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,
};

View file

@ -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 {

View file

@ -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,
},

View file

@ -47,41 +47,50 @@ export class CellEditorOptions extends Disposable {
private readonly _onDidChange = new Emitter<IEditorOptions>();
readonly onDidChange: Event<IEditorOptions> = 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<IEditorOptions>('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<IEditorOptions>('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 {

View file

@ -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,

View file

@ -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<string, unknown>;
}
@ -603,7 +602,6 @@ const _mimeTypeInfo = new Map<string, MimeTypeInfo>([
['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,

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, 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<number>('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<number>('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
});
}

View file

@ -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));

View file

@ -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') }]
}]
}
];

View file

@ -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);
});
});

View file

@ -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);
});
});