diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index a8a8d826f6f..a02f57a931b 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -996,10 +996,10 @@ declare module 'vscode' { } export interface NotebookCellExecutionSummary { - executionOrder?: number; - success?: boolean; - startTime?: number; - endTime?: number; + readonly executionOrder?: number; + readonly success?: boolean; + readonly startTime?: number; + readonly endTime?: number; } // todo@API support ids https://github.com/jupyter/enhancement-proposals/blob/master/62-cell-id/cell-id.md @@ -1043,17 +1043,67 @@ declare module 'vscode' { transientOutputs?: boolean; /** - * Controls if a meetadata 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. + * @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. + */ + transientCellMetadata?: { [K in keyof NotebookCellMetadata]?: boolean }; + + /** + * Controls if a document metadata property change will trigger notebook document content change and if it will be used in the diff editor + * Default to false. If the content provider doesn't persisit a metadata property in the file document, it should be set to true. + */ + 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. + */ export interface NotebookDocument { + + /** + * The associated uri for this notebook. + * + * *Note* that most notebooks use the `file`-scheme, which means they are files on disk. However, **not** all notebooks are + * saved on disk and therefore the `scheme` must be checked before trying to access the underlying file or siblings on disk. + * + * @see [FileSystemProvider](#FileSystemProvider) + * @see [TextDocumentContentProvider](#TextDocumentContentProvider) + */ readonly uri: Uri; + + // todo@API should we really expose this? + readonly viewType: string; + + /** + * The version number of this notebook (it will strictly increase after each + * change, including undo/redo). + */ readonly version: number; + /** + * `true` if there are unpersisted changes. + */ readonly isDirty: boolean; + + /** + * Is this notebook representing an untitled file which has not been saved yet. + */ readonly isUntitled: boolean; /** @@ -1062,13 +1112,13 @@ declare module 'vscode' { */ readonly isClosed: boolean; + /** + * The [metadata](#NotebookDocumentMetadata) for this notebook. + */ readonly metadata: NotebookDocumentMetadata; - // todo@API should we really expose this? - readonly viewType: string; - /** - * The number of cells in the notebook document. + * The number of cells in the notebook. */ readonly cellCount: number; @@ -1099,17 +1149,43 @@ declare module 'vscode' { save(): Thenable; } + /** + * A notebook range represents on ordered pair of two cell indicies. + * It is guaranteed that start is less than or equal to end. + */ export class NotebookRange { - readonly start: number; + /** - * exclusive + * The zero-based start index of this range. + */ + readonly start: number; + + /** + * The exclusive end index of this range (zero-based). */ readonly end: number; + /** + * `true` if `start` and `end` are equals + */ readonly isEmpty: boolean; + /** + * Create a new notebook range. If `start` is not + * before or equal to `end`, the values will be swapped. + * + * @param start start index + * @param end end index. + */ constructor(start: number, end: number); + /** + * Derive a new range for this range. + * + * @param change An object that describes a change to this range. + * @return A range that reflects the given change. Will return `this` range if the change + * is not changing anything. + */ with(change: { start?: number, end?: number }): NotebookRange; } @@ -1216,13 +1292,11 @@ declare module 'vscode' { // todo@API support ids https://github.com/jupyter/enhancement-proposals/blob/master/62-cell-id/cell-id.md export class NotebookCellData { - // todo@API should they all be readonly? kind: NotebookCellKind; // todo@API better names: value? text? source: string; // todo@API how does language and MD relate? language: string; - // todo@API ReadonlyArray? outputs?: NotebookCellOutput[]; metadata?: NotebookCellMetadata; latestExecutionSummary?: NotebookCellExecutionSummary; @@ -1230,50 +1304,23 @@ declare module 'vscode' { } export class NotebookData { - // todo@API should they all be readonly? cells: NotebookCellData[]; metadata: NotebookDocumentMetadata; constructor(cells: NotebookCellData[], metadata?: NotebookDocumentMetadata); } - /** - * Communication object passed to the {@link NotebookContentProvider} and - * {@link NotebookOutputRenderer} to communicate with the webview. - */ + /** @deprecated used NotebookController */ export interface NotebookCommunication { - /** - * ID of the editor this object communicates with. A single notebook - * document can have multiple attached webviews and editors, when the - * notebook is split for instance. The editor ID lets you differentiate - * between them. - */ + /** @deprecated used NotebookController */ readonly editorId: string; - - /** - * Fired when the output hosting webview posts a message. - */ + /** @deprecated used NotebookController */ readonly onDidReceiveMessage: Event; - /** - * Post a message to the output hosting webview. - * - * Messages are only delivered if the editor is live. - * - * @param message Body of the message. This must be a string or other json serializable object. - */ + /** @deprecated used NotebookController */ postMessage(message: any): Thenable; - - /** - * Convert a uri for the local file system to one that can be used inside outputs webview. - */ + /** @deprecated used NotebookController */ asWebviewUri(localResource: Uri): Uri; - - // @rebornix - // readonly onDidDispose: Event; } - // export function registerNotebookKernel(selector: string, kernel: NotebookKernel): Disposable; - - export interface NotebookDocumentShowOptions { viewColumn?: ViewColumn; preserveFocus?: boolean; @@ -1419,6 +1466,18 @@ declare module 'vscode' { export type NotebookSelector = NotebookFilter | string | ReadonlyArray; + export interface NotebookExecutionHandler { + /** + * @param cells The notebook cells to execute + * @param controller The controller that the handler is attached to + */ + (this: NotebookController, cells: NotebookCell[], controller: NotebookController): void + } + + export interface NotebookInterruptHandler { + (this: NotebookController): void; + } + export interface NotebookController { readonly id: string; @@ -1440,18 +1499,15 @@ declare module 'vscode' { supportedLanguages: string[]; hasExecutionOrder?: boolean; - preloads?: NotebookKernelPreload[]; /** * The execute handler is invoked when the run gestures in the UI are selected, e.g Run Cell, Run All, * Run Selection etc. */ - readonly executeHandler: (cells: NotebookCell[], controller: NotebookController) => void; + executeHandler: NotebookExecutionHandler; // optional kernel interrupt command - interruptHandler?: (notebook: NotebookDocument) => void - - // remove kernel + interruptHandler?: NotebookInterruptHandler dispose(): void; /** @@ -1465,24 +1521,24 @@ declare module 'vscode' { createNotebookCellExecutionTask(cell: NotebookCell): NotebookCellExecutionTask; // ipc + readonly preloads: NotebookKernelPreload[]; readonly onDidReceiveMessage: Event<{ editor: NotebookEditor, message: any }>; postMessage(message: any, editor?: NotebookEditor): Thenable; - asWebviewUri(localResource: Uri, editor: NotebookEditor): Uri; - } - - export interface NotebookControllerOptions { - id: string; - label: string; - description?: string; - selector: NotebookSelector; - supportedLanguages?: string[]; - hasExecutionOrder?: boolean; - executeHandler: (cells: NotebookCell[], controller: NotebookController) => void; - interruptHandler?: (notebook: NotebookDocument) => void + asWebviewUri(localResource: Uri): Uri; } export namespace notebook { - export function createNotebookController(options: NotebookControllerOptions): NotebookController; + + /** + * Creates a new notebook controller. + * + * @param id Unique identifier of the controller + * @param selector A notebook selector to narrow down notebook type or path + * @param label The label of the controller + * @param handler + * @param preloads + */ + export function createNotebookController(id: string, selector: NotebookSelector, label: string, handler?: NotebookExecutionHandler, preloads?: NotebookKernelPreload[]): NotebookController; } //#endregion @@ -1543,18 +1599,7 @@ declare module 'vscode' { // TODO@api use NotebookDocumentFilter instead of just notebookType:string? // TODO@API options duplicates the more powerful variant on NotebookContentProvider - export function registerNotebookContentProvider(notebookType: string, provider: NotebookContentProvider, - options?: NotebookDocumentContentOptions & { - /** - * Not ready for production or development use yet. - */ - viewOptions?: { - displayName: string; - filenamePattern: NotebookFilenamePattern[]; - exclusive?: boolean; - }; - } - ): Disposable; + export function registerNotebookContentProvider(notebookType: string, provider: NotebookContentProvider, options?: NotebookDocumentContentOptions): Disposable; } //#endregion @@ -1566,6 +1611,7 @@ declare module 'vscode' { uri: Uri; } + /** @deprecated used NotebookController */ export interface NotebookKernel { // todo@API make this mandatory? @@ -1679,8 +1725,7 @@ declare module 'vscode' { filenamePattern?: NotebookFilenamePattern; } - // todo@API very unclear, provider MUST not return alive object but only data object - // todo@API unclear how the flow goes + /** @deprecated used NotebookController */ export interface NotebookKernelProvider { onDidChangeKernels?: Event; provideKernels(document: NotebookDocument, token: CancellationToken): ProviderResult; @@ -1688,9 +1733,6 @@ declare module 'vscode' { } export interface NotebookEditor { - - // todo@API unsure about that - // kernel, kernel selection, kernel provider /** @deprecated kernels are private object*/ readonly kernel?: NotebookKernel; } @@ -1698,7 +1740,7 @@ declare module 'vscode' { export namespace notebook { /** @deprecated */ export const onDidChangeActiveNotebookKernel: Event<{ document: NotebookDocument, kernel: NotebookKernel | undefined; }>; - /** @deprecated use createNotebookKernel */ + /** @deprecated used NotebookController */ export function registerNotebookKernelProvider(selector: NotebookDocumentFilter, provider: NotebookKernelProvider): Disposable; } @@ -1757,7 +1799,14 @@ declare module 'vscode' { } interface NotebookCellStatusBarItemProvider { + /** + * Implement and fire this event to signal that statusbar items have changed. The provide method will be called again. + */ onDidChangeCellStatusBarItems?: Event; + + /** + * The provider will be called when the cell scrolls into view, when its content, outputs, language, or metadata change, and when it changes execution state. + */ provideCellStatusBarItems(cell: NotebookCell, token: CancellationToken): ProviderResult; } diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index be31750123b..ad7fc64e50b 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -14,7 +14,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/notebookEditorService'; import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService'; -import { ICellRange, INotebookCellStatusBarItemProvider, INotebookDocumentFilter, INotebookExclusiveDocumentFilter, INotebookKernel, NotebookDataDto, TransientMetadata, TransientOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ICellRange, INotebookCellStatusBarItemProvider, INotebookDocumentFilter, INotebookExclusiveDocumentFilter, INotebookKernel, NotebookDataDto, TransientCellMetadata, TransientDocumentMetadata, TransientOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { IMainNotebookController, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { ExtHostContext, ExtHostNotebookShape, IExtHostContext, MainContext, MainThreadNotebookShape, NotebookExtensionDescription } from '../common/extHost.protocol'; @@ -65,17 +65,19 @@ export class MainThreadNotebooks implements MainThreadNotebookShape { async $registerNotebookProvider(extension: NotebookExtensionDescription, viewType: string, options: { transientOutputs: boolean; - transientMetadata: TransientMetadata; + transientCellMetadata: TransientCellMetadata; + transientDocumentMetadata: TransientDocumentMetadata; viewOptions?: { displayName: string; filenamePattern: (string | IRelativePattern | INotebookExclusiveDocumentFilter)[]; exclusive: boolean; }; }): Promise { - let contentOptions = { transientOutputs: options.transientOutputs, transientMetadata: options.transientMetadata }; + let contentOptions = { transientOutputs: options.transientOutputs, transientCellMetadata: options.transientCellMetadata, transientDocumentMetadata: options.transientDocumentMetadata }; const controller: IMainNotebookController = { get options() { return contentOptions; }, set options(newOptions) { - contentOptions.transientMetadata = newOptions.transientMetadata; + contentOptions.transientCellMetadata = newOptions.transientCellMetadata; + contentOptions.transientDocumentMetadata = newOptions.transientDocumentMetadata; contentOptions.transientOutputs = newOptions.transientOutputs; }, viewOptions: options.viewOptions, @@ -107,7 +109,7 @@ export class MainThreadNotebooks implements MainThreadNotebookShape { this._notebookProviders.set(viewType, { controller, disposable }); } - async $updateNotebookProviderOptions(viewType: string, options?: { transientOutputs: boolean; transientMetadata: TransientMetadata; }): Promise { + async $updateNotebookProviderOptions(viewType: string, options?: { transientOutputs: boolean; transientCellMetadata: TransientCellMetadata; transientDocumentMetadata: TransientDocumentMetadata; }): Promise { const provider = this._notebookProviders.get(viewType); if (provider && options) { diff --git a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts index 95c87645843..8306de1f23a 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts @@ -177,7 +177,7 @@ export class MainThreadNotebookKernels implements MainThreadNotebookKernelsShape // --- kernel adding/updating/removal - $addKernel(handle: number, data: INotebookKernelDto2): void { + async $addKernel(handle: number, data: INotebookKernelDto2): Promise { const that = this; const kernel = new class extends MainThreadKernel { executeNotebookCellsRequest(uri: URI, ranges: ICellRange[]): void { diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index b93758e793c..c1dcf6773ca 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1106,9 +1106,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I checkProposedApiEnabled(extension); return extHostNotebook.createNotebookCellExecution(uri, index, kernelId); }, - createNotebookController(options) { + createNotebookController(id, selector, label, executeHandler, preloads) { checkProposedApiEnabled(extension); - return extHostNotebookKernels.createKernel(extension, options); + return extHostNotebookKernels.createNotebookController(extension, id, selector, label, executeHandler, preloads); } }; diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index ea3e035b202..263eb22ad51 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -50,7 +50,7 @@ import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService'; import { TunnelCreationOptions, TunnelProviderFeatures, TunnelOptions, ProvidedPortAttributes } from 'vs/platform/remote/common/tunnel'; import { Timeline, TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor, InternalTimelineOptions } from 'vs/workbench/contrib/timeline/common/timeline'; import { revive } from 'vs/base/common/marshalling'; -import { NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEventDto, NotebookDataDto, IMainCellDto, INotebookDocumentFilter, TransientMetadata, ICellRange, INotebookDecorationRenderOptions, INotebookExclusiveDocumentFilter, IOutputDto, TransientOptions, IImmediateCellEditOperation, INotebookCellStatusBarItem } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEventDto, NotebookDataDto, IMainCellDto, INotebookDocumentFilter, TransientCellMetadata, ICellRange, INotebookDecorationRenderOptions, INotebookExclusiveDocumentFilter, IOutputDto, TransientOptions, IImmediateCellEditOperation, INotebookCellStatusBarItem, TransientDocumentMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { CallHierarchyItem } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy'; import { Dto } from 'vs/base/common/types'; import { DebugConfigurationProviderTriggerKind, TestResultState } from 'vs/workbench/api/common/extHostTypes'; @@ -874,10 +874,11 @@ export interface INotebookCellStatusBarListDto { export interface MainThreadNotebookShape extends IDisposable { $registerNotebookProvider(extension: NotebookExtensionDescription, viewType: string, options: { transientOutputs: boolean; - transientMetadata: TransientMetadata; + transientCellMetadata: TransientCellMetadata; + transientDocumentMetadata: TransientDocumentMetadata; viewOptions?: { displayName: string; filenamePattern: (string | IRelativePattern | INotebookExclusiveDocumentFilter)[]; exclusive: boolean; }; }): Promise; - $updateNotebookProviderOptions(viewType: string, options?: { transientOutputs: boolean; transientMetadata: TransientMetadata; }): Promise; + $updateNotebookProviderOptions(viewType: string, options?: { transientOutputs: boolean; transientCellMetadata: TransientCellMetadata; transientDocumentMetadata: TransientDocumentMetadata; }): Promise; $unregisterNotebookProvider(viewType: string): Promise; $registerNotebookSerializer(handle: number, extension: NotebookExtensionDescription, viewType: string, options: TransientOptions): void; @@ -923,7 +924,7 @@ export interface INotebookKernelDto2 { export interface MainThreadNotebookKernelsShape extends IDisposable { $postMessage(handle: number, editorId: string | undefined, message: any): Promise; - $addKernel(handle: number, data: INotebookKernelDto2): void; + $addKernel(handle: number, data: INotebookKernelDto2): Promise; $updateKernel(handle: number, data: Partial): void; $removeKernel(handle: number): void; } diff --git a/src/vs/workbench/api/common/extHostApiCommands.ts b/src/vs/workbench/api/common/extHostApiCommands.ts index 7c2367c41f2..534c4e6b32f 100644 --- a/src/vs/workbench/api/common/extHostApiCommands.ts +++ b/src/vs/workbench/api/common/extHostApiCommands.ts @@ -18,7 +18,7 @@ import { ICommandsExecutor, RemoveFromRecentlyOpenedAPICommand, OpenIssueReporte import { isFalsyOrEmpty } from 'vs/base/common/arrays'; import { IRange } from 'vs/editor/common/core/range'; import { IPosition } from 'vs/editor/common/core/position'; -import { TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { TransientCellMetadata, TransientDocumentMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { VSBuffer } from 'vs/base/common/buffer'; import { decodeSemanticTokensDto } from 'vs/editor/common/services/semanticTokensDto'; @@ -343,7 +343,7 @@ const newCommands: ApiCommand[] = [ new ApiCommandResult<{ viewType: string; displayName: string; - options: { transientOutputs: boolean; transientMetadata: TransientMetadata }; + options: { transientOutputs: boolean; transientCellMetadata: TransientCellMetadata; transientDocumentMetadata: TransientDocumentMetadata; }; filenamePattern: (string | types.RelativePattern | { include: string | types.RelativePattern, exclude: string | types.RelativePattern })[] }[], { viewType: string; @@ -354,7 +354,12 @@ const newCommands: ApiCommand[] = [ return { viewType: item.viewType, displayName: item.displayName, - options: { transientOutputs: item.options.transientOutputs, transientMetadata: item.options.transientMetadata }, + options: { + transientOutputs: item.options.transientOutputs, + transientMetadata: item.options.transientCellMetadata, + transientCellMetadata: item.options.transientCellMetadata, + transientDocumentMetadata: item.options.transientDocumentMetadata + }, filenamePattern: item.filenamePattern.map(pattern => typeConverters.NotebookExclusiveDocumentPattern.to(pattern)) }; })) diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 76d2868f15f..2291021e66f 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -376,7 +376,8 @@ export class ExtHostNotebookController implements ExtHostNotebookShape { const internalOptions = typeConverters.NotebookDocumentContentOptions.from(options); this._notebookProxy.$registerNotebookProvider({ id: extension.identifier, location: extension.extensionLocation, description: extension.description }, viewType, { transientOutputs: internalOptions.transientOutputs, - transientMetadata: internalOptions.transientMetadata, + transientCellMetadata: internalOptions.transientCellMetadata, + transientDocumentMetadata: internalOptions.transientDocumentMetadata, viewOptions: options?.viewOptions && viewOptionsFilenamePattern ? { displayName: options.viewOptions.displayName, filenamePattern: viewOptionsFilenamePattern, exclusive: options.viewOptions.exclusive || false } : undefined }); @@ -623,7 +624,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape { } } - private cancelOneNotebookCellExecution(cell: ExtHostCell): void { + cancelOneNotebookCellExecution(cell: ExtHostCell): void { const execution = this._activeExecutions.get(cell.uri); execution?.cancel(); } diff --git a/src/vs/workbench/api/common/extHostNotebookKernels.ts b/src/vs/workbench/api/common/extHostNotebookKernels.ts index 2629fb98062..9430414832b 100644 --- a/src/vs/workbench/api/common/extHostNotebookKernels.ts +++ b/src/vs/workbench/api/common/extHostNotebookKernels.ts @@ -8,7 +8,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import { ExtHostNotebookKernelsShape, IMainContext, INotebookKernelDto2, MainContext, MainThreadNotebookKernelsShape } from 'vs/workbench/api/common/extHost.protocol'; import * as vscode from 'vscode'; import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook'; -import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { URI, UriComponents } from 'vs/base/common/uri'; import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import * as extHostTypeConverters from 'vs/workbench/api/common/extHostTypeConverters'; @@ -16,10 +16,8 @@ import { isNonEmptyArray } from 'vs/base/common/arrays'; import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; import { asWebviewUri } from 'vs/workbench/api/common/shared/webview'; -type ExecuteHandler = (cells: vscode.NotebookCell[], controller: vscode.NotebookController) => void; -type InterruptHandler = (notebook: vscode.NotebookDocument) => void; - interface IKernelData { + extensionId: ExtensionIdentifier, controller: vscode.NotebookController; onDidChangeSelection: Emitter<{ selected: boolean; notebook: vscode.NotebookDocument; }>; onDidReceiveMessage: Emitter<{ editor: vscode.NotebookEditor, message: any }>; @@ -40,11 +38,20 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { this._proxy = mainContext.getProxy(MainContext.MainThreadNotebookKernels); } - createKernel(extension: IExtensionDescription, options: vscode.NotebookControllerOptions): vscode.NotebookController { + createNotebookController(extension: IExtensionDescription, id: string, selector: vscode.NotebookSelector, label: string, handler?: vscode.NotebookExecutionHandler, preloads?: vscode.NotebookKernelPreload[]): vscode.NotebookController { + + for (let data of this._kernelData.values()) { + if (data.controller.id === id) { + throw new Error(`notebook controller with id '${id}' ALREADY exist`); + } + } 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; const commandDisposables = new DisposableStore(); @@ -52,20 +59,25 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { const onDidReceiveMessage = new Emitter<{ editor: vscode.NotebookEditor, message: any }>(); const data: INotebookKernelDto2 = { - id: options.id, - selector: options.selector, + id: id, + selector: selector, extensionId: extension.identifier, extensionLocation: extension.extensionLocation, - label: options.label, - supportedLanguages: [], + label: label || extension.identifier.value, + supportedLanguages: _defaultSupportedLanguages, + preloads: preloads ? preloads.map(extHostTypeConverters.NotebookKernelPreload.from) : [] }; // - let _executeHandler: ExecuteHandler = options.executeHandler; - let _interruptHandler: InterruptHandler | undefined = options.interruptHandler; + let _executeHandler: vscode.NotebookExecutionHandler = handler ?? _defaultExecutHandler; + let _interruptHandler: vscode.NotebookInterruptHandler | undefined; // todo@jrieken the selector needs to be massaged - this._proxy.$addKernel(handle, data); + this._proxy.$addKernel(handle, data).catch(err => { + // this can happen when a kernel with that ID is already registered + console.log(err); + isDisposed = true; + }); // update: all setters write directly into the dto object // and trigger an update. the actual update will only happen @@ -91,7 +103,7 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { return data.label; }, set label(value) { - data.label = value; + data.label = value ?? extension.displayName ?? extension.name; _update(); }, get description() { @@ -112,7 +124,7 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { return data.supportedLanguages; }, set supportedLanguages(value) { - data.supportedLanguages = isNonEmptyArray(value) ? value : ['plaintext']; + data.supportedLanguages = isNonEmptyArray(value) ? value : _defaultSupportedLanguages; _update(); }, get hasExecutionOrder() { @@ -123,15 +135,14 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { _update(); }, get preloads() { - return data.preloads && data.preloads.map(extHostTypeConverters.NotebookKernelPreload.to); - }, - set preloads(value) { - data.preloads = value && value.map(extHostTypeConverters.NotebookKernelPreload.from); - _update(); + return data.preloads ? data.preloads.map(extHostTypeConverters.NotebookKernelPreload.to) : []; }, get executeHandler() { return _executeHandler; }, + set executeHandler(value) { + _executeHandler = value ?? _defaultExecutHandler; + }, get interruptHandler() { return _interruptHandler; }, @@ -162,17 +173,12 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { postMessage(message, editor) { return that._proxy.$postMessage(handle, editor && that._extHostNotebook.getIdByEditor(editor), message); }, - asWebviewUri(uri: URI, editor) { - return asWebviewUri(that._initData.environment, that._extHostNotebook.getIdByEditor(editor)!, uri); + asWebviewUri(uri: URI) { + return asWebviewUri(that._initData.environment, data.id, uri); } }; - this._kernelData.set(handle, { controller, onDidChangeSelection, onDidReceiveMessage }); - - controller.supportedLanguages = options.supportedLanguages ?? []; - controller.interruptHandler = options.interruptHandler; - controller.hasExecutionOrder = options.hasExecutionOrder ?? false; - + this._kernelData.set(handle, { extensionId: extension.identifier, controller, onDidChangeSelection, onDidReceiveMessage }); return controller; } @@ -203,7 +209,7 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { } try { - obj.controller.executeHandler(cells, obj.controller); + obj.controller.executeHandler.call(obj.controller, cells, obj.controller); } catch (err) { // console.error(err); @@ -221,7 +227,17 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { throw new Error('MISSING notebook'); } if (obj.controller.interruptHandler) { - obj.controller.interruptHandler(document.notebookDocument); + obj.controller.interruptHandler.call(obj.controller); + } + + // we do both? interrupt and cancellation or should we be selective? + for (const range of ranges) { + for (let i = range.start; i < range.end; i++) { + const cell = document.getCellFromIndex(i); + if (cell) { + this._extHostNotebook.cancelOneNotebookCellExecution(cell); + } + } } } diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 66a4a2096c7..e360f85de1a 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -1419,7 +1419,17 @@ export namespace NotebookRange { export namespace NotebookCellMetadata { export function to(data: notebooks.NotebookCellMetadata): types.NotebookCellMetadata { - return new types.NotebookCellMetadata(data.inputCollapsed, data.outputCollapsed).with({ custom: data.custom }); + return new types.NotebookCellMetadata().with({ + ...data, + ...{ + executionOrder: null, + lastRunSuccess: null, + runState: null, + runStartTime: null, + runStartTimeAdjustment: null, + runEndTime: null + } + }); } } @@ -1430,7 +1440,7 @@ export namespace NotebookDocumentMetadata { } export function to(data: notebooks.NotebookDocumentMetadata): types.NotebookDocumentMetadata { - return new types.NotebookDocumentMetadata(data.trusted, data.custom); + return new types.NotebookDocumentMetadata().with(data); } } @@ -1633,15 +1643,16 @@ export namespace NotebookDocumentContentOptions { export function from(options: vscode.NotebookDocumentContentOptions | undefined): notebooks.TransientOptions { return { transientOutputs: options?.transientOutputs ?? false, - transientMetadata: { - ...options?.transientMetadata, + transientCellMetadata: { + ...(options?.transientCellMetadata ?? options?.transientMetadata), executionOrder: true, runState: true, runStartTime: true, runStartTimeAdjustment: true, runEndTime: true, lastRunSuccess: true - } + }, + transientDocumentMetadata: options?.transientDocumentMetadata ?? {} }; } } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 0ffbab2f9be..3d510c2f430 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -2916,11 +2916,16 @@ export class NotebookRange { if (start < 0) { throw illegalArgument('start must be positive'); } - if (end < start) { - throw illegalArgument('end cannot be smaller than start'); + if (end < 0) { + throw illegalArgument('end must be positive'); + } + if (start <= end) { + this._start = start; + this._end = end; + } else { + this._start = end; + this._end = start; } - this._start = start; - this._end = end; } with(change: { start?: number, end?: number }): NotebookRange { diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts index 1630f25609b..24c56ad1139 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts @@ -18,8 +18,8 @@ import { InputFocusedContext, InputFocusedContextKey } from 'vs/platform/context 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 } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { CellEditType, CellKind, ICellEditOperation, ICellRange, INotebookDocumentFilter, isDocumentExcludePattern, NotebookCellMetadata, NotebookCellExecutionState, NOTEBOOK_EDITOR_CURSOR_BOUNDARY, TransientMetadata, SelectionStateType, ICellReplaceEdit } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +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, INotebookDocumentFilter, isDocumentExcludePattern, NotebookCellMetadata, NotebookCellExecutionState, NOTEBOOK_EDITOR_CURSOR_BOUNDARY, TransientCellMetadata, TransientDocumentMetadata, SelectionStateType, ICellReplaceEdit } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -53,14 +53,12 @@ const CHANGE_CELL_TO_CODE_COMMAND_ID = 'notebook.cell.changeToCode'; const CHANGE_CELL_TO_MARKDOWN_COMMAND_ID = 'notebook.cell.changeToMarkdown'; const EDIT_CELL_COMMAND_ID = 'notebook.cell.edit'; -const QUIT_EDIT_CELL_COMMAND_ID = 'notebook.cell.quitEdit'; const DELETE_CELL_COMMAND_ID = 'notebook.cell.delete'; const CANCEL_CELL_COMMAND_ID = 'notebook.cell.cancelExecution'; const EXECUTE_CELL_SELECT_BELOW = 'notebook.cell.executeAndSelectBelow'; const EXECUTE_CELL_INSERT_BELOW = 'notebook.cell.executeAndInsertBelow'; const CLEAR_CELL_OUTPUTS_COMMAND_ID = 'notebook.cell.clearOutputs'; -const CHANGE_CELL_LANGUAGE = 'notebook.cell.changeLanguage'; const CENTER_ACTIVE_CELL = 'notebook.centerActiveCell'; const FOCUS_IN_OUTPUT_COMMAND_ID = 'notebook.cell.focusInOutput'; @@ -1339,7 +1337,7 @@ interface IChangeCellContext extends INotebookCellActionContext { language?: string; } -export class ChangeCellLanguageAction extends NotebookCellAction { +registerAction2(class ChangeCellLanguageAction extends NotebookCellAction { constructor() { super({ id: CHANGE_CELL_LANGUAGE, @@ -1495,8 +1493,7 @@ export class ChangeCellLanguageAction extends NotebookCellAction { return fakeResource; } -} -registerAction2(ChangeCellLanguageAction); +}); registerAction2(class extends NotebookAction { constructor() { @@ -1695,7 +1692,7 @@ CommandsRegistry.registerCommand('notebook.trust', (accessor, args) => { CommandsRegistry.registerCommand('_resolveNotebookContentProvider', (accessor, args): { viewType: string; displayName: string; - options: { transientOutputs: boolean; transientMetadata: TransientMetadata; }; + options: { transientOutputs: boolean; transientCellMetadata: TransientCellMetadata; transientDocumentMetadata: TransientDocumentMetadata; }; filenamePattern: (string | glob.IRelativePattern | { include: string | glob.IRelativePattern, exclude: string | glob.IRelativePattern; })[]; }[] => { const notebookService = accessor.get(INotebookService); @@ -1724,7 +1721,11 @@ CommandsRegistry.registerCommand('_resolveNotebookContentProvider', (accessor, a viewType: provider.id, displayName: provider.displayName, filenamePattern: filenamePatterns, - options: { transientMetadata: provider.options.transientMetadata, transientOutputs: provider.options.transientOutputs } + options: { + transientCellMetadata: provider.options.transientCellMetadata, + transientDocumentMetadata: provider.options.transientDocumentMetadata, + transientOutputs: provider.options.transientOutputs + } }; }); }); 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 13911ac1194..4e03ac9d920 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/statusBar/cellStatusBar.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/statusBar/cellStatusBar.ts @@ -6,7 +6,7 @@ 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 { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { ICellViewModel, INotebookEditor, INotebookEditorContribution } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { registerNotebookContribution } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions'; import { CellViewModel, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; @@ -18,6 +18,8 @@ export class NotebookStatusBarController extends Disposable implements INotebook private readonly _visibleCells = new Map(); + private readonly _viewModelDisposables = new DisposableStore(); + constructor( private readonly _notebookEditor: INotebookEditor, @INotebookCellStatusBarService private readonly _notebookCellStatusBarService: INotebookCellStatusBarService @@ -25,11 +27,22 @@ export class NotebookStatusBarController extends Disposable implements INotebook super(); this._updateVisibleCells(); this._register(this._notebookEditor.onDidChangeVisibleRanges(this._updateVisibleCells, this)); - this._register(this._notebookEditor.onDidChangeModel(this._updateEverything, this)); + this._register(this._notebookEditor.onDidChangeModel(this._onModelChange, this)); this._register(this._notebookCellStatusBarService.onDidChangeProviders(this._updateEverything, this)); this._register(this._notebookCellStatusBarService.onDidChangeItems(this._updateEverything, this)); } + private _onModelChange() { + this._viewModelDisposables.clear(); + const vm = this._notebookEditor.viewModel; + if (!vm) { + return; + } + + this._viewModelDisposables.add(vm.onDidChangeViewCells(() => this._updateEverything())); + this._updateEverything(); + } + private _updateEverything(): void { this._visibleCells.forEach(cell => cell.dispose()); this._visibleCells.clear(); 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 0429bd8d2af..f88cb921fdc 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/statusBar/statusBarProviders.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/statusBar/statusBarProviders.ts @@ -3,36 +3,55 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { localize } from 'vs/nls'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; -import { isWindows } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService'; -import { CellStatusbarAlignment, INotebookCellStatusBarItem, INotebookCellStatusBarItemList, INotebookCellStatusBarItemProvider, INotebookDocumentFilter } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, CellStatusbarAlignment, INotebookCellStatusBarItem, INotebookCellStatusBarItemList, INotebookCellStatusBarItemProvider, INotebookDocumentFilter } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { CHANGE_CELL_LANGUAGE, EXECUTE_CELL_COMMAND_ID, QUIT_EDIT_CELL_COMMAND_ID } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; class CellStatusBarPlaceholderProvider implements INotebookCellStatusBarItemProvider { readonly selector: INotebookDocumentFilter = { filenamePattern: '**/*' }; - constructor(@INotebookService private readonly _notebookService: INotebookService) { } + constructor( + @INotebookService private readonly _notebookService: INotebookService, + @IKeybindingService private readonly _keybindingService: IKeybindingService, + ) { } - onDidChangeStatusBarItems?: Event | undefined; - - async provideCellStatusBarItems(uri: URI, index: number, token: CancellationToken): Promise { + async provideCellStatusBarItems(uri: URI, index: number, token: CancellationToken): Promise { const doc = this._notebookService.getNotebookTextModel(uri); const cell = doc?.cells[index]; - if (typeof cell?.metadata.runState !== 'undefined' || typeof cell?.metadata.lastRunSuccess !== 'undefined') { - return { items: [] }; + if (!cell || typeof cell.metadata.runState !== 'undefined' || typeof cell.metadata.lastRunSuccess !== 'undefined') { + return; + } + + let text: string; + if (cell.cellKind === CellKind.Code) { + const keybinding = this._keybindingService.lookupKeybinding(EXECUTE_CELL_COMMAND_ID)?.getLabel(); + if (!keybinding) { + return; + } + + text = localize('notebook.cell.status.codeExecuteTip', "Press {0} to execute cell", keybinding); + } else { + const keybinding = this._keybindingService.lookupKeybinding(QUIT_EDIT_CELL_COMMAND_ID)?.getLabel(); + if (!keybinding) { + return; + } + + text = localize('notebook.cell.status.markdownExecuteTip', "Press {0} to stop editing", keybinding); } - const text = isWindows ? 'Ctrl + Alt + Enter to run' : 'Ctrl + Enter to run'; const item = { text, tooltip: text, @@ -46,12 +65,47 @@ class CellStatusBarPlaceholderProvider implements INotebookCellStatusBarItemProv } } +class CellStatusBarLanguagePickerProvider implements INotebookCellStatusBarItemProvider { + readonly selector: INotebookDocumentFilter = { + filenamePattern: '**/*' + }; + + constructor( + @INotebookService private readonly _notebookService: INotebookService, + @IModeService private readonly _modeService: IModeService, + ) { } + + async provideCellStatusBarItems(uri: URI, index: number, _token: CancellationToken): Promise { + const doc = this._notebookService.getNotebookTextModel(uri); + const cell = doc?.cells[index]; + if (!cell) { + return; + } + + const modeId = cell.cellKind === CellKind.Markdown ? + 'markdown' : + (this._modeService.getModeIdForLanguageName(cell.language) || cell.language); + const text = this._modeService.getLanguageName(modeId) || this._modeService.getLanguageName('plaintext'); + const item = { + text, + command: CHANGE_CELL_LANGUAGE, + tooltip: localize('notebook.cell.status.language', "Select Cell Language Mode"), + alignment: CellStatusbarAlignment.Right, + priority: -Number.MAX_SAFE_INTEGER + }; + return { + items: [item] + }; + } +} + class BuiltinCellStatusBarProviders extends Disposable { constructor( @IInstantiationService instantiationService: IInstantiationService, @INotebookCellStatusBarService notebookCellStatusBarService: INotebookCellStatusBarService) { super(); this._register(notebookCellStatusBarService.registerCellStatusBarItemProvider(instantiationService.createInstance(CellStatusBarPlaceholderProvider))); + this._register(notebookCellStatusBarService.registerCellStatusBarItemProvider(instantiationService.createInstance(CellStatusBarLanguagePickerProvider))); } } diff --git a/src/vs/workbench/contrib/notebook/browser/diff/diffElementViewModel.ts b/src/vs/workbench/contrib/notebook/browser/diff/diffElementViewModel.ts index 53c6484ba10..925415cd8ba 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/diffElementViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/diffElementViewModel.ts @@ -493,11 +493,11 @@ export function getFormatedMetadataJSON(documentTextModel: NotebookTextModel, me let filteredMetadata: { [key: string]: any } = {}; if (documentTextModel) { - const transientMetadata = documentTextModel.transientOptions.transientMetadata; + const transientCellMetadata = documentTextModel.transientOptions.transientCellMetadata; const keys = new Set([...Object.keys(metadata)]); for (let key of keys) { - if (!(transientMetadata[key as keyof NotebookCellMetadata]) + if (!(transientCellMetadata[key as keyof NotebookCellMetadata]) ) { filteredMetadata[key] = metadata[key as keyof NotebookCellMetadata]; } diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebook.css b/src/vs/workbench/contrib/notebook/browser/media/notebook.css index 278cabbc997..08f00a78366 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebook.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebook.css @@ -420,7 +420,6 @@ .monaco-workbench .notebookOverlay .cell-statusbar-container .cell-status-left, .monaco-workbench .notebookOverlay .cell-statusbar-container .cell-status-right { - padding-right: 12px; display: flex; z-index: 26; } @@ -448,24 +447,15 @@ cursor: pointer; } -.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-language-picker { - cursor: pointer; -} - .monaco-workbench .notebookOverlay .cell-statusbar-container .cell-run-duration { margin-right: 8px; } -.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-run-duration, -.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-status-message { +.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-run-duration { display: flex; align-items: center; } -.monaco-workbench .notebookOverlay .cell-statusbar-container .cell-status-message { - margin-right: 6px; -} - .monaco-workbench .notebookOverlay .cell-statusbar-container .cell-run-status { height: 100%; display: flex; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index b022a7314f1..635346a0f09 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -72,6 +72,8 @@ export const NOTEBOOK_INTERRUPTIBLE_KERNEL = new RawContextKey('noteboo //#region Shared commands export const EXPAND_CELL_INPUT_COMMAND_ID = 'notebook.cell.expandCellInput'; export const EXECUTE_CELL_COMMAND_ID = 'notebook.cell.execute'; +export const CHANGE_CELL_LANGUAGE = 'notebook.cell.changeLanguage'; +export const QUIT_EDIT_CELL_COMMAND_ID = 'notebook.cell.quitEdit'; //#endregion diff --git a/src/vs/workbench/contrib/notebook/browser/notebookCellStatusBarServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookCellStatusBarServiceImpl.ts index 35b74591b6a..01060a99f74 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookCellStatusBarServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookCellStatusBarServiceImpl.ts @@ -43,9 +43,9 @@ export class NotebookCellStatusBarService extends Disposable implements INoteboo async getStatusBarItemsForCell(docUri: URI, cellIndex: number, viewType: string, token: CancellationToken): Promise { const providers = this._providers.filter(p => notebookDocumentFilterMatch(p.selector, viewType, docUri)); - return await Promise.all(providers.map(p => { + return await Promise.all(providers.map(async p => { try { - return p.provideCellStatusBarItems(docUri, cellIndex, token); + return await p.provideCellStatusBarItems(docUri, cellIndex, token) ?? { items: [] }; } catch (e) { onUnexpectedExternalError(e); return { items: [] }; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookKernelServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookKernelServiceImpl.ts index 6ceaa6b68f6..448dea23ef3 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookKernelServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookKernelServiceImpl.ts @@ -40,7 +40,7 @@ export class NotebookKernelService implements INotebookKernelService { registerKernel(kernel: INotebookKernel2): IDisposable { if (this._kernels.has(kernel.id)) { - throw new Error(`KERNEL with id '${kernel.id}' already exists`); + throw new Error(`NOTEBOOK CONTROLLER with id '${kernel.id}' already exists`); } this._kernels.set(kernel.id, kernel); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index 400ebd17df7..55aa4501c5c 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -83,18 +83,23 @@ export class NotebookKernelProviderInfoStore { } export class NotebookProviderInfoStore extends Disposable { + private static readonly CUSTOM_EDITORS_STORAGE_ID = 'notebookEditors'; private static readonly CUSTOM_EDITORS_ENTRY_ID = 'editors'; private readonly _memento: Memento; private _handled: boolean = false; + + private readonly _contributedEditors = new Map(); + private readonly _contributedEditorDisposables = new DisposableStore(); + constructor( - storageService: IStorageService, - extensionService: IExtensionService, - private readonly _editorOverrideService: IEditorOverrideService, - private readonly _instantiationService: IInstantiationService, - private readonly _configurationService: IConfigurationService, - private readonly _accessibilityService: IAccessibilityService, + @IStorageService storageService: IStorageService, + @IExtensionService extensionService: IExtensionService, + @IEditorOverrideService private readonly _editorOverrideService: IEditorOverrideService, + @IConfigurationService private readonly _configurationService: IConfigurationService, + @IAccessibilityService private readonly _accessibilityService: IAccessibilityService, + @IInstantiationService private readonly _instantiationService: IInstantiationService, ) { super(); this._memento = new Memento(NotebookProviderInfoStore.CUSTOM_EDITORS_STORAGE_ID, storageService); @@ -110,18 +115,25 @@ export class NotebookProviderInfoStore extends Disposable { if (!this._handled) { // there is no extension point registered for notebook content provider // clear the memento and cache - this.clear(); + this._clear(); mementoObject[NotebookProviderInfoStore.CUSTOM_EDITORS_ENTRY_ID] = []; this._memento.saveMemento(); this._updateProviderExtensionsInfo(); } })); + + notebookProviderExtensionPoint.setHandler(extensions => this._setupHandler(extensions)); } - setupHandler(extensions: readonly IExtensionPointUser[]) { + override dispose(): void { + this._clear(); + super.dispose(); + } + + private _setupHandler(extensions: readonly IExtensionPointUser[]) { this._handled = true; - this.clear(); + this._clear(); for (const extension of extensions) { for (const notebookContribution of extension.value) { @@ -174,10 +186,10 @@ export class NotebookProviderInfoStore extends Disposable { } - private registerContributionPoint(notebookProviderInfo: NotebookProviderInfo): void { + private _registerContributionPoint(notebookProviderInfo: NotebookProviderInfo): void { for (const selector of notebookProviderInfo.selectors) { const globPattern = (selector as INotebookExclusiveDocumentFilter).include || selector as glob.IRelativePattern | string; - this._register(this._editorOverrideService.registerContributionPoint( + this._contributedEditorDisposables.add(this._editorOverrideService.registerContributionPoint( globPattern, priorityToRank(notebookProviderInfo.exclusive ? ContributedEditorPriority.exclusive : notebookProviderInfo.priority), { @@ -215,10 +227,10 @@ export class NotebookProviderInfoStore extends Disposable { } } - private readonly _contributedEditors = new Map(); - clear() { + private _clear(): void { this._contributedEditors.clear(); + this._contributedEditorDisposables.clear(); } get(viewType: string): NotebookProviderInfo | undefined { @@ -230,7 +242,7 @@ export class NotebookProviderInfoStore extends Disposable { return; } this._contributedEditors.set(info.id, info); - this.registerContributionPoint(info); + this._registerContributionPoint(info); const mementoObject = this._memento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); mementoObject[NotebookProviderInfoStore.CUSTOM_EDITORS_ENTRY_ID] = Array.from(this._contributedEditors.values()); @@ -318,8 +330,6 @@ class ModelData implements IDisposable { } } - - export class NotebookService extends Disposable implements INotebookService, IEditorTypesHandler { declare readonly _serviceBrand: undefined; @@ -352,25 +362,15 @@ export class NotebookService extends Disposable implements INotebookService, IEd @IExtensionService private readonly _extensionService: IExtensionService, @IConfigurationService private readonly _configurationService: IConfigurationService, @IAccessibilityService private readonly _accessibilityService: IAccessibilityService, - @IStorageService private readonly _storageService: IStorageService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @ICodeEditorService private readonly _codeEditorService: ICodeEditorService, @IConfigurationService private readonly configurationService: IConfigurationService, - @IEditorOverrideService private readonly _editorOverrideService: IEditorOverrideService, ) { super(); - this._notebookProviderInfoStore = new NotebookProviderInfoStore( - this._storageService, - this._extensionService, this._editorOverrideService, - this._instantiationService, this._configurationService, - this._accessibilityService - ); + this._notebookProviderInfoStore = _instantiationService.createInstance(NotebookProviderInfoStore); this._register(this._notebookProviderInfoStore); - notebookProviderExtensionPoint.setHandler((extensions) => { - this._notebookProviderInfoStore.setupHandler(extensions); - }); notebookRendererExtensionPoint.setHandler((renderers) => { this._notebookRenderersInfoStore.clear(); 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 b82b758f9bf..8598cac1a98 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets.ts @@ -7,22 +7,19 @@ import * as DOM from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { SimpleIconLabel } from 'vs/base/browser/ui/iconLabel/simpleIconLabel'; import { WorkbenchActionExecutedClassification, WorkbenchActionExecutedEvent } from 'vs/base/common/actions'; -import { stripIcons } from 'vs/base/common/iconLabels'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { Emitter, Event } from 'vs/base/common/event'; +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 { IModeService } from 'vs/editor/common/services/modeService'; -import { localize } from 'vs/nls'; 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 { ChangeCellLanguageAction, INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions'; -import { ICellViewModel, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { CellKind, CellStatusbarAlignment, INotebookCellStatusBarItem } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions'; +import { CellStatusbarAlignment, INotebookCellStatusBarItem } from 'vs/workbench/contrib/notebook/common/notebookCommon'; const $ = DOM.$; @@ -41,7 +38,6 @@ export const enum ClickTargetType { export class CellEditorStatusBar extends Disposable { readonly cellRunStatusContainer: HTMLElement; readonly statusBarContainer: HTMLElement; - readonly languageStatusBarItem: CellLanguageStatusBarItem; readonly durationContainer: HTMLElement; private readonly leftContributedItemsContainer: HTMLElement; @@ -65,7 +61,6 @@ export class CellEditorStatusBar extends Disposable { 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.languageStatusBarItem = instantiationService.createInstance(CellLanguageStatusBarItem, rightItemsContainer); this.itemsDisposable = this._register(new DisposableStore()); @@ -104,7 +99,6 @@ export class CellEditorStatusBar extends Disposable { update(context: INotebookCellActionContext) { this.currentContext = context; this.itemsDisposable.clear(); - this.languageStatusBarItem.update(context.cell, context.notebookEditor); this.updateStatusBarItems(); } @@ -224,61 +218,6 @@ class CellStatusBarItem extends Disposable { } } -export class CellLanguageStatusBarItem extends Disposable { - private readonly labelElement: HTMLElement; - - private cell: ICellViewModel | undefined; - private editor: INotebookEditor | undefined; - - private cellDisposables: DisposableStore; - - constructor( - readonly container: HTMLElement, - @IModeService private readonly modeService: IModeService, - @IInstantiationService private readonly instantiationService: IInstantiationService - ) { - super(); - this.labelElement = DOM.append(container, $('.cell-language-picker.cell-status-item')); - this.labelElement.tabIndex = 0; - this.labelElement.classList.add('cell-status-item-has-command'); - - this._register(DOM.addDisposableListener(this.labelElement, DOM.EventType.CLICK, () => { - this.run(); - })); - this._register(DOM.addDisposableListener(this.labelElement, DOM.EventType.KEY_DOWN, e => { - const event = new StandardKeyboardEvent(e); - if (event.equals(KeyCode.Space) || event.equals(KeyCode.Enter)) { - this.run(); - } - })); - this._register(this.cellDisposables = new DisposableStore()); - } - - private run() { - this.instantiationService.invokeFunction(accessor => { - if (!this.editor || !this.editor.hasModel() || !this.cell) { - return; - } - new ChangeCellLanguageAction().run(accessor, { notebookEditor: this.editor, cell: this.cell }); - }); - } - - update(cell: ICellViewModel, editor: INotebookEditor): void { - this.cellDisposables.clear(); - this.cell = cell; - this.editor = editor; - - this.render(); - this.cellDisposables.add(this.cell.model.onDidChangeLanguage(() => this.render())); - } - - private render(): void { - const modeId = this.cell?.cellKind === CellKind.Markdown ? 'markdown' : this.modeService.getModeIdForLanguageName(this.cell!.language) || this.cell!.language; - this.labelElement.textContent = this.modeService.getLanguageName(modeId) || this.modeService.getLanguageName('plaintext'); - this.labelElement.title = localize('notebook.cell.status.language', "Select Cell Language Mode"); - } -} - declare const ResizeObserver: any; export interface IResizeObserver { diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts index 31d4fb746b9..b5e6a193a79 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts @@ -159,11 +159,11 @@ export class NotebookCellTextModel extends Disposable implements ICell { private _getPersisentMetadata() { let filteredMetadata: { [key: string]: any } = {}; - const transientMetadata = this.transientOptions.transientMetadata; + const transientCellMetadata = this.transientOptions.transientCellMetadata; const keys = new Set([...Object.keys(this.metadata)]); for (let key of keys) { - if (!(transientMetadata[key as keyof NotebookCellMetadata]) + if (!(transientCellMetadata[key as keyof NotebookCellMetadata]) ) { filteredMetadata[key] = this.metadata[key as keyof NotebookCellMetadata]; } diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts index 2db4d08fc7e..fd36077f502 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts @@ -193,7 +193,7 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel private _cells: NotebookCellTextModel[] = []; metadata: NotebookDocumentMetadata = notebookDocumentMetadataDefaults; - transientOptions: TransientOptions = { transientMetadata: {}, transientOutputs: false }; + transientOptions: TransientOptions = { transientCellMetadata: {}, transientDocumentMetadata: {}, transientOutputs: false }; private _versionId = 0; /** @@ -515,25 +515,28 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel private _updateNotebookMetadata(metadata: NotebookDocumentMetadata, computeUndoRedo: boolean) { const oldMetadata = this.metadata; - this.metadata = metadata; + const triggerDirtyChange = this._isDocumentMetadataChanged(this.metadata, metadata); - if (computeUndoRedo) { - const that = this; - this._operationManager.pushEditOperation(new class implements IResourceUndoRedoElement { - readonly type: UndoRedoElementType.Resource = UndoRedoElementType.Resource; - get resource() { - return that.uri; - } - readonly label = 'Update Notebook Metadata'; - undo() { - that._updateNotebookMetadata(oldMetadata, false); - } - redo() { - that._updateNotebookMetadata(metadata, false); - } - }(), undefined, undefined); + if (triggerDirtyChange) { + if (computeUndoRedo) { + const that = this; + this._operationManager.pushEditOperation(new class implements IResourceUndoRedoElement { + readonly type: UndoRedoElementType.Resource = UndoRedoElementType.Resource; + get resource() { + return that.uri; + } + readonly label = 'Update Notebook Metadata'; + undo() { + that._updateNotebookMetadata(oldMetadata, false); + } + redo() { + that._updateNotebookMetadata(metadata, false); + } + }(), undefined, undefined); + } } + this.metadata = metadata; this._eventEmitter.emit({ kind: NotebookCellsChangeType.ChangeDocumentMetadata, metadata: this.metadata, transient: this._isDocumentMetadataChangeTransient(oldMetadata, metadata) }, true); } @@ -595,20 +598,42 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel } + private _isDocumentMetadataChanged(a: NotebookDocumentMetadata, b: NotebookDocumentMetadata) { + const keys = new Set([...Object.keys(a || {}), ...Object.keys(b || {})]); + for (let key of keys) { + if (key === 'custom') { + if (!this._customMetadataEqual(a[key], b[key]) + && + !(this.transientOptions.transientCellMetadata[key as keyof NotebookCellMetadata]) + ) { + return true; + } + } else if ( + (a[key as keyof NotebookDocumentMetadata] !== b[key as keyof NotebookDocumentMetadata]) + && + !(this.transientOptions.transientDocumentMetadata[key as keyof NotebookDocumentMetadata]) + ) { + return true; + } + } + + return false; + } + private _isCellMetadataChanged(a: NotebookCellMetadata, b: NotebookCellMetadata) { const keys = new Set([...Object.keys(a || {}), ...Object.keys(b || {})]); for (let key of keys) { if (key === 'custom') { if (!this._customMetadataEqual(a[key], b[key]) && - !(this.transientOptions.transientMetadata[key as keyof NotebookCellMetadata]) + !(this.transientOptions.transientCellMetadata[key as keyof NotebookCellMetadata]) ) { return true; } } else if ( (a[key as keyof NotebookCellMetadata] !== b[key as keyof NotebookCellMetadata]) && - !(this.transientOptions.transientMetadata[key as keyof NotebookCellMetadata]) + !(this.transientOptions.transientCellMetadata[key as keyof NotebookCellMetadata]) ) { return true; } diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 0bf4ecb9c44..a22954a200e 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -92,11 +92,13 @@ export interface NotebookCellMetadata { custom?: { [key: string]: unknown }; } -export type TransientMetadata = { [K in keyof NotebookCellMetadata]?: boolean }; +export type TransientCellMetadata = { [K in keyof NotebookCellMetadata]?: boolean }; +export type TransientDocumentMetadata = { [K in keyof NotebookDocumentMetadata]?: boolean }; export interface TransientOptions { transientOutputs: boolean; - transientMetadata: TransientMetadata; + transientCellMetadata: TransientCellMetadata; + transientDocumentMetadata: TransientDocumentMetadata; } export interface INotebookMimeTypeSelector { @@ -793,7 +795,7 @@ export interface INotebookKernelProvider { export interface INotebookCellStatusBarItemProvider { selector: INotebookDocumentFilter; onDidChangeStatusBarItems?: Event; - provideCellStatusBarItems(uri: URI, index: number, token: CancellationToken): Promise; + provideCellStatusBarItems(uri: URI, index: number, token: CancellationToken): Promise; } export class CellSequence implements ISequence { diff --git a/src/vs/workbench/contrib/notebook/common/notebookProvider.ts b/src/vs/workbench/contrib/notebook/common/notebookProvider.ts index a47f5375465..25fe5afc6bd 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookProvider.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookProvider.ts @@ -61,7 +61,8 @@ export class NotebookProviderInfo { this.dynamicContribution = descriptor.dynamicContribution; this.exclusive = descriptor.exclusive; this._options = { - transientMetadata: {}, + transientCellMetadata: {}, + transientDocumentMetadata: {}, transientOutputs: false }; } diff --git a/src/vs/workbench/contrib/notebook/test/notebookServiceImpl.test.ts b/src/vs/workbench/contrib/notebook/test/notebookServiceImpl.test.ts index cac96403a87..f7db87dad57 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookServiceImpl.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookServiceImpl.test.ts @@ -31,11 +31,9 @@ suite('NotebookProviderInfoStore', function () { override onDidRegisterExtensions = Event.None; }, instantiationService.createInstance(EditorOverrideService), - instantiationService, new TestConfigurationService(), - new class extends mock() { - - } + new class extends mock() { }, + instantiationService, ); const fooInfo = new NotebookProviderInfo({ diff --git a/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts b/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts index 213b6c019be..86556d7032b 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts @@ -34,7 +34,7 @@ suite('NotebookViewModel', () => { instantiationService.stub(IThemeService, new TestThemeService()); test('ctor', function () { - const notebook = new NotebookTextModel('notebook', URI.parse('test'), [], notebookDocumentMetadataDefaults, { transientMetadata: {}, transientOutputs: false }, undoRedoService, modelService, modeService); + const notebook = new NotebookTextModel('notebook', URI.parse('test'), [], notebookDocumentMetadataDefaults, { transientCellMetadata: {}, transientDocumentMetadata: {}, transientOutputs: false }, undoRedoService, modelService, modeService); const model = new NotebookEditorTestModel(notebook); const eventDispatcher = new NotebookEventDispatcher(); const viewModel = new NotebookViewModel('notebook', model.notebook, eventDispatcher, null, instantiationService, bulkEditService, undoRedoService, textModelService); diff --git a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts index ddc046d8f79..70a88c92cb3 100644 --- a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts @@ -47,7 +47,7 @@ export class TestCell extends NotebookCellTextModel { outputs: IOutputDto[], modeService: IModeService, ) { - super(CellUri.generate(URI.parse('test:///fake/notebook'), handle), handle, source, language, cellKind, outputs, undefined, { transientMetadata: {}, transientOutputs: false }, modeService); + super(CellUri.generate(URI.parse('test:///fake/notebook'), handle), handle, source, language, cellKind, outputs, undefined, { transientCellMetadata: {}, transientDocumentMetadata: {}, transientOutputs: false }, modeService); } } @@ -152,7 +152,7 @@ function _createTestNotebookEditor(instantiationService: TestInstantiationServic outputs: cell[3] ?? [], metadata: cell[4] }; - }), notebookDocumentMetadataDefaults, { transientMetadata: {}, transientOutputs: false }); + }), notebookDocumentMetadataDefaults, { transientCellMetadata: {}, transientDocumentMetadata: {}, transientOutputs: false }); const model = new NotebookEditorTestModel(notebook); const eventDispatcher = new NotebookEventDispatcher(); diff --git a/src/vs/workbench/test/browser/api/extHostNotebookKernel2.test.ts b/src/vs/workbench/test/browser/api/extHostNotebookKernel2.test.ts index 02b4cce8d00..3710f4b9e8a 100644 --- a/src/vs/workbench/test/browser/api/extHostNotebookKernel2.test.ts +++ b/src/vs/workbench/test/browser/api/extHostNotebookKernel2.test.ts @@ -29,7 +29,7 @@ suite('NotebookKernel', function () { override $registerCommand() { } }); rpcProtocol.set(MainContext.MainThreadNotebookKernels, new class extends mock() { - override $addKernel(handle: number, data: INotebookKernelDto2): void { + async override $addKernel(handle: number, data: INotebookKernelDto2): Promise { kernelData.set(handle, data); } override $removeKernel(handle: number) { @@ -50,7 +50,7 @@ suite('NotebookKernel', function () { test('create/dispose kernel', async function () { - const kernel = extHostNotebookKernels.createKernel(nullExtensionDescription, { id: 'foo', label: 'Foo', selector: '*', executeHandler: () => { }, supportedLanguages: ['plaintext'] }); + const kernel = extHostNotebookKernels.createNotebookController(nullExtensionDescription, 'foo', '*', 'Foo'); assert.ok(kernel); assert.strictEqual(kernel.id, 'foo'); @@ -73,7 +73,7 @@ suite('NotebookKernel', function () { test('update kernel', async function () { - const kernel = extHostNotebookKernels.createKernel(nullExtensionDescription, { id: 'foo', label: 'Foo', selector: '*', executeHandler: () => { }, supportedLanguages: ['plaintext'] }); + const kernel = extHostNotebookKernels.createNotebookController(nullExtensionDescription, 'foo', '*', 'Foo'); await rpcProtocol.sync(); assert.ok(kernel);