diff --git a/.vscode/notebooks/endgame.github-issues b/.vscode/notebooks/endgame.github-issues index 881af2c14b4..bc2fba29ddf 100644 --- a/.vscode/notebooks/endgame.github-issues +++ b/.vscode/notebooks/endgame.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-js-debug repo:microsoft/vscode-remote-release repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-remotehub\n\n$MILESTONE=milestone:\"April 2021\"" + "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-js-debug repo:microsoft/vscode-remote-release repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-remotehub\n\n$MILESTONE=milestone:\"May 2021\"" }, { "kind": 1, 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 1a1396d3edf..26f796d3fc8 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 @@ -40,9 +40,7 @@ suite('Notebook Editor', function () { }); - test.skip('showNotebookDocment', async function () { - - const count1 = vscode.notebook.notebookDocuments.length; + test('showNotebookDocment', async function () { const p = utils.asPromise(vscode.notebook.onDidOpenNotebookDocument); const uri = await utils.createRandomFile(undefined, undefined, '.nbdtest'); @@ -53,9 +51,8 @@ suite('Notebook Editor', function () { const event = await p; assert.strictEqual(event.uri.toString(), uri.toString()); - const count2 = vscode.notebook.notebookDocuments.length; - assert.strictEqual(count1 + 1, count2); - + const includes = vscode.notebook.notebookDocuments.includes(editor.document); + assert.strictEqual(true, includes); }); test('notebook editor has viewColumn', async function () { 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 76c80cb2810..74ee6bfa705 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts @@ -121,7 +121,7 @@ suite('Notebook API tests', function () { kind: vscode.NotebookCellKind.Code, outputs: [], metadata: new vscode.NotebookCellMetadata().with({ custom: { testCellMetadata: 123 } }), - latestExecutionSummary: { startTime: 10, endTime: 20 } + executionSummary: { startTime: 10, endTime: 20 } }, { value: 'test2', @@ -133,7 +133,7 @@ suite('Notebook API tests', function () { ], { testOutputMetadata: true }) ], - latestExecutionSummary: { executionOrder: 5, success: true }, + executionSummary: { executionOrder: 5, success: true }, metadata: new vscode.NotebookCellMetadata().with({ custom: { testCellMetadata: 456 } }) } ] @@ -478,8 +478,8 @@ suite('Notebook API tests', function () { assert.strictEqual(secondCell!.outputs[0].outputs[0].mime, 'text/plain'); assert.strictEqual(secondCell!.outputs[0].outputs[0].value, 'Hello World'); assert.deepStrictEqual(secondCell!.outputs[0].outputs[0].metadata, { testOutputItemMetadata: true }); - assert.strictEqual(secondCell!.latestExecutionSummary?.executionOrder, 5); - assert.strictEqual(secondCell!.latestExecutionSummary?.success, true); + assert.strictEqual(secondCell!.executionSummary?.executionOrder, 5); + assert.strictEqual(secondCell!.executionSummary?.success, true); await vscode.commands.executeCommand('notebook.cell.insertCodeCellBelow'); assert.strictEqual(getFocusedCell(vscode.window.activeNotebookEditor)?.document.getText(), ''); @@ -1205,30 +1205,30 @@ suite('Notebook API tests', function () { verifyOutputSyncKernel.controller.dispose(); }); - test('latestExecutionSummary', async () => { + test('executionSummary', async () => { const resource = await createRandomNotebookFile(); await vscode.commands.executeCommand('vscode.openWith', resource, 'notebookCoreTest'); const editor = vscode.window.activeNotebookEditor!; const cell = editor.document.cellAt(0); - assert.strictEqual(cell.latestExecutionSummary?.success, undefined); - assert.strictEqual(cell.latestExecutionSummary?.executionOrder, undefined); + assert.strictEqual(cell.executionSummary?.success, undefined); + assert.strictEqual(cell.executionSummary?.executionOrder, undefined); await vscode.commands.executeCommand('notebook.cell.execute'); assert.strictEqual(cell.outputs.length, 1, 'should execute'); - assert.ok(cell.latestExecutionSummary); - assert.strictEqual(cell.latestExecutionSummary!.success, true); - assert.strictEqual(typeof cell.latestExecutionSummary!.executionOrder, 'number'); + assert.ok(cell.executionSummary); + assert.strictEqual(cell.executionSummary!.success, true); + assert.strictEqual(typeof cell.executionSummary!.executionOrder, 'number'); }); - test('initialize latestExecutionSummary', async () => { + test('initialize executionSummary', async () => { const document = await openRandomNotebookDocument(); const cell = document.cellAt(0); - assert.strictEqual(cell.latestExecutionSummary?.success, undefined); - assert.strictEqual(cell.latestExecutionSummary?.startTime, 10); - assert.strictEqual(cell.latestExecutionSummary?.endTime, 20); + assert.strictEqual(cell.executionSummary?.success, undefined); + assert.strictEqual(cell.executionSummary?.startTime, 10); + assert.strictEqual(cell.executionSummary?.endTime, 20); }); diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index c61cff53778..b0b85e46603 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1071,8 +1071,10 @@ declare module 'vscode' { */ readonly outputs: ReadonlyArray; - // todo@API maybe just executionSummary or lastExecutionSummary? - readonly latestExecutionSummary: NotebookCellExecutionSummary | undefined; + /** + * The most recent {@link NotebookCellExecutionSummary excution summary} for this cell. + */ + readonly executionSummary?: NotebookCellExecutionSummary; } /** @@ -1269,9 +1271,17 @@ declare module 'vscode' { // static textplain(value:string): NotebookCellOutputItem; // static errortrace(value:any): NotebookCellOutputItem; + /** + * Creates `application/x.notebook.error` + * + * @param err An error for which an output item is wanted + */ + static error(err: Error): NotebookCellOutputItem; + mime: string; //todo@API string or Unit8Array? + // value: string | Uint8Array | unknown; value: unknown; metadata?: { [key: string]: any }; @@ -1320,8 +1330,10 @@ declare module 'vscode' { */ metadata?: NotebookCellMetadata; - // todo@API just executionSummary or lastExecutionSummary - latestExecutionSummary?: NotebookCellExecutionSummary; + /** + * The execution summary of this cell data. + */ + executionSummary?: NotebookCellExecutionSummary; /** * Create new cell data. Minimal cell data specifies its kind, its source value, and the @@ -1332,9 +1344,9 @@ declare module 'vscode' { * @param languageId The language identifier of the source value. * @param outputs //TODO@API remove ctor? * @param metadata //TODO@API remove ctor? - * @param latestExecutionSummary //TODO@API remove ctor? + * @param executionSummary //TODO@API remove ctor? */ - constructor(kind: NotebookCellKind, value: string, languageId: string, outputs?: NotebookCellOutput[], metadata?: NotebookCellMetadata, latestExecutionSummary?: NotebookCellExecutionSummary); + constructor(kind: NotebookCellKind, value: string, languageId: string, outputs?: NotebookCellOutput[], metadata?: NotebookCellMetadata, executionSummary?: NotebookCellExecutionSummary); } /** diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 919c4527ded..f6ad24aee5d 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -145,7 +145,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostDocumentContentProviders = rpcProtocol.set(ExtHostContext.ExtHostDocumentContentProviders, new ExtHostDocumentContentProvider(rpcProtocol, extHostDocumentsAndEditors, extHostLogService)); const extHostDocumentSaveParticipant = rpcProtocol.set(ExtHostContext.ExtHostDocumentSaveParticipant, new ExtHostDocumentSaveParticipant(extHostLogService, extHostDocuments, rpcProtocol.getProxy(MainContext.MainThreadBulkEdits))); const extHostNotebook = rpcProtocol.set(ExtHostContext.ExtHostNotebook, new ExtHostNotebookController(rpcProtocol, extHostCommands, extHostDocumentsAndEditors, extHostDocuments, extHostLogService, extensionStoragePaths)); - const extHostNotebookKernels = rpcProtocol.set(ExtHostContext.ExtHostNotebookKernels, new ExtHostNotebookKernels(rpcProtocol, initData, extHostNotebook)); + const extHostNotebookKernels = rpcProtocol.set(ExtHostContext.ExtHostNotebookKernels, new ExtHostNotebookKernels(rpcProtocol, initData, extHostNotebook, extHostLogService)); const extHostEditors = rpcProtocol.set(ExtHostContext.ExtHostEditors, new ExtHostEditors(rpcProtocol, extHostDocumentsAndEditors)); const extHostTreeViews = rpcProtocol.set(ExtHostContext.ExtHostTreeViews, new ExtHostTreeViews(rpcProtocol.getProxy(MainContext.MainThreadTreeViews), extHostCommands, extHostLogService)); const extHostEditorInsets = rpcProtocol.set(ExtHostContext.ExtHostEditorInsets, new ExtHostEditorInsets(rpcProtocol.getProxy(MainContext.MainThreadEditorInsets), extHostEditors, { ...initData.environment, remote: initData.remote })); diff --git a/src/vs/workbench/api/common/extHostNotebookDocument.ts b/src/vs/workbench/api/common/extHostNotebookDocument.ts index 58ccef8c010..78d10165bb5 100644 --- a/src/vs/workbench/api/common/extHostNotebookDocument.ts +++ b/src/vs/workbench/api/common/extHostNotebookDocument.ts @@ -87,7 +87,7 @@ export class ExtHostCell { document: data.document, get outputs() { return that._outputs.slice(0); }, get metadata() { return that._metadata; }, - get latestExecutionSummary() { return that._previousResult; } + get executionSummary() { return that._previousResult; } }); } return this._apiCell; diff --git a/src/vs/workbench/api/common/extHostNotebookKernels.ts b/src/vs/workbench/api/common/extHostNotebookKernels.ts index 3f91dd9e8f9..91007850945 100644 --- a/src/vs/workbench/api/common/extHostNotebookKernels.ts +++ b/src/vs/workbench/api/common/extHostNotebookKernels.ts @@ -20,6 +20,7 @@ import { CellEditType, IImmediateCellEditOperation, NullablePartialNotebookCellM import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { NotebookCellExecutionState } from 'vs/workbench/api/common/extHostTypes'; import { asArray } from 'vs/base/common/arrays'; +import { ILogService } from 'vs/platform/log/common/log'; interface IKernelData { extensionId: ExtensionIdentifier, @@ -40,7 +41,8 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { constructor( private readonly _mainContext: IMainContext, private readonly _initData: IExtHostInitDataService, - private readonly _extHostNotebook: ExtHostNotebookController + private readonly _extHostNotebook: ExtHostNotebookController, + @ILogService private readonly _logService: ILogService, ) { this._proxy = _mainContext.getProxy(MainContext.MainThreadNotebookKernels); } @@ -53,9 +55,12 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { } } + const handle = this._handlePool++; const that = this; + this._logService.trace(`NotebookController[${handle}], CREATED by ${extension.identifier.value}, ${id}`); + const _defaultExecutHandler = () => console.warn(`NO execute handler from notebook controller '${data.id}' of extension: '${extension.identifier}'`); let isDisposed = false; @@ -164,12 +169,14 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { throw new Error('notebook controller is DISPOSED'); } if (!associatedNotebooks.has(cell.notebook.uri)) { + that._logService.trace(`NotebookController[${handle}] NOT associated to notebook, associated to THESE notebooks:`, Array.from(associatedNotebooks.keys()).map(u => u.toString())); throw new Error(`notebook controller is NOT associated to notebook: ${cell.notebook.uri.toString()}`); } return that._createNotebookCellExecution(cell); }, dispose: () => { if (!isDisposed) { + this._logService.trace(`NotebookController[${handle}], DISPOSED`); isDisposed = true; this._kernelData.delete(handle); commandDisposables.dispose(); @@ -212,6 +219,7 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { } else { obj.associatedNotebooks.delete(notebook.uri); } + this._logService.trace(`NotebookController[${handle}] ASSOCIATE notebook`, notebook.uri.toString(), value); // send event obj.onDidChangeSelection.fire({ selected: value, @@ -236,9 +244,11 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { } try { + this._logService.trace(`NotebookController[${handle}] EXECUTE cells`, document.uri.toString(), cells.length); await obj.controller.executeHandler.call(obj.controller, cells, document.apiNotebook, obj.controller); } catch (err) { // + this._logService.error(`NotebookController[${handle}] execute cells FAILED`, err); console.error(err); } } diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index a197afac238..505ad95ddbe 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -1494,7 +1494,7 @@ export namespace NotebookCellData { source: data.value, metadata: { ...data.metadata, - ...NotebookCellPreviousExecutionResult.from(data.latestExecutionSummary ?? {}) + ...NotebookCellPreviousExecutionResult.from(data.executionSummary ?? {}) }, outputs: data.outputs ? data.outputs.map(NotebookCellOutput.from) : [] }; diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 5f9140d0621..0cd309dbf10 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -3049,15 +3049,15 @@ export class NotebookCellData { languageId: string; outputs?: NotebookCellOutput[]; metadata?: NotebookCellMetadata; - latestExecutionSummary?: vscode.NotebookCellExecutionSummary; + executionSummary?: vscode.NotebookCellExecutionSummary; - constructor(kind: NotebookCellKind, value: string, languageId: string, outputs?: NotebookCellOutput[], metadata?: NotebookCellMetadata, latestExecutionSummary?: vscode.NotebookCellExecutionSummary) { + constructor(kind: NotebookCellKind, value: string, languageId: string, outputs?: NotebookCellOutput[], metadata?: NotebookCellMetadata, executionSummary?: vscode.NotebookCellExecutionSummary) { this.kind = kind; this.value = value; this.languageId = languageId; this.outputs = outputs ?? []; this.metadata = metadata; - this.latestExecutionSummary = latestExecutionSummary; + this.executionSummary = executionSummary; NotebookCellData.validate(this); } @@ -3087,6 +3087,13 @@ export class NotebookCellOutputItem { return typeof (obj).mime === 'string'; } + static error(err: Error): NotebookCellOutputItem { + return new NotebookCellOutputItem( + 'application/x.notebook.error', + JSON.stringify({ name: err.name, message: err.message, stack: err.stack }) + ); + } + constructor( public mime: string, public value: unknown, // JSON'able diff --git a/src/vs/workbench/contrib/notebook/browser/constants.ts b/src/vs/workbench/contrib/notebook/browser/constants.ts index 06627113262..5ed8e659f83 100644 --- a/src/vs/workbench/contrib/notebook/browser/constants.ts +++ b/src/vs/workbench/contrib/notebook/browser/constants.ts @@ -6,41 +6,3 @@ // Scrollable Element 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_RIGHT_MARGIN = 16; -export const CELL_RUN_GUTTER = 28; -export const CODE_CELL_LEFT_MARGIN = 32; - -export const EDITOR_TOOLBAR_HEIGHT = 0; -export const BOTTOM_CELL_TOOLBAR_GAP = 18; -export const BOTTOM_CELL_TOOLBAR_HEIGHT = 22; -export const CELL_STATUSBAR_HEIGHT = 22; - -// Margin above editor -export const CELL_TOP_MARGIN = 6; -export const CELL_BOTTOM_MARGIN = 6; - -export const MARKDOWN_CELL_TOP_MARGIN = 8; -export const MARKDOWN_CELL_BOTTOM_MARGIN = 8; - -// Top and bottom padding inside the monaco editor in a cell, which are included in `cell.editorHeight` -// export const EDITOR_TOP_PADDING = 12; -export const EDITOR_BOTTOM_PADDING = 4; -export const EDITOR_BOTTOM_PADDING_WITHOUT_STATUSBAR = 12; - -export const CELL_OUTPUT_PADDING = 14; - -export const COLLAPSED_INDICATOR_HEIGHT = 24; - -export const MARKDOWN_PREVIEW_PADDING = 8; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts index 374c3759e3f..0ff295fff63 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/coreActions.ts @@ -35,6 +35,9 @@ import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/not import { Iterable } from 'vs/base/common/iterator'; import { flatten } from 'vs/base/common/arrays'; +// Kernel Command +export const SELECT_KERNEL_ID = 'notebook.selectKernel'; + // Notebook Commands const EXECUTE_NOTEBOOK_COMMAND_ID = 'notebook.execute'; const CANCEL_NOTEBOOK_COMMAND_ID = 'notebook.cancelExecution'; 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 f3fda701c19..73e57fe8921 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/fold/folding.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/fold/folding.ts @@ -38,7 +38,7 @@ export class FoldingController extends Disposable implements INotebookEditorCont return; } - this._localStore.add(this._notebookEditor.viewModel.eventDispatcher.onDidChangeCellState(e => { + this._localStore.add(this._notebookEditor.viewModel.viewContext.eventDispatcher.onDidChangeCellState(e => { if (e.source.editStateChanged && e.cell.cellKind === CellKind.Markup) { this._foldingModel?.recompute(); // this._updateEditorFoldingRanges(); 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 6907f5bc30c..1a5a3e8224a 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/status/editorStatus.ts @@ -5,11 +5,11 @@ import * as nls from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; -import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; +import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IQuickInputButton, IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; -import { NOTEBOOK_ACTIONS_CATEGORY } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions'; -import { getNotebookEditorFromEditorPane, INotebookEditor, NOTEBOOK_IS_ACTIVE_EDITOR } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { NOTEBOOK_ACTIONS_CATEGORY, SELECT_KERNEL_ID } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions'; +import { getNotebookEditorFromEditorPane, INotebookEditor, NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_KERNEL_COUNT } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; 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'; @@ -26,16 +26,23 @@ import { ILogService } from 'vs/platform/log/common/log'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { HoverProviderRegistry } from 'vs/editor/common/modes'; import { Schemas } from 'vs/base/common/network'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; registerAction2(class extends Action2 { constructor() { super({ - id: 'notebook.selectKernel', + id: SELECT_KERNEL_ID, category: NOTEBOOK_ACTIONS_CATEGORY, title: { value: nls.localize('notebookActions.selectKernel', "Select Notebook Kernel"), original: 'Select Notebook Kernel' }, precondition: NOTEBOOK_IS_ACTIVE_EDITOR, icon: selectKernelIcon, f1: true, + menu: { + id: MenuId.EditorTitle, + when: ContextKeyExpr.and(NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_KERNEL_COUNT.notEqualsTo(0), ContextKeyExpr.equals('config.notebook.experimental.showKernelInEditorTitle', true),), + group: 'navigation', + order: -10 + }, description: { description: nls.localize('notebookActions.selectKernel.args', "Notebook Kernel Args"), args: [ @@ -57,7 +64,6 @@ registerAction2(class extends Action2 { } ] }, - }); } @@ -265,7 +271,7 @@ export class KernelStatus extends Disposable implements IWorkbenchContribution { text: `$(notebook-kernel-select) ${kernel.label}`, ariaLabel: kernel.label, tooltip: isSuggested ? nls.localize('tooltop', "{0} (suggestion)", tooltip) : tooltip, - command: 'notebook.selectKernel', + command: SELECT_KERNEL_ID, }, 'notebook.selectKernel', nls.localize('notebook.info', "Notebook Kernel Info"), @@ -282,7 +288,7 @@ export class KernelStatus extends Disposable implements IWorkbenchContribution { { text: nls.localize('kernel.select.label', "Select Kernel"), ariaLabel: nls.localize('kernel.select.label', "Select Kernel"), - command: 'notebook.selectKernel', + command: SELECT_KERNEL_ID, backgroundColor: { id: 'statusBarItem.prominentBackground' } }, 'notebook.selectKernel', diff --git a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts index 3b095c3039b..18f142fe53f 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts @@ -10,7 +10,6 @@ import { IDiffEditorOptions, IEditorOptions } from 'vs/editor/common/config/edit import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { DiffElementViewModelBase, getFormatedMetadataJSON, OUTPUT_EDITOR_HEIGHT_MAGIC, PropertyFoldingState, SideBySideDiffElementViewModel, SingleSideDiffElementViewModel } from 'vs/workbench/contrib/notebook/browser/diff/diffElementViewModel'; import { CellDiffSideBySideRenderTemplate, CellDiffSingleSideRenderTemplate, DiffSide, DIFF_CELL_MARGIN, INotebookTextDiffEditor, NOTEBOOK_DIFF_CELL_PROPERTY, NOTEBOOK_DIFF_CELL_PROPERTY_EXPANDED } from 'vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser'; -import { EDITOR_BOTTOM_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; import { IModelService } from 'vs/editor/common/services/modelService'; @@ -26,7 +25,6 @@ import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/men import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { Delayer } from 'vs/base/common/async'; import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellActionView'; -import { getEditorTopPadding } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { collapsedIcon, expandedIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; import { OutputContainer } from 'vs/workbench/contrib/notebook/browser/diff/diffElementOutputs'; import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions'; @@ -980,7 +978,8 @@ export class DeletedElement extends SingleSideDiffElement { const originalCell = this.cell.original!; const lineCount = originalCell.textModel.textBuffer.getLineCount(); const lineHeight = this.notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; - const editorHeight = lineCount * lineHeight + getEditorTopPadding() + EDITOR_BOTTOM_PADDING; + const editorPadding = this.notebookEditor.notebookOptions.computeEditorPadding(); + const editorHeight = lineCount * lineHeight + editorPadding.top + editorPadding.bottom; this._editor = this.templateData.sourceEditor; this._editor.layout({ @@ -1131,7 +1130,8 @@ export class InsertElement extends SingleSideDiffElement { const modifiedCell = this.cell.modified!; const lineCount = modifiedCell.textModel.textBuffer.getLineCount(); const lineHeight = this.notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; - const editorHeight = lineCount * lineHeight + getEditorTopPadding() + EDITOR_BOTTOM_PADDING; + const editorPadding = this.notebookEditor.notebookOptions.computeEditorPadding(); + const editorHeight = lineCount * lineHeight + editorPadding.top + editorPadding.bottom; this._editor = this.templateData.sourceEditor; this._editor.layout( @@ -1469,7 +1469,9 @@ export class ModifiedElement extends AbstractElementRenderer { const modifiedCell = this.cell.modified!; const lineCount = modifiedCell.textModel.textBuffer.getLineCount(); const lineHeight = this.notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; - const editorHeight = this.cell.layoutInfo.editorHeight !== 0 ? this.cell.layoutInfo.editorHeight : lineCount * lineHeight + getEditorTopPadding() + EDITOR_BOTTOM_PADDING; + const editorPadding = this.notebookEditor.notebookOptions.computeEditorPadding(); + + const editorHeight = this.cell.layoutInfo.editorHeight !== 0 ? this.cell.layoutInfo.editorHeight : lineCount * lineHeight + editorPadding.top + editorPadding.bottom; this._editorContainer = this.templateData.editorContainer; this._editor = this.templateData.sourceEditor; diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser.ts index 0ca043aacff..9685efbaec3 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser.ts @@ -15,6 +15,7 @@ import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer'; import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { NotebookOptions } from 'vs/workbench/contrib/notebook/common/notebookOptions'; export enum DiffSide { Original = 0, @@ -26,6 +27,7 @@ export interface IDiffCellInfo extends ICommonCellInfo { } export interface INotebookTextDiffEditor extends ICommonNotebookEditor { + notebookOptions: NotebookOptions; readonly textModel?: NotebookTextModel; onMouseUp: Event<{ readonly event: MouseEvent; readonly target: DiffElementViewModelBase; }>; onDidDynamicOutputRendered: Event<{ cell: IGenericCellViewModel, output: ICellOutputViewModel }>; diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts index cf5718517bd..9db388ec10e 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookTextDiffEditor.ts @@ -38,9 +38,9 @@ import { generateUuid } from 'vs/base/common/uuid'; import { IMouseWheelEvent, StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { DiffNestedCellViewModel } from 'vs/workbench/contrib/notebook/browser/diff/diffNestedCellViewModel'; import { BackLayerWebView } from 'vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView'; -import { CELL_OUTPUT_PADDING, MARKDOWN_PREVIEW_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; import { NotebookDiffEditorEventDispatcher, NotebookDiffLayoutChangedEvent } from 'vs/workbench/contrib/notebook/browser/diff/eventDispatcher'; import { readFontInfo } from 'vs/editor/browser/config/configuration'; +import { NotebookOptions } from 'vs/workbench/contrib/notebook/common/notebookOptions'; const $ = DOM.$; @@ -75,6 +75,12 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD protected _onDidDynamicOutputRendered = new Emitter<{ cell: IGenericCellViewModel, output: ICellOutputViewModel }>(); onDidDynamicOutputRendered = this._onDidDynamicOutputRendered.event; + private _notebookOptions: NotebookOptions; + + get notebookOptions() { + return this._notebookOptions; + } + private readonly _localStore = this._register(new DisposableStore()); private _isDisposed: boolean = false; @@ -93,10 +99,11 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD @IStorageService storageService: IStorageService, ) { super(NotebookTextDiffEditor.ID, telemetryService, themeService, storageService); + this._notebookOptions = new NotebookOptions(this.configurationService); + this._register(this._notebookOptions); const editorOptions = this.configurationService.getValue('editor'); this._fontInfo = readFontInfo(BareFontInfo.createFromRawSettings(editorOptions, getZoomLevel(), getPixelRatio())); this._revealFirst = true; - this._outputRenderer = new OutputRenderer(this, this.instantiationService); } @@ -366,21 +373,12 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD })); } - private readonly webviewOptions = { - outputNodePadding: CELL_OUTPUT_PADDING, - outputNodeLeftPadding: 32, - previewNodePadding: MARKDOWN_PREVIEW_PADDING, - leftMargin: 0, - rightMargin: 0, - runGutter: 0 - }; - private async _createModifiedWebview(id: string, resource: URI): Promise { if (this._modifiedWebview) { this._modifiedWebview.dispose(); } - this._modifiedWebview = this.instantiationService.createInstance(BackLayerWebView, this, id, resource, this.webviewOptions) as BackLayerWebView; + this._modifiedWebview = this.instantiationService.createInstance(BackLayerWebView, this, id, resource, this._notebookOptions.computeDiffWebviewOptions()) as BackLayerWebView; // attach the webview container to the DOM tree first this._list.rowsContainer.insertAdjacentElement('afterbegin', this._modifiedWebview.element); await this._modifiedWebview.createWebview(); @@ -393,7 +391,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD this._originalWebview.dispose(); } - this._originalWebview = this.instantiationService.createInstance(BackLayerWebView, this, id, resource, this.webviewOptions) as BackLayerWebView; + this._originalWebview = this.instantiationService.createInstance(BackLayerWebView, this, id, resource, this._notebookOptions.computeDiffWebviewOptions()) as BackLayerWebView; // attach the webview container to the DOM tree first this._list.rowsContainer.insertAdjacentElement('afterbegin', this._originalWebview.element); await this._originalWebview.createWebview(); diff --git a/src/vs/workbench/contrib/notebook/browser/extensionPoint.ts b/src/vs/workbench/contrib/notebook/browser/extensionPoint.ts index 83d2d7c2c15..de640c7926e 100644 --- a/src/vs/workbench/contrib/notebook/browser/extensionPoint.ts +++ b/src/vs/workbench/contrib/notebook/browser/extensionPoint.ts @@ -149,12 +149,6 @@ const notebookRendererContribution: IJSONSchema = { } }; -export const notebooksExtensionPoint2 = ExtensionsRegistry.registerExtensionPoint( - { - extensionPoint: 'notebookProvider', - jsonSchema: { deprecationMessage: 'Use \'notebooks\' instead' } - }); - export const notebooksExtensionPoint = ExtensionsRegistry.registerExtensionPoint( { extensionPoint: 'notebooks', diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebookKernelActionViewItem.css b/src/vs/workbench/contrib/notebook/browser/media/notebookKernelActionViewItem.css new file mode 100644 index 00000000000..5f394da2d1f --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/media/notebookKernelActionViewItem.css @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-workbench .kernel-action-view-item { + border-radius: 5px; +} +.monaco-workbench .kernel-action-view-item:hover { + background-color: var(--code-toolbarHoverBackground); +} + +.monaco-workbench .kernel-action-view-item .action-label { + display: inline-flex; +} + +.monaco-workbench .kernel-action-view-item .kernel-label { + font-size: 11px; + padding: 3px 5px 3px 3px; + border-radius: 5px; + height: 16px; + display: inline-flex; + vertical-align: text-bottom; +} diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index ac887b9a8f0..fd69c02aa50 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -8,7 +8,7 @@ import { IListContextMenuEvent, IListEvent, IListMouseEvent } from 'vs/base/brow import { IListOptions, IListStyles } from 'vs/base/browser/ui/list/listWidget'; import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; -import { Emitter, Event } from 'vs/base/common/event'; +import { Event } from 'vs/base/common/event'; import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { ScrollEvent } from 'vs/base/common/scrollable'; import { URI } from 'vs/base/common/uri'; @@ -31,6 +31,7 @@ import { IResourceEditorInput } from 'vs/platform/editor/common/editor'; import { IConstructorSignature1 } from 'vs/platform/instantiation/common/instantiation'; import { CellEditorStatusBar } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets'; import { INotebookWebviewMessage } from 'vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView'; +import { NotebookOptions } from 'vs/workbench/contrib/notebook/common/notebookOptions'; export const NOTEBOOK_EDITOR_ID = 'workbench.editor.notebook'; export const NOTEBOOK_DIFF_EDITOR_ID = 'workbench.editor.notebookTextDiffEditor'; @@ -382,6 +383,7 @@ export interface INotebookEditor extends ICommonNotebookEditor { readonly onDidScroll: Event; readonly onDidChangeActiveCell: Event; + readonly notebookOptions: NotebookOptions; isDisposed: boolean; dispose(): void; @@ -908,20 +910,6 @@ export function getNotebookEditorFromEditorPane(editorPane?: IEditorPane): INote return editorPane?.getId() === NOTEBOOK_EDITOR_ID ? editorPane.getControl() as INotebookEditor | undefined : undefined; } -let EDITOR_TOP_PADDING = 12; -const editorTopPaddingChangeEmitter = new Emitter(); - -export const EditorTopPaddingChangeEvent = editorTopPaddingChangeEmitter.event; - -export function updateEditorTopPadding(top: number) { - EDITOR_TOP_PADDING = top; - editorTopPaddingChangeEmitter.fire(); -} - -export function getEditorTopPadding() { - return EDITOR_TOP_PADDING; -} - /** * ranges: model selections * this will convert model selections to view indexes first, and then include the hidden ranges in the list view diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts index 940296f98c5..218a8d1bec8 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts @@ -29,6 +29,10 @@ import { NotebookEditorOptions, NOTEBOOK_EDITOR_ID } from 'vs/workbench/contrib/ import { IBorrowValue, INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/notebookEditorService'; import { clearMarks, getAndClearMarks, mark } from 'vs/workbench/contrib/notebook/common/notebookPerformance'; import { IFileService } from 'vs/platform/files/common/files'; +import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IAction } from 'vs/base/common/actions'; +import { SELECT_KERNEL_ID } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions'; +import { NotebooKernelActionViewItem } from 'vs/workbench/contrib/notebook/browser/notebookKernelActionViewItem'; const NOTEBOOK_EDITOR_VIEW_STATE_PREFERENCE_KEY = 'NotebookEditorViewState'; @@ -103,6 +107,14 @@ export class NotebookEditor extends EditorPane { return this._rootElement; } + override getActionViewItem(action: IAction): IActionViewItem | undefined { + if (action.id === SELECT_KERNEL_ID) { + // this is being disposed by the consumer + return this.instantiationService.createInstance(NotebooKernelActionViewItem, action, this); + } + return undefined; + } + override getControl(): NotebookEditorWidget | undefined { return this._widget.value; } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorKernelManager.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorKernelManager.ts index c4417a2a3c8..033e28d165f 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorKernelManager.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorKernelManager.ts @@ -10,6 +10,7 @@ import { CellKind, INotebookKernel, INotebookTextModel, NotebookCellExecutionSta import { ICommandService } from 'vs/platform/commands/common/commands'; import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { IWorkspaceTrustRequestService } from 'vs/platform/workspace/common/workspaceTrust'; +import { SELECT_KERNEL_ID } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions'; export class NotebookEditorKernelManager extends Disposable { @@ -36,7 +37,7 @@ export class NotebookEditorKernelManager extends Disposable { let kernel = this.getSelectedOrSuggestedKernel(notebook); if (!kernel) { - await this._commandService.executeCommand('notebook.selectKernel'); + await this._commandService.executeCommand(SELECT_KERNEL_ID); kernel = this.getSelectedOrSuggestedKernel(notebook); } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index a5495997673..4e844150b73 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -42,7 +42,6 @@ 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_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'; @@ -59,7 +58,7 @@ 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, CellToolbarVisibility, ExperimentalUseMarkdownRenderer, SelectionStateType, ShowCellStatusBarKey } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, ExperimentalUseMarkdownRenderer, SelectionStateType } 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'; @@ -75,6 +74,9 @@ import { mark } from 'vs/workbench/contrib/notebook/common/notebookPerformance'; import { readFontInfo } from 'vs/editor/browser/config/configuration'; import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { NotebookEditorContextKeys } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidgetContextKeys'; +import { NotebookOptions } from 'vs/workbench/contrib/notebook/common/notebookOptions'; +import { SCROLLABLE_ELEMENT_PADDING_TOP } from 'vs/workbench/contrib/notebook/browser/constants'; +import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/viewContext'; const $ = DOM.$; @@ -203,6 +205,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor private _overlayContainer!: HTMLElement; private _notebookTopToolbarContainer!: HTMLElement; private _body!: HTMLElement; + private _styleElement!: HTMLStyleElement; private _overflowContainer!: HTMLElement; private _webview: BackLayerWebView | null = null; private _webviewResolvePromise: Promise | null> | null = null; @@ -213,7 +216,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor private _dndController: CellDragAndDropController | null = null; private _listTopCellToolbar: ListTopCellToolbar | null = null; private _renderedEditors: Map = new Map(); - private _eventDispatcher: NotebookEventDispatcher | undefined; + private _viewContext: ViewContext | undefined; private _notebookViewModel: NotebookViewModel | undefined; private _localStore: DisposableStore = this._register(new DisposableStore()); private _localCellStateListeners: DisposableStore[] = []; @@ -306,6 +309,11 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor public readonly scopedContextKeyService: IContextKeyService; private readonly instantiationService: IInstantiationService; + private readonly _notebookOptions: NotebookOptions; + + get notebookOptions() { + return this._notebookOptions; + } constructor( readonly creationOptions: INotebookEditorCreationOptions, @@ -330,6 +338,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this.isEmbedded = creationOptions.isEmbedded || false; this.useRenderer = !isWeb && !!this.configurationService.getValue(ExperimentalUseMarkdownRenderer) && !accessibilityService.isScreenReaderOptimized(); + this._notebookOptions = new NotebookOptions(this.configurationService); + this._register(this._notebookOptions); this._overlayContainer = document.createElement('div'); this.scopedContextKeyService = contextKeyService.createScoped(this._overlayContainer); @@ -356,8 +366,10 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this.layout(this._dimension); } } + })); - if (e.affectsConfiguration(CellToolbarLocKey) || e.affectsConfiguration(ShowCellStatusBarKey) || e.affectsConfiguration(CellToolbarVisibility)) { + this._register(this._notebookOptions.onDidChangeOptions(e => { + if (e.cellStatusBarVisibility || e.cellToolbarLocation || e.cellToolbarInteraction) { this._updateForNotebookConfiguration(); } })); @@ -488,43 +500,16 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor return; } - const cellToolbarLocation = this.configurationService.getValue(CellToolbarLocKey); this._overlayContainer.classList.remove('cell-title-toolbar-left'); this._overlayContainer.classList.remove('cell-title-toolbar-right'); this._overlayContainer.classList.remove('cell-title-toolbar-hidden'); + const cellToolbarLocation = this._notebookOptions.computeCellToolbarLocation(this.viewModel?.viewType); + this._overlayContainer.classList.add(`cell-title-toolbar-${cellToolbarLocation}`); - if (typeof cellToolbarLocation === 'string') { - if (cellToolbarLocation === 'left' || cellToolbarLocation === 'right' || cellToolbarLocation === 'hidden') { - this._overlayContainer.classList.add(`cell-title-toolbar-${cellToolbarLocation}`); - } - } else { - if (this.viewModel) { - const notebookSpecificSetting = cellToolbarLocation[this.viewModel.viewType] ?? cellToolbarLocation['default']; - let cellToolbarLocationForCurrentView = 'right'; - - switch (notebookSpecificSetting) { - case 'left': - cellToolbarLocationForCurrentView = 'left'; - break; - case 'right': - cellToolbarLocationForCurrentView = 'right'; - case 'hidden': - cellToolbarLocationForCurrentView = 'hidden'; - default: - cellToolbarLocationForCurrentView = 'right'; - break; - } - - this._overlayContainer.classList.add(`cell-title-toolbar-${cellToolbarLocationForCurrentView}`); - } else { - this._overlayContainer.classList.add(`cell-title-toolbar-right`); - } - } - - const showCellStatusBar = this.configurationService.getValue(ShowCellStatusBarKey); + const showCellStatusBar = this._notebookOptions.getLayoutConfiguration().showCellStatusBar; this._overlayContainer.classList.toggle('cell-statusbar-hidden', !showCellStatusBar); - const cellToolbarInteraction = this.configurationService.getValue(CellToolbarVisibility); + const cellToolbarInteraction = this._notebookOptions.getLayoutConfiguration().cellToolbarInteraction; let cellToolbarInteractionState = 'hover'; this._overlayContainer.classList.remove('cell-toolbar-hover'); this._overlayContainer.classList.remove('cell-toolbar-click'); @@ -547,15 +532,74 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this._notebookTopToolbarContainer.style.display = 'none'; DOM.append(parent, this._notebookTopToolbarContainer); this._body = document.createElement('div'); - this._body.classList.add('cell-list-container'); - this._createCellList(); DOM.append(parent, this._body); + this._body.classList.add('cell-list-container'); + this._createLayoutStyles(); + this._createCellList(); this._overflowContainer = document.createElement('div'); this._overflowContainer.classList.add('notebook-overflow-widget-container', 'monaco-editor'); DOM.append(parent, this._overflowContainer); } + private _createLayoutStyles(): void { + this._styleElement = DOM.createStyleSheet(this._body); + const { + cellRightMargin, + cellTopMargin, + cellRunGutter, + cellBottomMargin, + codeCellLeftMargin, + markdownCellBottomMargin, + markdownCellTopMargin, + bottomCellToolbarGap, + bottomCellToolbarHeight, + collapsedIndicatorHeight + } = this._notebookOptions.getLayoutConfiguration(); + + const styleSheets: string[] = []; + styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .markdown-cell-row div.cell.code { margin-left: ${codeCellLeftMargin}px; }`); + styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .code-cell-row div.cell.code { margin-left: ${codeCellLeftMargin + cellRunGutter}px; }`); + styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row div.cell { margin-right: ${cellRightMargin}px; }`); + styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row > .cell-inner-container { padding-top: ${cellTopMargin}px; }`); + styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .markdown-cell-row > .cell-inner-container { padding-bottom: ${markdownCellBottomMargin}px; padding-top: ${markdownCellTopMargin}px; }`); + styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .markdown-cell-row > .cell-inner-container.webview-backed-markdown-cell { padding: 0; }`); + styleSheets.push(`.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-bottom: ${markdownCellBottomMargin}px; padding-top: ${markdownCellTopMargin}px; }`); + styleSheets.push(`.notebookOverlay .output { margin: 0px ${cellRightMargin}px 0px ${codeCellLeftMargin + cellRunGutter}px; }`); + styleSheets.push(`.notebookOverlay .output { width: calc(100% - ${codeCellLeftMargin + cellRunGutter + cellRightMargin}px); }`); + + styleSheets.push(`.notebookOverlay .output-show-more-container { margin: 0px ${cellRightMargin}px 0px ${codeCellLeftMargin + cellRunGutter}px; }`); + styleSheets.push(`.notebookOverlay .output-show-more-container { width: calc(100% - ${codeCellLeftMargin + cellRunGutter + cellRightMargin}px); }`); + + styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row div.cell.markdown { padding-left: ${cellRunGutter}px; }`); + styleSheets.push(`.notebookOverlay .cell .run-button-container { width: 20px; left: ${codeCellLeftMargin + Math.floor(cellRunGutter - 20) / 2}px }`); + styleSheets.push(`.notebookOverlay .monaco-list .monaco-list-row :not(.webview-backed-markdown-cell) .cell-focus-indicator-top { height: ${cellTopMargin}px; }`); + styleSheets.push(`.notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-side { bottom: ${bottomCellToolbarGap}px; }`); + styleSheets.push(`.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: ${codeCellLeftMargin + cellRunGutter}px; }`); + styleSheets.push(`.notebookOverlay .monaco-list .monaco-list-row.markdown-cell-row .cell-focus-indicator-left { width: ${codeCellLeftMargin}px; }`); + styleSheets.push(`.notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator.cell-focus-indicator-right { width: ${cellRightMargin}px; }`); + styleSheets.push(`.notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-bottom { height: ${cellBottomMargin}px; }`); + styleSheets.push(`.notebookOverlay .monaco-list .monaco-list-row .cell-shadow-container-bottom { top: ${cellBottomMargin}px; }`); + + styleSheets.push(`.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-collapsed-part { margin-left: ${codeCellLeftMargin + cellRunGutter}px; height: ${collapsedIndicatorHeight}px; }`); + styleSheets.push(`.notebookOverlay .cell-list-top-cell-toolbar-container { top: -${SCROLLABLE_ELEMENT_PADDING_TOP}px }`); + + styleSheets.push(`.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container { height: ${bottomCellToolbarHeight}px }`); + styleSheets.push(`.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .cell-list-top-cell-toolbar-container { height: ${bottomCellToolbarHeight}px }`); + + // left and right border margins + styleSheets.push(` + .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row.focused .cell-focus-indicator-left:before, + .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row.focused .cell-focus-indicator-right:before, + .monaco-workbench .notebookOverlay .monaco-list.selection-multiple .monaco-list-row.code-cell-row.selected .cell-focus-indicator-left:before, + .monaco-workbench .notebookOverlay .monaco-list.selection-multiple .monaco-list-row.code-cell-row.selected .cell-focus-indicator-right:before { + top: -${cellTopMargin}px; height: calc(100% + ${cellTopMargin + cellBottomMargin}px) + }`); + + this._styleElement.textContent = styleSheets.join('\n'); + } + private _createCellList(): void { this._body.classList.add('cell-list-container'); @@ -1076,14 +1120,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor } private async _createWebview(id: string, resource: URI): Promise { - this._webview = this.instantiationService.createInstance(BackLayerWebView, this, id, resource, { - outputNodePadding: CELL_OUTPUT_PADDING, - outputNodeLeftPadding: CELL_OUTPUT_PADDING, - previewNodePadding: MARKDOWN_PREVIEW_PADDING, - leftMargin: CODE_CELL_LEFT_MARGIN, - rightMargin: CELL_RIGHT_MARGIN, - runGutter: CELL_RUN_GUTTER, - }); + this._webview = this.instantiationService.createInstance(BackLayerWebView, this, id, resource, this._notebookOptions.computeWebviewOptions()); this._webview.element.style.width = '100%'; // attach the webview container to the DOM tree first @@ -1093,9 +1130,9 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor private async _attachModel(textModel: NotebookTextModel, viewState: INotebookEditorViewState | undefined) { await this._createWebview(this.getId(), textModel.uri); - this._eventDispatcher = new NotebookEventDispatcher(); - this.viewModel = this.instantiationService.createInstance(NotebookViewModel, textModel.viewType, textModel, this._eventDispatcher, this.getLayoutInfo()); - this._eventDispatcher.emit([new NotebookLayoutChangedEvent({ width: true, fontInfo: true }, this.getLayoutInfo())]); + this._viewContext = new ViewContext(this._notebookOptions, new NotebookEventDispatcher()); + this.viewModel = this.instantiationService.createInstance(NotebookViewModel, textModel.viewType, textModel, this._viewContext, this.getLayoutInfo()); + this._viewContext.eventDispatcher.emit([new NotebookLayoutChangedEvent({ width: true, fontInfo: true }, this.getLayoutInfo())]); this._updateForOptions(); this._updateForNotebookConfiguration(); @@ -1449,7 +1486,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this._webviewTransparentCover.style.width = `${dimension.width}px`; } - this._eventDispatcher?.emit([new NotebookLayoutChangedEvent({ width: true, fontInfo: true }, this.getLayoutInfo())]); + this._viewContext?.eventDispatcher.emit([new NotebookLayoutChangedEvent({ width: true, fontInfo: true }, this.getLayoutInfo())]); } //#endregion @@ -2385,9 +2422,10 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor updateMarkdownCellHeight(cellId: string, height: number, isInit: boolean) { const cell = this.getCellById(cellId); + const layoutConfiguration = this._notebookOptions.getLayoutConfiguration(); if (cell && cell instanceof MarkdownCellViewModel) { - if (height + BOTTOM_CELL_TOOLBAR_GAP !== cell.layoutInfo.totalHeight) { - this._debug('updateMarkdownCellHeight', cell.handle, height + BOTTOM_CELL_TOOLBAR_GAP, isInit); + if (height + layoutConfiguration.bottomCellToolbarGap !== cell.layoutInfo.totalHeight) { + this._debug('updateMarkdownCellHeight', cell.handle, height + layoutConfiguration.bottomCellToolbarGap, isInit); cell.renderedMarkdownHeight = height; } } @@ -2461,7 +2499,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditor this._webviewTransparentCover = null; this._dndController = null; this._listTopCellToolbar = null; - this._eventDispatcher = undefined; + this._viewContext = undefined; this._notebookViewModel = undefined; this._cellContextKeyManager = null; this._renderedEditors.clear(); @@ -2653,17 +2691,20 @@ registerThemingParticipant((theme, collector) => { collector.addRule(`.notebookOverlay .output-show-more-container { background-color: ${containerBackground}; }`); } + const notebookBackground = theme.getColor(editorBackground); + if (notebookBackground) { + collector.addRule(`.notebookOverlay .cell-drag-image .cell-editor-container > div { background: ${notebookBackground} !important; }`); + collector.addRule(`.notebookOverlay .monaco-list-row .cell-title-toolbar { background-color: ${notebookBackground}; }`); + collector.addRule(`.notebookOverlay .monaco-list-row.cell-drag-image { background-color: ${notebookBackground}; }`); + collector.addRule(`.notebookOverlay .cell-bottom-toolbar-container .action-item { background-color: ${notebookBackground} }`); + collector.addRule(`.notebookOverlay .cell-list-top-cell-toolbar-container .action-item { background-color: ${notebookBackground} }`); + } + const editorBackgroundColor = theme.getColor(cellEditorBackground) ?? theme.getColor(editorBackground); if (editorBackgroundColor) { collector.addRule(`.notebookOverlay .cell .monaco-editor-background, - .notebookOverlay .cell .margin-view-overlays, - .notebookOverlay .cell .cell-statusbar-container { background: ${editorBackgroundColor}; }`); - collector.addRule(`.notebookOverlay .cell-drag-image .cell-editor-container > div { background: ${editorBackgroundColor} !important; }`); - - collector.addRule(`.notebookOverlay .monaco-list-row .cell-title-toolbar { background-color: ${editorBackgroundColor}; }`); - collector.addRule(`.notebookOverlay .monaco-list-row.cell-drag-image { background-color: ${editorBackgroundColor}; }`); - collector.addRule(`.notebookOverlay .cell-bottom-toolbar-container .action-item { background-color: ${editorBackgroundColor} }`); - collector.addRule(`.notebookOverlay .cell-list-top-cell-toolbar-container .action-item { background-color: ${editorBackgroundColor} }`); + .notebookOverlay .cell .margin-view-overlays, + .notebookOverlay .cell .cell-statusbar-container { background: ${editorBackgroundColor}; }`); } const cellToolbarSeperator = theme.getColor(CELL_TOOLBAR_SEPERATOR); @@ -2822,43 +2863,5 @@ registerThemingParticipant((theme, collector) => { }`); } - // Cell Margin - 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-bottom: ${MARKDOWN_CELL_BOTTOM_MARGIN}px; padding-top: ${MARKDOWN_CELL_TOP_MARGIN}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_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; 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_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; }`); - - collector.addRule(`.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-collapsed-part { margin-left: ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER}px; height: ${COLLAPSED_INDICATOR_HEIGHT}px; }`); - collector.addRule(`.notebookOverlay .cell-list-top-cell-toolbar-container { top: -${SCROLLABLE_ELEMENT_PADDING_TOP}px }`); - - collector.addRule(`.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container { height: ${BOTTOM_CELL_TOOLBAR_HEIGHT}px }`); - collector.addRule(`.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .cell-list-top-cell-toolbar-container { height: ${BOTTOM_CELL_TOOLBAR_HEIGHT}px }`); - - // left and right border margins - collector.addRule(` - .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row.focused .cell-focus-indicator-left:before, - .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row.focused .cell-focus-indicator-right:before, - .monaco-workbench .notebookOverlay .monaco-list.selection-multiple .monaco-list-row.code-cell-row.selected .cell-focus-indicator-left:before, - .monaco-workbench .notebookOverlay .monaco-list.selection-multiple .monaco-list-row.code-cell-row.selected .cell-focus-indicator-right:before { - top: -${CELL_TOP_MARGIN}px; height: calc(100% + ${CELL_TOP_MARGIN + CELL_BOTTOM_MARGIN}px) - }`); }); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookKernelActionViewItem.ts b/src/vs/workbench/contrib/notebook/browser/notebookKernelActionViewItem.ts new file mode 100644 index 00000000000..b473b71f80d --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/notebookKernelActionViewItem.ts @@ -0,0 +1,100 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/css!./media/notebookKernelActionViewItem'; +import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { Action, IAction } from 'vs/base/common/actions'; +import { localize } from 'vs/nls'; +import { registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { NotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookEditor'; +import { selectKernelIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; +import { INotebookKernelMatchResult, INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { toolbarHoverBackground } from 'vs/platform/theme/common/colorRegistry'; + +registerThemingParticipant((theme, collector) => { + const value = theme.getColor(toolbarHoverBackground); + collector.addRule(`:root { + --code-toolbarHoverBackground: ${value}; + }`); +}); + +export class NotebooKernelActionViewItem extends ActionViewItem { + + private _kernelLabel?: HTMLAnchorElement; + + constructor( + actualAction: IAction, + private readonly _editor: NotebookEditor, + @INotebookKernelService private readonly _notebookKernelService: INotebookKernelService, + ) { + super( + undefined, + new Action('fakeAction', undefined, ThemeIcon.asClassName(selectKernelIcon), true, (event) => actualAction.run(event)), + { label: false, icon: true } + ); + this._register(_editor.onDidChangeModel(this._update, this)); + this._register(_notebookKernelService.onDidChangeNotebookAffinity(this._update, this)); + this._register(_notebookKernelService.onDidChangeNotebookKernelBinding(this._update, this)); + } + + override render(container: HTMLElement): void { + this._update(); + super.render(container); + container.classList.add('kernel-action-view-item'); + this._kernelLabel = document.createElement('a'); + container.appendChild(this._kernelLabel); + this.updateLabel(); + } + + override updateLabel() { + if (this._kernelLabel) { + this._kernelLabel.classList.add('kernel-label'); + this._kernelLabel.innerText = this._action.label; + this._kernelLabel.title = this._action.tooltip; + } + } + + private _update(): void { + const widget = this._editor.getControl(); + if (!widget || !widget.hasModel()) { + this._resetAction(); + return; + } + const notebook = widget.viewModel.notebookDocument; + const info = this._notebookKernelService.getMatchingKernel(notebook); + this._updateActionFromKernelInfo(info); + } + + private _updateActionFromKernelInfo(info: INotebookKernelMatchResult): void { + + if (info.all.length === 0) { + // should not happen - means "bad" context keys + this._resetAction(); + return; + } + + this._action.enabled = true; + const selectedOrSuggested = info.selected ?? info.suggested; + if (selectedOrSuggested) { + // selected or suggested kernel + this._action.label = selectedOrSuggested.label; + this._action.tooltip = selectedOrSuggested.description ?? selectedOrSuggested.detail ?? ''; + if (!info.selected) { + // special UI for selected kernel? + } + + } else { + // many kernels + this._action.label = localize('select', "Select Kernel"); + this._action.tooltip = ''; + } + } + + private _resetAction(): void { + this._action.enabled = false; + this._action.label = ''; + this._action.class = ''; + } +} diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index bbcdeef131f..05db784c920 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -27,13 +27,14 @@ import { NotebookExtensionDescription } from 'vs/workbench/api/common/extHost.pr import { IEditorInput } from 'vs/workbench/common/editor'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { Memento } from 'vs/workbench/common/memento'; -import { INotebookEditorContribution, notebooksExtensionPoint, notebookRendererExtensionPoint, notebooksExtensionPoint2 } from 'vs/workbench/contrib/notebook/browser/extensionPoint'; -import { NotebookEditorOptions, updateEditorTopPadding } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { INotebookEditorContribution, notebooksExtensionPoint, notebookRendererExtensionPoint } from 'vs/workbench/contrib/notebook/browser/extensionPoint'; +import { NotebookEditorOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookDiffEditorInput } from 'vs/workbench/contrib/notebook/browser/notebookDiffEditorInput'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, BUILTIN_RENDERER_ID, CellUri, DisplayOrderKey, INotebookExclusiveDocumentFilter, INotebookContributionData, INotebookRendererInfo, INotebookTextModel, IOrderedMimeType, IOutputDto, mimeTypeIsAlwaysSecure, mimeTypeSupportedByCore, NotebookDataDto, NotebookEditorPriority, NotebookRendererMatch, NotebookTextDiffEditorPreview, RENDERER_NOT_AVAILABLE, sortMimeTypes, TransientOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/common/notebookEditorInput'; +import { updateEditorTopPadding } from 'vs/workbench/contrib/notebook/common/notebookOptions'; import { NotebookOutputRendererInfo } from 'vs/workbench/contrib/notebook/common/notebookOutputRenderer'; import { NotebookEditorDescriptor, NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider'; import { ComplexNotebookProviderInfo, INotebookContentProvider, INotebookSerializer, INotebookService, SimpleNotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookService'; @@ -80,7 +81,6 @@ export class NotebookProviderInfoStore extends Disposable { })); notebooksExtensionPoint.setHandler(extensions => this._setupHandler(extensions)); - notebooksExtensionPoint2.setHandler(extensions => this._setupHandler(extensions)); } override dispose(): void { diff --git a/src/vs/workbench/contrib/notebook/browser/view/output/outputRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/output/outputRenderer.ts index 9aa00edf264..14ce25b10ba 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/output/outputRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/output/outputRenderer.ts @@ -9,36 +9,35 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { ICellOutputViewModel, ICommonNotebookEditor, IOutputTransformContribution, IRenderOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { URI } from 'vs/base/common/uri'; import { Disposable } from 'vs/base/common/lifecycle'; +import { localize } from 'vs/nls'; export class OutputRenderer extends Disposable { - protected _contributions: { [key: string]: IOutputTransformContribution; }; - protected _renderers: IOutputTransformContribution[]; - private _richMimeTypeRenderers = new Map(); + + private readonly _richMimeTypeRenderers = new Map(); constructor( notebookEditor: ICommonNotebookEditor, private readonly instantiationService: IInstantiationService ) { super(); - - this._contributions = {}; - this._renderers = []; - - const contributions = NotebookRegistry.getOutputTransformContributions(); - - for (const desc of contributions) { + for (const desc of NotebookRegistry.getOutputTransformContributions()) { try { const contribution = this.instantiationService.createInstance(desc.ctor, notebookEditor); - this._contributions[desc.id] = contribution; contribution.getMimetypes().forEach(mimetype => { this._richMimeTypeRenderers.set(mimetype, contribution); }); + this._register(contribution); } catch (err) { onUnexpectedError(err); } } } + override dispose(): void { + super.dispose(); + this._richMimeTypeRenderers.clear(); + } + getContribution(preferredMimeType: string | undefined): IOutputTransformContribution | undefined { if (preferredMimeType) { return this._richMimeTypeRenderers.get(preferredMimeType); @@ -47,17 +46,16 @@ export class OutputRenderer extends Disposable { return undefined; } - renderNoop(viewModel: ICellOutputViewModel, container: HTMLElement): IRenderOutput { + private _renderNoop(viewModel: ICellOutputViewModel, container: HTMLElement): IRenderOutput { const contentNode = document.createElement('p'); - - contentNode.innerText = `No renderer could be found for output.`; + contentNode.innerText = localize('empty', "No renderer could be found for output."); container.appendChild(contentNode); return { type: RenderOutputType.Mainframe }; } render(viewModel: ICellOutputViewModel, container: HTMLElement, preferredMimeType: string | undefined, notebookUri: URI): IRenderOutput { if (!viewModel.model.outputs.length) { - return this.renderNoop(viewModel, container); + return this._renderNoop(viewModel, container); } if (!preferredMimeType || !this._richMimeTypeRenderers.has(preferredMimeType)) { @@ -67,9 +65,9 @@ export class OutputRenderer extends Disposable { const mimeTypesMessage = mimeTypes.join(', '); if (preferredMimeType) { - contentNode.innerText = `No renderer could be found for MIME type: ${preferredMimeType}`; + contentNode.innerText = localize('noRenderer.1', "No renderer could be found for MIME type: {0}", preferredMimeType); } else { - contentNode.innerText = `No renderer could be found for output. It has the following MIME types: ${mimeTypesMessage}`; + contentNode.innerText = localize('noRenderer.2', "No renderer could be found for output. It has the following MIME types: {0}", mimeTypesMessage); } container.appendChild(contentNode); @@ -82,13 +80,7 @@ export class OutputRenderer extends Disposable { if (items.length && renderer) { return renderer.render(viewModel, items, container, notebookUri); } else { - return this.renderNoop(viewModel, container); + return this._renderNoop(viewModel, container); } } - - override dispose() { - this._contributions = {}; - this._renderers = []; - this._richMimeTypeRenderers.clear(); - } } diff --git a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts index d9e5bd8ee35..60a32f043c7 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/output/transforms/richTransform.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as DOM from 'vs/base/browser/dom'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, dispose } from 'vs/base/common/lifecycle'; import { dirname } from 'vs/base/common/resources'; import { isArray } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; @@ -14,6 +14,7 @@ import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ILogService } from 'vs/platform/log/common/log'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { handleANSIOutput } from 'vs/workbench/contrib/debug/browser/debugANSIHandling'; @@ -120,6 +121,8 @@ class CodeRendererContrib extends Disposable implements IOutputRendererContribut return ['text/x-javascript']; } + private readonly _cellDisposables = new Map(); + constructor( public notebookEditor: ICommonNotebookEditor, @IInstantiationService private readonly instantiationService: IInstantiationService, @@ -129,7 +132,19 @@ class CodeRendererContrib extends Disposable implements IOutputRendererContribut super(); } + override dispose(): void { + dispose(this._cellDisposables.values()); + this._cellDisposables.clear(); + super.dispose(); + } + render(output: ICellOutputViewModel, items: IOutputItemDto[], container: HTMLElement, notebookUri: URI): IRenderOutput { + + let cellDisposables = this._cellDisposables.get(output.cellViewModel.handle); + cellDisposables?.dispose(); + cellDisposables = new DisposableStore(); + this._cellDisposables.set(output.cellViewModel.handle, cellDisposables); + const str = items.map(item => getStringValue(item.value)).join(''); const editor = this.instantiationService.createInstance(CodeEditorWidget, container, { ...getOutputSimpleEditorOptions(), @@ -155,6 +170,9 @@ class CodeRendererContrib extends Disposable implements IOutputRendererContribut width }); + cellDisposables.add(editor); + cellDisposables.add(textModel); + container.style.height = `${height + 8}px`; return { type: RenderOutputType.Mainframe }; @@ -260,6 +278,64 @@ class ErrorRendererContrib extends Disposable implements IOutputRendererContribu } } +class JSErrorRendererContrib implements IOutputRendererContribution { + + constructor( + public notebookEditor: ICommonNotebookEditor, + @IThemeService private readonly _themeService: IThemeService, + @IInstantiationService private readonly _instantiationService: IInstantiationService, + @ILogService private readonly _logService: ILogService, + ) { } + + dispose(): void { + // nothing + } + + getType() { + return RenderOutputType.Mainframe; + } + + getMimetypes() { + return ['application/x.notebook.error']; + } + + render(_output: ICellOutputViewModel, items: IOutputItemDto[], container: HTMLElement, _notebookUri: URI): IRenderOutput { + const linkDetector = this._instantiationService.createInstance(LinkDetector); + + for (let item of items) { + + if (typeof item.value !== 'string') { + this._logService.warn('INVALID output item (not a string)', item.value); + continue; + } + + let err: Error; + try { + err = JSON.parse(item.value); + } catch (e) { + this._logService.warn('INVALID output item (failed to parse)', e); + continue; + } + + const header = document.createElement('div'); + const headerMessage = err.name && err.message ? `${err.name}: ${err.message}` : err.name || err.message; + if (headerMessage) { + header.innerText = headerMessage; + container.appendChild(header); + } + const stack = document.createElement('pre'); + stack.classList.add('traceback'); + if (err.stack) { + stack.appendChild(handleANSIOutput(err.stack, linkDetector, this._themeService, undefined)); + } + container.appendChild(stack); + container.classList.add('error'); + } + + return { type: RenderOutputType.Mainframe }; + } +} + class PlainTextRendererContrib extends Disposable implements IOutputRendererContribution { getType() { return RenderOutputType.Mainframe; @@ -442,6 +518,7 @@ NotebookRegistry.registerOutputTransform('jpeg', JPEGRendererContrib); NotebookRegistry.registerOutputTransform('plain', PlainTextRendererContrib); NotebookRegistry.registerOutputTransform('code', CodeRendererContrib); NotebookRegistry.registerOutputTransform('error-trace', ErrorRendererContrib); +NotebookRegistry.registerOutputTransform('jserror', JSErrorRendererContrib); NotebookRegistry.registerOutputTransform('stream-text', StreamRendererContrib); NotebookRegistry.registerOutputTransform('stderr', StderrRendererContrib); 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 64265f4477a..fca5bd43ce6 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellDnd.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellDnd.ts @@ -9,7 +9,6 @@ import { domEvent } from 'vs/base/browser/event'; import { Delayer } from 'vs/base/common/async'; import { Disposable } from 'vs/base/common/lifecycle'; 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, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; @@ -157,7 +156,8 @@ export class CellDragAndDropController extends Disposable { } private updateInsertIndicator(dropDirection: string, insertionIndicatorAbsolutePos: number) { - const insertionIndicatorTop = insertionIndicatorAbsolutePos - this.list.scrollTop + BOTTOM_CELL_TOOLBAR_GAP / 2; + const layoutInfo = this.notebookEditor.notebookOptions.getLayoutConfiguration(); + const insertionIndicatorTop = insertionIndicatorAbsolutePos - this.list.scrollTop + layoutInfo.bottomCellToolbarGap / 2; if (insertionIndicatorTop >= 0) { this.listInsertionIndicator.style.top = `${insertionIndicatorTop}px`; this.setInsertIndicatorVisibility(true); @@ -199,7 +199,8 @@ export class CellDragAndDropController extends Disposable { const cellTop = this.list.getAbsoluteTopOfElement(draggedOverCell); const cellHeight = this.list.elementHeight(draggedOverCell); const insertionIndicatorAbsolutePos = dropDirection === 'above' ? cellTop : cellTop + cellHeight; - const insertionIndicatorTop = insertionIndicatorAbsolutePos - this.list.scrollTop + BOTTOM_CELL_TOOLBAR_GAP / 2; + const layoutInfo = this.notebookEditor.notebookOptions.getLayoutConfiguration(); + const insertionIndicatorTop = insertionIndicatorAbsolutePos - this.list.scrollTop + layoutInfo.bottomCellToolbarGap / 2; const editorHeight = this.notebookEditor.getDomNode().getBoundingClientRect().height; if (insertionIndicatorTop < 0 || insertionIndicatorTop > editorHeight) { // Ignore drop, insertion point is off-screen diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellEditorOptions.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellEditorOptions.ts index 78ffc778563..6cfebf38da7 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellEditorOptions.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellEditorOptions.ts @@ -10,15 +10,14 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; import { IEditorOptions, LineNumbersType } from 'vs/editor/common/config/editorOptions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { EDITOR_BOTTOM_PADDING, EDITOR_BOTTOM_PADDING_WITHOUT_STATUSBAR } from 'vs/workbench/contrib/notebook/browser/constants'; -import { EditorTopPaddingChangeEvent, getEditorTopPadding, getNotebookEditorFromEditorPane, ICellViewModel, NOTEBOOK_CELL_LINE_NUMBERS, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_IS_ACTIVE_EDITOR } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { ShowCellStatusBarKey } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { getNotebookEditorFromEditorPane, ICellViewModel, NOTEBOOK_CELL_LINE_NUMBERS, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_IS_ACTIVE_EDITOR } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { localize } from 'vs/nls'; import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { NOTEBOOK_ACTIONS_CATEGORY } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions'; +import { NotebookOptions } from 'vs/workbench/contrib/notebook/common/notebookOptions'; export class CellEditorOptions extends Disposable { @@ -48,27 +47,24 @@ export class CellEditorOptions extends Disposable { private readonly _onDidChange = new Emitter(); readonly onDidChange: Event = this._onDidChange.event; - constructor(readonly configurationService: IConfigurationService, language: string) { + constructor(readonly notebookOptions: NotebookOptions, readonly configurationService: IConfigurationService, language: string) { super(); this._register(configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration('editor') || e.affectsConfiguration('notebook') || e.affectsConfiguration(ShowCellStatusBarKey)) { + if (e.affectsConfiguration('editor') || e.affectsConfiguration('notebook')) { this._value = computeEditorOptions(); this._onDidChange.fire(this.value); } })); - this._register(EditorTopPaddingChangeEvent(() => { - this._value = computeEditorOptions(); - this._onDidChange.fire(this.value); + this._register(notebookOptions.onDidChangeOptions(e => { + if (e.cellStatusBarVisibility || e.editorTopPadding) { + this._value = computeEditorOptions(); + this._onDidChange.fire(this.value); + } })); const computeEditorOptions = () => { - const showCellStatusBar = configurationService.getValue(ShowCellStatusBarKey); - const editorPadding = { - top: getEditorTopPadding(), - bottom: showCellStatusBar ? EDITOR_BOTTOM_PADDING : EDITOR_BOTTOM_PADDING_WITHOUT_STATUSBAR - }; - + const editorPadding = this.notebookOptions.computeEditorPadding(); const renderLiNumbers = configurationService.getValue<'on' | 'off'>('notebook.lineNumbers') === 'on'; const lineNumbers: LineNumbersType = renderLiNumbers ? 'on' : 'off'; const editorOptions = deepClone(configurationService.getValue('editor', { overrideIdentifier: language })); diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellOutput.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellOutput.ts index 8b5c7447bca..421cbba176b 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellOutput.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellOutput.ts @@ -13,7 +13,7 @@ import { IQuickPickItem, IQuickInputService } from 'vs/platform/quickinput/commo import { CodeCellRenderTemplate, ICellOutputViewModel, IInsetRenderOutput, INotebookEditor, IRenderOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; -import { BUILTIN_RENDERER_ID, CellUri, NotebookCellOutputsSplice, IOrderedMimeType, mimeTypeIsAlwaysSecure, INotebookKernel } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { BUILTIN_RENDERER_ID, CellUri, NotebookCellOutputsSplice, IOrderedMimeType, INotebookKernel } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { renderMarkdown } from 'vs/base/browser/markdownRenderer'; @@ -444,18 +444,7 @@ export class CellOutputContainer extends Disposable { } private _calcuateOutputsToRender(): ICellOutputViewModel[] { - const outputs = this.viewCell.outputsViewModels.slice(0, Math.min(OUTPUT_COUNT_LIMIT, this.viewCell.outputsViewModels.length)); - if (!this.notebookEditor.viewModel!.metadata.trusted) { - // not trusted - const secureOutput = outputs.filter(output => { - const mimeTypes = output.model.outputs.map(op => op.mime); - return mimeTypes.some(mimeTypeIsAlwaysSecure); - }); - - return secureOutput; - } - - return outputs; + return this.viewCell.outputsViewModels.slice(0, Math.min(OUTPUT_COUNT_LIMIT, this.viewCell.outputsViewModels.length)); } private _outputHeightTimer: any = null; 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 d93c98350db..9ebb664546d 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -35,7 +35,6 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { BOTTOM_CELL_TOOLBAR_GAP, CELL_BOTTOM_MARGIN, CELL_TOP_MARGIN, EDITOR_TOOLBAR_HEIGHT } from 'vs/workbench/contrib/notebook/browser/constants'; import { DeleteCellAction, INotebookActionContext, INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions'; import { BaseCellRenderTemplate, CellEditState, CodeCellLayoutInfo, CodeCellRenderTemplate, EXPAND_CELL_INPUT_COMMAND_ID, ICellViewModel, INotebookEditor, isCodeCellRenderTemplate, MarkdownCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys'; @@ -100,7 +99,7 @@ abstract class AbstractCellRenderer { language: string, protected dndController: CellDragAndDropController | undefined, ) { - this.editorOptions = new CellEditorOptions(configurationService, language); + this.editorOptions = new CellEditorOptions(notebookEditor.notebookOptions, configurationService, language); this.cellMenus = this.instantiationService.createInstance(CellMenus); } @@ -184,17 +183,18 @@ abstract class AbstractCellRenderer { this.notebookEditor.focus(); } + const layoutInfo = this.notebookEditor.notebookOptions.getLayoutConfiguration(); if (actions.primary.length || actions.secondary.length) { templateData.container.classList.add('cell-has-toolbar-actions'); if (isCodeCellRenderTemplate(templateData)) { - templateData.focusIndicatorLeft.style.top = `${EDITOR_TOOLBAR_HEIGHT + CELL_TOP_MARGIN}px`; - templateData.focusIndicatorRight.style.top = `${EDITOR_TOOLBAR_HEIGHT + CELL_TOP_MARGIN}px`; + templateData.focusIndicatorLeft.style.top = `${layoutInfo.editorToolbarHeight + layoutInfo.cellTopMargin}px`; + templateData.focusIndicatorRight.style.top = `${layoutInfo.editorToolbarHeight + layoutInfo.cellTopMargin}px`; } } else { templateData.container.classList.remove('cell-has-toolbar-actions'); if (isCodeCellRenderTemplate(templateData)) { - templateData.focusIndicatorLeft.style.top = `${CELL_TOP_MARGIN}px`; - templateData.focusIndicatorRight.style.top = `${CELL_TOP_MARGIN}px`; + templateData.focusIndicatorLeft.style.top = `${layoutInfo.cellTopMargin}px`; + templateData.focusIndicatorRight.style.top = `${layoutInfo.cellTopMargin}px`; } } }; @@ -467,7 +467,7 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR })); this.updateForHover(element, templateData); - const cellEditorOptions = new CellEditorOptions(this.configurationService, 'markdown'); + const cellEditorOptions = new CellEditorOptions(this.notebookEditor.notebookOptions, this.configurationService, 'markdown'); cellEditorOptions.setLineNumbers(element.lineNumbers); elementDisposables.add(cellEditorOptions); @@ -508,11 +508,10 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR } private updateForLayout(element: MarkdownCellViewModel, templateData: MarkdownCellRenderTemplate): void { - templateData.focusIndicatorBottom.style.top = `${element.layoutInfo.totalHeight - BOTTOM_CELL_TOOLBAR_GAP - CELL_BOTTOM_MARGIN}px`; - - const focusSideHeight = element.layoutInfo.totalHeight - BOTTOM_CELL_TOOLBAR_GAP; - templateData.focusIndicatorLeft.style.height = `${focusSideHeight}px`; - templateData.focusIndicatorRight.style.height = `${focusSideHeight}px`; + const indicatorPostion = this.notebookEditor.notebookOptions.computeIndicatorPosition(element.layoutInfo.totalHeight); + templateData.focusIndicatorBottom.style.top = `${indicatorPostion.bottomIndicatorTop}px`; + templateData.focusIndicatorLeft.style.height = `${indicatorPostion.verticalIndicatorHeight}px`; + templateData.focusIndicatorRight.style.height = `${indicatorPostion.verticalIndicatorHeight}px`; } private updateForHover(element: MarkdownCellViewModel, templateData: MarkdownCellRenderTemplate): void { @@ -879,12 +878,13 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende } private updateForLayout(element: CodeCellViewModel, templateData: CodeCellRenderTemplate): void { + const layoutInfo = this.notebookEditor.notebookOptions.getLayoutConfiguration(); templateData.focusIndicatorLeft.style.height = `${element.layoutInfo.indicatorHeight}px`; templateData.focusIndicatorRight.style.height = `${element.layoutInfo.indicatorHeight}px`; - templateData.focusIndicatorBottom.style.top = `${element.layoutInfo.totalHeight - BOTTOM_CELL_TOOLBAR_GAP - CELL_BOTTOM_MARGIN}px`; + templateData.focusIndicatorBottom.style.top = `${element.layoutInfo.totalHeight - layoutInfo.bottomCellToolbarGap - layoutInfo.cellBottomMargin}px`; templateData.outputContainer.style.top = `${element.layoutInfo.outputContainerOffset}px`; templateData.outputShowMoreContainer.style.top = `${element.layoutInfo.outputShowMoreContainerOffset}px`; - templateData.dragHandle.style.height = `${element.layoutInfo.totalHeight - BOTTOM_CELL_TOOLBAR_GAP}px`; + templateData.dragHandle.style.height = `${element.layoutInfo.totalHeight - layoutInfo.bottomCellToolbarGap}px`; } renderElement(element: CodeCellViewModel, index: number, templateData: CodeCellRenderTemplate, height: number | undefined): void { @@ -938,7 +938,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende elementDisposables.add(this.instantiationService.createInstance(CodeCell, this.notebookEditor, element, templateData)); this.renderedEditors.set(element, templateData.editor); - const cellEditorOptions = new CellEditorOptions(this.configurationService, element.language); + const cellEditorOptions = new CellEditorOptions(this.notebookEditor.notebookOptions, this.configurationService, element.language); elementDisposables.add(cellEditorOptions); elementDisposables.add(cellEditorOptions.onDidChange(newValue => templateData.editor.updateOptions(newValue))); templateData.editor.updateOptions(cellEditorOptions.value); 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 8cbe4509cb1..9e7058f4dba 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts @@ -10,8 +10,7 @@ import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { IDimension } from 'vs/editor/common/editorCommon'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { EDITOR_BOTTOM_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; -import { CellFocusMode, CodeCellRenderTemplate, getEditorTopPadding, IActiveNotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellFocusMode, CodeCellRenderTemplate, IActiveNotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellOutputContainer } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellOutput'; import { ClickTargetType } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; @@ -40,8 +39,10 @@ export class CodeCell extends Disposable { const width = this.viewCell.layoutInfo.editorWidth; const lineNum = this.viewCell.lineCount; const lineHeight = this.viewCell.layoutInfo.fontInfo?.lineHeight || 17; + const editorPadding = this.notebookEditor.notebookOptions.computeEditorPadding(); + const editorHeight = this.viewCell.layoutInfo.editorHeight === 0 - ? lineNum * lineHeight + getEditorTopPadding() + EDITOR_BOTTOM_PADDING + ? lineNum * lineHeight + editorPadding.top + editorPadding.bottom : this.viewCell.layoutInfo.editorHeight; this.layoutEditor( 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 8cb61486064..8da7491ee30 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/markdownCell.ts @@ -10,8 +10,7 @@ import { Disposable, DisposableStore, IDisposable, MutableDisposable, toDisposab import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { EDITOR_BOTTOM_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; -import { CellEditState, CellFocusMode, MarkdownCellRenderTemplate, ICellViewModel, getEditorTopPadding, IActiveNotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellEditState, CellFocusMode, MarkdownCellRenderTemplate, ICellViewModel, IActiveNotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellFoldingState } from 'vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel'; import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; @@ -295,7 +294,8 @@ export class StatefulMarkdownCell extends Disposable { const width = this.viewCell.layoutInfo.editorWidth; const lineNum = this.viewCell.lineCount; const lineHeight = this.viewCell.layoutInfo.fontInfo?.lineHeight || 17; - editorHeight = Math.max(lineNum, 1) * lineHeight + getEditorTopPadding() + EDITOR_BOTTOM_PADDING; + const editorPadding = this.notebookEditor.notebookOptions.computeEditorPadding(); + editorHeight = Math.max(lineNum, 1) * lineHeight + editorPadding.top + editorPadding.bottom; this.templateData.editorContainer.innerText = ''; diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts index 74b720ee021..8ed3cb6b951 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts @@ -12,12 +12,12 @@ import { IPosition } from 'vs/editor/common/core/position'; import * as editorCommon from 'vs/editor/common/editorCommon'; 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, INotebookSearchOptions, ShowCellStatusBarKey, INotebookCellStatusBarItem } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellEditState, CellFocusMode, CursorAtBoundary, CellViewModelStateChangeEvent, IEditableCellViewModel, INotebookCellDecorationOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellKind, INotebookSearchOptions, 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'; +import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/viewContext'; export abstract class BaseCellViewModel extends Disposable { @@ -129,6 +129,7 @@ export abstract class BaseCellViewModel extends Disposable { readonly viewType: string, readonly model: NotebookCellTextModel, public id: string, + private readonly _viewContext: ViewContext, private readonly _configurationService: IConfigurationService, private readonly _modelService: ITextModelService, ) { @@ -138,11 +139,13 @@ export abstract class BaseCellViewModel extends Disposable { this._onDidChangeState.fire({ metadataChanged: true, runStateChanged: e.runStateChanged }); })); - this._register(this._configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration(ShowCellStatusBarKey)) { + this._register(this._viewContext.notebookOptions.onDidChangeOptions(e => { + if (e.cellStatusBarVisibility) { this.layoutChange({}); } + })); + this._register(this._configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration('notebook.lineNumbers')) { this.lineNumbers = 'inherit'; } @@ -150,8 +153,7 @@ export abstract class BaseCellViewModel extends Disposable { } getEditorStatusbarHeight() { - const showCellStatusBar = this._configurationService.getValue(ShowCellStatusBarKey); - return showCellStatusBar ? CELL_STATUSBAR_HEIGHT : 0; + return this._viewContext.notebookOptions.computeStatusBarHeight(); } abstract hasDynamicHeight(): boolean; @@ -394,7 +396,8 @@ export abstract class BaseCellViewModel extends Disposable { return 0; } - return this._textEditor.getTopForLineNumber(line) + getEditorTopPadding(); + const editorPadding = this._viewContext.notebookOptions.computeEditorPadding(); + return this._textEditor.getTopForLineNumber(line) + editorPadding.top; } getPositionScrollTopOffset(line: number, column: number): number { @@ -402,7 +405,8 @@ export abstract class BaseCellViewModel extends Disposable { return 0; } - return this._textEditor.getTopForPosition(line, column) + getEditorTopPadding(); + const editorPadding = this._viewContext.notebookOptions.computeEditorPadding(); + return this._textEditor.getTopForPosition(line, column) + editorPadding.top; } cursorAtBoundary(): CursorAtBoundary { diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts index 0527dea30c0..70e00498851 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts @@ -9,10 +9,9 @@ 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_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 { CellEditState, CellFindMatch, CodeCellLayoutChangeEvent, CodeCellLayoutInfo, CodeCellLayoutState, 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'; +import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/viewContext'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { CellKind, INotebookSearchOptions, NotebookCellOutputsSplice } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; @@ -96,12 +95,12 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod viewType: string, model: NotebookCellTextModel, initialNotebookLayoutInfo: NotebookLayoutInfo | null, - readonly eventDispatcher: NotebookEventDispatcher, + readonly viewContext: ViewContext, @IConfigurationService configurationService: IConfigurationService, @INotebookService private readonly _notebookService: INotebookService, @ITextModelService modelService: ITextModelService, ) { - super(viewType, model, UUID.generateUuid(), configurationService, modelService); + super(viewType, model, UUID.generateUuid(), viewContext, configurationService, modelService); this._outputViewModels = this.model.outputs.map(output => new CellOutputViewModel(this, output, this._notebookService)); this._register(this.model.onDidChangeOutputs((splices) => { @@ -132,7 +131,9 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod this._layoutInfo = { fontInfo: initialNotebookLayoutInfo?.fontInfo || null, editorHeight: 0, - editorWidth: initialNotebookLayoutInfo ? this.computeEditorWidth(initialNotebookLayoutInfo.width) : 0, + editorWidth: initialNotebookLayoutInfo + ? this.viewContext.notebookOptions.computeCodeCellEditorWidth(initialNotebookLayoutInfo.width) + : 0, outputContainerOffset: 0, outputTotalHeight: 0, outputShowMoreContainerHeight: 0, @@ -144,15 +145,12 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod }; } - private computeEditorWidth(outerWidth: number): number { - return outerWidth - (CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER + CELL_RIGHT_MARGIN); - } - layoutChange(state: CodeCellLayoutChangeEvent, source?: string) { // recompute this._ensureOutputsTop(); + const notebookLayoutConfiguration = this.viewContext.notebookOptions.getLayoutConfiguration(); const outputShowMoreContainerHeight = state.outputShowMoreContainerHeight ? state.outputShowMoreContainerHeight : this._layoutInfo.outputShowMoreContainerHeight; - let outputTotalHeight = Math.max(this._outputMinHeight, this.metadata?.outputCollapsed ? COLLAPSED_INDICATOR_HEIGHT : this._outputsTop!.getTotalValue()); + let outputTotalHeight = Math.max(this._outputMinHeight, this.metadata?.outputCollapsed ? notebookLayoutConfiguration.collapsedIndicatorHeight : this._outputsTop!.getTotalValue()); if (!this.metadata?.inputCollapsed) { let newState: CodeCellLayoutState; @@ -176,10 +174,18 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod const statusbarHeight = this.getEditorStatusbarHeight(); const indicatorHeight = editorHeight + statusbarHeight + outputTotalHeight + outputShowMoreContainerHeight; - const outputContainerOffset = EDITOR_TOOLBAR_HEIGHT + CELL_TOP_MARGIN + editorHeight + statusbarHeight; - const outputShowMoreContainerOffset = totalHeight - BOTTOM_CELL_TOOLBAR_GAP - BOTTOM_CELL_TOOLBAR_HEIGHT / 2 - outputShowMoreContainerHeight; - const bottomToolbarOffset = totalHeight - BOTTOM_CELL_TOOLBAR_GAP - BOTTOM_CELL_TOOLBAR_HEIGHT / 2; - const editorWidth = state.outerWidth !== undefined ? this.computeEditorWidth(state.outerWidth) : this._layoutInfo?.editorWidth; + const outputContainerOffset = notebookLayoutConfiguration.editorToolbarHeight + + notebookLayoutConfiguration.cellTopMargin // CELL_TOP_MARGIN + + editorHeight + + statusbarHeight; + const outputShowMoreContainerOffset = totalHeight + - notebookLayoutConfiguration.bottomCellToolbarGap + - notebookLayoutConfiguration.bottomCellToolbarHeight / 2 + - outputShowMoreContainerHeight; + const bottomToolbarOffset = this.viewContext.notebookOptions.computeBottomToolbarOffset(totalHeight); + const editorWidth = state.outerWidth !== undefined + ? this.viewContext.notebookOptions.computeCodeCellEditorWidth(state.outerWidth) + : this._layoutInfo?.editorWidth; this._layoutInfo = { fontInfo: state.font ?? this._layoutInfo.fontInfo ?? null, @@ -196,12 +202,23 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod }; } else { outputTotalHeight = Math.max(this._outputMinHeight, this.metadata?.inputCollapsed && this.metadata.outputCollapsed ? 0 : outputTotalHeight); - const indicatorHeight = COLLAPSED_INDICATOR_HEIGHT + outputTotalHeight + outputShowMoreContainerHeight; - const outputContainerOffset = CELL_TOP_MARGIN + COLLAPSED_INDICATOR_HEIGHT; - const totalHeight = CELL_TOP_MARGIN + COLLAPSED_INDICATOR_HEIGHT + CELL_BOTTOM_MARGIN + BOTTOM_CELL_TOOLBAR_GAP + outputTotalHeight + outputShowMoreContainerHeight; - const outputShowMoreContainerOffset = totalHeight - BOTTOM_CELL_TOOLBAR_GAP - BOTTOM_CELL_TOOLBAR_HEIGHT / 2 - outputShowMoreContainerHeight; - const bottomToolbarOffset = totalHeight - BOTTOM_CELL_TOOLBAR_GAP - BOTTOM_CELL_TOOLBAR_HEIGHT / 2; - const editorWidth = state.outerWidth !== undefined ? this.computeEditorWidth(state.outerWidth) : this._layoutInfo?.editorWidth; + const indicatorHeight = notebookLayoutConfiguration.collapsedIndicatorHeight + outputTotalHeight + outputShowMoreContainerHeight; + + const outputContainerOffset = notebookLayoutConfiguration.cellTopMargin + notebookLayoutConfiguration.collapsedIndicatorHeight; + const totalHeight = + notebookLayoutConfiguration.cellTopMargin + + notebookLayoutConfiguration.collapsedIndicatorHeight + + notebookLayoutConfiguration.cellBottomMargin //CELL_BOTTOM_MARGIN + + notebookLayoutConfiguration.bottomCellToolbarGap //BOTTOM_CELL_TOOLBAR_GAP + + outputTotalHeight + outputShowMoreContainerHeight; + const outputShowMoreContainerOffset = totalHeight + - notebookLayoutConfiguration.bottomCellToolbarGap + - notebookLayoutConfiguration.bottomCellToolbarHeight / 2 + - outputShowMoreContainerHeight; + const bottomToolbarOffset = this.viewContext.notebookOptions.computeBottomToolbarOffset(totalHeight); + const editorWidth = state.outerWidth !== undefined + ? this.viewContext.notebookOptions.computeCodeCellEditorWidth(state.outerWidth) + : this._layoutInfo?.editorWidth; this._layoutInfo = { fontInfo: state.font ?? this._layoutInfo.fontInfo ?? null, @@ -282,11 +299,23 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod } const verticalScrollbarHeight = hasScrolling ? 12 : 0; // take zoom level into account - return this.lineCount * lineHeight + getEditorTopPadding() + EDITOR_BOTTOM_PADDING + verticalScrollbarHeight; + const editorPadding = this.viewContext.notebookOptions.computeEditorPadding(); + return this.lineCount * lineHeight + + editorPadding.top + + editorPadding.bottom // EDITOR_BOTTOM_PADDING + + verticalScrollbarHeight; } private computeTotalHeight(editorHeight: number, outputsTotalHeight: number, outputShowMoreContainerHeight: number): number { - return EDITOR_TOOLBAR_HEIGHT + CELL_TOP_MARGIN + editorHeight + this.getEditorStatusbarHeight() + outputsTotalHeight + outputShowMoreContainerHeight + BOTTOM_CELL_TOOLBAR_GAP + CELL_BOTTOM_MARGIN; + const layoutConfiguration = this.viewContext.notebookOptions.getLayoutConfiguration(); + return layoutConfiguration.editorToolbarHeight //EDITOR_TOOLBAR_HEIGHT + + layoutConfiguration.cellTopMargin // CELL_TOP_MARGIN + + editorHeight + + this.getEditorStatusbarHeight() + + outputsTotalHeight + + outputShowMoreContainerHeight + + layoutConfiguration.bottomCellToolbarGap //BOTTOM_CELL_TOOLBAR_GAP + + layoutConfiguration.cellBottomMargin; // CELL_BOTTOM_MARGIN; } protected onDidChangeTextModelContent(): void { diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts index 17b3583204c..fc457a84707 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts @@ -8,15 +8,15 @@ 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, 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'; import { BaseCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel'; -import { NotebookCellStateChangedEvent, NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; +import { NotebookCellStateChangedEvent } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { CellKind, INotebookSearchOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/viewContext'; export class MarkdownCellViewModel extends BaseCellViewModel implements ICellViewModel { readonly cellKind = CellKind.Markup; @@ -29,7 +29,7 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie set renderedMarkdownHeight(newHeight: number) { if (this.getEditState() === CellEditState.Preview) { - const newTotalHeight = newHeight + BOTTOM_CELL_TOOLBAR_GAP; + const newTotalHeight = newHeight + this.viewContext.notebookOptions.getLayoutConfiguration().bottomCellToolbarGap; // BOTTOM_CELL_TOOLBAR_GAP; this.totalHeight = newTotalHeight; } } @@ -47,8 +47,13 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie private _editorHeight = 0; set editorHeight(newHeight: number) { this._editorHeight = newHeight; + const layoutConfiguration = this.viewContext.notebookOptions.getLayoutConfiguration(); - this.totalHeight = this._editorHeight + MARKDOWN_CELL_TOP_MARGIN + MARKDOWN_CELL_BOTTOM_MARGIN + BOTTOM_CELL_TOOLBAR_GAP + this.getEditorStatusbarHeight(); + this.totalHeight = this._editorHeight + + layoutConfiguration.markdownCellTopMargin // MARKDOWN_CELL_TOP_MARGIN + + layoutConfiguration.markdownCellBottomMargin // MARKDOWN_CELL_BOTTOM_MARGIN + + layoutConfiguration.bottomCellToolbarGap // BOTTOM_CELL_TOOLBAR_GAP + + this.viewContext.notebookOptions.computeStatusBarHeight(); } get editorHeight() { @@ -102,23 +107,25 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie model: NotebookCellTextModel, initialNotebookLayoutInfo: NotebookLayoutInfo | null, readonly foldingDelegate: EditorFoldingStateDelegate, - readonly eventDispatcher: NotebookEventDispatcher, + readonly viewContext: ViewContext, private readonly _mdRenderer: MarkdownRenderer, @IConfigurationService configurationService: IConfigurationService, @ITextModelService textModelService: ITextModelService, ) { - super(viewType, model, UUID.generateUuid(), configurationService, textModelService); + super(viewType, model, UUID.generateUuid(), viewContext, configurationService, textModelService); this._layoutInfo = { editorHeight: 0, fontInfo: initialNotebookLayoutInfo?.fontInfo || null, - editorWidth: initialNotebookLayoutInfo?.width ? this.computeEditorWidth(initialNotebookLayoutInfo.width) : 0, - bottomToolbarOffset: BOTTOM_CELL_TOOLBAR_GAP, + editorWidth: initialNotebookLayoutInfo?.width + ? this.viewContext.notebookOptions.computeMarkdownCellEditorWidth(initialNotebookLayoutInfo.width) + : 0, + bottomToolbarOffset: this.viewContext.notebookOptions.getLayoutConfiguration().bottomCellToolbarGap, // BOTTOM_CELL_TOOLBAR_GAP, totalHeight: 0 }; this._register(this.onDidChangeState(e => { - eventDispatcher.emit([new NotebookCellStateChangedEvent(e, this)]); + this.viewContext.eventDispatcher.emit([new NotebookCellStateChangedEvent(e, this)]); })); this._register(model.onDidChangeMetadata(e => { @@ -144,34 +151,34 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie this._onDidChangeState.fire({ foldingStateChanged: true }); } - private computeEditorWidth(outerWidth: number) { - return outerWidth - CODE_CELL_LEFT_MARGIN - CELL_RIGHT_MARGIN; - } - layoutChange(state: MarkdownCellLayoutChangeEvent) { // recompute - if (!this.metadata?.inputCollapsed) { - const editorWidth = state.outerWidth !== undefined ? this.computeEditorWidth(state.outerWidth) : this._layoutInfo.editorWidth; + const editorWidth = state.outerWidth !== undefined + ? this.viewContext.notebookOptions.computeMarkdownCellEditorWidth(state.outerWidth) + : this._layoutInfo.editorWidth; const totalHeight = state.totalHeight === undefined ? this._layoutInfo.totalHeight : state.totalHeight; this._layoutInfo = { fontInfo: state.font || this._layoutInfo.fontInfo, editorWidth, editorHeight: this._editorHeight, - bottomToolbarOffset: totalHeight - BOTTOM_CELL_TOOLBAR_GAP - BOTTOM_CELL_TOOLBAR_HEIGHT / 2, + bottomToolbarOffset: this.viewContext.notebookOptions.computeBottomToolbarOffset(totalHeight), totalHeight }; } else { - const editorWidth = state.outerWidth !== undefined ? this.computeEditorWidth(state.outerWidth) : this._layoutInfo.editorWidth; - const totalHeight = MARKDOWN_CELL_TOP_MARGIN + COLLAPSED_INDICATOR_HEIGHT + BOTTOM_CELL_TOOLBAR_GAP + MARKDOWN_CELL_BOTTOM_MARGIN; + const editorWidth = state.outerWidth !== undefined + ? this.viewContext.notebookOptions.computeMarkdownCellEditorWidth(state.outerWidth) + : this._layoutInfo.editorWidth; + const totalHeight = this.viewContext.notebookOptions.computeCollapsedMarkdownCellHeight(); + state.totalHeight = totalHeight; this._layoutInfo = { fontInfo: state.font || this._layoutInfo.fontInfo, editorWidth, editorHeight: this._editorHeight, - bottomToolbarOffset: totalHeight - BOTTOM_CELL_TOOLBAR_GAP - BOTTOM_CELL_TOOLBAR_HEIGHT / 2, + bottomToolbarOffset: this.viewContext.notebookOptions.computeBottomToolbarOffset(totalHeight), totalHeight }; } diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts index faee24774d7..840b8621878 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts @@ -20,7 +20,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { CellEditState, CellFindMatch, ICellViewModel, NotebookLayoutInfo, INotebookDeltaDecoration, INotebookDeltaCellStatusBarItems, CellFocusMode, CellFindMatchWithIndex } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; -import { NotebookEventDispatcher, NotebookMetadataChangedEvent } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; +import { NotebookMetadataChangedEvent } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; 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'; @@ -36,6 +36,7 @@ import { ResourceNotebookCellEdit } from 'vs/workbench/contrib/bulkEdit/browser/ import { NotebookCellSelectionCollection } from 'vs/workbench/contrib/notebook/browser/viewModel/cellSelectionCollection'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { groupByNumber } from 'vs/base/common/collections'; +import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/viewContext'; export interface INotebookEditorViewState { editingCells: { [key: number]: boolean }; @@ -172,10 +173,6 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD return this._notebook.metadata; } - get trusted() { - return !!this._notebook.metadata?.trusted; - } - private readonly _onDidChangeViewCells = this._register(new Emitter()); get onDidChangeViewCells(): Event { return this._onDidChangeViewCells.event; } @@ -233,7 +230,7 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD constructor( public viewType: string, private _notebook: NotebookTextModel, - readonly eventDispatcher: NotebookEventDispatcher, + readonly viewContext: ViewContext, private _layoutInfo: NotebookLayoutInfo | null, @IInstantiationService private readonly _instantiationService: IInstantiationService, @IBulkEditService private readonly _bulkEditService: IBulkEditService, @@ -326,7 +323,7 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD this._register(this._notebook.onDidChangeContent(contentChanges => { contentChanges.rawEvents.forEach(e => { if (e.kind === NotebookCellsChangeType.ChangeDocumentMetadata) { - this.eventDispatcher.emit([new NotebookMetadataChangedEvent(this._notebook.metadata)]); + this.viewContext.eventDispatcher.emit([new NotebookMetadataChangedEvent(this._notebook.metadata)]); } }); @@ -335,7 +332,7 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD } })); - this._register(this.eventDispatcher.onDidChangeLayout((e) => { + this._register(this.viewContext.eventDispatcher.onDidChangeLayout((e) => { this._layoutInfo = e.value; this._viewCells.forEach(cell => { @@ -1181,9 +1178,9 @@ export type CellViewModel = CodeCellViewModel | MarkdownCellViewModel; export function createCellViewModel(instantiationService: IInstantiationService, notebookViewModel: NotebookViewModel, cell: NotebookCellTextModel) { if (cell.cellKind === CellKind.Code) { - return instantiationService.createInstance(CodeCellViewModel, notebookViewModel.viewType, cell, notebookViewModel.layoutInfo, notebookViewModel.eventDispatcher); + return instantiationService.createInstance(CodeCellViewModel, notebookViewModel.viewType, cell, notebookViewModel.layoutInfo, notebookViewModel.viewContext); } else { const mdRenderer = instantiationService.createInstance(MarkdownRenderer, { baseUrl: dirname(notebookViewModel.uri) }); - return instantiationService.createInstance(MarkdownCellViewModel, notebookViewModel.viewType, cell, notebookViewModel.layoutInfo, notebookViewModel, notebookViewModel.eventDispatcher, mdRenderer); + return instantiationService.createInstance(MarkdownCellViewModel, notebookViewModel.viewType, cell, notebookViewModel.layoutInfo, notebookViewModel, notebookViewModel.viewContext, mdRenderer); } } diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/viewContext.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/viewContext.ts new file mode 100644 index 00000000000..8ffe0ce0bed --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/viewContext.ts @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; +import { NotebookOptions } from 'vs/workbench/contrib/notebook/common/notebookOptions'; + +export class ViewContext { + constructor( + readonly notebookOptions: NotebookOptions, + readonly eventDispatcher: NotebookEventDispatcher + ) { + } +} diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 24566e25353..3950d2f7d70 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -544,8 +544,9 @@ const _mimeTypeInfo = new Map([ ['text/html', { supportedByCore: true }], ['image/svg+xml', { supportedByCore: true }], ['image/jpeg', { supportedByCore: true }], - ['text/x-javascript', { supportedByCore: true }], + ['text/x-javascript', { alwaysSecure: true, supportedByCore: true }], // secure because rendered as text, not executed ['application/x.notebook.error-traceback', { alwaysSecure: true, supportedByCore: true }], + ['application/x.notebook.error', { alwaysSecure: true, supportedByCore: true }], ['application/x.notebook.stream', { alwaysSecure: true, supportedByCore: true, mergeable: true }], ['application/x.notebook.stdout', { alwaysSecure: true, supportedByCore: true, mergeable: true }], ['application/x.notebook.stderr', { alwaysSecure: true, supportedByCore: true, mergeable: true }], diff --git a/src/vs/workbench/contrib/notebook/common/notebookOptions.ts b/src/vs/workbench/contrib/notebook/common/notebookOptions.ts new file mode 100644 index 00000000000..6a6261310c3 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/common/notebookOptions.ts @@ -0,0 +1,266 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Emitter } from 'vs/base/common/event'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { CellToolbarLocKey, CellToolbarVisibility, ShowCellStatusBarKey } from 'vs/workbench/contrib/notebook/common/notebookCommon'; + +const CELL_STATUSBAR_HEIGHT = 22; +const EDITOR_TOOLBAR_HEIGHT = 0; +const CELL_OUTPUT_PADDING = 14; +const MARKDOWN_PREVIEW_PADDING = 8; +const CELL_RIGHT_MARGIN = 16; +const CELL_RUN_GUTTER = 28; +const CODE_CELL_LEFT_MARGIN = 32; +const BOTTOM_CELL_TOOLBAR_GAP = 18; +const BOTTOM_CELL_TOOLBAR_HEIGHT = 22; + +// Margin above editor +const CELL_TOP_MARGIN = 6; +const CELL_BOTTOM_MARGIN = 6; + +const MARKDOWN_CELL_TOP_MARGIN = 8; +const MARKDOWN_CELL_BOTTOM_MARGIN = 8; + +const COLLAPSED_INDICATOR_HEIGHT = 24; +const EDITOR_BOTTOM_PADDING_WITHOUT_STATUSBAR = 12; +const EDITOR_BOTTOM_PADDING = 4; + +let EDITOR_TOP_PADDING = 12; +const editorTopPaddingChangeEmitter = new Emitter(); + +export const EditorTopPaddingChangeEvent = editorTopPaddingChangeEmitter.event; + +export function updateEditorTopPadding(top: number) { + EDITOR_TOP_PADDING = top; + editorTopPaddingChangeEmitter.fire(); +} + +export function getEditorTopPadding() { + return EDITOR_TOP_PADDING; +} + +export interface NotebookLayoutConfiguration { + cellRightMargin: number; + cellRunGutter: number; + cellTopMargin: number; + cellBottomMargin: number; + cellOutputPadding: number; + codeCellLeftMargin: number; + markdownCellTopMargin: number; + markdownCellBottomMargin: number; + markdownPreviewPadding: number; + bottomCellToolbarGap: number; + bottomCellToolbarHeight: number; + editorToolbarHeight: number; + editorTopPadding: number; + editorBottomPadding: number; + editorBottomPaddingWithoutStatusBar: number; + collapsedIndicatorHeight: number; + showCellStatusBar: boolean; + cellStatusBarHeight: number; + cellToolbarLocation: string | { [key: string]: string }; + cellToolbarInteraction: string; +} + +interface NotebookOptionsChangeEvent { + cellStatusBarVisibility?: boolean; + cellToolbarLocation?: boolean; + cellToolbarInteraction?: boolean; + editorTopPadding?: boolean; +} +export class NotebookOptions { + private _layoutConfiguration: NotebookLayoutConfiguration; + protected readonly _onDidChangeOptions = new Emitter(); + readonly onDidChangeOptions = this._onDidChangeOptions.event; + private _disposables: IDisposable[]; + + constructor(readonly configurationService: IConfigurationService) { + const showCellStatusBar = this.configurationService.getValue(ShowCellStatusBarKey); + const cellToolbarLocation = this.configurationService.getValue(CellToolbarLocKey); + const cellToolbarInteraction = this.configurationService.getValue(CellToolbarVisibility); + + this._disposables = []; + this._layoutConfiguration = { + cellRightMargin: CELL_RIGHT_MARGIN, + cellRunGutter: CELL_RUN_GUTTER, + cellTopMargin: CELL_TOP_MARGIN, + cellBottomMargin: CELL_BOTTOM_MARGIN, + codeCellLeftMargin: CODE_CELL_LEFT_MARGIN, + markdownCellTopMargin: MARKDOWN_CELL_TOP_MARGIN, + markdownCellBottomMargin: MARKDOWN_CELL_BOTTOM_MARGIN, + bottomCellToolbarGap: BOTTOM_CELL_TOOLBAR_GAP, + bottomCellToolbarHeight: BOTTOM_CELL_TOOLBAR_HEIGHT, + editorTopPadding: EDITOR_TOP_PADDING, + editorBottomPadding: EDITOR_BOTTOM_PADDING, + editorBottomPaddingWithoutStatusBar: EDITOR_BOTTOM_PADDING_WITHOUT_STATUSBAR, + editorToolbarHeight: EDITOR_TOOLBAR_HEIGHT, + cellOutputPadding: CELL_OUTPUT_PADDING, + collapsedIndicatorHeight: COLLAPSED_INDICATOR_HEIGHT, + markdownPreviewPadding: MARKDOWN_PREVIEW_PADDING, + cellStatusBarHeight: CELL_STATUSBAR_HEIGHT, + showCellStatusBar, + cellToolbarLocation, + cellToolbarInteraction + }; + + this._disposables.push(this.configurationService.onDidChangeConfiguration(e => { + let cellStatusBarVisibility = e.affectsConfiguration(ShowCellStatusBarKey); + let cellToolbarLocation = e.affectsConfiguration(CellToolbarLocKey); + let cellToolbarInteraction = e.affectsConfiguration(CellToolbarVisibility); + + if (!cellStatusBarVisibility && !cellToolbarLocation && !cellToolbarInteraction) { + return; + } + + const configuration = Object.assign({}, this._layoutConfiguration); + + if (cellStatusBarVisibility) { + configuration.showCellStatusBar = this.configurationService.getValue(ShowCellStatusBarKey); + } + + if (cellToolbarLocation) { + configuration.cellToolbarLocation = this.configurationService.getValue(CellToolbarLocKey); + } + + if (cellToolbarInteraction) { + configuration.cellToolbarInteraction = this.configurationService.getValue(CellToolbarVisibility); + } + + this._layoutConfiguration = configuration; + + // trigger event + this._onDidChangeOptions.fire({ + cellStatusBarVisibility: cellStatusBarVisibility, + cellToolbarLocation: cellToolbarLocation, + cellToolbarInteraction: cellToolbarInteraction + }); + })); + + this._disposables.push(EditorTopPaddingChangeEvent(() => { + const configuration = Object.assign({}, this._layoutConfiguration); + configuration.editorTopPadding = getEditorTopPadding(); + this._layoutConfiguration = configuration; + this._onDidChangeOptions.fire({ editorTopPadding: true }); + })); + } + + getLayoutConfiguration(): NotebookLayoutConfiguration { + return this._layoutConfiguration; + } + + computeCollapsedMarkdownCellHeight(): number { + return this._layoutConfiguration.markdownCellTopMargin // MARKDOWN_CELL_TOP_MARGIN + + this._layoutConfiguration.collapsedIndicatorHeight // COLLAPSED_INDICATOR_HEIGHT + + this._layoutConfiguration.bottomCellToolbarGap // BOTTOM_CELL_TOOLBAR_GAP + + this._layoutConfiguration.markdownCellBottomMargin; // MARKDOWN_CELL_BOTTOM_MARGIN; + } + + computeBottomToolbarOffset(totalHeight: number) { + return totalHeight + - this._layoutConfiguration.bottomCellToolbarGap // BOTTOM_CELL_TOOLBAR_GAP + - this._layoutConfiguration.bottomCellToolbarHeight / 2; + } + + computeCodeCellEditorWidth(outerWidth: number): number { + return outerWidth - ( + this._layoutConfiguration.codeCellLeftMargin // CODE_CELL_LEFT_MARGIN + + this._layoutConfiguration.cellRunGutter // CELL_RUN_GUTTER + + this._layoutConfiguration.cellRightMargin // CELL_RIGHT_MARGIN + ); + } + + computeMarkdownCellEditorWidth(outerWidth: number): number { + return outerWidth + - this._layoutConfiguration.codeCellLeftMargin // CODE_CELL_LEFT_MARGIN + - this._layoutConfiguration.cellRightMargin; // CELL_RIGHT_MARGIN; + } + + computeStatusBarHeight(): number { + if (this._layoutConfiguration.showCellStatusBar) { + return this._layoutConfiguration.cellStatusBarHeight; + } else { + return 0; + } + } + + computeCellToolbarLocation(viewType?: string): 'right' | 'left' | 'hidden' { + const cellToolbarLocation = this._layoutConfiguration.cellToolbarLocation; + + if (typeof cellToolbarLocation === 'string') { + if (cellToolbarLocation === 'left' || cellToolbarLocation === 'right' || cellToolbarLocation === 'hidden') { + return cellToolbarLocation; + } + } else { + if (viewType) { + const notebookSpecificSetting = cellToolbarLocation[viewType] ?? cellToolbarLocation['default']; + let cellToolbarLocationForCurrentView: 'right' | 'left' | 'hidden' = 'right'; + + switch (notebookSpecificSetting) { + case 'left': + cellToolbarLocationForCurrentView = 'left'; + break; + case 'right': + cellToolbarLocationForCurrentView = 'right'; + break; + case 'hidden': + cellToolbarLocationForCurrentView = 'hidden'; + break; + default: + cellToolbarLocationForCurrentView = 'right'; + break; + } + + return cellToolbarLocationForCurrentView; + } + } + + return 'right'; + } + + computeEditorPadding() { + return { + top: getEditorTopPadding(), + bottom: this._layoutConfiguration.showCellStatusBar + ? this._layoutConfiguration.editorBottomPadding// EDITOR_BOTTOM_PADDING + : this._layoutConfiguration.editorBottomPaddingWithoutStatusBar // EDITOR_BOTTOM_PADDING_WITHOUT_STATUSBAR + }; + } + + computeWebviewOptions() { + return { + outputNodePadding: this._layoutConfiguration.cellOutputPadding, // CELL_OUTPUT_PADDING, + outputNodeLeftPadding: this._layoutConfiguration.cellOutputPadding, // CELL_OUTPUT_PADDING, + previewNodePadding: this._layoutConfiguration.markdownPreviewPadding, // MARKDOWN_PREVIEW_PADDING, + leftMargin: this._layoutConfiguration.codeCellLeftMargin, // CODE_CELL_LEFT_MARGIN, + rightMargin: this._layoutConfiguration.cellRightMargin, // CELL_RIGHT_MARGIN, + runGutter: this._layoutConfiguration.cellRunGutter, // CELL_RUN_GUTTER, + }; + } + + computeDiffWebviewOptions() { + return { + outputNodePadding: this._layoutConfiguration.cellOutputPadding, // CELL_OUTPUT_PADDING, + outputNodeLeftPadding: 32, + previewNodePadding: this._layoutConfiguration.markdownPreviewPadding, // MARKDOWN_PREVIEW_PADDING, + leftMargin: 0, + rightMargin: 0, + runGutter: 0 + }; + } + + computeIndicatorPosition(totalHeight: number) { + return { + bottomIndicatorTop: totalHeight - this._layoutConfiguration.bottomCellToolbarGap - this._layoutConfiguration.cellBottomMargin, + verticalIndicatorHeight: totalHeight - this._layoutConfiguration.bottomCellToolbarGap + }; + } + + dispose() { + this._disposables.forEach(d => d.dispose()); + this._disposables = []; + } +} diff --git a/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts b/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts index 91515c0c385..78f6e82be8a 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts @@ -18,8 +18,10 @@ import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { reduceCellRanges } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; import { NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; +import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/viewContext'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { CellKind, diff, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { NotebookOptions } from 'vs/workbench/contrib/notebook/common/notebookOptions'; import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { NotebookEditorTestModel, setupInstantiationService, withTestNotebook } from 'vs/workbench/contrib/notebook/test/testNotebookEditor'; @@ -37,8 +39,8 @@ suite('NotebookViewModel', () => { test('ctor', function () { 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); + const viewContext = new ViewContext(new NotebookOptions(instantiationService.get(IConfigurationService)), new NotebookEventDispatcher()); + const viewModel = new NotebookViewModel('notebook', model.notebook, viewContext, null, instantiationService, bulkEditService, undoRedoService, textModelService); assert.strictEqual(viewModel.viewType, 'notebook'); }); diff --git a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts index 48487ed3409..8d667d11aa6 100644 --- a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts @@ -42,6 +42,8 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; import { IWorkspaceTrustRequestService } from 'vs/platform/workspace/common/workspaceTrust'; import { TestWorkspaceTrustRequestService } from 'vs/workbench/services/workspaces/test/common/testWorkspaceTrustService'; +import { NotebookOptions } from 'vs/workbench/contrib/notebook/common/notebookOptions'; +import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/viewContext'; export class TestCell extends NotebookCellTextModel { constructor( @@ -172,8 +174,8 @@ function _createTestNotebookEditor(instantiationService: TestInstantiationServic }), notebookDocumentMetadataDefaults, { transientCellMetadata: {}, transientDocumentMetadata: {}, transientOutputs: false }); const model = new NotebookEditorTestModel(notebook); - const eventDispatcher = new NotebookEventDispatcher(); - const viewModel: NotebookViewModel = instantiationService.createInstance(NotebookViewModel, viewType, model.notebook, eventDispatcher, null); + const viewContext = new ViewContext(new NotebookOptions(instantiationService.get(IConfigurationService)), new NotebookEventDispatcher()); + const viewModel: NotebookViewModel = instantiationService.createInstance(NotebookViewModel, viewType, model.notebook, viewContext, null); const cellList = createNotebookCellList(instantiationService); cellList.attachViewModel(viewModel); diff --git a/src/vs/workbench/test/browser/api/extHostNotebookKernel2.test.ts b/src/vs/workbench/test/browser/api/extHostNotebookKernel2.test.ts index adf67d28a42..5bfdd1356c3 100644 --- a/src/vs/workbench/test/browser/api/extHostNotebookKernel2.test.ts +++ b/src/vs/workbench/test/browser/api/extHostNotebookKernel2.test.ts @@ -114,7 +114,8 @@ suite('NotebookKernel', function () { extHostNotebookKernels = new ExtHostNotebookKernels( rpcProtocol, new class extends mock() { }, - extHostNotebooks + extHostNotebooks, + new NullLogService() ); });