diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.document.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.document.test.ts index bb8b9a01674..70e6e704231 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.document.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.document.test.ts @@ -248,67 +248,6 @@ suite('Notebook Document', function () { assert.strictEqual(data.changes[0].items[1], document.cellAt(1)); }); - test('workspace edit API (appendNotebookCellOutput, replaceCellOutput, event)', async function () { - const uri = await utils.createRandomFile(undefined, undefined, '.nbdtest'); - const document = await vscode.notebook.openNotebookDocument(uri); - - const outputChangeEvent = utils.asPromise(vscode.notebook.onDidChangeCellOutputs); - const edit = new vscode.WorkspaceEdit(); - const firstCellOutput = new vscode.NotebookCellOutput([new vscode.NotebookCellOutputItem('foo', 'bar')]); - edit.appendNotebookCellOutput(document.uri, 0, [firstCellOutput]); - const success = await vscode.workspace.applyEdit(edit); - const data = await outputChangeEvent; - - assert.strictEqual(success, true); - assert.strictEqual(document.cellCount, 1); - assert.strictEqual(document.cellAt(0).outputs.length, 1); - assert.deepStrictEqual(document.cellAt(0).outputs, [firstCellOutput]); - - assert.strictEqual(data.document === document, true); - assert.strictEqual(data.cells.length, 1); - assert.strictEqual(data.cells[0].outputs.length, 1); - assert.deepStrictEqual(data.cells[0].outputs, [firstCellOutput]); - - - { - const outputChangeEvent = utils.asPromise(vscode.notebook.onDidChangeCellOutputs); - const edit = new vscode.WorkspaceEdit(); - const secondCellOutput = new vscode.NotebookCellOutput([new vscode.NotebookCellOutputItem('foo', 'baz')]); - edit.appendNotebookCellOutput(document.uri, 0, [secondCellOutput]); - const success = await vscode.workspace.applyEdit(edit); - const data = await outputChangeEvent; - - assert.strictEqual(success, true); - assert.strictEqual(document.cellCount, 1); - assert.strictEqual(document.cellAt(0).outputs.length, 2); - assert.deepStrictEqual(document.cellAt(0).outputs, [firstCellOutput, secondCellOutput]); - - assert.strictEqual(data.document === document, true); - assert.strictEqual(data.cells.length, 1); - assert.strictEqual(data.cells[0].outputs.length, 2); - assert.deepStrictEqual(data.cells[0].outputs, [firstCellOutput, secondCellOutput]); - } - - { - const outputChangeEvent = utils.asPromise(vscode.notebook.onDidChangeCellOutputs); - const edit = new vscode.WorkspaceEdit(); - const thirdOutput = new vscode.NotebookCellOutput([new vscode.NotebookCellOutputItem('foo', 'baz1')]); - edit.replaceNotebookCellOutput(document.uri, 0, [thirdOutput]); - const success = await vscode.workspace.applyEdit(edit); - const data = await outputChangeEvent; - - assert.strictEqual(success, true); - assert.strictEqual(document.cellCount, 1); - assert.strictEqual(document.cellAt(0).outputs.length, 1); - assert.deepStrictEqual(document.cellAt(0).outputs, [thirdOutput]); - - assert.strictEqual(data.document === document, true); - assert.strictEqual(data.cells.length, 1); - assert.strictEqual(data.cells[0].outputs.length, 1); - assert.deepStrictEqual(data.cells[0].outputs, [thirdOutput]); - } - }); - test('document save API', async function () { const uri = await utils.createRandomFile(undefined, undefined, '.nbdtest'); const notebook = await vscode.notebook.openNotebookDocument(uri); @@ -375,45 +314,6 @@ suite('Notebook Document', function () { assert.strictEqual(cellDoc.languageId, 'css'); }); - test('#117273, Add multiple outputs', async function () { - - const resource = await utils.createRandomFile(undefined, undefined, '.nbdtest'); - const document = await vscode.notebook.openNotebookDocument(resource); - - const edit = new vscode.WorkspaceEdit(); - edit.replaceNotebookCellOutput(document.uri, 0, [ - new vscode.NotebookCellOutput( - [new vscode.NotebookCellOutputItem('application/x.notebook.stream', '1', { outputType: 'stream', streamName: 'stdout' })], - { outputType: 'stream', streamName: 'stdout' } - ) - ]); - let success = await vscode.workspace.applyEdit(edit); - - assert.ok(success); - assert.strictEqual(document.cellAt(0).outputs.length, 1); - assert.strictEqual(document.cellAt(0).outputs[0].outputs.length, 1); - assert.deepStrictEqual(document.cellAt(0).outputs[0].metadata, { outputType: 'stream', streamName: 'stdout' }); - assert.deepStrictEqual(document.cellAt(0).outputs[0].outputs[0].metadata, { outputType: 'stream', streamName: 'stdout' }); - - const edit2 = new vscode.WorkspaceEdit(); - edit2.appendNotebookCellOutput(document.uri, 0, [ - new vscode.NotebookCellOutput( - [new vscode.NotebookCellOutputItem('hello', '1', { outputType: 'stream', streamName: 'stderr' })], - { outputType: 'stream', streamName: 'stderr' } - ) - ]); - success = await vscode.workspace.applyEdit(edit2); - assert.ok(success); - - assert.strictEqual(document.cellAt(0).outputs.length, 2); - assert.strictEqual(document.cellAt(0).outputs[0].outputs.length, 1); - assert.strictEqual(document.cellAt(0).outputs[1].outputs.length, 1); - assert.deepStrictEqual(document.cellAt(0).outputs[0].metadata, { outputType: 'stream', streamName: 'stdout' }); - assert.deepStrictEqual(document.cellAt(0).outputs[0].outputs[0].metadata, { outputType: 'stream', streamName: 'stdout' }); - assert.deepStrictEqual(document.cellAt(0).outputs[1].metadata, { outputType: 'stream', streamName: 'stderr' }); - assert.deepStrictEqual(document.cellAt(0).outputs[1].outputs[0].metadata, { outputType: 'stream', streamName: 'stderr' }); - }); - test('dirty state - complex', async function () { const resource = await utils.createRandomFile(undefined, undefined, '.nbdtest'); const document = await vscode.notebook.openNotebookDocument(resource); diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.editor.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.editor.test.ts index 031101a0306..11792c4f5bb 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.editor.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.editor.test.ts @@ -9,22 +9,15 @@ import * as utils from '../utils'; suite('Notebook Editor', function () { - const contentProvider = new class implements vscode.NotebookContentProvider { - async openNotebook(uri: vscode.Uri, _openContext: vscode.NotebookDocumentOpenContext): Promise { + const contentSerializer = new class implements vscode.NotebookSerializer { + deserializeNotebook() { return new vscode.NotebookData( - [new vscode.NotebookCellData(vscode.NotebookCellKind.Code, uri.toString(), 'javascript')], + [new vscode.NotebookCellData(vscode.NotebookCellKind.Code, '// code cell', 'javascript')], new vscode.NotebookDocumentMetadata() ); - } - async saveNotebook(_document: vscode.NotebookDocument, _cancellation: vscode.CancellationToken) { - // - } - async saveNotebookAs(_targetResource: vscode.Uri, _document: vscode.NotebookDocument, _cancellation: vscode.CancellationToken) { - // - } - async backupNotebook(_document: vscode.NotebookDocument, _context: vscode.NotebookDocumentBackupContext, _cancellation: vscode.CancellationToken) { - return { id: '', delete() { } }; + serializeNotebook() { + return new Uint8Array(); } }; @@ -43,7 +36,7 @@ suite('Notebook Editor', function () { }); suiteSetup(function () { - disposables.push(vscode.notebook.registerNotebookContentProvider('notebook.nbdtest', contentProvider)); + disposables.push(vscode.notebook.registerNotebookSerializer('notebook.nbdtest', contentSerializer)); }); 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 8f41be5cf54..86ed95ffa2e 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts @@ -1169,33 +1169,6 @@ suite('Notebook API tests', function () { await closeAllEditors(); }); - test('#116598, output items change event.', async function () { - - const resource = await createRandomNotebookFile(); - await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); - - const edit = new vscode.WorkspaceEdit(); - edit.appendNotebookCellOutput(resource, 0, [new vscode.NotebookCellOutput([ - new vscode.NotebookCellOutputItem('application/foo', 'bar'), - new vscode.NotebookCellOutputItem('application/json', { data: true }, { metadata: true }), - ])]); - await vscode.workspace.applyEdit(edit); - assert.strictEqual(vscode.window.activeNotebookEditor!.document.cellAt(0).outputs.length, 1); - assert.strictEqual(vscode.window.activeNotebookEditor!.document.cellAt(0).outputs[0].outputs.length, 2); - - const appendEdit = new vscode.WorkspaceEdit(); - const newItem = new vscode.NotebookCellOutputItem('text/plain', '1'); - appendEdit.appendNotebookCellOutputItems( - resource, - 0, - vscode.window.activeNotebookEditor!.document.cellAt(0).outputs[0].id, - [newItem] - ); - await vscode.workspace.applyEdit(appendEdit); - assert.strictEqual(vscode.window.activeNotebookEditor!.document.cellAt(0).outputs[0].outputs.length, 3); - assert.deepStrictEqual(vscode.window.activeNotebookEditor!.document.cellAt(0).outputs[0].outputs[2], newItem); - }); - test('#115855 onDidSaveNotebookDocument', async function () { const resource = await createRandomNotebookFile(); const notebook = await vscode.notebook.openNotebookDocument(resource); diff --git a/src/vs/base/browser/ui/codicons/codicon/codicon-modifiers.css b/src/vs/base/browser/ui/codicons/codicon/codicon-modifiers.css index 4b5b5141489..9666216f6ae 100644 --- a/src/vs/base/browser/ui/codicons/codicon/codicon-modifiers.css +++ b/src/vs/base/browser/ui/codicons/codicon/codicon-modifiers.css @@ -13,7 +13,10 @@ } } -.codicon-sync.codicon-modifier-spin, .codicon-loading.codicon-modifier-spin, .codicon-gear.codicon-modifier-spin { +.codicon-sync.codicon-modifier-spin, +.codicon-loading.codicon-modifier-spin, +.codicon-gear.codicon-modifier-spin, +.codicon-notebook-state-executing.codicon-modifier-spin { /* Use steps to throttle FPS to reduce CPU usage */ animation: codicon-spin 1.5s steps(30) infinite; } diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index a5e1e706ff4..08193a5f945 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -964,6 +964,7 @@ declare module 'vscode' { //#region https://github.com/microsoft/vscode/issues/106744, Notebooks (misc) export enum NotebookCellKind { + // todo@API rename/rethink as "Markup" cell Markdown = 1, Code = 2 } @@ -979,20 +980,27 @@ declare module 'vscode' { */ readonly outputCollapsed?: boolean; - /** - * @deprecated - * Additional attributes of a cell metadata. - */ - readonly custom?: Record; - /** * Additional attributes of a cell metadata. */ readonly [key: string]: any; - constructor(inputCollapsed?: boolean, outputCollapsed?: boolean, custom?: Record); + /** + * Create a new notebook cell metadata. + * + * @param inputCollapsed Whether a code cell's editor is collapsed + * @param outputCollapsed Whether a code cell's outputs are collapsed + */ + constructor(inputCollapsed?: boolean, outputCollapsed?: boolean); - with(change: { inputCollapsed?: boolean | null, outputCollapsed?: boolean | null, custom?: Record | null, [key: string]: any }): NotebookCellMetadata; + /** + * Derived a new cell metadata from this metadata. + * + * @param change An object that describes a change to this NotebookCellMetadata. + * @return A new NotebookCellMetadata that reflects the given change. Will return `this` NotebookCellMetadata if the change + * is not changing anything. + */ + with(change: { inputCollapsed?: boolean | null, outputCollapsed?: boolean | null, [key: string]: any }): NotebookCellMetadata; } export interface NotebookCellExecutionSummary { @@ -1010,15 +1018,12 @@ declare module 'vscode' { readonly document: TextDocument; readonly metadata: NotebookCellMetadata readonly outputs: ReadonlyArray; + + // todo@API maybe just executionSummary or lastExecutionSummary? readonly latestExecutionSummary: NotebookCellExecutionSummary | undefined; } export class NotebookDocumentMetadata { - /** - * @deprecated - * Additional attributes of the document metadata. - */ - readonly custom: { [key: string]: any; }; /** * Whether the document is trusted, default to true * When false, insecure outputs like HTML, JavaScript, SVG will not be rendered. @@ -1030,9 +1035,20 @@ declare module 'vscode' { */ readonly [key: string]: any; - constructor(trusted?: boolean, custom?: { [key: string]: any; }); + /** + * Create a new notebook document metadata + * @param trusted Whether the document metadata is trusted. + */ + constructor(trusted?: boolean); - with(change: { trusted?: boolean | null, custom?: { [key: string]: any; } | null, [key: string]: any }): NotebookDocumentMetadata + /** + * Derived a new document metadata from this metadata. + * + * @param change An object that describes a change to this NotebookDocumentMetadata. + * @return A new NotebookDocumentMetadata that reflects the given change. Will return `this` NotebookDocumentMetadata if the change + * is not changing anything. + */ + with(change: { trusted?: boolean | null, [key: string]: any }): NotebookDocumentMetadata } export interface NotebookDocumentContentOptions { @@ -1042,11 +1058,6 @@ declare module 'vscode' { */ transientOutputs?: boolean; - /** - * @deprecated use transientCellMetadata instead - */ - transientMetadata?: { [K in keyof NotebookCellMetadata]?: boolean }; - /** * Controls if a cell metadata property change will trigger notebook document content change and if it will be used in the diff editor * Default to false. If the content provider doesn't persisit a metadata property in the file document, it should be set to true. @@ -1060,17 +1071,6 @@ declare module 'vscode' { transientDocumentMetadata?: { [K in keyof NotebookDocumentMetadata]?: boolean }; } - export interface NotebookDocumentContentOptions { - /** - * Not ready for production or development use yet. - */ - viewOptions?: { - displayName: string; - filenamePattern: NotebookFilenamePattern[]; - exclusive?: boolean; - }; - } - /** * Represents a notebook. Notebooks are composed of cells and metadata. */ @@ -1088,6 +1088,7 @@ declare module 'vscode' { readonly uri: Uri; // todo@API should we really expose this? + // todo@API should this be called `notebookType` or `notebookKind` readonly viewType: string; /** @@ -1166,7 +1167,7 @@ declare module 'vscode' { readonly end: number; /** - * `true` if `start` and `end` are equals + * `true` if `start` and `end` are equal. */ readonly isEmpty: boolean; @@ -1194,6 +1195,7 @@ declare module 'vscode' { * The range will be revealed with as little scrolling as possible. */ Default = 0, + /** * The range will always be revealed in the center of the viewport. */ @@ -1314,10 +1316,11 @@ declare module 'vscode' { kind: NotebookCellKind; // todo@API better names: value? text? source: string; - // todo@API how does language and MD relate? + // todo@API languageId (as in TextDocument) language: string; outputs?: NotebookCellOutput[]; metadata?: NotebookCellMetadata; + // todo@API just executionSummary or lastExecutionSummary latestExecutionSummary?: NotebookCellExecutionSummary; constructor(kind: NotebookCellKind, source: string, language: string, outputs?: NotebookCellOutput[], metadata?: NotebookCellMetadata, latestExecutionSummary?: NotebookCellExecutionSummary); } @@ -1350,8 +1353,11 @@ declare module 'vscode' { export const notebookDocuments: ReadonlyArray; export const onDidChangeNotebookDocumentMetadata: Event; export const onDidChangeNotebookCells: Event; + + // todo@API add onDidChangeNotebookCellOutputs export const onDidChangeCellOutputs: Event; + // todo@API add onDidChangeNotebookCellMetadata export const onDidChangeCellMetadata: Event; } @@ -1383,23 +1389,19 @@ declare module 'vscode' { // static textplain(value:string): NotebookCellOutputItem; // static errortrace(value:any): NotebookCellOutputItem; - readonly mime: string; - readonly value: unknown; - readonly metadata?: Record; + mime: string; + value: unknown; + metadata?: Record; constructor(mime: string, value: unknown, metadata?: Record); } - // @jrieken - // todo@API think about readonly... - //TODO@API add execution count to cell output? + // @jrieken transient export class NotebookCellOutput { - readonly id: string; - readonly outputs: NotebookCellOutputItem[]; - readonly metadata?: Record; - + id: string; + outputs: NotebookCellOutputItem[]; + metadata?: Record; constructor(outputs: NotebookCellOutputItem[], metadata?: Record); - constructor(outputs: NotebookCellOutputItem[], id: string, metadata?: Record); } @@ -1407,23 +1409,32 @@ declare module 'vscode' { //#region https://github.com/microsoft/vscode/issues/106744, NotebookEditorEdit + // todo@API add NotebookEdit-type which handles all these cases? + // export class NotebookEdit { + // range: NotebookRange; + // newCells: NotebookCellData[]; + // newMetadata?: NotebookDocumentMetadata; + // constructor(range: NotebookRange, newCells: NotebookCellData) + // } + + // export class NotebookCellEdit { + // newMetadata?: NotebookCellMetadata; + // } + + // export interface WorkspaceEdit { + // set(uri: Uri, edits: TextEdit[] | NotebookEdit[]): void + // } + export interface WorkspaceEdit { // todo@API add NotebookEdit-type which handles all these cases? replaceNotebookMetadata(uri: Uri, value: NotebookDocumentMetadata): void; replaceNotebookCells(uri: Uri, range: NotebookRange, cells: NotebookCellData[], metadata?: WorkspaceEditEntryMetadata): void; replaceNotebookCellMetadata(uri: Uri, index: number, cellMetadata: NotebookCellMetadata, metadata?: WorkspaceEditEntryMetadata): void; - - //todo@API remove output modifications? - replaceNotebookCellOutput(uri: Uri, index: number, outputs: NotebookCellOutput[], metadata?: WorkspaceEditEntryMetadata): void; - appendNotebookCellOutput(uri: Uri, index: number, outputs: NotebookCellOutput[], metadata?: WorkspaceEditEntryMetadata): void; - replaceNotebookCellOutputItems(uri: Uri, index: number, outputId: string, items: NotebookCellOutputItem[], metadata?: WorkspaceEditEntryMetadata): void; - appendNotebookCellOutputItems(uri: Uri, index: number, outputId: string, items: NotebookCellOutputItem[], metadata?: WorkspaceEditEntryMetadata): void; } export interface NotebookEditorEdit { replaceMetadata(value: NotebookDocumentMetadata): void; replaceCells(start: number, end: number, cells: NotebookCellData[]): void; - replaceCellOutput(index: number, outputs: NotebookCellOutput[]): void; replaceCellMetadata(index: number, metadata: NotebookCellMetadata): void; } @@ -1531,13 +1542,6 @@ declare module 'vscode' { */ readonly selector: NotebookSelector; - /** - * A kernel can apply to one or many notebook documents but a notebook has only one active - * kernel. This event fires whenever a notebook has been associated to a kernel or when - * that association has been removed. - */ - readonly onDidChangeNotebookAssociation: Event<{ notebook: NotebookDocument, selected: boolean }>; - /** * The human-readable label of this notebook controller. */ @@ -1594,15 +1598,25 @@ declare module 'vscode' { dispose(): void; /** - * Manually create an execution task. This should only be used when cell execution - * has started before creating the kernel instance or when execution can be triggered - * from another source. + * A kernel can apply to one or many notebook documents but a notebook has only one active + * kernel. This event fires whenever a notebook has been associated to a kernel or when + * that association has been removed. + */ + readonly onDidChangeNotebookAssociation: Event<{ notebook: NotebookDocument, selected: boolean }>; + + /** + * Create a cell execution task. * - * @param cell The notebook cell for which to create the execution + * This should be used in response to the [execution handler](#NotebookController.executeHandler) + * being calleed or when cell execution has been started else, e.g when a cell was already + * executing or when cell execution was triggered from another source. + * + * @param cell The notebook cell for which to create the execution. * @returns A notebook cell execution. */ createNotebookCellExecutionTask(cell: NotebookCell): NotebookCellExecutionTask; + // todo@API allow add, not remove // ipc readonly preloads: NotebookKernelPreload[]; @@ -1623,7 +1637,7 @@ declare module 'vscode' { */ postMessage(message: any, editor?: NotebookEditor): Thenable; - + //todo@API validate this works asWebviewUri(localResource: Uri): Uri; } @@ -1695,6 +1709,17 @@ declare module 'vscode' { backupNotebook(document: NotebookDocument, context: NotebookDocumentBackupContext, token: CancellationToken): Thenable; } + export interface NotebookDocumentContentOptions { + /** + * Not ready for production or development use yet. + */ + viewOptions?: { + displayName: string; + filenamePattern: (GlobPattern | { include: GlobPattern; exclude: GlobPattern; })[]; + exclusive?: boolean; + }; + } + export namespace notebook { // TODO@api use NotebookDocumentFilter instead of just notebookType:string? @@ -1706,6 +1731,7 @@ declare module 'vscode' { //#region https://github.com/microsoft/vscode/issues/106744, NotebookKernel + // todo@API make class? export interface NotebookKernelPreload { provides?: string | string[]; uri: Uri; @@ -1773,8 +1799,6 @@ declare module 'vscode' { export const onDidChangeCellExecutionState: Event; } - export type NotebookFilenamePattern = GlobPattern | { include: GlobPattern; exclude: GlobPattern; }; - //#endregion //#region https://github.com/microsoft/vscode/issues/106744, NotebookEditorDecorationType @@ -1818,6 +1842,7 @@ declare module 'vscode' { Right = 2 } + // todo@API remove readonlyness. export class NotebookCellStatusBarItem { readonly text: string; readonly alignment: NotebookCellStatusBarAlignment; @@ -1857,17 +1882,16 @@ declare module 'vscode' { * @param notebook * @param selector */ - // @jrieken REMOVE. p_never // todo@API really needed? we didn't find a user here export function createConcatTextDocument(notebook: NotebookDocument, selector?: DocumentSelector): NotebookConcatTextDocument; } export interface NotebookConcatTextDocument { - uri: Uri; - isClosed: boolean; + readonly uri: Uri; + readonly isClosed: boolean; dispose(): void; - onDidChange: Event; - version: number; + readonly onDidChange: Event; + readonly version: number; getText(): string; getText(range: Range): string; diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index e1d399b9610..acc83786481 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -13,7 +13,7 @@ import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService'; import { NotebookSelector } from 'vs/workbench/contrib/notebook/common/notebookSelector'; import { INotebookCellStatusBarItemProvider, INotebookExclusiveDocumentFilter, NotebookDataDto, TransientCellMetadata, TransientDocumentMetadata, TransientOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { IMainNotebookController, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; +import { INotebookContentProvider, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { ExtHostContext, ExtHostNotebookShape, IExtHostContext, MainContext, MainThreadNotebookShape, NotebookExtensionDescription } from '../common/extHost.protocol'; @extHostNamedCustomer(MainContext.MainThreadNotebook) @@ -22,7 +22,7 @@ export class MainThreadNotebooks implements MainThreadNotebookShape { private readonly _disposables = new DisposableStore(); private readonly _proxy: ExtHostNotebookShape; - private readonly _notebookProviders = new Map(); + private readonly _notebookProviders = new Map(); private readonly _notebookSerializer = new Map(); private readonly _notebookCellStatusBarRegistrations = new Map(); @@ -51,7 +51,7 @@ export class MainThreadNotebooks implements MainThreadNotebookShape { }): Promise { let contentOptions = { transientOutputs: options.transientOutputs, transientCellMetadata: options.transientCellMetadata, transientDocumentMetadata: options.transientDocumentMetadata }; - const controller: IMainNotebookController = { + const controller: INotebookContentProvider = { get options() { return contentOptions; }, diff --git a/src/vs/workbench/api/browser/mainThreadNotebookEditors.ts b/src/vs/workbench/api/browser/mainThreadNotebookEditors.ts index 98d122ba31c..a8ca5a5ac46 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebookEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebookEditors.ts @@ -8,7 +8,8 @@ import { getNotebookEditorFromEditorPane, INotebookEditor, NotebookEditorOptions import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/notebookEditorService'; import { ExtHostContext, ExtHostNotebookShape, IExtHostContext, INotebookDocumentShowOptions, INotebookEditorViewColumnInfo, MainThreadNotebookEditorsShape, NotebookEditorRevealType } from '../common/extHost.protocol'; import { MainThreadNotebooksAndEditors } from 'vs/workbench/api/browser/mainThreadNotebookDocumentsAndEditors'; -import { ICellEditOperation, ICellRange, INotebookDecorationRenderOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ICellEditOperation, INotebookDecorationRenderOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { ILogService } from 'vs/platform/log/common/log'; import { URI, UriComponents } from 'vs/base/common/uri'; import { EditorActivation, EditorOverride } from 'vs/platform/editor/common/editor'; diff --git a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts index a7409e97223..6a3ce60f4fc 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts @@ -3,10 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { flatten } from 'vs/base/common/arrays'; +import { flatten, isNonEmptyArray } from 'vs/base/common/arrays'; import { Emitter, Event } from 'vs/base/common/event'; import { combinedDisposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; +import { IModeService } from 'vs/editor/common/services/modeService'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; @@ -43,7 +44,7 @@ abstract class MainThreadKernel implements INotebookKernel { return flatten(this.preloads.map(p => p.provides)); } - constructor(data: INotebookKernelDto2) { + constructor(data: INotebookKernelDto2, private _modeService: IModeService) { this.id = data.id; this.selector = data.selector; this.extension = data.extensionId; @@ -53,7 +54,7 @@ abstract class MainThreadKernel implements INotebookKernel { this.description = data.description; this.detail = data.detail; this.isPreferred = data.isPreferred; - this.supportedLanguages = data.supportedLanguages; + this.supportedLanguages = isNonEmptyArray(data.supportedLanguages) ? data.supportedLanguages : _modeService.getRegisteredModes(); this.implementsExecutionOrder = data.hasExecutionOrder ?? false; this.localResourceRoot = URI.revive(data.extensionLocation); this.preloads = data.preloads?.map(u => ({ uri: URI.revive(u.uri), provides: u.provides })) ?? []; @@ -80,7 +81,7 @@ abstract class MainThreadKernel implements INotebookKernel { event.isPreferred = true; } if (data.supportedLanguages !== undefined) { - this.supportedLanguages = data.supportedLanguages; + this.supportedLanguages = isNonEmptyArray(data.supportedLanguages) ? data.supportedLanguages : this._modeService.getRegisteredModes(); event.supportedLanguages = true; } if (data.hasExecutionOrder !== undefined) { @@ -105,6 +106,7 @@ export class MainThreadNotebookKernels implements MainThreadNotebookKernelsShape constructor( extHostContext: IExtHostContext, + @IModeService private readonly _modeService: IModeService, @INotebookKernelService private readonly _notebookKernelService: INotebookKernelService, @INotebookEditorService notebookEditorService: INotebookEditorService ) { @@ -192,7 +194,7 @@ export class MainThreadNotebookKernels implements MainThreadNotebookKernelsShape async cancelNotebookCellExecution(uri: URI, handles: number[]): Promise { await that._proxy.$cancelCells(handle, uri, handles); } - }(data); + }(data, this._modeService); const registration = this._notebookKernelService.registerKernel(kernel); const listener = this._notebookKernelService.onDidChangeNotebookKernelBinding(e => { diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 3fd03f1b18f..141a95a82a4 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1054,7 +1054,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I }, get notebookDocuments(): vscode.NotebookDocument[] { checkProposedApiEnabled(extension); - return extHostNotebook.notebookDocuments.map(d => d.notebookDocument); + return extHostNotebook.notebookDocuments.map(d => d.apiNotebook); }, registerNotebookSerializer(viewType, serializer, options) { checkProposedApiEnabled(extension); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 61bf086e9e0..3e9342817f9 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -50,7 +50,8 @@ import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService'; import { TunnelCreationOptions, TunnelProviderFeatures, TunnelOptions, ProvidedPortAttributes } from 'vs/platform/remote/common/tunnel'; import { Timeline, TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor, InternalTimelineOptions } from 'vs/workbench/contrib/timeline/common/timeline'; import { revive } from 'vs/base/common/marshalling'; -import { NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEventDto, NotebookDataDto, IMainCellDto, TransientCellMetadata, ICellRange, INotebookDecorationRenderOptions, INotebookExclusiveDocumentFilter, IOutputDto, TransientOptions, IImmediateCellEditOperation, INotebookCellStatusBarItem, TransientDocumentMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEventDto, NotebookDataDto, IMainCellDto, TransientCellMetadata, INotebookDecorationRenderOptions, INotebookExclusiveDocumentFilter, IOutputDto, TransientOptions, IImmediateCellEditOperation, INotebookCellStatusBarItem, TransientDocumentMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { CallHierarchyItem } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy'; import { Dto } from 'vs/base/common/types'; import { DebugConfigurationProviderTriggerKind, TestResultState } from 'vs/workbench/api/common/extHostTypes'; @@ -913,7 +914,7 @@ export interface INotebookKernelDto2 { detail?: string; description?: string; isPreferred?: boolean; - supportedLanguages: string[]; + supportedLanguages?: string[]; supportsInterrupt?: boolean; hasExecutionOrder?: boolean; preloads?: { uri: UriComponents; provides: string[] }[]; diff --git a/src/vs/workbench/api/common/extHostApiCommands.ts b/src/vs/workbench/api/common/extHostApiCommands.ts index 2c44e40c33f..f088f4ded9d 100644 --- a/src/vs/workbench/api/common/extHostApiCommands.ts +++ b/src/vs/workbench/api/common/extHostApiCommands.ts @@ -348,7 +348,7 @@ const newCommands: ApiCommand[] = [ }[], { viewType: string; displayName: string; - filenamePattern: vscode.NotebookFilenamePattern[]; + filenamePattern: (vscode.GlobPattern | { include: vscode.GlobPattern; exclude: vscode.GlobPattern; })[]; options: vscode.NotebookDocumentContentOptions; }[] | undefined>('A promise that resolves to an array of NotebookContentProvider static info objects.', tryMapWith(item => { return { @@ -356,7 +356,6 @@ const newCommands: ApiCommand[] = [ displayName: item.displayName, options: { transientOutputs: item.options.transientOutputs, - transientMetadata: item.options.transientCellMetadata, transientCellMetadata: item.options.transientCellMetadata, transientDocumentMetadata: item.options.transientDocumentMetadata }, diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index dc2c4fc32e4..672776d70aa 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -27,6 +27,7 @@ import { VSBuffer } from 'vs/base/common/buffer'; import { hash } from 'vs/base/common/hash'; import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; import { Cache } from 'vs/workbench/api/common/cache'; +import { isFalsyOrWhitespace } from 'vs/base/common/strings'; export class NotebookEditorDecorationType { @@ -128,7 +129,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape { const data = this._documents.get(notebookUri); const cell = data?.getCell(cellHandle); if (cell) { - return cell.cell; + return cell.apiCell; } } return arg; @@ -185,7 +186,9 @@ export class ExtHostNotebookController implements ExtHostNotebookShape { }; } ): vscode.Disposable { - + if (isFalsyOrWhitespace(viewType)) { + throw new Error(`viewType cannot be empty or just whitespace`); + } if (this._notebookContentProviders.has(viewType)) { throw new Error(`Notebook provider for '${viewType}' already registered`); } @@ -253,11 +256,11 @@ export class ExtHostNotebookController implements ExtHostNotebookShape { async openNotebookDocument(uri: URI): Promise { const cached = this._documents.get(uri); if (cached) { - return cached.notebookDocument; + return cached.apiNotebook; } const canonicalUri = await this._notebookDocumentsProxy.$tryOpenDocument(uri); const document = this._documents.get(URI.revive(canonicalUri)); - return assertIsDefined(document?.notebookDocument); + return assertIsDefined(document?.apiNotebook); } @@ -308,7 +311,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape { return; } - const result = await provider.provideCellStatusBarItems(cell.cell, token); + const result = await provider.provideCellStatusBarItems(cell.apiCell, token); if (!result) { return undefined; } @@ -332,6 +335,9 @@ export class ExtHostNotebookController implements ExtHostNotebookShape { private readonly _notebookSerializer = new Map(); registerNotebookSerializer(extension: IExtensionDescription, viewType: string, serializer: vscode.NotebookSerializer, options?: vscode.NotebookDocumentContentOptions): vscode.Disposable { + if (isFalsyOrWhitespace(viewType)) { + throw new Error(`viewType cannot be empty or just whitespace`); + } const handle = this._handlePool++; this._notebookSerializer.set(handle, serializer); const internalOptions = typeConverters.NotebookDocumentContentOptions.from(options); @@ -389,14 +395,14 @@ export class ExtHostNotebookController implements ExtHostNotebookShape { async $saveNotebook(viewType: string, uri: UriComponents, token: CancellationToken): Promise { const document = this._getNotebookDocument(URI.revive(uri)); const { provider } = this._getProviderData(viewType); - await provider.saveNotebook(document.notebookDocument, token); + await provider.saveNotebook(document.apiNotebook, token); return true; } async $saveNotebookAs(viewType: string, uri: UriComponents, target: UriComponents, token: CancellationToken): Promise { const document = this._getNotebookDocument(URI.revive(uri)); const { provider } = this._getProviderData(viewType); - await provider.saveNotebookAs(URI.revive(target), document.notebookDocument, token); + await provider.saveNotebookAs(URI.revive(target), document.apiNotebook, token); return true; } @@ -410,7 +416,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape { const fileName = String(hash([document.uri.toString(), this._backupIdPool++])); const backupUri = URI.joinPath(storagePath, fileName); - const backup = await provider.provider.backupNotebook(document.notebookDocument, { destination: backupUri }, cancellation); + const backup = await provider.provider.backupNotebook(document.apiNotebook, { destination: backupUri }, cancellation); document.updateBackup(backup); return backup.id; } @@ -422,12 +428,12 @@ export class ExtHostNotebookController implements ExtHostNotebookShape { $acceptDirtyStateChanged(uri: UriComponents, isDirty: boolean): void { const document = this._getNotebookDocument(URI.revive(uri)); - document.acceptModelChanged({ rawEvents: [], versionId: document.notebookDocument.version }, isDirty); + document.acceptModelChanged({ rawEvents: [], versionId: document.apiNotebook.version }, isDirty); } $acceptModelSaved(uri: UriComponents): void { const document = this._getNotebookDocument(URI.revive(uri)); - this._onDidSaveNotebookDocument.fire(document.notebookDocument); + this._onDidSaveNotebookDocument.fire(document.apiNotebook); } $acceptEditorPropertiesChanged(id: string, data: INotebookEditorPropertiesChangeData): void { @@ -476,7 +482,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape { const document = this._getNotebookDocument(URI.revive(uri)); document.acceptDocumentPropertiesChanged(data); if (data.metadata) { - this._onDidChangeNotebookDocumentMetadata.fire({ document: document.notebookDocument }); + this._onDidChangeNotebookDocumentMetadata.fire({ document: document.apiNotebook }); } } @@ -508,8 +514,8 @@ export class ExtHostNotebookController implements ExtHostNotebookShape { if (document) { document.dispose(); this._documents.delete(revivedUri); - this._textDocumentsAndEditors.$acceptDocumentsAndEditorsDelta({ removedDocuments: document.notebookDocument.getCells().map(cell => cell.document.uri) }); - this._onDidCloseNotebookDocument.fire(document.notebookDocument); + this._textDocumentsAndEditors.$acceptDocumentsAndEditorsDelta({ removedDocuments: document.apiNotebook.getCells().map(cell => cell.document.uri) }); + this._onDidCloseNotebookDocument.fire(document.apiNotebook); } for (const editor of this._editors.values()) { @@ -565,13 +571,13 @@ export class ExtHostNotebookController implements ExtHostNotebookShape { }, false); // add cell document as vscode.TextDocument - addedCellDocuments.push(...modelData.cells.map(cell => ExtHostCell.asModelAddData(document.notebookDocument, cell))); + addedCellDocuments.push(...modelData.cells.map(cell => ExtHostCell.asModelAddData(document.apiNotebook, cell))); this._documents.get(uri)?.dispose(); this._documents.set(uri, document); this._textDocumentsAndEditors.$acceptDocumentsAndEditorsDelta({ addedDocuments: addedCellDocuments }); - this._onDidOpenNotebookDocument.fire(document.notebookDocument); + this._onDidOpenNotebookDocument.fire(document.apiNotebook); } } @@ -731,8 +737,8 @@ class NotebookCellExecutionTask extends Disposable { asApiObject(): vscode.NotebookCellExecutionTask { const that = this; return Object.freeze({ - get document() { return that._document.notebookDocument; }, - get cell() { return that._cell.cell; }, + get document() { return that._document.apiNotebook; }, + get cell() { return that._cell.apiCell; }, get executionOrder() { return that._executionOrder; }, set executionOrder(v: number | undefined) { @@ -752,7 +758,7 @@ class NotebookCellExecutionTask extends Disposable { that.mixinMetadata({ runState: extHostTypes.NotebookCellExecutionState.Executing, - runStartTime: context?.startTime + runStartTime: context?.startTime ?? null }); }, diff --git a/src/vs/workbench/api/common/extHostNotebookDocument.ts b/src/vs/workbench/api/common/extHostNotebookDocument.ts index ad6dd4380d4..4e46c581946 100644 --- a/src/vs/workbench/api/common/extHostNotebookDocument.ts +++ b/src/vs/workbench/api/common/extHostNotebookDocument.ts @@ -24,7 +24,7 @@ class RawContentChangeEvent { start: event.start, deletedCount: event.deletedCount, deletedItems: event.deletedItems, - items: event.items.map(data => data.cell) + items: event.items.map(data => data.apiCell) }; }); } @@ -73,7 +73,7 @@ export class ExtHostCell { return this._internalMetadata; } - get cell(): vscode.NotebookCell { + get apiCell(): vscode.NotebookCell { if (!this._cell) { const that = this; const data = this._extHostDocument.getDocument(this.uri); @@ -82,7 +82,7 @@ export class ExtHostCell { } this._cell = Object.freeze({ get index() { return that._notebook.getCellIndex(that); }, - notebook: that._notebook.notebookDocument, + notebook: that._notebook.apiNotebook, kind: extHostTypeConverters.NotebookCellKind.to(this._cellData.cellKind), document: data.document, get outputs() { return that._outputs.slice(0); }, @@ -150,7 +150,7 @@ export class ExtHostNotebookDocument { this._disposed = true; } - get notebookDocument(): vscode.NotebookDocument { + get apiNotebook(): vscode.NotebookDocument { if (!this._notebook) { const that = this; this._notebook = { @@ -164,11 +164,11 @@ export class ExtHostNotebookDocument { get cellCount() { return that._cells.length; }, cellAt(index) { index = that._validateIndex(index); - return that._cells[index].cell; + return that._cells[index].apiCell; }, getCells(range) { const cells = range ? that._getCells(range) : that._cells; - return cells.map(cell => cell.cell); + return cells.map(cell => cell.apiCell); }, save() { return that._save(); @@ -268,7 +268,7 @@ export class ExtHostNotebookDocument { const extCell = new ExtHostCell(this, this._textDocumentsAndEditors, cell); if (!initialization) { - addedCellDocuments.push(ExtHostCell.asModelAddData(this.notebookDocument, cell)); + addedCellDocuments.push(ExtHostCell.asModelAddData(this.apiNotebook, cell)); } return extCell; }); @@ -277,7 +277,7 @@ export class ExtHostNotebookDocument { const deletedItems = this._cells.splice(splice[0], splice[1], ...newCells); for (let cell of deletedItems) { removedCellDocuments.push(cell.uri); - changeEvent.deletedItems.push(cell.cell); + changeEvent.deletedItems.push(cell.apiCell); } contentChangeEvents.push(changeEvent); @@ -290,7 +290,7 @@ export class ExtHostNotebookDocument { if (!initialization) { this._emitter.emitModelChange(deepFreeze({ - document: this.notebookDocument, + document: this.apiNotebook, changes: RawContentChangeEvent.asApiEvents(contentChangeEvents) })); } @@ -300,11 +300,11 @@ export class ExtHostNotebookDocument { const cells = this._cells.splice(index, 1); this._cells.splice(newIdx, 0, ...cells); const changes = [ - new RawContentChangeEvent(index, 1, cells.map(c => c.cell), []), + new RawContentChangeEvent(index, 1, cells.map(c => c.apiCell), []), new RawContentChangeEvent(newIdx, 0, [], cells) ]; this._emitter.emitModelChange(deepFreeze({ - document: this.notebookDocument, + document: this.apiNotebook, changes: RawContentChangeEvent.asApiEvents(changes) })); } @@ -312,18 +312,18 @@ export class ExtHostNotebookDocument { private _setCellOutputs(index: number, outputs: IOutputDto[]): void { const cell = this._cells[index]; cell.setOutputs(outputs); - this._emitter.emitCellOutputsChange(deepFreeze({ document: this.notebookDocument, cells: [cell.cell] })); + this._emitter.emitCellOutputsChange(deepFreeze({ document: this.apiNotebook, cells: [cell.apiCell] })); } private _setCellOutputItems(index: number, outputId: string, append: boolean, outputItems: IOutputItemDto[]): void { const cell = this._cells[index]; cell.setOutputItems(outputId, append, outputItems); - this._emitter.emitCellOutputsChange(deepFreeze({ document: this.notebookDocument, cells: [cell.cell] })); + this._emitter.emitCellOutputsChange(deepFreeze({ document: this.apiNotebook, cells: [cell.apiCell] })); } private _changeCellLanguage(index: number, newModeId: string): void { const cell = this._cells[index]; - if (cell.cell.document.languageId !== newModeId) { + if (cell.apiCell.document.languageId !== newModeId) { this._textDocuments.$acceptModelModeChanged(cell.uri, newModeId); } } @@ -332,17 +332,17 @@ export class ExtHostNotebookDocument { const cell = this._cells[index]; const originalInternalMetadata = cell.internalMetadata; - const originalExtMetadata = cell.cell.metadata; + const originalExtMetadata = cell.apiCell.metadata; cell.setMetadata(newMetadata); - const newExtMetadata = cell.cell.metadata; + const newExtMetadata = cell.apiCell.metadata; if (!equals(originalExtMetadata, newExtMetadata)) { - this._emitter.emitCellMetadataChange(deepFreeze({ document: this.notebookDocument, cell: cell.cell })); + this._emitter.emitCellMetadataChange(deepFreeze({ document: this.apiNotebook, cell: cell.apiCell })); } if (originalInternalMetadata.runState !== newMetadata.runState) { const executionState = newMetadata.runState ?? extHostTypes.NotebookCellExecutionState.Idle; - this._emitter.emitCellExecutionStateChange(deepFreeze({ document: this.notebookDocument, cell: cell.cell, executionState })); + this._emitter.emitCellExecutionStateChange(deepFreeze({ document: this.apiNotebook, cell: cell.apiCell, executionState })); } } diff --git a/src/vs/workbench/api/common/extHostNotebookEditor.ts b/src/vs/workbench/api/common/extHostNotebookEditor.ts index 24b8b687742..30f8c6d52e4 100644 --- a/src/vs/workbench/api/common/extHostNotebookEditor.ts +++ b/src/vs/workbench/api/common/extHostNotebookEditor.ts @@ -57,17 +57,6 @@ class NotebookEditorCellEditBuilder implements vscode.NotebookEditorEdit { }); } - replaceCellOutput(index: number, outputs: vscode.NotebookCellOutput[]): void { - this._throwIfFinalized(); - this._collectedEdits.push({ - editType: CellEditType.Output, - index, - outputs: outputs.map(output => { - return extHostConverter.NotebookCellOutput.from(output); - }) - }); - } - replaceCells(from: number, to: number, cells: vscode.NotebookCellData[]): void { this._throwIfFinalized(); if (from === to && cells.length === 0) { @@ -111,7 +100,7 @@ export class ExtHostNotebookEditor { const that = this; this._editor = { get document() { - return that.notebookData.notebookDocument; + return that.notebookData.apiNotebook; }, get selections() { return that._selections; diff --git a/src/vs/workbench/api/common/extHostNotebookKernels.ts b/src/vs/workbench/api/common/extHostNotebookKernels.ts index b874b90e189..aed6b40385e 100644 --- a/src/vs/workbench/api/common/extHostNotebookKernels.ts +++ b/src/vs/workbench/api/common/extHostNotebookKernels.ts @@ -11,7 +11,6 @@ import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebo import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { URI, UriComponents } from 'vs/base/common/uri'; import * as extHostTypeConverters from 'vs/workbench/api/common/extHostTypeConverters'; -import { isNonEmptyArray } from 'vs/base/common/arrays'; import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; import { asWebviewUri } from 'vs/workbench/api/common/shared/webview'; @@ -48,7 +47,6 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { const handle = this._handlePool++; const that = this; - const _defaultSupportedLanguages = ['plaintext']; const _defaultExecutHandler = () => console.warn(`NO execute handler from notebook controller '${data.id}' of extension: '${extension.identifier}'`); let isDisposed = false; @@ -63,7 +61,6 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { extensionId: extension.identifier, extensionLocation: extension.extensionLocation, label: label || extension.identifier.value, - supportedLanguages: _defaultSupportedLanguages, preloads: preloads ? preloads.map(extHostTypeConverters.NotebookKernelPreload.from) : [] }; @@ -127,10 +124,10 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { _update(); }, get supportedLanguages() { - return data.supportedLanguages; + return data.supportedLanguages ?? []; }, set supportedLanguages(value) { - data.supportedLanguages = isNonEmptyArray(value) ? value : _defaultSupportedLanguages; + data.supportedLanguages = value; _update(); }, get hasExecutionOrder() { @@ -193,7 +190,7 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { if (obj) { obj.onDidChangeSelection.fire({ selected: value, - notebook: this._extHostNotebook.lookupNotebookDocument(URI.revive(uri))!.notebookDocument + notebook: this._extHostNotebook.lookupNotebookDocument(URI.revive(uri))!.apiNotebook }); } } @@ -213,7 +210,7 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { for (let cellHandle of handles) { const cell = document.getCell(cellHandle); if (cell) { - cells.push(cell.cell); + cells.push(cell.apiCell); } } diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index f9e8d73a13a..99656948075 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -28,6 +28,7 @@ import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocum import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook'; import { EditorGroupColumn, SaveReason } from 'vs/workbench/common/editor'; import * as notebooks from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; import * as search from 'vs/workbench/contrib/search/common/search'; import { ISerializedTestResults, ITestItem, ITestMessage, SerializedTestResultItem } from 'vs/workbench/contrib/testing/common/testCollection'; import { ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; @@ -544,7 +545,7 @@ export namespace WorkspaceEdit { resource: entry.uri, edit: entry.edit, notebookMetadata: entry.notebookMetadata, - notebookVersionId: extHostNotebooks?.lookupNotebookDocument(entry.uri)?.notebookDocument.version + notebookVersionId: extHostNotebooks?.lookupNotebookDocument(entry.uri)?.apiNotebook.version }); } else if (entry._type === types.FileEditType.CellOutput) { @@ -579,7 +580,7 @@ export namespace WorkspaceEdit { _type: extHostProtocol.WorkspaceEditType.Cell, metadata: entry.metadata, resource: entry.uri, - notebookVersionId: extHostNotebooks?.lookupNotebookDocument(entry.uri)?.notebookDocument.version, + notebookVersionId: extHostNotebooks?.lookupNotebookDocument(entry.uri)?.apiNotebook.version, edit: { editType: notebooks.CellEditType.Replace, index: entry.index, @@ -1407,11 +1408,11 @@ export namespace LanguageSelector { export namespace NotebookRange { - export function from(range: vscode.NotebookRange): notebooks.ICellRange { + export function from(range: vscode.NotebookRange): ICellRange { return { start: range.start, end: range.end }; } - export function to(range: notebooks.ICellRange): types.NotebookRange { + export function to(range: ICellRange): types.NotebookRange { return new types.NotebookRange(range.start, range.end); } } @@ -1644,7 +1645,7 @@ export namespace NotebookDocumentContentOptions { return { transientOutputs: options?.transientOutputs ?? false, transientCellMetadata: { - ...(options?.transientCellMetadata ?? options?.transientMetadata), + ...options?.transientCellMetadata, executionOrder: true, runState: true, runStartTime: true, diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 86271f2dcfc..6c8639d49b8 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -8,6 +8,7 @@ import { illegalArgument } from 'vs/base/common/errors'; import { IRelativePattern } from 'vs/base/common/glob'; import { isMarkdownString, MarkdownString as BaseMarkdownString } from 'vs/base/common/htmlContent'; import { ReadonlyMapView, ResourceMap } from 'vs/base/common/map'; +import { isFalsyOrWhitespace } from 'vs/base/common/strings'; import { isStringArray } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; @@ -706,30 +707,6 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit { } } - replaceNotebookCellOutput(uri: URI, index: number, outputs: vscode.NotebookCellOutput[], metadata?: vscode.WorkspaceEditEntryMetadata): void { - this._editNotebookCellOutput(uri, index, false, outputs, metadata); - } - - appendNotebookCellOutput(uri: URI, index: number, outputs: vscode.NotebookCellOutput[], metadata?: vscode.WorkspaceEditEntryMetadata): void { - this._editNotebookCellOutput(uri, index, true, outputs, metadata); - } - - replaceNotebookCellOutputItems(uri: URI, index: number, outputId: string, items: NotebookCellOutputItem[], metadata?: vscode.WorkspaceEditEntryMetadata): void { - this._editNotebookCellOutputItems(uri, index, outputId, false, items, metadata); - } - - appendNotebookCellOutputItems(uri: URI, index: number, outputId: string, items: NotebookCellOutputItem[], metadata?: vscode.WorkspaceEditEntryMetadata): void { - this._editNotebookCellOutputItems(uri, index, outputId, true, items, metadata); - } - - private _editNotebookCellOutputItems(uri: URI, index: number, id: string, append: boolean, items: vscode.NotebookCellOutputItem[], metadata: vscode.WorkspaceEditEntryMetadata | undefined): void { - this._edits.push({ _type: FileEditType.CellOutputItem, metadata, uri, index, outputId: id, append, newOutputItems: items }); - } - - private _editNotebookCellOutput(uri: URI, index: number, append: boolean, outputs: vscode.NotebookCellOutput[], metadata: vscode.WorkspaceEditEntryMetadata | undefined): void { - this._edits.push({ _type: FileEditType.CellOutput, metadata, uri, index, append, newOutputs: outputs, newMetadata: undefined }); - } - replaceNotebookCellMetadata(uri: URI, index: number, cellMetadata: vscode.NotebookCellMetadata, metadata?: vscode.WorkspaceEditEntryMetadata): void { this._edits.push({ _type: FileEditType.Cell, metadata, uri, edit: { editType: CellEditType.PartialMetadata, index, metadata: cellMetadata } }); } @@ -2982,7 +2959,6 @@ export class NotebookRange { export class NotebookCellMetadata { readonly inputCollapsed?: boolean; readonly outputCollapsed?: boolean; - readonly custom?: Record; readonly [key: string]: any; constructor(inputCollapsed?: boolean, outputCollapsed?: boolean); @@ -2999,11 +2975,10 @@ export class NotebookCellMetadata { with(change: { inputCollapsed?: boolean | null, outputCollapsed?: boolean | null, - custom?: Record | null, [key: string]: any }): NotebookCellMetadata { - let { inputCollapsed, outputCollapsed, custom, ...remaining } = change; + let { inputCollapsed, outputCollapsed, ...remaining } = change; if (inputCollapsed === undefined) { inputCollapsed = this.inputCollapsed; @@ -3015,15 +2990,9 @@ export class NotebookCellMetadata { } else if (outputCollapsed === null) { outputCollapsed = undefined; } - if (custom === undefined) { - custom = this.custom; - } else if (custom === null) { - custom = undefined; - } if (inputCollapsed === this.inputCollapsed && outputCollapsed === this.outputCollapsed && - custom === this.custom && Object.keys(remaining).length === 0 ) { return this; @@ -3033,7 +3002,6 @@ export class NotebookCellMetadata { { inputCollapsed, outputCollapsed, - custom, ...remaining } ); @@ -3042,43 +3010,33 @@ export class NotebookCellMetadata { export class NotebookDocumentMetadata { readonly trusted: boolean; - readonly custom: { [key: string]: any; }; readonly [key: string]: any; - constructor(trusted?: boolean, custom?: { [key: string]: any; }); + constructor(trusted?: boolean); constructor(data: Record); - constructor(trustedOrData: boolean | Record = true, custom: { [key: string]: any; } = {}) { + constructor(trustedOrData: boolean | Record = true) { if (typeof trustedOrData === 'object') { Object.assign(this, trustedOrData); this.trusted = trustedOrData.trusted ?? true; - this.custom = trustedOrData.custom ?? {}; } else { this.trusted = trustedOrData; - this.custom = custom; } } with(change: { trusted?: boolean | null, - custom?: { [key: string]: any; } | null, [key: string]: any }): NotebookDocumentMetadata { - let { custom, trusted, ...remaining } = change; + let { trusted, ...remaining } = change; - if (custom === undefined) { - custom = this.custom; - } else if (custom === null) { - custom = undefined; - } if (trusted === undefined) { trusted = this.trusted; } else if (trusted === null) { trusted = undefined; } - if (custom === this.custom && - trusted === this.trusted && + if (trusted === this.trusted && Object.keys(remaining).length === 0 ) { return this; @@ -3087,7 +3045,6 @@ export class NotebookDocumentMetadata { return new NotebookDocumentMetadata( { trusted, - custom, ...remaining } ); @@ -3141,17 +3098,21 @@ export class NotebookCellOutputItem { } constructor( - readonly mime: string, - readonly value: unknown, // JSON'able - readonly metadata?: Record - ) { } + public mime: string, + public value: unknown, // JSON'able + public metadata?: Record + ) { + if (isFalsyOrWhitespace(this.mime)) { + throw new Error('INVALID mime type, must not be empty or falsy'); + } + } } export class NotebookCellOutput { - readonly outputs: NotebookCellOutputItem[]; - readonly id: string; - readonly metadata?: Record; + id: string; + outputs: NotebookCellOutputItem[]; + metadata?: Record; constructor( outputs: NotebookCellOutputItem[], diff --git a/src/vs/workbench/contrib/notebook/browser/constants.ts b/src/vs/workbench/contrib/notebook/browser/constants.ts index 23eec63779b..06627113262 100644 --- a/src/vs/workbench/contrib/notebook/browser/constants.ts +++ b/src/vs/workbench/contrib/notebook/browser/constants.ts @@ -8,8 +8,17 @@ export const SCROLLABLE_ELEMENT_PADDING_TOP = 20; // export const SCROLLABLE_ELEMENT_PADDING_TOP_WITH_TOOLBAR = 8; +// Code cell layout: +// [CODE_CELL_LEFT_MARGIN][CELL_RUN_GUTTER][editorWidth][CELL_RIGHT_MARGIN] + +// Markdown cell layout: +// [CELL_MARGIN][content][CELL_RIGHT_MARGIN] + +// Markdown editor cell layout: +// [CODE_CELL_LEFT_MARGIN][content][CELL_RIGHT_MARGIN] + // Cell sizing related -export const CELL_MARGIN = 8; +export const CELL_RIGHT_MARGIN = 16; export const CELL_RUN_GUTTER = 28; export const CODE_CELL_LEFT_MARGIN = 32; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/cellOperations/cellOperations.ts b/src/vs/workbench/contrib/notebook/browser/contrib/cellOperations/cellOperations.ts index 45422b276f4..ff1a0bc9ed4 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/cellOperations/cellOperations.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/cellOperations/cellOperations.ts @@ -14,7 +14,8 @@ import { CellOverflowToolbarGroups, CellToolbarOrder, CELL_TITLE_CELL_GROUP_ID, import { CellEditState, expandCellRangesWithHiddenCells, ICellViewModel, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import * as icons from 'vs/workbench/contrib/notebook/browser/notebookIcons'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { CellEditType, CellKind, cellRangeContains, cellRangesToIndexes, ICellRange, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellEditType, CellKind, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { cellRangeContains, cellRangesToIndexes, ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { cloneNotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { CellViewModel, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { IBulkEditService, ResourceEdit, ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService'; @@ -318,9 +319,6 @@ export async function joinNotebookCells(viewModel: NotebookViewModel, range: ICe for (let i = 0; i < cells.length; i++) { const cell = cells[i]; - if (!cell.getEvaluatedMetadata(viewModel.notebookDocument.metadata).editable) { - return null; - } if (constraint && cell.cellKind !== constraint) { return null; @@ -333,10 +331,6 @@ export async function joinNotebookCells(viewModel: NotebookViewModel, range: ICe return null; } - if (!above.getEvaluatedMetadata(viewModel.notebookDocument.metadata).editable) { - return null; - } - const insertContent = cells.map(cell => (cell.textBuffer.getEOL() ?? '') + cell.getText()).join(''); const aboveCellLineCount = above.textBuffer.getLineCount(); const aboveCellLastLineEndColumn = above.textBuffer.getLineLength(aboveCellLineCount); @@ -363,10 +357,6 @@ export async function joinNotebookCells(viewModel: NotebookViewModel, range: ICe return null; } - if (!below.getEvaluatedMetadata(viewModel.notebookDocument.metadata).editable) { - return null; - } - const cell = cells[0]; const restCells = [...cells.slice(1), below]; const insertContent = restCells.map(cl => (cl.textBuffer.getEOL() ?? '') + cl.getText()).join(''); @@ -492,7 +482,7 @@ registerAction2(class extends NotebookCellAction { }, menu: { id: MenuId.NotebookCellTitle, - when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE), + when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE), group: CellOverflowToolbarGroups.Edit, order: 10 } @@ -518,7 +508,7 @@ registerAction2(class extends NotebookCellAction { }, menu: { id: MenuId.NotebookCellTitle, - when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE), + when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE), group: CellOverflowToolbarGroups.Edit, order: 11 } diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/clipboard/notebookClipboard.ts b/src/vs/workbench/contrib/notebook/browser/contrib/clipboard/notebookClipboard.ts index 224c640864c..c1e818abea5 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/clipboard/notebookClipboard.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/clipboard/notebookClipboard.ts @@ -14,7 +14,8 @@ import { CopyAction, CutAction, PasteAction } from 'vs/editor/contrib/clipboard/ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { CellViewModel, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { cloneNotebookCellTextModel, NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; -import { CellEditType, ICellEditOperation, ICellRange, ISelectionState, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellEditType, ICellEditOperation, ISelectionState, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import * as platform from 'vs/base/common/platform'; import { MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts index d92592ca140..920d09fe2bd 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts @@ -19,7 +19,8 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IQuickInputService, IQuickPickItem, QuickPickInput } from 'vs/platform/quickinput/common/quickInput'; import { BaseCellRenderTemplate, CellEditState, CellFocusMode, EXECUTE_CELL_COMMAND_ID, EXPAND_CELL_INPUT_COMMAND_ID, getNotebookEditorFromEditorPane, IActiveNotebookEditor, ICellViewModel, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_CELL_INPUT_COLLAPSED, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_OUTPUT_COLLAPSED, NOTEBOOK_CELL_EXECUTION_STATE, NOTEBOOK_CELL_TYPE, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_KERNEL_COUNT, NOTEBOOK_OUTPUT_FOCUSED, NOTEBOOK_INTERRUPTIBLE_KERNEL, NOTEBOOK_HAS_RUNNING_CELL, CHANGE_CELL_LANGUAGE, QUIT_EDIT_CELL_COMMAND_ID } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { CellEditType, CellKind, ICellEditOperation, ICellRange, isDocumentExcludePattern, NotebookCellMetadata, NotebookCellExecutionState, NOTEBOOK_EDITOR_CURSOR_BOUNDARY, TransientCellMetadata, TransientDocumentMetadata, SelectionStateType, ICellReplaceEdit } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellEditType, CellKind, ICellEditOperation, isDocumentExcludePattern, NotebookCellMetadata, NotebookCellExecutionState, NOTEBOOK_EDITOR_CURSOR_BOUNDARY, TransientCellMetadata, TransientDocumentMetadata, SelectionStateType, ICellReplaceEdit } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -1415,10 +1416,11 @@ registerAction2(class ChangeCellLanguageAction extends NotebookCellAction { let description: string; if (context.cell.cellKind === CellKind.Markdown ? (languageId === 'markdown') : (languageId === context.cell.language)) { diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/fold/folding.ts b/src/vs/workbench/contrib/notebook/browser/contrib/fold/folding.ts index 7d2bf8a2629..04f01b0575d 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/fold/folding.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/fold/folding.ts @@ -6,7 +6,8 @@ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { INotebookEditor, INotebookEditorMouseEvent, INotebookEditorContribution, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_IS_ACTIVE_EDITOR, getNotebookEditorFromEditorPane } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellFoldingState, FoldingModel } from 'vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel'; -import { CellKind, ICellRange } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { registerNotebookContribution } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions'; import { registerAction2, Action2 } from 'vs/platform/actions/common/actions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel.ts b/src/vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel.ts index 766b6907ac6..b409e7eb737 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel.ts @@ -9,7 +9,8 @@ import { TrackedRangeStickiness } from 'vs/editor/common/model'; import { FoldingRegion, FoldingRegions } from 'vs/editor/contrib/folding/foldingRanges'; import { IFoldingRangeData, sanitizeRanges } from 'vs/editor/contrib/folding/syntaxRangeProvider'; import { CellViewModel, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; -import { CellKind, cellRangesToIndexes, ICellRange } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { cellRangesToIndexes, ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; type RegionFilter = (r: FoldingRegion) => boolean; type RegionFilterWithLevel = (r: FoldingRegion, level: number) => boolean; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts b/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts index c9cec69e03f..63373701d52 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts @@ -13,7 +13,7 @@ import { getNotebookEditorFromEditorPane, INotebookEditor, NOTEBOOK_IS_ACTIVE_ED import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry, IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; -import { Disposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; +import { combinedDisposable, Disposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; import { IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment } from 'vs/workbench/services/statusbar/common/statusbar'; import { NotebookKernelProviderAssociation, NotebookKernelProviderAssociations, notebookKernelProviderAssociationsSettingId } from 'vs/workbench/contrib/notebook/browser/notebookKernelAssociation'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -22,7 +22,7 @@ import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; -import { INotebookKernel } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { INotebookKernel, INotebookTextModel } from 'vs/workbench/contrib/notebook/common/notebookCommon'; registerAction2(class extends Action2 { constructor() { @@ -145,7 +145,7 @@ registerAction2(class extends Action2 { export class KernelStatus extends Disposable implements IWorkbenchContribution { private readonly _editorDisposables = this._register(new DisposableStore()); - private readonly _kernelInfoElement = this._register(new MutableDisposable()); + private readonly _kernelInfoElement = this._register(new MutableDisposable()); constructor( @IEditorService private readonly _editorService: IEditorService, @@ -169,8 +169,7 @@ export class KernelStatus extends Disposable implements IWorkbenchContribution { const updateStatus = () => { const notebook = activeEditor.viewModel?.notebookDocument; if (notebook) { - const info = this._notebookKernelService.getNotebookKernels(notebook); - this._showKernelStatus(info.bound, info.all); + this._showKernelStatus(notebook); } else { this._kernelInfoElement.clear(); } @@ -182,29 +181,33 @@ export class KernelStatus extends Disposable implements IWorkbenchContribution { updateStatus(); } - private _showKernelStatus(boundKernel: INotebookKernel | undefined, availableKernels: INotebookKernel[]) { + private _showKernelStatus(notebook: INotebookTextModel) { - if (availableKernels.length === 0) { + let { bound, all } = this._notebookKernelService.getNotebookKernels(notebook); + + if (all.length === 0) { this._kernelInfoElement.clear(); return; } - if (!boundKernel) { - boundKernel = availableKernels[0]; + if (!bound) { + bound = all[0]; } - this._kernelInfoElement.value = this._statusbarService.addEntry( + const registration = this._statusbarService.addEntry( { - text: `$(notebook-kernel-select) ${boundKernel.label}`, - ariaLabel: boundKernel.label, - tooltip: boundKernel.description ?? boundKernel.detail ?? boundKernel.label, - command: availableKernels.length > 1 ? 'notebook.selectKernel' : undefined, + text: `$(notebook-kernel-select) ${bound.label}`, + ariaLabel: bound.label, + tooltip: bound.description ?? bound.detail ?? bound.label, + command: all.length > 1 ? 'notebook.selectKernel' : undefined, }, 'notebook.selectKernel', nls.localize('notebook.info', "Notebook Kernel Info"), StatusbarAlignment.RIGHT, 100 ); + const listener = bound.onDidChange(() => this._showKernelStatus(notebook)); + this._kernelInfoElement.value = combinedDisposable(listener, registration); } } diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/statusBar/cellStatusBar.ts b/src/vs/workbench/contrib/notebook/browser/contrib/statusBar/cellStatusBar.ts index 7ac92e61f2d..edce9fb60fb 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/statusBar/cellStatusBar.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/statusBar/cellStatusBar.ts @@ -11,7 +11,8 @@ import { ICellViewModel, INotebookEditor, INotebookEditorContribution } from 'vs import { registerNotebookContribution } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions'; import { CellViewModel, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService'; -import { cellRangesToIndexes, INotebookCellStatusBarItemList } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { INotebookCellStatusBarItemList } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { cellRangesToIndexes } from 'vs/workbench/contrib/notebook/common/notebookRange'; export class NotebookStatusBarController extends Disposable implements INotebookEditorContribution { static id: string = 'workbench.notebook.statusBar'; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/statusBar/contributedStatusBarItemController.ts b/src/vs/workbench/contrib/notebook/browser/contrib/statusBar/contributedStatusBarItemController.ts new file mode 100644 index 00000000000..22ee9a032f4 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/contrib/statusBar/contributedStatusBarItemController.ts @@ -0,0 +1,123 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { flatten } from 'vs/base/common/arrays'; +import { Throttler } from 'vs/base/common/async'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; +import { ICellVisibilityChangeEvent, NotebookVisibleCellObserver } from 'vs/workbench/contrib/notebook/browser/contrib/statusBar/notebookVisibleCellObserver'; +import { ICellViewModel, INotebookEditor, INotebookEditorContribution } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { registerNotebookContribution } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions'; +import { NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; +import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService'; +import { INotebookCellStatusBarItemList } from 'vs/workbench/contrib/notebook/common/notebookCommon'; + +export class ContributedStatusBarItemController extends Disposable implements INotebookEditorContribution { + static id: string = 'workbench.notebook.statusBar'; + + private readonly _visibleCells = new Map(); + + private readonly _observer: NotebookVisibleCellObserver; + + constructor( + private readonly _notebookEditor: INotebookEditor, + @INotebookCellStatusBarService private readonly _notebookCellStatusBarService: INotebookCellStatusBarService + ) { + super(); + this._observer = this._register(new NotebookVisibleCellObserver(this._notebookEditor)); + this._register(this._observer.onDidChangeVisibleCells(this._updateVisibleCells, this)); + + this._updateEverything(); + this._register(this._notebookCellStatusBarService.onDidChangeProviders(this._updateEverything, this)); + this._register(this._notebookCellStatusBarService.onDidChangeItems(this._updateEverything, this)); + } + + private _updateEverything(): void { + this._visibleCells.forEach(cell => cell.dispose()); + this._visibleCells.clear(); + this._updateVisibleCells({ added: this._observer.visibleCells, removed: [] }); + } + + private _updateVisibleCells(e: ICellVisibilityChangeEvent): void { + const vm = this._notebookEditor.viewModel; + if (!vm) { + return; + } + + for (let newCell of e.added) { + const helper = new CellStatusBarHelper(vm, newCell, this._notebookCellStatusBarService); + this._visibleCells.set(newCell.handle, helper); + } + + for (let oldCell of e.removed) { + this._visibleCells.get(oldCell.handle)?.dispose(); + this._visibleCells.delete(oldCell.handle); + } + } + + override dispose(): void { + super.dispose(); + + this._visibleCells.forEach(cell => cell.dispose()); + this._visibleCells.clear(); + } +} + +class CellStatusBarHelper extends Disposable { + private _currentItemIds: string[] = []; + private _currentItemLists: INotebookCellStatusBarItemList[] = []; + + private readonly _cancelTokenSource: CancellationTokenSource; + + private readonly _updateThrottler = new Throttler(); + + constructor( + private readonly _notebookViewModel: NotebookViewModel, + private readonly _cell: ICellViewModel, + private readonly _notebookCellStatusBarService: INotebookCellStatusBarService + ) { + super(); + + this._cancelTokenSource = new CancellationTokenSource(); + this._register(toDisposable(() => this._cancelTokenSource.dispose(true))); + + this._updateSoon(); + this._register(this._cell.model.onDidChangeContent(() => this._updateSoon())); + this._register(this._cell.model.onDidChangeLanguage(() => this._updateSoon())); + this._register(this._cell.model.onDidChangeMetadata(() => this._updateSoon())); + this._register(this._cell.model.onDidChangeOutputs(() => this._updateSoon())); + } + + private _updateSoon(): void { + this._updateThrottler.queue(() => this._update()); + } + + private async _update() { + const cellIndex = this._notebookViewModel.getCellIndex(this._cell); + const docUri = this._notebookViewModel.notebookDocument.uri; + const viewType = this._notebookViewModel.notebookDocument.viewType; + const itemLists = await this._notebookCellStatusBarService.getStatusBarItemsForCell(docUri, cellIndex, viewType, this._cancelTokenSource.token); + if (this._cancelTokenSource.token.isCancellationRequested) { + itemLists.forEach(itemList => itemList.dispose && itemList.dispose()); + return; + } + + const items = flatten(itemLists.map(itemList => itemList.items)); + const newIds = this._notebookViewModel.deltaCellStatusBarItems(this._currentItemIds, [{ handle: this._cell.handle, items }]); + + this._currentItemLists.forEach(itemList => itemList.dispose && itemList.dispose()); + this._currentItemLists = itemLists; + this._currentItemIds = newIds; + } + + override dispose() { + super.dispose(); + + this._notebookViewModel.deltaCellStatusBarItems(this._currentItemIds, [{ handle: this._cell.handle, items: [] }]); + this._currentItemLists.forEach(itemList => itemList.dispose && itemList.dispose()); + } +} + +registerNotebookContribution(ContributedStatusBarItemController.id, ContributedStatusBarItemController); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/statusBar/executionStatusBarItemController.ts b/src/vs/workbench/contrib/notebook/browser/contrib/statusBar/executionStatusBarItemController.ts new file mode 100644 index 00000000000..9eed1fef299 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/contrib/statusBar/executionStatusBarItemController.ts @@ -0,0 +1,228 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { RunOnceScheduler } from 'vs/base/common/async'; +import { Event } from 'vs/base/common/event'; +import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { localize } from 'vs/nls'; +import { themeColorFromId, ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { ICellVisibilityChangeEvent, NotebookVisibleCellObserver } from 'vs/workbench/contrib/notebook/browser/contrib/statusBar/notebookVisibleCellObserver'; +import { ICellViewModel, INotebookEditor, INotebookEditorContribution } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { registerNotebookContribution } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions'; +import { cellStatusIconSuccess, cellStatusIconError } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; +import { successStateIcon, errorStateIcon, pendingStateIcon, executingStateIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; +import { NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; +import { CellStatusbarAlignment, INotebookCellStatusBarItem, NotebookCellExecutionState } from 'vs/workbench/contrib/notebook/common/notebookCommon'; + +export class NotebookStatusBarController extends Disposable implements INotebookEditorContribution { + static id: string = 'workbench.notebook.statusBar'; + + private readonly _visibleCells = new Map(); + + private readonly _observer: NotebookVisibleCellObserver; + + constructor( + private readonly _notebookEditor: INotebookEditor, + ) { + super(); + this._observer = this._register(new NotebookVisibleCellObserver(this._notebookEditor)); + this._register(this._observer.onDidChangeVisibleCells(this._updateVisibleCells, this)); + + this._updateEverything(); + } + + private _updateEverything(): void { + this._visibleCells.forEach(dispose); + this._visibleCells.clear(); + this._updateVisibleCells({ added: this._observer.visibleCells, removed: [] }); + } + + private _updateVisibleCells(e: ICellVisibilityChangeEvent): void { + const vm = this._notebookEditor.viewModel; + if (!vm) { + return; + } + + for (let newCell of e.added) { + const helpers = [ + new ExecutionStateCellStatusBarHelper(vm, newCell), + new TimerCellStatusBarHelper(vm, newCell) + ]; + this._visibleCells.set(newCell.handle, helpers); + } + + for (let oldCell of e.removed) { + this._visibleCells.get(oldCell.handle)?.forEach(dispose); + this._visibleCells.delete(oldCell.handle); + } + } + + override dispose(): void { + super.dispose(); + + this._visibleCells.forEach(dispose); + this._visibleCells.clear(); + } +} + +/** + * Shows the cell's execution state in the cell status bar. When the "executing" state is shown, it will be shown for a minimum brief time. + */ +class ExecutionStateCellStatusBarHelper extends Disposable { + private static readonly MIN_SPINNER_TIME = 500; + + private _currentItemIds: string[] = []; + + private _currentExecutingStateTimer: any; + + constructor( + private readonly _notebookViewModel: NotebookViewModel, + private readonly _cell: ICellViewModel, + ) { + super(); + + this._update(); + this._register( + Event.filter(this._cell.model.onDidChangeMetadata, e => !!e.runStateChanged) + (() => this._update())); + } + + private async _update() { + const items = this._getItemsForCell(this._cell); + if (Array.isArray(items)) { + this._currentItemIds = this._notebookViewModel.deltaCellStatusBarItems(this._currentItemIds, [{ handle: this._cell.handle, items }]); + } + } + + /** + * Returns undefined if there should be no change, and an empty array if all items should be removed. + */ + private _getItemsForCell(cell: ICellViewModel): INotebookCellStatusBarItem[] | undefined { + if (this._currentExecutingStateTimer) { + return; + } + + const item = this._getItemForState(cell.metadata?.runState, cell.metadata?.lastRunSuccess); + + // Show the execution spinner for a minimum time + if (cell.metadata?.runState === NotebookCellExecutionState.Executing) { + this._currentExecutingStateTimer = setTimeout(() => { + this._currentExecutingStateTimer = undefined; + if (cell.metadata?.runState !== NotebookCellExecutionState.Executing) { + this._update(); + } + }, ExecutionStateCellStatusBarHelper.MIN_SPINNER_TIME); + } + + return item ? [item] : []; + } + + private _getItemForState(runState: NotebookCellExecutionState | undefined, lastRunSuccess: boolean | undefined): INotebookCellStatusBarItem | undefined { + if (runState === NotebookCellExecutionState.Idle && lastRunSuccess) { + return { + icon: successStateIcon, + iconColor: themeColorFromId(cellStatusIconSuccess), + tooltip: localize('notebook.cell.status.success', "Success"), + alignment: CellStatusbarAlignment.Left, + priority: Number.MAX_SAFE_INTEGER + }; + } else if (runState === NotebookCellExecutionState.Idle && !lastRunSuccess) { + return { + icon: errorStateIcon, + iconColor: themeColorFromId(cellStatusIconError), + tooltip: localize('notebook.cell.status.failed', "Failed"), + alignment: CellStatusbarAlignment.Left, + priority: Number.MAX_SAFE_INTEGER + }; + } else if (runState === NotebookCellExecutionState.Pending) { + return { + icon: pendingStateIcon, + tooltip: localize('notebook.cell.status.pending', "Pending"), + alignment: CellStatusbarAlignment.Left, + priority: Number.MAX_SAFE_INTEGER + }; + } else if (runState === NotebookCellExecutionState.Executing) { + return { + icon: ThemeIcon.modify(executingStateIcon, 'spin'), + tooltip: localize('notebook.cell.status.executing', "Executing"), + alignment: CellStatusbarAlignment.Left, + priority: Number.MAX_SAFE_INTEGER + }; + } + + return; + } + + override dispose() { + super.dispose(); + + this._notebookViewModel.deltaCellStatusBarItems(this._currentItemIds, [{ handle: this._cell.handle, items: [] }]); + } +} + +class TimerCellStatusBarHelper extends Disposable { + private static UPDATE_INTERVAL = 100; + private _currentItemIds: string[] = []; + + private _scheduler: RunOnceScheduler; + + constructor( + private readonly _notebookViewModel: NotebookViewModel, + private readonly _cell: ICellViewModel, + ) { + super(); + + this._update(); + this._register( + Event.filter(this._cell.model.onDidChangeMetadata, e => !!e.runStateChanged) + (() => this._update())); + this._scheduler = this._register(new RunOnceScheduler(() => this._update(), TimerCellStatusBarHelper.UPDATE_INTERVAL)); + } + + private async _update() { + let item: INotebookCellStatusBarItem | undefined; + if (this._cell.metadata?.runState === NotebookCellExecutionState.Executing) { + const startTime = this._cell.metadata.runStartTime; + const adjustment = this._cell.metadata.runStartTimeAdjustment; + if (typeof startTime === 'number') { + item = this._getTimeItem(startTime, Date.now(), adjustment); + this._scheduler.schedule(); + } + } else if (this._cell.metadata?.runState === NotebookCellExecutionState.Idle) { + const startTime = this._cell.metadata.runStartTime; + const endTime = this._cell.metadata.runEndTime; + if (typeof startTime === 'number' && typeof endTime === 'number') { + item = this._getTimeItem(startTime, endTime); + } + } + + const items = item ? [item] : []; + this._currentItemIds = this._notebookViewModel.deltaCellStatusBarItems(this._currentItemIds, [{ handle: this._cell.handle, items }]); + } + + private _getTimeItem(startTime: number, endTime: number, adjustment: number = 0): INotebookCellStatusBarItem { + const duration = endTime - startTime + adjustment; + return { + text: this._formatDuration(duration), + alignment: CellStatusbarAlignment.Left, + priority: Number.MAX_SAFE_INTEGER - 1 + }; + } + + private _formatDuration(duration: number) { + const seconds = Math.floor(duration / 1000); + const tenths = String(duration - seconds * 1000).charAt(0); + + return `${seconds}.${tenths}s`; + } + + override dispose() { + super.dispose(); + + this._notebookViewModel.deltaCellStatusBarItems(this._currentItemIds, [{ handle: this._cell.handle, items: [] }]); + } +} + +registerNotebookContribution(NotebookStatusBarController.id, NotebookStatusBarController); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/statusBar/notebookVisibleCellObserver.ts b/src/vs/workbench/contrib/notebook/browser/contrib/statusBar/notebookVisibleCellObserver.ts new file mode 100644 index 00000000000..c0827f60e77 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/contrib/statusBar/notebookVisibleCellObserver.ts @@ -0,0 +1,83 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { diffSets } from 'vs/base/common/collections'; +import { Emitter } from 'vs/base/common/event'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { isDefined } from 'vs/base/common/types'; +import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; +import { cellRangesToIndexes } from 'vs/workbench/contrib/notebook/common/notebookRange'; + +export interface ICellVisibilityChangeEvent { + added: CellViewModel[]; + removed: CellViewModel[]; +} + +export class NotebookVisibleCellObserver extends Disposable { + private readonly _onDidChangeVisibleCells = this._register(new Emitter()); + readonly onDidChangeVisibleCells = this._onDidChangeVisibleCells.event; + + private readonly _viewModelDisposables = this._register(new DisposableStore()); + + private _visibleCells: CellViewModel[] = []; + + get visibleCells(): CellViewModel[] { + return this._visibleCells; + } + + constructor(private readonly _notebookEditor: INotebookEditor) { + super(); + + this._register(this._notebookEditor.onDidChangeVisibleRanges(this._updateVisibleCells, this)); + this._register(this._notebookEditor.onDidChangeModel(this._onModelChange, this)); + this._updateVisibleCells(); + } + + private _onModelChange() { + this._viewModelDisposables.clear(); + const vm = this._notebookEditor.viewModel; + if (vm) { + this._viewModelDisposables.add(vm.onDidChangeViewCells(() => this.updateEverything())); + } + + this.updateEverything(); + } + + protected updateEverything(): void { + this._onDidChangeVisibleCells.fire({ added: [], removed: Array.from(this._visibleCells) }); + this._visibleCells = []; + this._updateVisibleCells(); + } + + private _updateVisibleCells(): void { + const vm = this._notebookEditor.viewModel; + if (!vm) { + return; + } + + const rangesWithEnd = this._notebookEditor.visibleRanges + .map(range => ({ start: range.start, end: range.end + 1 })); + const newVisibleCells = cellRangesToIndexes(rangesWithEnd) + .map(index => vm.cellAt(index)) + .filter(isDefined); + const newVisibleHandles = new Set(newVisibleCells.map(cell => cell.handle)); + const oldVisibleHandles = new Set(this._visibleCells.map(cell => cell.handle)); + const diff = diffSets(oldVisibleHandles, newVisibleHandles); + + const added = diff.added + .map(handle => vm.getCellByHandle(handle)) + .filter(isDefined); + const removed = diff.removed + .map(handle => vm.getCellByHandle(handle)) + .filter(isDefined); + + this._visibleCells = newVisibleCells; + this._onDidChangeVisibleCells.fire({ + added, + removed + }); + } +} diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/statusBar/statusBarProviders.ts b/src/vs/workbench/contrib/notebook/browser/contrib/statusBar/statusBarProviders.ts index 52babeda60a..172ea36e907 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/statusBar/statusBarProviders.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/statusBar/statusBarProviders.ts @@ -105,8 +105,14 @@ class BuiltinCellStatusBarProviders extends Disposable { @IInstantiationService instantiationService: IInstantiationService, @INotebookCellStatusBarService notebookCellStatusBarService: INotebookCellStatusBarService) { super(); - this._register(notebookCellStatusBarService.registerCellStatusBarItemProvider(instantiationService.createInstance(CellStatusBarPlaceholderProvider))); - this._register(notebookCellStatusBarService.registerCellStatusBarItemProvider(instantiationService.createInstance(CellStatusBarLanguagePickerProvider))); + + const builtinProviders = [ + CellStatusBarPlaceholderProvider, + CellStatusBarLanguagePickerProvider, + ]; + builtinProviders.forEach(p => { + this._register(notebookCellStatusBarService.registerCellStatusBarItemProvider(instantiationService.createInstance(p))); + }); } } diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/viewportCustomMarkdown/viewportCustomMarkdown.ts b/src/vs/workbench/contrib/notebook/browser/contrib/viewportCustomMarkdown/viewportCustomMarkdown.ts index cfd2520950a..f5a6f053ebf 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/viewportCustomMarkdown/viewportCustomMarkdown.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/viewportCustomMarkdown/viewportCustomMarkdown.ts @@ -9,7 +9,8 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { CellEditState, IInsetRenderOutput, INotebookEditor, INotebookEditorContribution, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { registerNotebookContribution } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; -import { BUILTIN_RENDERER_ID, CellKind, cellRangesToIndexes } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { BUILTIN_RENDERER_ID, CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { cellRangesToIndexes } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; class NotebookClipboardContribution extends Disposable implements INotebookEditorContribution { diff --git a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts index ad45d5481c1..da02d0400db 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts @@ -481,8 +481,6 @@ abstract class AbstractElementRenderer extends Disposable { const keys = new Set([...Object.keys(newMetadataObj)]); for (let key of keys) { switch (key as keyof NotebookCellMetadata) { - case 'breakpointMargin': - case 'editable': case 'inputCollapsed': case 'outputCollapsed': // boolean diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts index 63ab9bb1853..13ef9a3138e 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts @@ -371,7 +371,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD outputNodeLeftPadding: 32, previewNodePadding: MARKDOWN_PREVIEW_PADDING, leftMargin: 0, - cellMargin: 0, + rightMargin: 0, runGutter: 0 }; diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebook.css b/src/vs/workbench/contrib/notebook/browser/media/notebook.css index 62a7105fb43..1784cdf40e1 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebook.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebook.css @@ -359,7 +359,7 @@ } .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-title-toolbar { - visibility: hidden; + opacity: 0; display: inline-flex; position: absolute; height: 26px; @@ -425,10 +425,6 @@ z-index: 26; } -.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-status-right { - flex-direction: row-reverse; -} - .monaco-workbench .notebookOverlay .cell-statusbar-container .cell-status-right .cell-contributed-items { justify-content: flex-end; } @@ -454,26 +450,8 @@ cursor: pointer; } -.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-run-duration { - margin-right: 8px; -} - -.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-run-duration { - display: flex; - align-items: center; -} - -.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-run-status { - height: 100%; - display: flex; - align-items: center; - margin-left: 18px; - width: 18px; - margin-right: 2px; -} - -.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-status-left > div:first-child { - margin-left: 18px; +.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-status-left > .cell-contributed-items { + margin-left: 10px; } .monaco-workbench .notebookOverlay .cell-statusbar-container .codicon { @@ -489,10 +467,9 @@ } .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .run-button-container { - position: relative; + position: absolute; flex-shrink: 0; - z-index: 27; - /* Above the drag handle */ + z-index: 27; /* Above the drag handle */ } .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .run-button-container .monaco-toolbar { @@ -555,11 +532,12 @@ .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row:hover .cell-has-toolbar-actions .cell-title-toolbar, .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .markdown-cell-hover.cell-has-toolbar-actions .cell-title-toolbar, .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-has-toolbar-actions.cell-output-hover .cell-title-toolbar, -.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-has-toolbar-actions:hover .cell-title-toolbar { - visibility: visible; +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-has-toolbar-actions:hover .cell-title-toolbar, +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-title-toolbar:hover, +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-toolbar-dropdown-active .cell-title-toolbar { + opacity: 1; } - .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list:not(.element-focused):focus:before { outline: none !important; } @@ -946,6 +924,10 @@ margin-left: 4px; } +.cell-contributed-items.cell-contributed-items-right { + flex-direction: row-reverse; +} + .monaco-workbench .notebookOverlay .output .error::-webkit-scrollbar, .monaco-workbench .notebookOverlay .output-plaintext::-webkit-scrollbar { width: 10px; /* width of the entire scrollbar */ diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index f61b5d13a7d..b6384990c65 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -59,7 +59,8 @@ import 'vs/workbench/contrib/notebook/browser/contrib/format/formatting'; import 'vs/workbench/contrib/notebook/browser/contrib/marker/markerProvider'; import 'vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline'; import 'vs/workbench/contrib/notebook/browser/contrib/statusBar/statusBarProviders'; -import 'vs/workbench/contrib/notebook/browser/contrib/statusBar/cellStatusBar'; +import 'vs/workbench/contrib/notebook/browser/contrib/statusBar/contributedStatusBarItemController'; +import 'vs/workbench/contrib/notebook/browser/contrib/statusBar/executionStatusBarItemController'; import 'vs/workbench/contrib/notebook/browser/contrib/status/editorStatus'; import 'vs/workbench/contrib/notebook/browser/contrib/undoRedo/notebookUndoRedo'; import 'vs/workbench/contrib/notebook/browser/contrib/cellOperations/cellOperations'; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 204449f9e72..6ded5c79ec9 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -19,10 +19,10 @@ import { Range } from 'vs/editor/common/core/range'; import { FindMatch, IReadonlyTextBuffer, ITextModel } from 'vs/editor/common/model'; import { ContextKeyExpr, RawContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer'; -import { RunStateRenderer, TimerRenderer } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer'; import { CellViewModel, IModelDecorationsChangeAccessor, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; -import { CellKind, NotebookCellMetadata, NotebookDocumentMetadata, INotebookKernel, ICellRange, IOrderedMimeType, INotebookRendererInfo, ICellOutput, IOutputItemDto, cellRangesToIndexes, reduceRanges, INotebookCellStatusBarItem } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, NotebookCellMetadata, INotebookKernel, IOrderedMimeType, INotebookRendererInfo, ICellOutput, IOutputItemDto, INotebookCellStatusBarItem } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ICellRange, cellRangesToIndexes, reduceRanges } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { Webview } from 'vs/workbench/contrib/webview/browser/webview'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { IMenu } from 'vs/platform/actions/common/actions'; @@ -263,7 +263,6 @@ export interface ICellViewModel extends IGenericCellViewModel { textModel: ITextModel | undefined; hasModel(): this is IEditableCellViewModel; resolveTextModel(): Promise; - getEvaluatedMetadata(documentMetadata: NotebookDocumentMetadata | undefined): NotebookCellMetadata; getSelectionsStartPosition(): IPosition[] | undefined; getCellDecorations(): INotebookCellDecorationOptions[]; getCellStatusBarItems(): INotebookCellStatusBarItem[]; @@ -730,7 +729,6 @@ export interface MarkdownCellRenderTemplate extends BaseCellRenderTemplate { } export interface CodeCellRenderTemplate extends BaseCellRenderTemplate { - cellRunState: RunStateRenderer; runToolbar: ToolBar; runButtonContainer: HTMLElement; executionOrderLabel: HTMLElement; @@ -739,7 +737,6 @@ export interface CodeCellRenderTemplate extends BaseCellRenderTemplate { focusSinkElement: HTMLElement; editor: ICodeEditor; progressBar: ProgressBar; - timer: TimerRenderer; focusIndicatorRight: HTMLElement; focusIndicatorBottom: HTMLElement; dragHandle: HTMLElement; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 61f9490d67a..b1022ca2d00 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -36,19 +36,18 @@ import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { contrastBorder, diffInserted, diffRemoved, editorBackground, errorForeground, focusBorder, foreground, listInactiveSelectionBackground, registerColor, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, textBlockQuoteBackground, textBlockQuoteBorder, textLinkActiveForeground, textLinkForeground, textPreformatForeground, transparent } from 'vs/platform/theme/common/colorRegistry'; -import { IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { EditorMemento } from 'vs/workbench/browser/parts/editor/editorPane'; import { IEditorMemento } from 'vs/workbench/common/editor'; import { Memento, MementoObject } from 'vs/workbench/common/memento'; import { PANEL_BORDER } from 'vs/workbench/common/theme'; import { debugIconStartForeground } from 'vs/workbench/contrib/debug/browser/debugColors'; -import { BOTTOM_CELL_TOOLBAR_GAP, BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_BOTTOM_MARGIN, CELL_MARGIN, CELL_OUTPUT_PADDING, CELL_RUN_GUTTER, CELL_TOP_MARGIN, CODE_CELL_LEFT_MARGIN, COLLAPSED_INDICATOR_HEIGHT, MARKDOWN_CELL_BOTTOM_MARGIN, MARKDOWN_CELL_TOP_MARGIN, MARKDOWN_PREVIEW_PADDING, SCROLLABLE_ELEMENT_PADDING_TOP } from 'vs/workbench/contrib/notebook/browser/constants'; +import { BOTTOM_CELL_TOOLBAR_GAP, BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_BOTTOM_MARGIN, CELL_OUTPUT_PADDING, CELL_RIGHT_MARGIN, CELL_RUN_GUTTER, CELL_TOP_MARGIN, CODE_CELL_LEFT_MARGIN, COLLAPSED_INDICATOR_HEIGHT, MARKDOWN_CELL_BOTTOM_MARGIN, MARKDOWN_CELL_TOP_MARGIN, MARKDOWN_PREVIEW_PADDING, SCROLLABLE_ELEMENT_PADDING_TOP } from 'vs/workbench/contrib/notebook/browser/constants'; import { CellEditState, CellFocusMode, IActiveNotebookEditor, ICellOutputViewModel, ICellViewModel, ICommonCellInfo, IDisplayOutputLayoutUpdateRequest, IFocusNotebookCellOptions, IGenericCellViewModel, IInsetRenderOutput, INotebookCellList, INotebookCellOutputLayoutInfo, INotebookDeltaDecoration, INotebookEditor, INotebookEditorContribution, INotebookEditorContributionDescription, INotebookEditorCreationOptions, INotebookEditorMouseEvent, NotebookEditorOptions, NotebookLayoutInfo, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_ID, NOTEBOOK_OUTPUT_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookDecorationCSSRules, NotebookRefCountedStyleSheet } from 'vs/workbench/contrib/notebook/browser/notebookEditorDecorations'; import { NotebookEditorExtensionsRegistry } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions'; import { NotebookEditorKernelManager } from 'vs/workbench/contrib/notebook/browser/notebookEditorKernelManager'; import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/notebookEditorService'; -import { errorStateIcon, successStateIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; import { NotebookCellList } from 'vs/workbench/contrib/notebook/browser/view/notebookCellList'; import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer'; import { BackLayerWebView, INotebookWebviewMessage } from 'vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView'; @@ -60,7 +59,8 @@ import { NotebookEventDispatcher, NotebookLayoutChangedEvent } from 'vs/workbenc import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel'; import { CellViewModel, IModelDecorationsChangeAccessor, INotebookEditorViewState, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { CellKind, CellToolbarLocKey, ExperimentalUseMarkdownRenderer, ICellRange, SelectionStateType, ShowCellStatusBarKey } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, CellToolbarLocKey, ExperimentalUseMarkdownRenderer, SelectionStateType, ShowCellStatusBarKey } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { editorGutterModifiedBackground } from 'vs/workbench/contrib/scm/browser/dirtydiffDecorator'; import { Webview } from 'vs/workbench/contrib/webview/browser/webview'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; @@ -879,7 +879,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor const focused = this._list.getFocusedElements()[0]; if (focused) { if (!this._cellContextKeyManager) { - this._cellContextKeyManager = this._localStore.add(new CellContextKeyManager(this.scopedContextKeyService, this, this.viewModel.notebookDocument, focused as CellViewModel)); + this._cellContextKeyManager = this._localStore.add(new CellContextKeyManager(this.scopedContextKeyService, this, focused as CellViewModel)); } this._cellContextKeyManager.updateForElement(focused as CellViewModel); @@ -1020,7 +1020,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor outputNodeLeftPadding: CELL_OUTPUT_PADDING, previewNodePadding: MARKDOWN_PREVIEW_PADDING, leftMargin: CODE_CELL_LEFT_MARGIN, - cellMargin: CELL_MARGIN, + rightMargin: CELL_RIGHT_MARGIN, runGutter: CELL_RUN_GUTTER, }); this._webview.element.style.width = '100%'; @@ -2659,21 +2659,6 @@ registerThemingParticipant((theme, collector) => { collector.addRule(`.notebookOverlay .monaco-list-row .cell-editor-part:before { outline: solid 1px ${cellBorderColor}; }`); } - const cellStatusSuccessIcon = theme.getColor(cellStatusIconSuccess); - if (cellStatusSuccessIcon) { - collector.addRule(`.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-run-status ${ThemeIcon.asCSSSelector(successStateIcon)} { color: ${cellStatusSuccessIcon} }`); - } - - const cellStatusErrorIcon = theme.getColor(cellStatusIconError); - if (cellStatusErrorIcon) { - collector.addRule(`.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-run-status ${ThemeIcon.asCSSSelector(errorStateIcon)} { color: ${cellStatusErrorIcon} }`); - } - - const cellStatusRunningIcon = theme.getColor(cellStatusIconRunning); - if (cellStatusRunningIcon) { - collector.addRule(`.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-run-status .codicon-sync { color: ${cellStatusRunningIcon} }`); - } - const cellStatusBarHoverBg = theme.getColor(cellStatusBarItemHover); if (cellStatusBarHoverBg) { collector.addRule(`.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-language-picker:hover, @@ -2744,26 +2729,27 @@ registerThemingParticipant((theme, collector) => { } // Cell Margin - collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row div.cell { margin: 0px ${CELL_MARGIN * 2}px 0px ${CELL_MARGIN}px; }`); - collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row div.cell.code { margin-left: ${CODE_CELL_LEFT_MARGIN}px; }`); + collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .markdown-cell-row div.cell.code { margin-left: ${CODE_CELL_LEFT_MARGIN}px; }`); + collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .code-cell-row div.cell.code { margin-left: ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER}px; }`); + collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row div.cell { margin-right: ${CELL_RIGHT_MARGIN}px; }`); collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row > .cell-inner-container { padding-top: ${CELL_TOP_MARGIN}px; }`); collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .markdown-cell-row > .cell-inner-container { padding-bottom: ${MARKDOWN_CELL_BOTTOM_MARGIN}px; padding-top: ${MARKDOWN_CELL_TOP_MARGIN}px; }`); collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .markdown-cell-row > .cell-inner-container.webview-backed-markdown-cell { padding: 0; }`); collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .markdown-cell-row > .webview-backed-markdown-cell.markdown-cell-edit-mode .cell.code { padding-top: ${MARKDOWN_CELL_TOP_MARGIN}px; }`); - collector.addRule(`.notebookOverlay .output { margin: 0px ${CELL_MARGIN}px 0px ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER}px; }`); - collector.addRule(`.notebookOverlay .output { width: calc(100% - ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER + (CELL_MARGIN * 2)}px); }`); + collector.addRule(`.notebookOverlay .output { margin: 0px ${CELL_RIGHT_MARGIN}px 0px ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER}px; }`); + collector.addRule(`.notebookOverlay .output { width: calc(100% - ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER + CELL_RIGHT_MARGIN}px); }`); - collector.addRule(`.notebookOverlay .output-show-more-container { margin: 0px ${CELL_MARGIN}px 0px ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER}px; }`); - collector.addRule(`.notebookOverlay .output-show-more-container { width: calc(100% - ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER + (CELL_MARGIN * 2)}px); }`); + collector.addRule(`.notebookOverlay .output-show-more-container { margin: 0px ${CELL_RIGHT_MARGIN}px 0px ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER}px; }`); + collector.addRule(`.notebookOverlay .output-show-more-container { width: calc(100% - ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER + CELL_RIGHT_MARGIN}px); }`); collector.addRule(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row div.cell.markdown { padding-left: ${CELL_RUN_GUTTER}px; }`); - collector.addRule(`.notebookOverlay .cell .run-button-container { width: 20px; margin: 0px ${Math.floor(CELL_RUN_GUTTER - 20) / 2}px; }`); + collector.addRule(`.notebookOverlay .cell .run-button-container { width: 20px; left: ${CODE_CELL_LEFT_MARGIN + Math.floor(CELL_RUN_GUTTER - 20) / 2}px }`); collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row :not(.webview-backed-markdown-cell) .cell-focus-indicator-top { height: ${CELL_TOP_MARGIN}px; }`); collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-side { bottom: ${BOTTOM_CELL_TOOLBAR_GAP}px; }`); collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row.code-cell-row .cell-focus-indicator-left, .notebookOverlay .monaco-list .monaco-list-row.code-cell-row .cell-drag-handle { width: ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER}px; }`); collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row.markdown-cell-row .cell-focus-indicator-left { width: ${CODE_CELL_LEFT_MARGIN}px; }`); - collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator.cell-focus-indicator-right { width: ${CELL_MARGIN * 2}px; }`); + collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator.cell-focus-indicator-right { width: ${CELL_RIGHT_MARGIN}px; }`); collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-bottom { height: ${CELL_BOTTOM_MARGIN}px; }`); collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row .cell-shadow-container-bottom { top: ${CELL_BOTTOM_MARGIN}px; }`); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookIcons.ts b/src/vs/workbench/contrib/notebook/browser/notebookIcons.ts index 22adce5fbc2..edeff4ff744 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookIcons.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookIcons.ts @@ -24,6 +24,8 @@ export const unfoldIcon = registerIcon('notebook-unfold', Codicon.unfold, locali export const successStateIcon = registerIcon('notebook-state-success', Codicon.check, localize('successStateIcon', 'Icon to indicate a success state in notebook editors.')); export const errorStateIcon = registerIcon('notebook-state-error', Codicon.error, localize('errorStateIcon', 'Icon to indicate an error state in notebook editors.')); +export const pendingStateIcon = registerIcon('notebook-state-pending', Codicon.clock, localize('pendingStateIcon', 'Icon to indicate a pending state in notebook editors.')); +export const executingStateIcon = registerIcon('notebook-state-executing', Codicon.sync, localize('executingStateIcon', 'Icon to indicate an executing state in notebook editors.')); export const collapsedIcon = registerIcon('notebook-collapsed', Codicon.chevronRight, localize('collapsedIcon', 'Icon to annotate a collapsed section in notebook editors.')); export const expandedIcon = registerIcon('notebook-expanded', Codicon.chevronDown, localize('expandedIcon', 'Icon to annotate an expanded section in notebook editors.')); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookKernelServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookKernelServiceImpl.ts index 16927775ebc..00db1d4fde0 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookKernelServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookKernelServiceImpl.ts @@ -15,16 +15,40 @@ import { runWhenIdle } from 'vs/base/common/async'; import { ILogService } from 'vs/platform/log/common/log'; import { isEqual } from 'vs/base/common/resources'; -interface IKernelInfo { - kernel: INotebookKernel; - score: number; +class KernelInfo { + + private static _logicClock = 0; + + readonly kernel: INotebookKernel; + public score: number; + readonly time: number; + + constructor(kernel: INotebookKernel) { + this.kernel = kernel; + this.score = -1; + this.time = KernelInfo._logicClock++; + } } class ScoreInfo { - constructor(private readonly _anchor: INotebookTextModelLike) { } - equals(candidate: INotebookTextModelLike): boolean { - return this._anchor.viewType === candidate.viewType && isEqual(this._anchor.uri, candidate.uri); + private _anchor?: INotebookTextModelLike; + + constructor() { } + + matches(candidate: INotebookTextModelLike): boolean { + if (!this._anchor) { + return false; + } + if (this._anchor.viewType === candidate.viewType && isEqual(this._anchor.uri, candidate.uri)) { + return true; + } + this._anchor = candidate; + return false; + } + + reset(): void { + this._anchor = undefined; } } @@ -34,9 +58,9 @@ export class NotebookKernelService implements INotebookKernelService { private static _storageKey = 'notebook.kernelBindings'; - private readonly _kernels = new Map(); + private readonly _kernels = new Map(); private readonly _kernelBindings = new LRUCache(1000, 0.7); - private _scoreInfo?: ScoreInfo; + private readonly _scoreInfo = new ScoreInfo(); private readonly _onDidChangeNotebookKernelBinding = new Emitter(); private readonly _onDidAddKernel = new Emitter(); @@ -79,12 +103,12 @@ export class NotebookKernelService implements INotebookKernelService { throw new Error(`NOTEBOOK CONTROLLER with id '${kernel.id}' already exists`); } - this._scoreInfo = undefined; - this._kernels.set(kernel.id, { kernel, score: -1 }); + this._kernels.set(kernel.id, new KernelInfo(kernel)); + this._scoreInfo.reset(); this._onDidAddKernel.fire(kernel); return toDisposable(() => { - this._scoreInfo = undefined; + this._scoreInfo.reset(); if (this._kernels.delete(kernel.id)) { this._onDidRemoveKernel.fire(kernel); } @@ -100,11 +124,10 @@ export class NotebookKernelService implements INotebookKernelService { getNotebookKernels(notebook: INotebookTextModelLike): { bound: INotebookKernel | undefined, all: INotebookKernel[] } { // update score if needed - if (!this._scoreInfo?.equals(notebook)) { - for (let item of this._kernels.values()) { + if (!this._scoreInfo.matches(notebook)) { + for (const item of this._kernels.values()) { item.score = score(item.kernel.selector, notebook.uri, notebook.viewType); } - this._scoreInfo = new ScoreInfo(notebook); } // all applicable kernels @@ -123,8 +146,8 @@ export class NotebookKernelService implements INotebookKernelService { if (b.score !== a.score) { return b.score - a.score; } - // (3) sort by name - return a.kernel.label.localeCompare(b.kernel.label); + // (3) sort by time + return a.time - b.time; }) .map(item => item.kernel); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index 30591c7aabd..042291190ce 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -29,7 +29,7 @@ import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, BUILTIN_RENDERER_ID, CellUri, Displa import { NotebookMarkdownRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookMarkdownRenderer'; import { NotebookOutputRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookOutputRenderer'; import { NotebookEditorDescriptor, NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider'; -import { ComplexNotebookProviderInfo, IMainNotebookController, INotebookSerializer, INotebookService, SimpleNotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookService'; +import { ComplexNotebookProviderInfo, INotebookContentProvider, INotebookSerializer, INotebookService, SimpleNotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -304,10 +304,6 @@ export class NotebookService extends Disposable implements INotebookService, IEd private readonly _onDidChangeEditorTypes = this._register(new Emitter()); onDidChangeEditorTypes: Event = this._onDidChangeEditorTypes.event; - private readonly _onDidChangeKernels = this._register(new Emitter()); - onDidChangeKernels: Event = this._onDidChangeKernels.event; - private readonly _onDidChangeNotebookActiveKernel = this._register(new Emitter<{ uri: URI, providerHandle: number | undefined, kernelFriendlyId: string | undefined; }>()); - onDidChangeNotebookActiveKernel: Event<{ uri: URI, providerHandle: number | undefined, kernelFriendlyId: string | undefined; }> = this._onDidChangeNotebookActiveKernel.event; private _cutItems: NotebookCellTextModel[] | undefined; private _lastClipboardIsCopy: boolean = true; @@ -479,7 +475,7 @@ export class NotebookService extends Disposable implements INotebookService, IEd this._notebookProviders.set(viewType, data); } - registerNotebookController(viewType: string, extensionData: NotebookExtensionDescription, controller: IMainNotebookController): IDisposable { + registerNotebookController(viewType: string, extensionData: NotebookExtensionDescription, controller: INotebookContentProvider): IDisposable { this._registerProviderData(viewType, new ComplexNotebookProviderInfo(viewType, controller, extensionData)); if (controller.viewOptions && !this._notebookProviderInfoStore.get(viewType)) { // register this content provider to the static contribution, if it does not exist diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts index 1728a431531..9fb913d82a6 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts @@ -21,7 +21,8 @@ import { IListService, IWorkbenchListOptions, WorkbenchList } from 'vs/platform/ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { CellRevealPosition, CellRevealType, CursorAtBoundary, getVisibleCells, ICellViewModel, INotebookCellList, reduceCellRanges, CellEditState, CellFocusMode, BaseCellRenderTemplate, NOTEBOOK_CELL_LIST_FOCUSED, cellRangesEqual, ICellOutputViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellViewModel, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; -import { diff, NOTEBOOK_EDITOR_CURSOR_BOUNDARY, CellKind, ICellRange, cellRangesToIndexes, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { diff, NOTEBOOK_EDITOR_CURSOR_BOUNDARY, CellKind, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ICellRange, cellRangesToIndexes } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { clamp } from 'vs/base/common/numbers'; import { SCROLLABLE_ELEMENT_PADDING_TOP } from 'vs/workbench/contrib/notebook/browser/constants'; import { ISplice } from 'vs/base/common/sequence'; 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 ad5f137f8fd..e731c144646 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -413,7 +413,7 @@ export class BackLayerWebView extends Disposable { outputNodeLeftPadding: number, previewNodePadding: number, leftMargin: number, - cellMargin: number, + rightMargin: number, runGutter: number, }, @IWebviewService readonly webviewService: IWebviewService, @@ -436,7 +436,7 @@ export class BackLayerWebView extends Disposable { } private generateContent(coreDependencies: string, baseUrl: string) { const markdownRenderersSrc = this.getMarkdownRendererScripts(); - const outputWidth = `calc(100% - ${this.options.leftMargin + (this.options.cellMargin * 2) + this.options.runGutter}px)`; + const outputWidth = `calc(100% - ${this.options.leftMargin + this.options.rightMargin + this.options.runGutter}px)`; const outputMarginLeft = `${this.options.leftMargin + this.options.runGutter}px`; return html` diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys.ts index c4544e3acb1..6ec359bcd53 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { INotebookTextModel, NotebookCellExecutionState } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { NotebookCellExecutionState } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NOTEBOOK_CELL_TYPE, NOTEBOOK_VIEW_TYPE, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_EXECUTION_STATE, NOTEBOOK_CELL_HAS_OUTPUTS, CellViewModelStateChangeEvent, CellEditState, NOTEBOOK_CELL_INPUT_COLLAPSED, NOTEBOOK_CELL_OUTPUT_COLLAPSED, NOTEBOOK_CELL_FOCUSED, INotebookEditor, NOTEBOOK_CELL_EDITOR_FOCUSED, CellFocusMode, NotebookCellExecutionStateContext, NOTEBOOK_CELL_LINE_NUMBERS } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel'; @@ -30,7 +30,6 @@ export class CellContextKeyManager extends Disposable { constructor( private readonly contextKeyService: IContextKeyService, private readonly notebookEditor: INotebookEditor, - private readonly notebookTextModel: INotebookTextModel, private element: CodeCellViewModel | MarkdownCellViewModel ) { super(); @@ -119,8 +118,8 @@ export class CellContextKeyManager extends Disposable { } private updateForMetadata() { - const metadata = this.element.getEvaluatedMetadata(this.notebookTextModel.metadata); - this.cellEditable.set(!!metadata.editable); + const metadata = this.element.metadata; + this.cellEditable.set(!this.notebookEditor.viewModel?.options.isReadOnly); const runState = metadata.runState ?? NotebookCellExecutionState.Idle; if (runState === NotebookCellExecutionState.Idle) { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellDnd.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellDnd.ts index f996743ab6c..64265f4477a 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellDnd.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellDnd.ts @@ -12,7 +12,8 @@ import * as platform from 'vs/base/common/platform'; import { BOTTOM_CELL_TOOLBAR_GAP } from 'vs/workbench/contrib/notebook/browser/constants'; import { BaseCellRenderTemplate, expandCellRangesWithHiddenCells, ICellViewModel, INotebookCellList, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { cloneNotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; -import { CellEditType, cellRangesToIndexes, ICellRange, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellEditType, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { cellRangesToIndexes, ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; const $ = DOM.$; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index fa99276fbe3..07dc8196f77 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -14,7 +14,7 @@ import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import { IAction } from 'vs/base/common/actions'; import { Color } from 'vs/base/common/color'; -import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { combinedDisposable, Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; @@ -215,8 +215,10 @@ abstract class AbstractCellRenderer { updateActions(); })); + templateData.container.classList.toggle('cell-toolbar-dropdown-active', false); disposables.add(templateData.toolbar.onDidChangeDropdownVisibility(visible => { dropdownIsVisible = visible; + templateData.container.classList.toggle('cell-toolbar-dropdown-active', visible); if (deferredUpdate && !visible) { setTimeout(() => { @@ -343,8 +345,6 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR const focusIndicatorBottom = DOM.append(container, $('.cell-focus-indicator.cell-focus-indicator-bottom')); const statusBar = disposables.add(this.instantiationService.createInstance(CellEditorStatusBar, editorPart)); - DOM.hide(statusBar.durationContainer); - DOM.hide(statusBar.cellRunStatusContainer); const titleMenu = disposables.add(this.cellMenus.getCellTitleMenu(contextKeyService)); @@ -458,7 +458,7 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR } })); - elementDisposables.add(new CellContextKeyManager(templateData.contextKeyService, this.notebookEditor, this.notebookEditor.viewModel.notebookDocument!, element)); + elementDisposables.add(new CellContextKeyManager(templateData.contextKeyService, this.notebookEditor, element)); this.updateForLayout(element, templateData); elementDisposables.add(element.onDidChangeLayout(() => { @@ -717,8 +717,6 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende disposables.add(progressBar); const statusBar = disposables.add(this.instantiationService.createInstance(CellEditorStatusBar, editorPart)); - const timer = new TimerRenderer(statusBar.durationContainer); - const cellRunState = new RunStateRenderer(statusBar.cellRunStatusContainer); const outputContainer = DOM.append(container, $('.output')); const outputShowMoreContainer = DOM.append(container, $('.output-show-more-container')); @@ -742,7 +740,6 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende container, decorationContainer, cellContainer, - cellRunState, progressBar, statusBar, focusIndicatorLeft: focusIndicator, @@ -761,7 +758,6 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende disposables, elementDisposables: new DisposableStore(), bottomCellContainer, - timer, titleMenu, dragHandle, toJSON: () => { return {}; } @@ -782,7 +778,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende } private addDoubleClickCollapseHandler(templateData: CodeCellRenderTemplate): IDisposable { - return DOM.addDisposableListener(templateData.dragHandle, DOM.EventType.DBLCLICK, e => { + const dragHandleListener = DOM.addDisposableListener(templateData.dragHandle, DOM.EventType.DBLCLICK, e => { const cell = templateData.currentRenderedCell; if (!cell) { return; @@ -801,6 +797,27 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende } ], true, undefined, () => undefined, undefined); }); + + const collapsedPartListener = DOM.addDisposableListener(templateData.collapsedPart, DOM.EventType.DBLCLICK, e => { + const cell = templateData.currentRenderedCell; + if (!cell) { + return; + } + + const metadata: Partial = cell.metadata?.inputCollapsed ? + { inputCollapsed: false } : + { outputCollapsed: false }; + const viewModel = this.notebookEditor.viewModel!; + viewModel.notebookDocument.applyEdits([ + { + editType: CellEditType.PartialMetadata, + index: viewModel.getCellIndex(cell), + metadata + } + ], true, undefined, () => undefined, undefined); + }); + + return combinedDisposable(dragHandleListener, collapsedPartListener); } private setupRunToolbar(runButtonContainer: HTMLElement, contextKeyService: IContextKeyService, disposables: DisposableStore): ToolBar { @@ -830,33 +847,9 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende return; } - const metadata = element.getEvaluatedMetadata(this.notebookEditor.viewModel.notebookDocument.metadata); + const metadata = element.metadata; this.updateExecutionOrder(metadata, templateData); - templateData.cellRunState.renderState(element.metadata?.runState, () => { - if (!this.notebookEditor.viewModel) { - return -1; - } - - return this.notebookEditor.viewModel.getCellIndex(element); - }, element.metadata?.lastRunSuccess); - - if (metadata.runState === NotebookCellExecutionState.Executing) { - if (metadata.runStartTime) { - templateData.elementDisposables.add(templateData.timer.start(metadata.runStartTime, metadata.runStartTimeAdjustment ?? 0)); - } else { - templateData.timer.clear(); - } - } else if (metadata.runState !== NotebookCellExecutionState.Pending && metadata.runStartTime && metadata.runEndTime) { - templateData.timer.show(metadata.runEndTime - metadata.runStartTime); - } else { - templateData.timer.clear(); - } - - if (typeof metadata.breakpointMargin === 'boolean') { - editorOptions.setGlyphMargin(metadata.breakpointMargin); - } - if (metadata.runState === NotebookCellExecutionState.Executing) { templateData.progressBar.infinite().show(500); } else { @@ -948,14 +941,13 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende elementDisposables.add(cellEditorOptions.onDidChange(newValue => templateData.editor.updateOptions(newValue))); templateData.editor.updateOptions(cellEditorOptions.value); - elementDisposables.add(new CellContextKeyManager(templateData.contextKeyService, this.notebookEditor, this.notebookEditor.viewModel.notebookDocument!, element)); + elementDisposables.add(new CellContextKeyManager(templateData.contextKeyService, this.notebookEditor, element)); this.updateForLayout(element, templateData); elementDisposables.add(element.onDidChangeLayout(() => { this.updateForLayout(element, templateData); })); - templateData.cellRunState.clear(); this.updateForMetadata(element, templateData, cellEditorOptions); this.updateForHover(element, templateData); this.updateForFocus(element, templateData); diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets.ts index a065a735404..6fe9bd5debe 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets.ts @@ -5,6 +5,7 @@ import * as DOM from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { renderIcon } from 'vs/base/browser/ui/iconLabel/iconLabels'; import { SimpleIconLabel } from 'vs/base/browser/ui/iconLabel/simpleIconLabel'; import { WorkbenchActionExecutedClassification, WorkbenchActionExecutedEvent } from 'vs/base/common/actions'; import { toErrorMessage } from 'vs/base/common/errorMessage'; @@ -13,12 +14,14 @@ import { stripIcons } from 'vs/base/common/iconLabels'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { ElementSizeObserver } from 'vs/editor/browser/config/elementSizeObserver'; -import { IDimension } from 'vs/editor/common/editorCommon'; +import { IDimension, isThemeColor } from 'vs/editor/common/editorCommon'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IThemeService, ThemeColor } from 'vs/platform/theme/common/themeService'; import { INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions'; +import { CodeCellLayoutInfo, MarkdownCellLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellStatusbarAlignment, INotebookCellStatusBarItem } from 'vs/workbench/contrib/notebook/common/notebookCommon'; const $ = DOM.$; @@ -30,15 +33,12 @@ export interface IClickTarget { export const enum ClickTargetType { Container = 0, - CellStatus = 1, - ContributedTextItem = 2, - ContributedCommandItem = 3 + ContributedTextItem = 1, + ContributedCommandItem = 2 } export class CellEditorStatusBar extends Disposable { - readonly cellRunStatusContainer: HTMLElement; readonly statusBarContainer: HTMLElement; - readonly durationContainer: HTMLElement; private readonly leftContributedItemsContainer: HTMLElement; private readonly rightContributedItemsContainer: HTMLElement; @@ -53,20 +53,21 @@ export class CellEditorStatusBar extends Disposable { constructor( container: HTMLElement, - @IInstantiationService private readonly instantiationService: IInstantiationService, + @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IThemeService private readonly _themeService: IThemeService, ) { super(); this.statusBarContainer = DOM.append(container, $('.cell-statusbar-container')); this.statusBarContainer.tabIndex = -1; const leftItemsContainer = DOM.append(this.statusBarContainer, $('.cell-status-left')); const rightItemsContainer = DOM.append(this.statusBarContainer, $('.cell-status-right')); - this.cellRunStatusContainer = DOM.append(leftItemsContainer, $('.cell-run-status')); - this.durationContainer = DOM.append(leftItemsContainer, $('.cell-run-duration')); this.leftContributedItemsContainer = DOM.append(leftItemsContainer, $('.cell-contributed-items.cell-contributed-items-left')); this.rightContributedItemsContainer = DOM.append(rightItemsContainer, $('.cell-contributed-items.cell-contributed-items-right')); this.itemsDisposable = this._register(new DisposableStore()); + this._register(this._themeService.onDidColorThemeChange(() => this.currentContext && this.update(this.currentContext))); + this._register(DOM.addDisposableListener(this.statusBarContainer, DOM.EventType.CLICK, e => { if (e.target === leftItemsContainer || e.target === rightItemsContainer || e.target === this.statusBarContainer) { // hit on empty space @@ -74,14 +75,6 @@ export class CellEditorStatusBar extends Disposable { type: ClickTargetType.Container, event: e }); - } else if (e.target && ( - this.cellRunStatusContainer.contains(e.target as Node) - || this.durationContainer.contains(e.target as Node) - )) { - this._onDidClick.fire({ - type: ClickTargetType.CellStatus, - event: e - }); } else { if ((e.target as HTMLElement).classList.contains('cell-status-item-has-command')) { this._onDidClick.fire({ @@ -99,13 +92,18 @@ export class CellEditorStatusBar extends Disposable { })); } - update(context: INotebookCellActionContext) { - this.currentContext = context; - this.itemsDisposable.clear(); - this.updateStatusBarItems(); - } + private layout(): void { + if (!this.currentContext) { + return; + } + + // TODO@roblou maybe more props should be in common layoutInfo? + const layoutInfo = this.currentContext.cell.layoutInfo as CodeCellLayoutInfo | MarkdownCellLayoutInfo; + const width = layoutInfo.editorWidth; + if (!width) { + return; + } - layout(width: number): void { this.width = width; this.statusBarContainer.style.width = `${width}px`; @@ -117,13 +115,17 @@ export class CellEditorStatusBar extends Disposable { return this.width / 2; } - private async updateStatusBarItems() { + update(context: INotebookCellActionContext) { + this.currentContext = context; + this.itemsDisposable.clear(); + if (!this.currentContext) { return; } this.itemsDisposable.add(this.currentContext.notebookEditor.onDidChangeActiveCell(() => this.updateActiveCell())); this.itemsDisposable.add(this.currentContext.cell.onDidChangeCellStatusBarItems(() => this.updateRenderedItems())); + this.itemsDisposable.add(this.currentContext.cell.onDidChangeLayout(e => this.layout())); this.updateActiveCell(); this.updateRenderedItems(); } @@ -144,9 +146,9 @@ export class CellEditorStatusBar extends Disposable { const maxItemWidth = this.getMaxItemWidth(); const leftItems = items.filter(item => item.alignment === CellStatusbarAlignment.Left) - .map(item => this.itemsDisposable.add(this.instantiationService.createInstance(CellStatusBarItem, this.currentContext!, item, maxItemWidth))); + .map(item => this.itemsDisposable.add(this._instantiationService.createInstance(CellStatusBarItem, this.currentContext!, item, maxItemWidth))); const rightItems = items.filter(item => item.alignment === CellStatusbarAlignment.Right).reverse() - .map(item => this.itemsDisposable.add(this.instantiationService.createInstance(CellStatusBarItem, this.currentContext!, item, maxItemWidth))); + .map(item => this.itemsDisposable.add(this._instantiationService.createInstance(CellStatusBarItem, this.currentContext!, item, maxItemWidth))); leftItems.forEach(itemView => this.leftContributedItemsContainer.appendChild(itemView.container)); rightItems.forEach(itemView => this.rightContributedItemsContainer.appendChild(itemView.container)); this.items = [...leftItems, ...rightItems]; @@ -165,12 +167,43 @@ class CellStatusBarItem extends Disposable { private readonly _context: INotebookCellActionContext, private readonly _itemModel: INotebookCellStatusBarItem, maxWidth: number | undefined, - @ITelemetryService private readonly telemetryService: ITelemetryService, - @ICommandService private readonly commandService: ICommandService, - @INotificationService private readonly notificationService: INotificationService + @ITelemetryService private readonly _telemetryService: ITelemetryService, + @ICommandService private readonly _commandService: ICommandService, + @INotificationService private readonly _notificationService: INotificationService, + @IThemeService private readonly _themeService: IThemeService, ) { super(); - new SimpleIconLabel(this.container).text = this._itemModel.text.replace(/\n/g, ' '); + + const resolveColor = (color: ThemeColor | string) => { + return isThemeColor(color) ? + this._themeService.getColorTheme().getColor(color.id)?.toString() : + color; + }; + + if (this._itemModel.icon) { + const iconContainer = renderIcon(this._itemModel.icon); + if (this._itemModel.iconColor) { + const colorResult = resolveColor(this._itemModel.iconColor); + iconContainer.style.color = colorResult || ''; + } + + this.container.appendChild(iconContainer); + } + + if (this._itemModel.text) { + const textContainer = $('span', undefined); + new SimpleIconLabel(textContainer).text = this._itemModel.text.replace(/\n/g, ' '); + this.container.appendChild(textContainer); + } + + if (this._itemModel.color) { + this.container.style.color = resolveColor(this._itemModel.color) || ''; + } + + if (this._itemModel.backgroundColor) { + const colorResult = resolveColor(this._itemModel.backgroundColor); + this.container.style.backgroundColor = colorResult || ''; + } if (this._itemModel.opacity) { this.container.style.opacity = this._itemModel.opacity; @@ -230,11 +263,11 @@ class CellStatusBarItem extends Disposable { args.unshift(this._context); - this.telemetryService.publicLog2('workbenchActionExecuted', { id, from: 'cell status bar' }); + this._telemetryService.publicLog2('workbenchActionExecuted', { id, from: 'cell status bar' }); try { - await this.commandService.executeCommand(id, ...args); + await this._commandService.executeCommand(id, ...args); } catch (error) { - this.notificationService.error(toErrorMessage(error)); + this._notificationService.error(toErrorMessage(error)); } } } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts index 3751cac638c..39d075c91ad 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts @@ -90,10 +90,10 @@ export class CodeCell extends Disposable { })); updateForFocusMode(); - templateData.editor?.updateOptions({ readOnly: !(viewCell.getEvaluatedMetadata(notebookEditor.viewModel.metadata).editable) }); + templateData.editor?.updateOptions({ readOnly: notebookEditor.viewModel.options.isReadOnly }); this._register(viewCell.onDidChangeState((e) => { if (e.metadataChanged) { - templateData.editor?.updateOptions({ readOnly: !(viewCell.getEvaluatedMetadata(notebookEditor.viewModel.metadata).editable) }); + templateData.editor?.updateOptions({ readOnly: notebookEditor.viewModel.options.isReadOnly }); if (this.updateForCollapseState()) { this.relayoutCell(); @@ -293,7 +293,6 @@ export class CodeCell extends Disposable { private layoutEditor(dimension: IDimension): void { this.templateData.editor?.layout(dimension); - this.templateData.statusBar.layout(dimension.width); } private onCellWidthChange(): void { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts index 08ae528666b..fb7db714bf8 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts @@ -375,7 +375,6 @@ export class StatefulMarkdownCell extends Disposable { private layoutEditor(dimension: DOM.IDimension): void { this.editor?.layout(dimension); - this.templateData.statusBar.layout(dimension.width); } private onCellEditorWidthChange(): void { diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts index 8626b22daa4..ca7cab2e11d 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts @@ -14,7 +14,7 @@ import * as model from 'vs/editor/common/model'; import { SearchParams } from 'vs/editor/common/model/textModelSearch'; import { CELL_STATUSBAR_HEIGHT } from 'vs/workbench/contrib/notebook/browser/constants'; import { CellEditState, CellFocusMode, CursorAtBoundary, CellViewModelStateChangeEvent, IEditableCellViewModel, INotebookCellDecorationOptions, getEditorTopPadding } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { CellKind, NotebookCellMetadata, NotebookDocumentMetadata, INotebookSearchOptions, ShowCellStatusBarKey, INotebookCellStatusBarItem } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, INotebookSearchOptions, ShowCellStatusBarKey, INotebookCellStatusBarItem } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IResolvedTextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService'; @@ -481,17 +481,6 @@ export abstract class BaseCellViewModel extends Disposable { return cellMatches; } - getEvaluatedMetadata(documentMetadata: NotebookDocumentMetadata): NotebookCellMetadata { - const editable = this.metadata?.editable ?? true; - - return { - ...(this.metadata || {}), - ...{ - editable, - } - }; - } - override dispose() { super.dispose(); diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/cellSelectionCollection.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/cellSelectionCollection.ts index 587cd6ecce4..ba3c117629b 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/cellSelectionCollection.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/cellSelectionCollection.ts @@ -5,7 +5,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; -import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; function rangesEqual(a: ICellRange[], b: ICellRange[]) { if (a.length !== b.length) { diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts index 282a1d8cde6..962949e920d 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts @@ -9,7 +9,7 @@ import * as editorCommon from 'vs/editor/common/editorCommon'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { BOTTOM_CELL_TOOLBAR_GAP, BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_BOTTOM_MARGIN, CELL_MARGIN, CELL_RUN_GUTTER, CELL_TOP_MARGIN, CODE_CELL_LEFT_MARGIN, COLLAPSED_INDICATOR_HEIGHT, EDITOR_BOTTOM_PADDING, EDITOR_TOOLBAR_HEIGHT } from 'vs/workbench/contrib/notebook/browser/constants'; +import { BOTTOM_CELL_TOOLBAR_GAP, BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_BOTTOM_MARGIN, CELL_RIGHT_MARGIN, CELL_RUN_GUTTER, CELL_TOP_MARGIN, CODE_CELL_LEFT_MARGIN, COLLAPSED_INDICATOR_HEIGHT, EDITOR_BOTTOM_PADDING, EDITOR_TOOLBAR_HEIGHT } from 'vs/workbench/contrib/notebook/browser/constants'; import { CellEditState, CellFindMatch, CodeCellLayoutChangeEvent, CodeCellLayoutInfo, CodeCellLayoutState, getEditorTopPadding, ICellOutputViewModel, ICellViewModel, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellOutputViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/cellOutputViewModel'; import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; @@ -131,7 +131,7 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod } private computeEditorWidth(outerWidth: number): number { - return outerWidth - (CODE_CELL_LEFT_MARGIN + (CELL_MARGIN * 2) + CELL_RUN_GUTTER); + return outerWidth - (CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER + CELL_RIGHT_MARGIN); } layoutChange(state: CodeCellLayoutChangeEvent, source?: string) { diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts index 923dc916cb0..f6456858744 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts @@ -8,7 +8,7 @@ import * as UUID from 'vs/base/common/uuid'; import * as editorCommon from 'vs/editor/common/editorCommon'; import * as nls from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { BOTTOM_CELL_TOOLBAR_GAP, BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_MARGIN, CODE_CELL_LEFT_MARGIN, COLLAPSED_INDICATOR_HEIGHT, MARKDOWN_CELL_BOTTOM_MARGIN, MARKDOWN_CELL_TOP_MARGIN } from 'vs/workbench/contrib/notebook/browser/constants'; +import { BOTTOM_CELL_TOOLBAR_GAP, BOTTOM_CELL_TOOLBAR_HEIGHT, CODE_CELL_LEFT_MARGIN, COLLAPSED_INDICATOR_HEIGHT, MARKDOWN_CELL_BOTTOM_MARGIN, MARKDOWN_CELL_TOP_MARGIN, CELL_RIGHT_MARGIN } from 'vs/workbench/contrib/notebook/browser/constants'; import { EditorFoldingStateDelegate } from 'vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel'; import { CellEditState, CellFindMatch, ICellOutputViewModel, ICellViewModel, MarkdownCellLayoutChangeEvent, MarkdownCellLayoutInfo, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { MarkdownRenderer } from 'vs/editor/browser/core/markdownRenderer'; @@ -136,7 +136,7 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie } private computeEditorWidth(outerWidth: number) { - return outerWidth - (CELL_MARGIN * 2) - CODE_CELL_LEFT_MARGIN; + return outerWidth - CODE_CELL_LEFT_MARGIN - CELL_RIGHT_MARGIN; } layoutChange(state: MarkdownCellLayoutChangeEvent) { diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts index c3f1ca673e3..19499bb5c61 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts @@ -24,7 +24,8 @@ import { NotebookEventDispatcher, NotebookMetadataChangedEvent } from 'vs/workbe import { CellFoldingState, EditorFoldingStateDelegate } from 'vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel'; import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; -import { CellKind, NotebookCellMetadata, INotebookSearchOptions, ICellRange, NotebookCellsChangeType, ICell, NotebookCellTextModelSplice, CellEditType, IOutputDto, SelectionStateType, ISelectionState, cellIndexesToRanges, cellRangesToIndexes, reduceRanges } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, NotebookCellMetadata, INotebookSearchOptions, NotebookCellsChangeType, ICell, NotebookCellTextModelSplice, CellEditType, IOutputDto, SelectionStateType, ISelectionState } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ICellRange, cellIndexesToRanges, cellRangesToIndexes, reduceRanges } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { FoldingRegions } from 'vs/editor/contrib/folding/foldingRanges'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { MarkdownRenderer } from 'vs/editor/browser/core/markdownRenderer'; @@ -918,10 +919,6 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD return null; } - if (!cell.getEvaluatedMetadata(this.notebookDocument.metadata).editable) { - return null; - } - const splitPoints = cell.focusMode === CellFocusMode.Container ? [{ lineNumber: 1, column: 1 }] : cell.getSelectionsStartPosition(); if (splitPoints && splitPoints.length > 0) { diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts index 6187b7d634f..8eb4a90f2c5 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts @@ -220,10 +220,7 @@ export class NotebookCellTextModel extends Disposable implements ICell { export function cloneMetadata(cell: NotebookCellTextModel) { return { - editable: cell.metadata?.editable, - inputCollapsed: cell.metadata?.inputCollapsed, - outputCollapsed: cell.metadata?.outputCollapsed, - custom: cell.metadata?.custom + ...cell.metadata }; } diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 5675dbff036..eca6480e5ef 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -20,9 +20,10 @@ import { IEditorModel } from 'vs/platform/editor/common/editor'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IEditorInput, IRevertOptions, ISaveOptions } from 'vs/workbench/common/editor'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { ThemeColor } from 'vs/platform/theme/common/themeService'; +import { ThemeColor, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IWorkingCopyBackupMeta } from 'vs/workbench/services/workingCopy/common/workingCopyBackup'; import { NotebookSelector } from 'vs/workbench/contrib/notebook/common/notebookSelector'; +import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; export enum CellKind { Markdown = 1, @@ -66,6 +67,7 @@ export const notebookDocumentMetadataDefaults: Required a - b); - const first = indexes.shift(); - - if (first === undefined) { - return []; - } - - return indexes.reduce(function (ranges, num) { - if (num <= ranges[0][1]) { - ranges[0][1] = num + 1; - } else { - ranges.unshift([num, num + 1]); - } - return ranges; - }, [[first, first + 1]]).reverse().map(val => ({ start: val[0], end: val[1] })); -} - -export function cellRangesToIndexes(ranges: ICellRange[]) { - const indexes = ranges.reduce((a, b) => { - for (let i = b.start; i < b.end; i++) { - a.push(i); - } - - return a; - }, [] as number[]); - - return indexes; -} - -/** - * todo@rebornix notebookBrowser.reduceCellRanges - * @returns - */ -export function reduceRanges(ranges: ICellRange[]) { - const sorted = ranges.sort((a, b) => a.start - b.start); - const first = sorted[0]; - - if (!first) { - return []; - } - - return sorted.reduce((prev: ICellRange[], curr) => { - const last = prev[prev.length - 1]; - if (last.end >= curr.start) { - last.end = Math.max(last.end, curr.end); - } else { - prev.push(curr); - } - return prev; - }, [first] as ICellRange[]); -} - -/** - * todo@rebornix test and sort - * @param range - * @param other - * @returns - */ -export function cellRangeContains(range: ICellRange, other: ICellRange): boolean { - return other.start >= range.start && other.end <= range.end; -} diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts index b5e50f65ffa..246139071fa 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts @@ -8,7 +8,7 @@ import { EditorModel, IEditorInput, IRevertOptions, ISaveOptions } from 'vs/work import { Emitter, Event } from 'vs/base/common/event'; import { INotebookEditorModel, INotebookLoadOptions, IResolvedNotebookEditorModel, NotebookCellsChangeType, NotebookDataDto, NotebookDocumentBackupData } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { IMainNotebookController, INotebookSerializer, INotebookService, SimpleNotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookService'; +import { INotebookContentProvider, INotebookSerializer, INotebookService, SimpleNotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookService'; import { URI } from 'vs/base/common/uri'; import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { IWorkingCopy, IWorkingCopyBackup, WorkingCopyCapabilities, NO_TYPE_ID, IWorkingCopyIdentifier } from 'vs/workbench/services/workingCopy/common/workingCopy'; @@ -52,7 +52,7 @@ export class ComplexNotebookEditorModel extends EditorModel implements INotebook constructor( readonly resource: URI, readonly viewType: string, - private readonly _contentProvider: IMainNotebookController, + private readonly _contentProvider: INotebookContentProvider, @IInstantiationService private readonly _instantiationService: IInstantiationService, @INotebookService private readonly _notebookService: INotebookService, @IWorkingCopyService private readonly _workingCopyService: IWorkingCopyService, diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorModelResolverServiceImpl.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorModelResolverServiceImpl.ts index 2275e5eb25b..6ad8c0cfcd5 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorModelResolverServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorModelResolverServiceImpl.ts @@ -60,11 +60,12 @@ class NotebookModelReferenceCollection extends ReferenceCollection>this._instantiationService.createInstance( FileWorkingCopyManager, - viewType, + workingCopyTypeId, new NotebookFileWorkingCopyModelFactory(this._notebookService) ); } diff --git a/src/vs/workbench/contrib/notebook/common/notebookRange.ts b/src/vs/workbench/contrib/notebook/common/notebookRange.ts new file mode 100644 index 00000000000..2dbc890040c --- /dev/null +++ b/src/vs/workbench/contrib/notebook/common/notebookRange.ts @@ -0,0 +1,83 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/** + * [start, end] + */ +export interface ICellRange { + /** + * zero based index + */ + start: number; + + /** + * zero based index + */ + end: number; +} + + +export function cellIndexesToRanges(indexes: number[]) { + indexes.sort((a, b) => a - b); + const first = indexes.shift(); + + if (first === undefined) { + return []; + } + + return indexes.reduce(function (ranges, num) { + if (num <= ranges[0][1]) { + ranges[0][1] = num + 1; + } else { + ranges.unshift([num, num + 1]); + } + return ranges; + }, [[first, first + 1]]).reverse().map(val => ({ start: val[0], end: val[1] })); +} + +export function cellRangesToIndexes(ranges: ICellRange[]) { + const indexes = ranges.reduce((a, b) => { + for (let i = b.start; i < b.end; i++) { + a.push(i); + } + + return a; + }, [] as number[]); + + return indexes; +} +/** + * todo@rebornix notebookBrowser.reduceCellRanges + * @returns + */ + +export function reduceRanges(ranges: ICellRange[]) { + const sorted = ranges.sort((a, b) => a.start - b.start); + const first = sorted[0]; + + if (!first) { + return []; + } + + return sorted.reduce((prev: ICellRange[], curr) => { + const last = prev[prev.length - 1]; + if (last.end >= curr.start) { + last.end = Math.max(last.end, curr.end); + } else { + prev.push(curr); + } + return prev; + }, [first] as ICellRange[]); +} +/** + * todo@rebornix test and sort + * @param range + * @param other + * @returns + */ + +export function cellRangeContains(range: ICellRange, other: ICellRange): boolean { + return other.start >= range.start && other.end <= range.end; +} diff --git a/src/vs/workbench/contrib/notebook/common/notebookService.ts b/src/vs/workbench/contrib/notebook/common/notebookService.ts index 0f677256cd5..f9ea0a33b9f 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookService.ts @@ -19,7 +19,7 @@ import { VSBuffer } from 'vs/base/common/buffer'; export const INotebookService = createDecorator('notebookService'); -export interface IMainNotebookController { +export interface INotebookContentProvider { viewOptions?: { displayName: string; filenamePattern: (string | IRelativePattern | INotebookExclusiveDocumentFilter)[]; exclusive: boolean; }; options: TransientOptions; @@ -43,7 +43,7 @@ export interface INotebookRawData { export class ComplexNotebookProviderInfo { constructor( readonly viewType: string, - readonly controller: IMainNotebookController, + readonly controller: INotebookContentProvider, readonly extensionData: NotebookExtensionDescription ) { } } @@ -63,10 +63,7 @@ export interface INotebookService { onDidRemoveNotebookDocument: Event; onDidAddNotebookDocument: Event; - onDidChangeKernels: Event; - onDidChangeNotebookActiveKernel: Event<{ uri: URI, providerHandle: number | undefined, kernelFriendlyId: string | undefined; }>; - - registerNotebookController(viewType: string, extensionData: NotebookExtensionDescription, controller: IMainNotebookController): IDisposable; + registerNotebookController(viewType: string, extensionData: NotebookExtensionDescription, controller: INotebookContentProvider): IDisposable; registerNotebookSerializer(viewType: string, extensionData: NotebookExtensionDescription, serializer: INotebookSerializer): IDisposable; withNotebookDataProvider(resource: URI, viewType?: string): Promise; diff --git a/src/vs/workbench/contrib/notebook/test/notebookCommon.test.ts b/src/vs/workbench/contrib/notebook/test/notebookCommon.test.ts index f2619f20785..f431f6cd4dd 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookCommon.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookCommon.test.ts @@ -4,7 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { NOTEBOOK_DISPLAY_ORDER, sortMimeTypes, CellKind, diff, CellUri, cellRangesToIndexes, cellIndexesToRanges } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { NOTEBOOK_DISPLAY_ORDER, sortMimeTypes, CellKind, diff, CellUri } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { cellRangesToIndexes, cellIndexesToRanges } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { TestCell, setupInstantiationService } from 'vs/workbench/contrib/notebook/test/testNotebookEditor'; import { URI } from 'vs/base/common/uri'; import { IModeService } from 'vs/editor/common/services/modeService'; diff --git a/src/vs/workbench/contrib/notebook/test/notebookEditorModel.test.ts b/src/vs/workbench/contrib/notebook/test/notebookEditorModel.test.ts index 3792dc84d36..46adfd9eef6 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookEditorModel.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookEditorModel.test.ts @@ -15,7 +15,7 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { NullLogService } from 'vs/platform/log/common/log'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { ComplexNotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookEditorModel'; -import { IMainNotebookController, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; +import { INotebookContentProvider, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { IWorkingCopyBackupService } from 'vs/workbench/services/workingCopy/common/workingCopyBackup'; import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; @@ -35,7 +35,7 @@ suite('NotebookEditorModel', function () { override getUriBasenameLabel(uri: URI) { return uri.toString(); } }; - const notebookDataProvider = new class extends mock() { }; + const notebookDataProvider = new class extends mock() { }; test('working copy uri', function () { diff --git a/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts b/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts index 022a0973e37..ce0b6f7becd 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookTextModel.test.ts @@ -18,10 +18,10 @@ suite('NotebookTextModel', () => { test('insert', async function () { await withTestNotebook( [ - ['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }], - ['var b = 2;', 'javascript', CellKind.Code, [], { editable: false }], - ['var c = 3;', 'javascript', CellKind.Code, [], { editable: false }], - ['var d = 4;', 'javascript', CellKind.Code, [], { editable: false }] + ['var a = 1;', 'javascript', CellKind.Code, [], {}], + ['var b = 2;', 'javascript', CellKind.Code, [], {}], + ['var c = 3;', 'javascript', CellKind.Code, [], {}], + ['var d = 4;', 'javascript', CellKind.Code, [], {}] ], (editor) => { const viewModel = editor.viewModel; @@ -42,10 +42,10 @@ suite('NotebookTextModel', () => { test('multiple inserts at same position', async function () { await withTestNotebook( [ - ['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }], - ['var b = 2;', 'javascript', CellKind.Code, [], { editable: false }], - ['var c = 3;', 'javascript', CellKind.Code, [], { editable: false }], - ['var d = 4;', 'javascript', CellKind.Code, [], { editable: false }] + ['var a = 1;', 'javascript', CellKind.Code, [], {}], + ['var b = 2;', 'javascript', CellKind.Code, [], {}], + ['var c = 3;', 'javascript', CellKind.Code, [], {}], + ['var d = 4;', 'javascript', CellKind.Code, [], {}] ], (editor) => { const viewModel = editor.viewModel; @@ -66,10 +66,10 @@ suite('NotebookTextModel', () => { test('delete', async function () { await withTestNotebook( [ - ['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }], - ['var b = 2;', 'javascript', CellKind.Code, [], { editable: false }], - ['var c = 3;', 'javascript', CellKind.Code, [], { editable: false }], - ['var d = 4;', 'javascript', CellKind.Code, [], { editable: false }] + ['var a = 1;', 'javascript', CellKind.Code, [], {}], + ['var b = 2;', 'javascript', CellKind.Code, [], {}], + ['var c = 3;', 'javascript', CellKind.Code, [], {}], + ['var d = 4;', 'javascript', CellKind.Code, [], {}] ], (editor) => { const textModel = editor.viewModel.notebookDocument; @@ -87,10 +87,10 @@ suite('NotebookTextModel', () => { test('delete + insert', async function () { await withTestNotebook( [ - ['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }], - ['var b = 2;', 'javascript', CellKind.Code, [], { editable: false }], - ['var c = 3;', 'javascript', CellKind.Code, [], { editable: false }], - ['var d = 4;', 'javascript', CellKind.Code, [], { editable: false }] + ['var a = 1;', 'javascript', CellKind.Code, [], {}], + ['var b = 2;', 'javascript', CellKind.Code, [], {}], + ['var c = 3;', 'javascript', CellKind.Code, [], {}], + ['var d = 4;', 'javascript', CellKind.Code, [], {}] ], (editor) => { const viewModel = editor.viewModel; @@ -111,10 +111,10 @@ suite('NotebookTextModel', () => { test('delete + insert at same position', async function () { await withTestNotebook( [ - ['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }], - ['var b = 2;', 'javascript', CellKind.Code, [], { editable: false }], - ['var c = 3;', 'javascript', CellKind.Code, [], { editable: false }], - ['var d = 4;', 'javascript', CellKind.Code, [], { editable: false }] + ['var a = 1;', 'javascript', CellKind.Code, [], {}], + ['var b = 2;', 'javascript', CellKind.Code, [], {}], + ['var c = 3;', 'javascript', CellKind.Code, [], {}], + ['var d = 4;', 'javascript', CellKind.Code, [], {}] ], (editor) => { const viewModel = editor.viewModel; @@ -135,10 +135,10 @@ suite('NotebookTextModel', () => { test('(replace) delete + insert at same position', async function () { await withTestNotebook( [ - ['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }], - ['var b = 2;', 'javascript', CellKind.Code, [], { editable: false }], - ['var c = 3;', 'javascript', CellKind.Code, [], { editable: false }], - ['var d = 4;', 'javascript', CellKind.Code, [], { editable: false }] + ['var a = 1;', 'javascript', CellKind.Code, [], {}], + ['var b = 2;', 'javascript', CellKind.Code, [], {}], + ['var c = 3;', 'javascript', CellKind.Code, [], {}], + ['var d = 4;', 'javascript', CellKind.Code, [], {}] ], (editor) => { const viewModel = editor.viewModel; @@ -158,7 +158,7 @@ suite('NotebookTextModel', () => { test('output', async function () { await withTestNotebook( [ - ['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }], + ['var a = 1;', 'javascript', CellKind.Code, [], {}], ], (editor) => { const textModel = editor.viewModel.notebookDocument; @@ -231,7 +231,7 @@ suite('NotebookTextModel', () => { test('metadata', async function () { await withTestNotebook( [ - ['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }], + ['var a = 1;', 'javascript', CellKind.Code, [], {}], ], (editor) => { const textModel = editor.viewModel.notebookDocument; @@ -241,7 +241,7 @@ suite('NotebookTextModel', () => { textModel.applyEdits([{ index: Number.MAX_VALUE, editType: CellEditType.Metadata, - metadata: { editable: false } + metadata: {} }], true, undefined, () => undefined, undefined); }); @@ -250,7 +250,7 @@ suite('NotebookTextModel', () => { textModel.applyEdits([{ index: -1, editType: CellEditType.Metadata, - metadata: { editable: false } + metadata: {} }], true, undefined, () => undefined, undefined); }); @@ -263,11 +263,10 @@ suite('NotebookTextModel', () => { textModel.applyEdits([{ index: 0, editType: CellEditType.Metadata, - metadata: { editable: false }, + metadata: {}, }], true, undefined, () => undefined, undefined); assert.strictEqual(textModel.cells.length, 1); - assert.strictEqual(textModel.cells[0].metadata?.editable, false); assert.strictEqual(textModel.cells[0].metadata?.executionOrder, undefined); } ); @@ -276,7 +275,7 @@ suite('NotebookTextModel', () => { test('partial metadata', async function () { await withTestNotebook( [ - ['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }], + ['var a = 1;', 'javascript', CellKind.Code, [], {}], ], (editor) => { const textModel = editor.viewModel.notebookDocument; @@ -290,11 +289,10 @@ suite('NotebookTextModel', () => { textModel.applyEdits([{ index: 0, editType: CellEditType.PartialMetadata, - metadata: { editable: false }, + metadata: {}, }], true, undefined, () => undefined, undefined); assert.strictEqual(textModel.cells.length, 1); - assert.strictEqual(textModel.cells[0].metadata?.editable, false); assert.strictEqual(textModel.cells[0].metadata?.executionOrder, 15); } ); @@ -303,10 +301,10 @@ suite('NotebookTextModel', () => { test('multiple inserts in one edit', async function () { await withTestNotebook( [ - ['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }], - ['var b = 2;', 'javascript', CellKind.Code, [], { editable: false }], - ['var c = 3;', 'javascript', CellKind.Code, [], { editable: false }], - ['var d = 4;', 'javascript', CellKind.Code, [], { editable: false }] + ['var a = 1;', 'javascript', CellKind.Code, [], {}], + ['var b = 2;', 'javascript', CellKind.Code, [], {}], + ['var c = 3;', 'javascript', CellKind.Code, [], {}], + ['var d = 4;', 'javascript', CellKind.Code, [], {}] ], (editor) => { const viewModel = editor.viewModel; @@ -339,10 +337,10 @@ suite('NotebookTextModel', () => { test('insert and metadata change in one edit', async function () { await withTestNotebook( [ - ['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }], - ['var b = 2;', 'javascript', CellKind.Code, [], { editable: false }], - ['var c = 3;', 'javascript', CellKind.Code, [], { editable: false }], - ['var d = 4;', 'javascript', CellKind.Code, [], { editable: false }] + ['var a = 1;', 'javascript', CellKind.Code, [], {}], + ['var b = 2;', 'javascript', CellKind.Code, [], {}], + ['var c = 3;', 'javascript', CellKind.Code, [], {}], + ['var d = 4;', 'javascript', CellKind.Code, [], {}] ], (editor) => { const textModel = editor.viewModel.notebookDocument; @@ -357,7 +355,7 @@ suite('NotebookTextModel', () => { { index: 0, editType: CellEditType.Metadata, - metadata: { editable: false }, + metadata: {}, } ], true, undefined, () => ({ kind: SelectionStateType.Index, focus: { start: 0, end: 1 }, selections: [{ start: 0, end: 1 }] }), undefined); @@ -373,7 +371,7 @@ suite('NotebookTextModel', () => { test('Updating appending/updating output in Notebooks does not work as expected #117273', function () { withTestNotebook([ - ['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }] + ['var a = 1;', 'javascript', CellKind.Code, [], {}] ], (editor) => { const model = editor.viewModel.notebookDocument; @@ -408,8 +406,8 @@ suite('NotebookTextModel', () => { test('Clearing output of an empty notebook makes it dirty #119608', function () { withTestNotebook([ - ['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }], - ['var b = 2;', 'javascript', CellKind.Code, [], { editable: true }] + ['var a = 1;', 'javascript', CellKind.Code, [], {}], + ['var b = 2;', 'javascript', CellKind.Code, [], {}] ], (editor) => { const model = editor.viewModel.notebookDocument; @@ -478,8 +476,8 @@ suite('NotebookTextModel', () => { test('Cell text model update increases notebook model version id #119561', function () { withTestNotebook([ - ['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }], - ['var b = 2;', 'javascript', CellKind.Code, [], { editable: true }] + ['var a = 1;', 'javascript', CellKind.Code, [], {}], + ['var b = 2;', 'javascript', CellKind.Code, [], {}] ], async (editor) => { const textModel = await editor.viewModel.cellAt(0)!.resolveTextModel(); assert.ok(textModel !== undefined); diff --git a/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts b/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts index 86556d7032b..b771bd7fb37 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts @@ -19,7 +19,8 @@ import { reduceCellRanges } from 'vs/workbench/contrib/notebook/browser/notebook import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; import { NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { CellKind, diff, ICellRange, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, diff, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { NotebookEditorTestModel, setupInstantiationService, withTestNotebook } from 'vs/workbench/contrib/notebook/test/testNotebookEditor'; suite('NotebookViewModel', () => { @@ -44,13 +45,11 @@ suite('NotebookViewModel', () => { test('insert/delete', async function () { await withTestNotebook( [ - ['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }], - ['var b = 2;', 'javascript', CellKind.Code, [], { editable: false }] + ['var a = 1;', 'javascript', CellKind.Code, [], {}], + ['var b = 2;', 'javascript', CellKind.Code, [], {}] ], (editor) => { const viewModel = editor.viewModel; - assert.strictEqual(viewModel.cellAt(0)?.metadata?.editable, true); - assert.strictEqual(viewModel.cellAt(1)?.metadata?.editable, false); const cell = viewModel.createCell(1, 'var c = 3', 'javascript', CellKind.Code, {}, [], true, true, null, []); assert.strictEqual(viewModel.length, 3); @@ -68,9 +67,9 @@ suite('NotebookViewModel', () => { test('move cells down', async function () { await withTestNotebook( [ - ['//a', 'javascript', CellKind.Code, [], { editable: true }], - ['//b', 'javascript', CellKind.Code, [], { editable: true }], - ['//c', 'javascript', CellKind.Code, [], { editable: true }], + ['//a', 'javascript', CellKind.Code, [], {}], + ['//b', 'javascript', CellKind.Code, [], {}], + ['//c', 'javascript', CellKind.Code, [], {}], ], (editor) => { const viewModel = editor.viewModel; @@ -97,9 +96,9 @@ suite('NotebookViewModel', () => { test('move cells up', async function () { await withTestNotebook( [ - ['//a', 'javascript', CellKind.Code, [], { editable: true }], - ['//b', 'javascript', CellKind.Code, [], { editable: true }], - ['//c', 'javascript', CellKind.Code, [], { editable: true }], + ['//a', 'javascript', CellKind.Code, [], {}], + ['//b', 'javascript', CellKind.Code, [], {}], + ['//c', 'javascript', CellKind.Code, [], {}], ], (editor) => { const viewModel = editor.viewModel; @@ -120,8 +119,8 @@ suite('NotebookViewModel', () => { test('index', async function () { await withTestNotebook( [ - ['var a = 1;', 'javascript', CellKind.Code, [], { editable: true }], - ['var b = 2;', 'javascript', CellKind.Code, [], { editable: true }] + ['var a = 1;', 'javascript', CellKind.Code, [], {}], + ['var b = 2;', 'javascript', CellKind.Code, [], {}] ], (editor) => { const viewModel = editor.viewModel; @@ -175,10 +174,10 @@ suite('NotebookViewModel Decorations', () => { await withTestNotebook( [ ['var a = 1;', 'javascript', CellKind.Code, [], {}], - ['var b = 2;', 'javascript', CellKind.Code, [], { editable: true }], - ['var c = 3;', 'javascript', CellKind.Code, [], { editable: true }], - ['var d = 4;', 'javascript', CellKind.Code, [], { editable: false }], - ['var e = 5;', 'javascript', CellKind.Code, [], { editable: false }], + ['var b = 2;', 'javascript', CellKind.Code, [], {}], + ['var c = 3;', 'javascript', CellKind.Code, [], {}], + ['var d = 4;', 'javascript', CellKind.Code, [], {}], + ['var e = 5;', 'javascript', CellKind.Code, [], {}], ], (editor) => { const viewModel = editor.viewModel; @@ -231,12 +230,12 @@ suite('NotebookViewModel Decorations', () => { await withTestNotebook( [ ['var a = 1;', 'javascript', CellKind.Code, [], {}], - ['var b = 2;', 'javascript', CellKind.Code, [], { editable: true }], - ['var c = 3;', 'javascript', CellKind.Code, [], { editable: true }], - ['var d = 4;', 'javascript', CellKind.Code, [], { editable: false }], - ['var e = 5;', 'javascript', CellKind.Code, [], { editable: false }], - ['var e = 6;', 'javascript', CellKind.Code, [], { editable: false }], - ['var e = 7;', 'javascript', CellKind.Code, [], { editable: false }], + ['var b = 2;', 'javascript', CellKind.Code, [], {}], + ['var c = 3;', 'javascript', CellKind.Code, [], {}], + ['var d = 4;', 'javascript', CellKind.Code, [], {}], + ['var e = 5;', 'javascript', CellKind.Code, [], {}], + ['var e = 6;', 'javascript', CellKind.Code, [], {}], + ['var e = 7;', 'javascript', CellKind.Code, [], {}], ], (editor) => { const viewModel = editor.viewModel; diff --git a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts index 70a88c92cb3..ff2b249ad59 100644 --- a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts @@ -20,7 +20,8 @@ import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/v import { CellViewModel, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { CellKind, CellUri, ICellRange, INotebookEditorModel, IOutputDto, IResolvedNotebookEditorModel, NotebookCellMetadata, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, CellUri, INotebookEditorModel, IOutputDto, IResolvedNotebookEditorModel, NotebookCellMetadata, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { TextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl'; diff --git a/src/vs/workbench/test/browser/api/extHostNotebook.test.ts b/src/vs/workbench/test/browser/api/extHostNotebook.test.ts index 7c0e6c85035..787927f5413 100644 --- a/src/vs/workbench/test/browser/api/extHostNotebook.test.ts +++ b/src/vs/workbench/test/browser/api/extHostNotebook.test.ts @@ -100,26 +100,26 @@ suite('NotebookCell#Document', function () { test('cell document is vscode.TextDocument', async function () { - assert.strictEqual(notebook.notebookDocument.cellCount, 2); + assert.strictEqual(notebook.apiNotebook.cellCount, 2); - const [c1, c2] = notebook.notebookDocument.getCells(); + const [c1, c2] = notebook.apiNotebook.getCells(); const d1 = extHostDocuments.getDocument(c1.document.uri); assert.ok(d1); assert.strictEqual(d1.languageId, c1.document.languageId); assert.strictEqual(d1.version, 1); - assert.ok(d1.notebook === notebook.notebookDocument); + assert.ok(d1.notebook === notebook.apiNotebook); const d2 = extHostDocuments.getDocument(c2.document.uri); assert.ok(d2); assert.strictEqual(d2.languageId, c2.document.languageId); assert.strictEqual(d2.version, 1); - assert.ok(d2.notebook === notebook.notebookDocument); + assert.ok(d2.notebook === notebook.apiNotebook); }); test('cell document goes when notebook closes', async function () { const cellUris: string[] = []; - for (let cell of notebook.notebookDocument.getCells()) { + for (let cell of notebook.apiNotebook.getCells()) { assert.ok(extHostDocuments.getDocument(cell.document.uri)); cellUris.push(cell.document.uri.toString()); } @@ -163,7 +163,7 @@ suite('NotebookCell#Document', function () { }); extHostNotebooks.$acceptModelChanged(notebookUri, { - versionId: notebook.notebookDocument.version + 1, + versionId: notebook.apiNotebook.version + 1, rawEvents: [ { kind: NotebookCellsChangeType.ModelChange, @@ -196,7 +196,7 @@ suite('NotebookCell#Document', function () { const docs: vscode.TextDocument[] = []; const addData: IModelAddedData[] = []; - for (let cell of notebook.notebookDocument.getCells()) { + for (let cell of notebook.apiNotebook.getCells()) { const doc = extHostDocuments.getDocument(cell.document.uri); assert.ok(doc); assert.strictEqual(extHostDocuments.getDocument(cell.document.uri).isClosed, false); @@ -218,14 +218,14 @@ suite('NotebookCell#Document', function () { extHostDocumentsAndEditors.$acceptDocumentsAndEditorsDelta({ removedDocuments: docs.map(d => d.uri) }); // notebook is still open -> cell documents stay open - for (let cell of notebook.notebookDocument.getCells()) { + for (let cell of notebook.apiNotebook.getCells()) { assert.ok(extHostDocuments.getDocument(cell.document.uri)); assert.strictEqual(extHostDocuments.getDocument(cell.document.uri).isClosed, false); } // close notebook -> docs are closed extHostNotebooks.$acceptDocumentAndEditorsDelta({ removedDocuments: [notebook.uri] }); - for (let cell of notebook.notebookDocument.getCells()) { + for (let cell of notebook.apiNotebook.getCells()) { assert.throws(() => extHostDocuments.getDocument(cell.document.uri)); } for (let doc of docs) { @@ -235,8 +235,8 @@ suite('NotebookCell#Document', function () { test('cell document goes when cell is removed', async function () { - assert.strictEqual(notebook.notebookDocument.cellCount, 2); - const [cell1, cell2] = notebook.notebookDocument.getCells(); + assert.strictEqual(notebook.apiNotebook.cellCount, 2); + const [cell1, cell2] = notebook.apiNotebook.getCells(); extHostNotebooks.$acceptModelChanged(notebook.uri, { versionId: 2, @@ -248,7 +248,7 @@ suite('NotebookCell#Document', function () { ] }, false); - assert.strictEqual(notebook.notebookDocument.cellCount, 1); + assert.strictEqual(notebook.apiNotebook.cellCount, 1); assert.strictEqual(cell1.document.isClosed, true); // ref still alive! assert.strictEqual(cell2.document.isClosed, false); @@ -256,32 +256,32 @@ suite('NotebookCell#Document', function () { }); test('cell document knows notebook', function () { - for (let cells of notebook.notebookDocument.getCells()) { - assert.strictEqual(cells.document.notebook === notebook.notebookDocument, true); + for (let cells of notebook.apiNotebook.getCells()) { + assert.strictEqual(cells.document.notebook === notebook.apiNotebook, true); } }); test('cell#index', function () { - assert.strictEqual(notebook.notebookDocument.cellCount, 2); - const [first, second] = notebook.notebookDocument.getCells(); + assert.strictEqual(notebook.apiNotebook.cellCount, 2); + const [first, second] = notebook.apiNotebook.getCells(); assert.strictEqual(first.index, 0); assert.strictEqual(second.index, 1); // remove first cell extHostNotebooks.$acceptModelChanged(notebook.uri, { - versionId: notebook.notebookDocument.version + 1, + versionId: notebook.apiNotebook.version + 1, rawEvents: [{ kind: NotebookCellsChangeType.ModelChange, changes: [[0, 1, []]] }] }, false); - assert.strictEqual(notebook.notebookDocument.cellCount, 1); + assert.strictEqual(notebook.apiNotebook.cellCount, 1); assert.strictEqual(second.index, 0); extHostNotebooks.$acceptModelChanged(notebookUri, { - versionId: notebook.notebookDocument.version + 1, + versionId: notebook.apiNotebook.version + 1, rawEvents: [{ kind: NotebookCellsChangeType.ModelChange, changes: [[0, 0, [{ @@ -304,7 +304,7 @@ suite('NotebookCell#Document', function () { }] }, false); - assert.strictEqual(notebook.notebookDocument.cellCount, 3); + assert.strictEqual(notebook.apiNotebook.cellCount, 3); assert.strictEqual(second.index, 2); }); @@ -339,11 +339,11 @@ suite('NotebookCell#Document', function () { }] }, false); - assert.strictEqual(notebook.notebookDocument.cellCount, 2); + assert.strictEqual(notebook.apiNotebook.cellCount, 2); const event = await p; - assert.strictEqual(event.document === notebook.notebookDocument, true); + assert.strictEqual(event.document === notebook.apiNotebook, true); assert.strictEqual(event.changes.length, 1); assert.strictEqual(event.changes[0].deletedCount, 2); assert.strictEqual(event.changes[0].deletedItems[0].document.isClosed, true); @@ -391,7 +391,7 @@ suite('NotebookCell#Document', function () { test('change cell language triggers onDidChange events', async function () { - const first = notebook.notebookDocument.cellAt(0); + const first = notebook.apiNotebook.cellAt(0); assert.strictEqual(first.document.languageId, 'markdown'); diff --git a/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts b/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts index a41ca0edf71..2eca9f59429 100644 --- a/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts +++ b/src/vs/workbench/test/browser/api/extHostNotebookConcatDocument.test.ts @@ -87,7 +87,7 @@ suite('NotebookConcatDocument', function () { }); test('empty', function () { - let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook.notebookDocument, undefined); + let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook.apiNotebook, undefined); assert.strictEqual(doc.getText(), ''); assert.strictEqual(doc.version, 0); @@ -123,7 +123,7 @@ suite('NotebookConcatDocument', function () { const cellUri2 = CellUri.generate(notebook.uri, 2); extHostNotebooks.$acceptModelChanged(notebookUri, { - versionId: notebook.notebookDocument.version + 1, + versionId: notebook.apiNotebook.version + 1, rawEvents: [{ kind: NotebookCellsChangeType.ModelChange, changes: [[0, 0, [{ @@ -148,9 +148,9 @@ suite('NotebookConcatDocument', function () { }, false); - assert.strictEqual(notebook.notebookDocument.cellCount, 1 + 2); // markdown and code + assert.strictEqual(notebook.apiNotebook.cellCount, 1 + 2); // markdown and code - let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook.notebookDocument, undefined); + let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook.apiNotebook, undefined); assert.strictEqual(doc.contains(cellUri1), true); assert.strictEqual(doc.contains(cellUri2), true); @@ -160,7 +160,7 @@ suite('NotebookConcatDocument', function () { test('location, position mapping', function () { extHostNotebooks.$acceptModelChanged(notebookUri, { - versionId: notebook.notebookDocument.version + 1, + versionId: notebook.apiNotebook.version + 1, rawEvents: [ { kind: NotebookCellsChangeType.ModelChange, @@ -186,26 +186,26 @@ suite('NotebookConcatDocument', function () { }, false); - assert.strictEqual(notebook.notebookDocument.cellCount, 1 + 2); // markdown and code + assert.strictEqual(notebook.apiNotebook.cellCount, 1 + 2); // markdown and code - let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook.notebookDocument, undefined); + let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook.apiNotebook, undefined); assertLines(doc, 'Hello', 'World', 'Hello World!', 'Hallo', 'Welt', 'Hallo Welt!'); - assertLocation(doc, new Position(0, 0), new Location(notebook.notebookDocument.cellAt(0).document.uri, new Position(0, 0))); - assertLocation(doc, new Position(4, 0), new Location(notebook.notebookDocument.cellAt(1).document.uri, new Position(1, 0))); - assertLocation(doc, new Position(4, 3), new Location(notebook.notebookDocument.cellAt(1).document.uri, new Position(1, 3))); - assertLocation(doc, new Position(5, 11), new Location(notebook.notebookDocument.cellAt(1).document.uri, new Position(2, 11))); - assertLocation(doc, new Position(5, 12), new Location(notebook.notebookDocument.cellAt(1).document.uri, new Position(2, 11)), false); // don't check identity because position will be clamped + assertLocation(doc, new Position(0, 0), new Location(notebook.apiNotebook.cellAt(0).document.uri, new Position(0, 0))); + assertLocation(doc, new Position(4, 0), new Location(notebook.apiNotebook.cellAt(1).document.uri, new Position(1, 0))); + assertLocation(doc, new Position(4, 3), new Location(notebook.apiNotebook.cellAt(1).document.uri, new Position(1, 3))); + assertLocation(doc, new Position(5, 11), new Location(notebook.apiNotebook.cellAt(1).document.uri, new Position(2, 11))); + assertLocation(doc, new Position(5, 12), new Location(notebook.apiNotebook.cellAt(1).document.uri, new Position(2, 11)), false); // don't check identity because position will be clamped }); test('location, position mapping, cell changes', function () { - let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook.notebookDocument, undefined); + let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook.apiNotebook, undefined); // UPDATE 1 extHostNotebooks.$acceptModelChanged(notebookUri, { - versionId: notebook.notebookDocument.version + 1, + versionId: notebook.apiNotebook.version + 1, rawEvents: [ { kind: NotebookCellsChangeType.ModelChange, @@ -221,18 +221,18 @@ suite('NotebookConcatDocument', function () { } ] }, false); - assert.strictEqual(notebook.notebookDocument.cellCount, 1 + 1); + assert.strictEqual(notebook.apiNotebook.cellCount, 1 + 1); assert.strictEqual(doc.version, 1); assertLines(doc, 'Hello', 'World', 'Hello World!'); - assertLocation(doc, new Position(0, 0), new Location(notebook.notebookDocument.cellAt(0).document.uri, new Position(0, 0))); - assertLocation(doc, new Position(2, 2), new Location(notebook.notebookDocument.cellAt(0).document.uri, new Position(2, 2))); - assertLocation(doc, new Position(4, 0), new Location(notebook.notebookDocument.cellAt(0).document.uri, new Position(2, 12)), false); // clamped + assertLocation(doc, new Position(0, 0), new Location(notebook.apiNotebook.cellAt(0).document.uri, new Position(0, 0))); + assertLocation(doc, new Position(2, 2), new Location(notebook.apiNotebook.cellAt(0).document.uri, new Position(2, 2))); + assertLocation(doc, new Position(4, 0), new Location(notebook.apiNotebook.cellAt(0).document.uri, new Position(2, 12)), false); // clamped // UPDATE 2 extHostNotebooks.$acceptModelChanged(notebookUri, { - versionId: notebook.notebookDocument.version + 1, + versionId: notebook.apiNotebook.version + 1, rawEvents: [ { kind: NotebookCellsChangeType.ModelChange, @@ -249,18 +249,18 @@ suite('NotebookConcatDocument', function () { ] }, false); - assert.strictEqual(notebook.notebookDocument.cellCount, 1 + 2); + assert.strictEqual(notebook.apiNotebook.cellCount, 1 + 2); assert.strictEqual(doc.version, 2); assertLines(doc, 'Hello', 'World', 'Hello World!', 'Hallo', 'Welt', 'Hallo Welt!'); - assertLocation(doc, new Position(0, 0), new Location(notebook.notebookDocument.cellAt(0).document.uri, new Position(0, 0))); - assertLocation(doc, new Position(4, 0), new Location(notebook.notebookDocument.cellAt(1).document.uri, new Position(1, 0))); - assertLocation(doc, new Position(4, 3), new Location(notebook.notebookDocument.cellAt(1).document.uri, new Position(1, 3))); - assertLocation(doc, new Position(5, 11), new Location(notebook.notebookDocument.cellAt(1).document.uri, new Position(2, 11))); - assertLocation(doc, new Position(5, 12), new Location(notebook.notebookDocument.cellAt(1).document.uri, new Position(2, 11)), false); // don't check identity because position will be clamped + assertLocation(doc, new Position(0, 0), new Location(notebook.apiNotebook.cellAt(0).document.uri, new Position(0, 0))); + assertLocation(doc, new Position(4, 0), new Location(notebook.apiNotebook.cellAt(1).document.uri, new Position(1, 0))); + assertLocation(doc, new Position(4, 3), new Location(notebook.apiNotebook.cellAt(1).document.uri, new Position(1, 3))); + assertLocation(doc, new Position(5, 11), new Location(notebook.apiNotebook.cellAt(1).document.uri, new Position(2, 11))); + assertLocation(doc, new Position(5, 12), new Location(notebook.apiNotebook.cellAt(1).document.uri, new Position(2, 11)), false); // don't check identity because position will be clamped // UPDATE 3 (remove cell #2 again) extHostNotebooks.$acceptModelChanged(notebookUri, { - versionId: notebook.notebookDocument.version + 1, + versionId: notebook.apiNotebook.version + 1, rawEvents: [ { kind: NotebookCellsChangeType.ModelChange, @@ -268,21 +268,21 @@ suite('NotebookConcatDocument', function () { } ] }, false); - assert.strictEqual(notebook.notebookDocument.cellCount, 1 + 1); + assert.strictEqual(notebook.apiNotebook.cellCount, 1 + 1); assert.strictEqual(doc.version, 3); assertLines(doc, 'Hello', 'World', 'Hello World!'); - assertLocation(doc, new Position(0, 0), new Location(notebook.notebookDocument.cellAt(0).document.uri, new Position(0, 0))); - assertLocation(doc, new Position(2, 2), new Location(notebook.notebookDocument.cellAt(0).document.uri, new Position(2, 2))); - assertLocation(doc, new Position(4, 0), new Location(notebook.notebookDocument.cellAt(0).document.uri, new Position(2, 12)), false); // clamped + assertLocation(doc, new Position(0, 0), new Location(notebook.apiNotebook.cellAt(0).document.uri, new Position(0, 0))); + assertLocation(doc, new Position(2, 2), new Location(notebook.apiNotebook.cellAt(0).document.uri, new Position(2, 2))); + assertLocation(doc, new Position(4, 0), new Location(notebook.apiNotebook.cellAt(0).document.uri, new Position(2, 12)), false); // clamped }); test('location, position mapping, cell-document changes', function () { - let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook.notebookDocument, undefined); + let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook.apiNotebook, undefined); // UPDATE 1 extHostNotebooks.$acceptModelChanged(notebookUri, { - versionId: notebook.notebookDocument.version + 1, + versionId: notebook.apiNotebook.version + 1, rawEvents: [ { @@ -307,21 +307,21 @@ suite('NotebookConcatDocument', function () { } ] }, false); - assert.strictEqual(notebook.notebookDocument.cellCount, 1 + 2); + assert.strictEqual(notebook.apiNotebook.cellCount, 1 + 2); assert.strictEqual(doc.version, 1); assertLines(doc, 'Hello', 'World', 'Hello World!', 'Hallo', 'Welt', 'Hallo Welt!'); - assertLocation(doc, new Position(0, 0), new Location(notebook.notebookDocument.cellAt(0).document.uri, new Position(0, 0))); - assertLocation(doc, new Position(2, 2), new Location(notebook.notebookDocument.cellAt(0).document.uri, new Position(2, 2))); - assertLocation(doc, new Position(2, 12), new Location(notebook.notebookDocument.cellAt(0).document.uri, new Position(2, 12))); - assertLocation(doc, new Position(4, 0), new Location(notebook.notebookDocument.cellAt(1).document.uri, new Position(1, 0))); - assertLocation(doc, new Position(4, 3), new Location(notebook.notebookDocument.cellAt(1).document.uri, new Position(1, 3))); + assertLocation(doc, new Position(0, 0), new Location(notebook.apiNotebook.cellAt(0).document.uri, new Position(0, 0))); + assertLocation(doc, new Position(2, 2), new Location(notebook.apiNotebook.cellAt(0).document.uri, new Position(2, 2))); + assertLocation(doc, new Position(2, 12), new Location(notebook.apiNotebook.cellAt(0).document.uri, new Position(2, 12))); + assertLocation(doc, new Position(4, 0), new Location(notebook.apiNotebook.cellAt(1).document.uri, new Position(1, 0))); + assertLocation(doc, new Position(4, 3), new Location(notebook.apiNotebook.cellAt(1).document.uri, new Position(1, 3))); // offset math let cell1End = doc.offsetAt(new Position(2, 12)); assert.strictEqual(doc.positionAt(cell1End).isEqual(new Position(2, 12)), true); - extHostDocuments.$acceptModelChanged(notebook.notebookDocument.cellAt(0).document.uri, { + extHostDocuments.$acceptModelChanged(notebook.apiNotebook.cellAt(0).document.uri, { versionId: 0, eol: '\n', changes: [{ @@ -332,7 +332,7 @@ suite('NotebookConcatDocument', function () { }] }, false); assertLines(doc, 'Hello', 'World', 'Hi World!', 'Hallo', 'Welt', 'Hallo Welt!'); - assertLocation(doc, new Position(2, 12), new Location(notebook.notebookDocument.cellAt(0).document.uri, new Position(2, 9)), false); + assertLocation(doc, new Position(2, 12), new Location(notebook.apiNotebook.cellAt(0).document.uri, new Position(2, 9)), false); assert.strictEqual(doc.positionAt(cell1End).isEqual(new Position(3, 2)), true); @@ -341,7 +341,7 @@ suite('NotebookConcatDocument', function () { test('selector', function () { extHostNotebooks.$acceptModelChanged(notebookUri, { - versionId: notebook.notebookDocument.version + 1, + versionId: notebook.apiNotebook.version + 1, rawEvents: [ { kind: NotebookCellsChangeType.ModelChange, @@ -366,16 +366,16 @@ suite('NotebookConcatDocument', function () { ] }, false); - const mixedDoc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook.notebookDocument, undefined); - const fooLangDoc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook.notebookDocument, 'fooLang'); - const barLangDoc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook.notebookDocument, 'barLang'); + const mixedDoc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook.apiNotebook, undefined); + const fooLangDoc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook.apiNotebook, 'fooLang'); + const barLangDoc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook.apiNotebook, 'barLang'); assertLines(mixedDoc, 'fooLang-document', 'barLang-document'); assertLines(fooLangDoc, 'fooLang-document'); assertLines(barLangDoc, 'barLang-document'); extHostNotebooks.$acceptModelChanged(notebookUri, { - versionId: notebook.notebookDocument.version + 1, + versionId: notebook.apiNotebook.version + 1, rawEvents: [ { kind: NotebookCellsChangeType.ModelChange, @@ -413,7 +413,7 @@ suite('NotebookConcatDocument', function () { test('offsetAt(position) <-> positionAt(offset)', function () { extHostNotebooks.$acceptModelChanged(notebookUri, { - versionId: notebook.notebookDocument.version + 1, + versionId: notebook.apiNotebook.version + 1, rawEvents: [ { kind: NotebookCellsChangeType.ModelChange, @@ -438,9 +438,9 @@ suite('NotebookConcatDocument', function () { ] }, false); - assert.strictEqual(notebook.notebookDocument.cellCount, 1 + 2); // markdown and code + assert.strictEqual(notebook.apiNotebook.cellCount, 1 + 2); // markdown and code - let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook.notebookDocument, undefined); + let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook.apiNotebook, undefined); assertLines(doc, 'Hello', 'World', 'Hello World!', 'Hallo', 'Welt', 'Hallo Welt!'); assertOffsetAtPosition(doc, 0, { line: 0, character: 0 }); @@ -470,7 +470,7 @@ suite('NotebookConcatDocument', function () { test('locationAt(position) <-> positionAt(location)', function () { extHostNotebooks.$acceptModelChanged(notebookUri, { - versionId: notebook.notebookDocument.version + 1, + versionId: notebook.apiNotebook.version + 1, rawEvents: [ { kind: NotebookCellsChangeType.ModelChange, @@ -495,23 +495,23 @@ suite('NotebookConcatDocument', function () { ] }, false); - assert.strictEqual(notebook.notebookDocument.cellCount, 1 + 2); // markdown and code + assert.strictEqual(notebook.apiNotebook.cellCount, 1 + 2); // markdown and code - let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook.notebookDocument, undefined); + let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook.apiNotebook, undefined); assertLines(doc, 'Hello', 'World', 'Hello World!', 'Hallo', 'Welt', 'Hallo Welt!'); - assertLocationAtPosition(doc, { line: 0, character: 0 }, { uri: notebook.notebookDocument.cellAt(0).document.uri, line: 0, character: 0 }); - assertLocationAtPosition(doc, { line: 2, character: 0 }, { uri: notebook.notebookDocument.cellAt(0).document.uri, line: 2, character: 0 }); - assertLocationAtPosition(doc, { line: 2, character: 12 }, { uri: notebook.notebookDocument.cellAt(0).document.uri, line: 2, character: 12 }); - assertLocationAtPosition(doc, { line: 3, character: 0 }, { uri: notebook.notebookDocument.cellAt(1).document.uri, line: 0, character: 0 }); - assertLocationAtPosition(doc, { line: 5, character: 0 }, { uri: notebook.notebookDocument.cellAt(1).document.uri, line: 2, character: 0 }); - assertLocationAtPosition(doc, { line: 5, character: 11 }, { uri: notebook.notebookDocument.cellAt(1).document.uri, line: 2, character: 11 }); + assertLocationAtPosition(doc, { line: 0, character: 0 }, { uri: notebook.apiNotebook.cellAt(0).document.uri, line: 0, character: 0 }); + assertLocationAtPosition(doc, { line: 2, character: 0 }, { uri: notebook.apiNotebook.cellAt(0).document.uri, line: 2, character: 0 }); + assertLocationAtPosition(doc, { line: 2, character: 12 }, { uri: notebook.apiNotebook.cellAt(0).document.uri, line: 2, character: 12 }); + assertLocationAtPosition(doc, { line: 3, character: 0 }, { uri: notebook.apiNotebook.cellAt(1).document.uri, line: 0, character: 0 }); + assertLocationAtPosition(doc, { line: 5, character: 0 }, { uri: notebook.apiNotebook.cellAt(1).document.uri, line: 2, character: 0 }); + assertLocationAtPosition(doc, { line: 5, character: 11 }, { uri: notebook.apiNotebook.cellAt(1).document.uri, line: 2, character: 11 }); }); test('getText(range)', function () { extHostNotebooks.$acceptModelChanged(notebookUri, { - versionId: notebook.notebookDocument.version + 1, + versionId: notebook.apiNotebook.version + 1, rawEvents: [ { kind: NotebookCellsChangeType.ModelChange, @@ -536,9 +536,9 @@ suite('NotebookConcatDocument', function () { ] }, false); - assert.strictEqual(notebook.notebookDocument.cellCount, 1 + 2); // markdown and code + assert.strictEqual(notebook.apiNotebook.cellCount, 1 + 2); // markdown and code - let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook.notebookDocument, undefined); + let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook.apiNotebook, undefined); assertLines(doc, 'Hello', 'World', 'Hello World!', 'Hallo', 'Welt', 'Hallo Welt!'); assert.strictEqual(doc.getText(new Range(0, 0, 0, 0)), ''); @@ -549,7 +549,7 @@ suite('NotebookConcatDocument', function () { test('validateRange/Position', function () { extHostNotebooks.$acceptModelChanged(notebookUri, { - versionId: notebook.notebookDocument.version + 1, + versionId: notebook.apiNotebook.version + 1, rawEvents: [ { kind: NotebookCellsChangeType.ModelChange, @@ -574,9 +574,9 @@ suite('NotebookConcatDocument', function () { ] }, false); - assert.strictEqual(notebook.notebookDocument.cellCount, 1 + 2); // markdown and code + assert.strictEqual(notebook.apiNotebook.cellCount, 1 + 2); // markdown and code - let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook.notebookDocument, undefined); + let doc = new ExtHostNotebookConcatDocument(extHostNotebooks, extHostDocuments, notebook.apiNotebook, undefined); assertLines(doc, 'Hello', 'World', 'Hello World!', 'Hallo', 'Welt', 'Hallo Welt!'); diff --git a/src/vs/workbench/test/browser/api/extHostTypes.test.ts b/src/vs/workbench/test/browser/api/extHostTypes.test.ts index da64e5adc9c..788aec8bc4e 100644 --- a/src/vs/workbench/test/browser/api/extHostTypes.test.ts +++ b/src/vs/workbench/test/browser/api/extHostTypes.test.ts @@ -651,7 +651,6 @@ suite('ExtHostTypes', function () { test('NotebookMetadata - defaults', function () { const obj = new types.NotebookDocumentMetadata(); - assert.deepStrictEqual(obj.custom, notebookDocumentMetadataDefaults.custom); assert.strictEqual(obj.trusted, notebookDocumentMetadataDefaults.trusted); }); @@ -690,13 +689,13 @@ suite('ExtHostTypes', function () { test('NotebookCellMetadata - with custom', function () { const obj = new types.NotebookCellMetadata(true, true); - const newObj = obj.with({ inputCollapsed: false, mycustom: { display: 'hello' } }); + const newObj = obj.with({ inputCollapsed: false, custom: { display: 'hello' } }); assert.ok(obj !== newObj); const sameObj = newObj.with({ inputCollapsed: false }); assert.ok(newObj === sameObj); assert.strictEqual(obj.inputCollapsed, true); assert.strictEqual(newObj.inputCollapsed, false); - assert.deepStrictEqual(newObj.mycustom, { display: 'hello' }); + assert.deepStrictEqual(newObj.custom, { display: 'hello' }); const newCustom = newObj.with({ anotherCustom: { display: 'hello2' } }); assert.strictEqual(newCustom.inputCollapsed, false);