Merge branch 'notebook/dev' into main

This commit is contained in:
rebornix 2021-05-12 14:37:05 -07:00
commit a37ee2baf1
No known key found for this signature in database
GPG key ID: 181FC90D15393C20
42 changed files with 865 additions and 364 deletions

View file

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

View file

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

View file

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

View file

@ -1071,8 +1071,10 @@ declare module 'vscode' {
*/
readonly outputs: ReadonlyArray<NotebookCellOutput>;
// 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);
}
/**

View file

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

View file

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

View file

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

View file

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

View file

@ -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 (<vscode.NotebookCellOutputItem>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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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<IEditorOptions>('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<void> {
if (this._modifiedWebview) {
this._modifiedWebview.dispose();
}
this._modifiedWebview = this.instantiationService.createInstance(BackLayerWebView, this, id, resource, this.webviewOptions) as BackLayerWebView<IDiffCellInfo>;
this._modifiedWebview = this.instantiationService.createInstance(BackLayerWebView, this, id, resource, this._notebookOptions.computeDiffWebviewOptions()) as BackLayerWebView<IDiffCellInfo>;
// 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<IDiffCellInfo>;
this._originalWebview = this.instantiationService.createInstance(BackLayerWebView, this, id, resource, this._notebookOptions.computeDiffWebviewOptions()) as BackLayerWebView<IDiffCellInfo>;
// attach the webview container to the DOM tree first
this._list.rowsContainer.insertAdjacentElement('afterbegin', this._originalWebview.element);
await this._originalWebview.createWebview();

View file

@ -149,12 +149,6 @@ const notebookRendererContribution: IJSONSchema = {
}
};
export const notebooksExtensionPoint2 = ExtensionsRegistry.registerExtensionPoint<INotebookEditorContribution[]>(
{
extensionPoint: 'notebookProvider',
jsonSchema: { deprecationMessage: 'Use \'notebooks\' instead' }
});
export const notebooksExtensionPoint = ExtensionsRegistry.registerExtensionPoint<INotebookEditorContribution[]>(
{
extensionPoint: 'notebooks',

View file

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

View file

@ -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<void>;
readonly onDidChangeActiveCell: Event<void>;
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<void>();
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

View file

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

View file

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

View file

@ -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<ICommonCellInfo> | null = null;
private _webviewResolvePromise: Promise<BackLayerWebView<ICommonCellInfo> | 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<ICellViewModel, ICodeEditor | undefined> = 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<boolean>(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<string | { [key: string]: string }>(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<boolean>(ShowCellStatusBarKey);
const showCellStatusBar = this._notebookOptions.getLayoutConfiguration().showCellStatusBar;
this._overlayContainer.classList.toggle('cell-statusbar-hidden', !showCellStatusBar);
const cellToolbarInteraction = this.configurationService.getValue<string>(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<void> {
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)
}`);
});

View file

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

View file

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

View file

@ -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<string, IOutputTransformContribution>();
private readonly _richMimeTypeRenderers = new Map<string, IOutputTransformContribution>();
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();
}
}

View file

@ -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<number, DisposableStore>();
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 = <Error>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);

View file

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

View file

@ -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<IEditorOptions>();
readonly onDidChange: Event<IEditorOptions> = 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<boolean>(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<IEditorOptions>('editor', { overrideIdentifier: language }));

View file

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

View file

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

View file

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

View file

@ -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 = '';

View file

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

View file

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

View file

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

View file

@ -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<INotebookViewCellsUpdateEvent>());
get onDidChangeViewCells(): Event<INotebookViewCellsUpdateEvent> { 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);
}
}

View file

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

View file

@ -544,8 +544,9 @@ const _mimeTypeInfo = new Map<string, MimeTypeInfo>([
['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 }],

View file

@ -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<void>();
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<NotebookOptionsChangeEvent>();
readonly onDidChangeOptions = this._onDidChangeOptions.event;
private _disposables: IDisposable[];
constructor(readonly configurationService: IConfigurationService) {
const showCellStatusBar = this.configurationService.getValue<boolean>(ShowCellStatusBarKey);
const cellToolbarLocation = this.configurationService.getValue<string | { [key: string]: string }>(CellToolbarLocKey);
const cellToolbarInteraction = this.configurationService.getValue<string>(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<boolean>(ShowCellStatusBarKey);
}
if (cellToolbarLocation) {
configuration.cellToolbarLocation = this.configurationService.getValue<string | { [key: string]: string }>(CellToolbarLocKey);
}
if (cellToolbarInteraction) {
configuration.cellToolbarInteraction = this.configurationService.getValue<string>(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 = [];
}
}

View file

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

View file

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

View file

@ -114,7 +114,8 @@ suite('NotebookKernel', function () {
extHostNotebookKernels = new ExtHostNotebookKernels(
rpcProtocol,
new class extends mock<IExtHostInitDataService>() { },
extHostNotebooks
extHostNotebooks,
new NullLogService()
);
});