Path: blob/main/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import './media/notebook.css';6import './media/notebookCellChat.css';7import './media/notebookCellEditorHint.css';8import './media/notebookCellInsertToolbar.css';9import './media/notebookCellStatusBar.css';10import './media/notebookCellTitleToolbar.css';11import './media/notebookFocusIndicator.css';12import './media/notebookToolbar.css';13import './media/notebookDnd.css';14import './media/notebookFolding.css';15import './media/notebookCellOutput.css';16import './media/notebookEditorStickyScroll.css';17import './media/notebookKernelActionViewItem.css';18import './media/notebookOutline.css';19import './media/notebookChatEditController.css';20import './media/notebookChatEditorOverlay.css';21import * as DOM from '../../../../base/browser/dom.js';22import * as domStylesheets from '../../../../base/browser/domStylesheets.js';23import { IMouseWheelEvent, StandardMouseEvent } from '../../../../base/browser/mouseEvent.js';24import { IListContextMenuEvent } from '../../../../base/browser/ui/list/list.js';25import { mainWindow } from '../../../../base/browser/window.js';26import { SequencerByKey } from '../../../../base/common/async.js';27import { CancellationToken } from '../../../../base/common/cancellation.js';28import { Color, RGBA } from '../../../../base/common/color.js';29import { onUnexpectedError } from '../../../../base/common/errors.js';30import { Emitter, Event } from '../../../../base/common/event.js';31import { combinedDisposable, Disposable, DisposableStore, dispose } from '../../../../base/common/lifecycle.js';32import { setTimeout0 } from '../../../../base/common/platform.js';33import { extname, isEqual } from '../../../../base/common/resources.js';34import { URI } from '../../../../base/common/uri.js';35import { generateUuid } from '../../../../base/common/uuid.js';36import { FontMeasurements } from '../../../../editor/browser/config/fontMeasurements.js';37import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js';38import { IEditorOptions } from '../../../../editor/common/config/editorOptions.js';39import { BareFontInfo, FontInfo } from '../../../../editor/common/config/fontInfo.js';40import { Range } from '../../../../editor/common/core/range.js';41import { Selection } from '../../../../editor/common/core/selection.js';42import { SuggestController } from '../../../../editor/contrib/suggest/browser/suggestController.js';43import * as nls from '../../../../nls.js';44import { MenuId } from '../../../../platform/actions/common/actions.js';45import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';46import { IContextKey, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js';47import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js';48import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';49import { ServiceCollection } from '../../../../platform/instantiation/common/serviceCollection.js';50import { ILayoutService } from '../../../../platform/layout/browser/layoutService.js';51import { registerZIndex, ZIndex } from '../../../../platform/layout/browser/zIndexRegistry.js';52import { IEditorProgressService, IProgressRunner } from '../../../../platform/progress/common/progress.js';53import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js';54import { contrastBorder, errorForeground, focusBorder, foreground, listInactiveSelectionBackground, registerColor, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, transparent } from '../../../../platform/theme/common/colorRegistry.js';55import { EDITOR_PANE_BACKGROUND, PANEL_BORDER, SIDE_BAR_BACKGROUND } from '../../../common/theme.js';56import { debugIconStartForeground } from '../../debug/browser/debugColors.js';57import { CellEditState, CellFindMatchWithIndex, CellFocusMode, CellLayoutContext, CellRevealRangeType, CellRevealType, IActiveNotebookEditorDelegate, IBaseCellEditorOptions, ICellOutputViewModel, ICellViewModel, ICommonCellInfo, IDisplayOutputLayoutUpdateRequest, IFocusNotebookCellOptions, IInsetRenderOutput, IModelDecorationsChangeAccessor, INotebookCellOverlayChangeAccessor, INotebookDeltaDecoration, INotebookEditor, INotebookEditorContribution, INotebookEditorContributionDescription, INotebookEditorCreationOptions, INotebookEditorDelegate, INotebookEditorMouseEvent, INotebookEditorOptions, INotebookEditorViewState, INotebookViewCellsUpdateEvent, INotebookViewZoneChangeAccessor, INotebookWebviewMessage, RenderOutputType, ScrollToRevealBehavior } from './notebookBrowser.js';58import { NotebookEditorExtensionsRegistry } from './notebookEditorExtensions.js';59import { INotebookEditorService } from './services/notebookEditorService.js';60import { notebookDebug } from './notebookLogger.js';61import { NotebookCellStateChangedEvent, NotebookLayoutChangedEvent, NotebookLayoutInfo } from './notebookViewEvents.js';62import { CellContextKeyManager } from './view/cellParts/cellContextKeys.js';63import { CellDragAndDropController } from './view/cellParts/cellDnd.js';64import { ListViewInfoAccessor, NotebookCellList, NOTEBOOK_WEBVIEW_BOUNDARY } from './view/notebookCellList.js';65import { INotebookCellList } from './view/notebookRenderingCommon.js';66import { BackLayerWebView } from './view/renderers/backLayerWebView.js';67import { CodeCellRenderer, MarkupCellRenderer, NotebookCellListDelegate } from './view/renderers/cellRenderer.js';68import { IAckOutputHeight, IMarkupCellInitialization } from './view/renderers/webviewMessages.js';69import { CodeCellViewModel, outputDisplayLimit } from './viewModel/codeCellViewModel.js';70import { NotebookEventDispatcher } from './viewModel/eventDispatcher.js';71import { MarkupCellViewModel } from './viewModel/markupCellViewModel.js';72import { CellViewModel, NotebookViewModel } from './viewModel/notebookViewModelImpl.js';73import { ViewContext } from './viewModel/viewContext.js';74import { NotebookEditorWorkbenchToolbar } from './viewParts/notebookEditorToolbar.js';75import { NotebookEditorContextKeys } from './viewParts/notebookEditorWidgetContextKeys.js';76import { NotebookOverviewRuler } from './viewParts/notebookOverviewRuler.js';77import { ListTopCellToolbar } from './viewParts/notebookTopCellToolbar.js';78import { NotebookTextModel } from '../common/model/notebookTextModel.js';79import { CellEditType, CellKind, INotebookFindOptions, NotebookFindScopeType, RENDERER_NOT_AVAILABLE, SelectionStateType } from '../common/notebookCommon.js';80import { NOTEBOOK_CURSOR_NAVIGATION_MODE, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_OUTPUT_FOCUSED, NOTEBOOK_OUTPUT_INPUT_FOCUSED } from '../common/notebookContextKeys.js';81import { INotebookExecutionService } from '../common/notebookExecutionService.js';82import { INotebookKernelService } from '../common/notebookKernelService.js';83import { NotebookOptions, OutputInnerContainerTopPadding } from './notebookOptions.js';84import { cellRangesToIndexes, ICellRange } from '../common/notebookRange.js';85import { INotebookRendererMessagingService } from '../common/notebookRendererMessagingService.js';86import { INotebookService } from '../common/notebookService.js';87import { IWebviewElement } from '../../webview/browser/webview.js';88import { EditorExtensionsRegistry } from '../../../../editor/browser/editorExtensions.js';89import { IEditorGroupsService } from '../../../services/editor/common/editorGroupsService.js';90import { NotebookPerfMarks } from '../common/notebookPerformance.js';91import { BaseCellEditorOptions } from './viewModel/cellEditorOptions.js';92import { FloatingEditorClickMenu } from '../../../browser/codeeditor.js';93import { IDimension } from '../../../../editor/common/core/2d/dimension.js';94import { CellFindMatchModel } from './contrib/find/findModel.js';95import { INotebookLoggingService } from '../common/notebookLoggingService.js';96import { Schemas } from '../../../../base/common/network.js';97import { DropIntoEditorController } from '../../../../editor/contrib/dropOrPasteInto/browser/dropIntoEditorController.js';98import { CopyPasteController } from '../../../../editor/contrib/dropOrPasteInto/browser/copyPasteController.js';99import { NotebookStickyScroll } from './viewParts/notebookEditorStickyScroll.js';100import { PixelRatio } from '../../../../base/browser/pixelRatio.js';101import { PreventDefaultContextMenuItemsContextKeyName } from '../../webview/browser/webview.contribution.js';102import { NotebookAccessibilityProvider } from './notebookAccessibilityProvider.js';103import { NotebookHorizontalTracker } from './viewParts/notebookHorizontalTracker.js';104import { NotebookCellEditorPool } from './view/notebookCellEditorPool.js';105import { InlineCompletionsController } from '../../../../editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.js';106import { NotebookCellLayoutManager } from './notebookCellLayoutManager.js';107108const $ = DOM.$;109110export function getDefaultNotebookCreationOptions(): INotebookEditorCreationOptions {111// We inlined the id to avoid loading comment contrib in tests112const skipContributions = [113'editor.contrib.review',114FloatingEditorClickMenu.ID,115'editor.contrib.dirtydiff',116'editor.contrib.testingOutputPeek',117'editor.contrib.testingDecorations',118'store.contrib.stickyScrollController',119'editor.contrib.findController',120'editor.contrib.emptyTextEditorHint'121];122const contributions = EditorExtensionsRegistry.getEditorContributions().filter(c => skipContributions.indexOf(c.id) === -1);123124return {125menuIds: {126notebookToolbar: MenuId.NotebookToolbar,127cellTitleToolbar: MenuId.NotebookCellTitle,128cellDeleteToolbar: MenuId.NotebookCellDelete,129cellInsertToolbar: MenuId.NotebookCellBetween,130cellTopInsertToolbar: MenuId.NotebookCellListTop,131cellExecuteToolbar: MenuId.NotebookCellExecute,132cellExecutePrimary: MenuId.NotebookCellExecutePrimary,133},134cellEditorContributions: contributions135};136}137138export class NotebookEditorWidget extends Disposable implements INotebookEditorDelegate, INotebookEditor {139//#region Eventing140private readonly _onDidChangeCellState = this._register(new Emitter<NotebookCellStateChangedEvent>());141readonly onDidChangeCellState = this._onDidChangeCellState.event;142private readonly _onDidChangeViewCells = this._register(new Emitter<INotebookViewCellsUpdateEvent>());143readonly onDidChangeViewCells: Event<INotebookViewCellsUpdateEvent> = this._onDidChangeViewCells.event;144private readonly _onWillChangeModel = this._register(new Emitter<NotebookTextModel | undefined>());145readonly onWillChangeModel: Event<NotebookTextModel | undefined> = this._onWillChangeModel.event;146private readonly _onDidChangeModel = this._register(new Emitter<NotebookTextModel | undefined>());147readonly onDidChangeModel: Event<NotebookTextModel | undefined> = this._onDidChangeModel.event;148private readonly _onDidAttachViewModel = this._register(new Emitter<void>());149readonly onDidAttachViewModel: Event<void> = this._onDidAttachViewModel.event;150private readonly _onDidChangeOptions = this._register(new Emitter<void>());151readonly onDidChangeOptions: Event<void> = this._onDidChangeOptions.event;152private readonly _onDidChangeDecorations = this._register(new Emitter<void>());153readonly onDidChangeDecorations: Event<void> = this._onDidChangeDecorations.event;154private readonly _onDidScroll = this._register(new Emitter<void>());155readonly onDidScroll: Event<void> = this._onDidScroll.event;156private readonly _onDidChangeLayout = this._register(new Emitter<void>());157readonly onDidChangeLayout: Event<void> = this._onDidChangeLayout.event;158private readonly _onDidChangeActiveCell = this._register(new Emitter<void>());159readonly onDidChangeActiveCell: Event<void> = this._onDidChangeActiveCell.event;160private readonly _onDidChangeFocus = this._register(new Emitter<void>());161readonly onDidChangeFocus: Event<void> = this._onDidChangeFocus.event;162private readonly _onDidChangeSelection = this._register(new Emitter<void>());163readonly onDidChangeSelection: Event<void> = this._onDidChangeSelection.event;164private readonly _onDidChangeVisibleRanges = this._register(new Emitter<void>());165readonly onDidChangeVisibleRanges: Event<void> = this._onDidChangeVisibleRanges.event;166private readonly _onDidFocusEmitter = this._register(new Emitter<void>());167readonly onDidFocusWidget = this._onDidFocusEmitter.event;168private readonly _onDidBlurEmitter = this._register(new Emitter<void>());169readonly onDidBlurWidget = this._onDidBlurEmitter.event;170private readonly _onDidChangeActiveEditor = this._register(new Emitter<this>());171readonly onDidChangeActiveEditor: Event<this> = this._onDidChangeActiveEditor.event;172private readonly _onDidChangeActiveKernel = this._register(new Emitter<void>());173readonly onDidChangeActiveKernel: Event<void> = this._onDidChangeActiveKernel.event;174private readonly _onMouseUp: Emitter<INotebookEditorMouseEvent> = this._register(new Emitter<INotebookEditorMouseEvent>());175readonly onMouseUp: Event<INotebookEditorMouseEvent> = this._onMouseUp.event;176private readonly _onMouseDown: Emitter<INotebookEditorMouseEvent> = this._register(new Emitter<INotebookEditorMouseEvent>());177readonly onMouseDown: Event<INotebookEditorMouseEvent> = this._onMouseDown.event;178private readonly _onDidReceiveMessage = this._register(new Emitter<INotebookWebviewMessage>());179readonly onDidReceiveMessage: Event<INotebookWebviewMessage> = this._onDidReceiveMessage.event;180private readonly _onDidRenderOutput = this._register(new Emitter<ICellOutputViewModel>());181private readonly onDidRenderOutput = this._onDidRenderOutput.event;182private readonly _onDidRemoveOutput = this._register(new Emitter<ICellOutputViewModel>());183private readonly onDidRemoveOutput = this._onDidRemoveOutput.event;184private readonly _onDidResizeOutputEmitter = this._register(new Emitter<ICellViewModel>());185readonly onDidResizeOutput = this._onDidResizeOutputEmitter.event;186187//#endregion188private _overlayContainer!: HTMLElement;189private _notebookTopToolbarContainer!: HTMLElement;190private _notebookTopToolbar!: NotebookEditorWorkbenchToolbar;191private _notebookStickyScrollContainer!: HTMLElement;192private _notebookStickyScroll!: NotebookStickyScroll;193private _notebookOverviewRulerContainer!: HTMLElement;194private _notebookOverviewRuler!: NotebookOverviewRuler;195private _body!: HTMLElement;196private _styleElement!: HTMLStyleElement;197private _overflowContainer!: HTMLElement;198private _webview: BackLayerWebView<ICommonCellInfo> | null = null;199private _webviewResolvePromise: Promise<BackLayerWebView<ICommonCellInfo> | null> | null = null;200private _webviewTransparentCover: HTMLElement | null = null;201private _listDelegate: NotebookCellListDelegate | null = null;202private _list!: INotebookCellList;203private _listViewInfoAccessor!: ListViewInfoAccessor;204private _dndController: CellDragAndDropController | null = null;205private _listTopCellToolbar: ListTopCellToolbar | null = null;206private _renderedEditors: Map<ICellViewModel, ICodeEditor> = new Map();207private _editorPool!: NotebookCellEditorPool;208private _viewContext: ViewContext;209private _notebookViewModel: NotebookViewModel | undefined;210private readonly _localStore: DisposableStore = this._register(new DisposableStore());211private _localCellStateListeners: DisposableStore[] = [];212private _fontInfo: FontInfo | undefined;213private _dimension?: DOM.Dimension;214private _position?: DOM.IDomPosition;215private _shadowElement?: HTMLElement;216private _shadowElementViewInfo: { height: number; width: number; top: number; left: number } | null = null;217private _cellLayoutManager: NotebookCellLayoutManager | undefined;218219private readonly _editorFocus: IContextKey<boolean>;220private readonly _outputFocus: IContextKey<boolean>;221private readonly _editorEditable: IContextKey<boolean>;222private readonly _cursorNavMode: IContextKey<boolean>;223private readonly _outputInputFocus: IContextKey<boolean>;224protected readonly _contributions = new Map<string, INotebookEditorContribution>();225private _scrollBeyondLastLine: boolean;226private readonly _insetModifyQueueByOutputId = new SequencerByKey<string>();227private _cellContextKeyManager: CellContextKeyManager | null = null;228private readonly _uuid = generateUuid();229private _focusTracker!: DOM.IFocusTracker;230private _webviewFocused: boolean = false;231private _isVisible = false;232get isVisible() {233return this._isVisible;234}235236private _isDisposed: boolean = false;237238get isDisposed() {239return this._isDisposed;240}241242set viewModel(newModel: NotebookViewModel | undefined) {243this._onWillChangeModel.fire(this._notebookViewModel?.notebookDocument);244this._notebookViewModel = newModel;245this._onDidChangeModel.fire(newModel?.notebookDocument);246}247248get viewModel() {249return this._notebookViewModel;250}251252get textModel() {253return this._notebookViewModel?.notebookDocument;254}255256get isReadOnly() {257return this._notebookViewModel?.options.isReadOnly ?? false;258}259260get activeCodeEditor(): ICodeEditor | undefined {261if (this._isDisposed) {262return;263}264265const [focused] = this._list.getFocusedElements();266return this._renderedEditors.get(focused);267}268269get activeCellAndCodeEditor(): [ICellViewModel, ICodeEditor] | undefined {270if (this._isDisposed) {271return;272}273274const [focused] = this._list.getFocusedElements();275const editor = this._renderedEditors.get(focused);276if (!editor) {277return;278}279return [focused, editor];280}281282get codeEditors(): [ICellViewModel, ICodeEditor][] {283return [...this._renderedEditors];284}285286get visibleRanges() {287return this._list ? (this._list.visibleRanges || []) : [];288}289290private _baseCellEditorOptions = new Map<string, IBaseCellEditorOptions>();291292readonly isReplHistory: boolean;293private _readOnly: boolean;294295public readonly scopedContextKeyService: IContextKeyService;296private readonly instantiationService: IInstantiationService;297private readonly _notebookOptions: NotebookOptions;298299private _currentProgress: IProgressRunner | undefined;300301get notebookOptions() {302return this._notebookOptions;303}304305constructor(306readonly creationOptions: INotebookEditorCreationOptions,307dimension: DOM.Dimension | undefined,308@IInstantiationService instantiationService: IInstantiationService,309@IEditorGroupsService editorGroupsService: IEditorGroupsService,310@INotebookRendererMessagingService private readonly notebookRendererMessaging: INotebookRendererMessagingService,311@INotebookEditorService private readonly notebookEditorService: INotebookEditorService,312@INotebookKernelService private readonly notebookKernelService: INotebookKernelService,313@INotebookService private readonly _notebookService: INotebookService,314@IConfigurationService private readonly configurationService: IConfigurationService,315@IContextKeyService contextKeyService: IContextKeyService,316@ILayoutService private readonly layoutService: ILayoutService,317@IContextMenuService private readonly contextMenuService: IContextMenuService,318@ITelemetryService private readonly telemetryService: ITelemetryService,319@INotebookExecutionService private readonly notebookExecutionService: INotebookExecutionService,320@IEditorProgressService private editorProgressService: IEditorProgressService,321@INotebookLoggingService private readonly logService: INotebookLoggingService,322) {323super();324325this._dimension = dimension;326327this.isReplHistory = creationOptions.isReplHistory ?? false;328this._readOnly = creationOptions.isReadOnly ?? false;329330this._overlayContainer = document.createElement('div');331this.scopedContextKeyService = this._register(contextKeyService.createScoped(this._overlayContainer));332this.instantiationService = this._register(instantiationService.createChild(new ServiceCollection([IContextKeyService, this.scopedContextKeyService])));333334this._notebookOptions = creationOptions.options ??335this.instantiationService.createInstance(NotebookOptions, this.creationOptions?.codeWindow ?? mainWindow, this._readOnly, undefined);336this._register(this._notebookOptions);337const eventDispatcher = this._register(new NotebookEventDispatcher());338this._viewContext = new ViewContext(339this._notebookOptions,340eventDispatcher,341language => this.getBaseCellEditorOptions(language));342this._register(this._viewContext.eventDispatcher.onDidChangeLayout(() => {343this._onDidChangeLayout.fire();344}));345this._register(this._viewContext.eventDispatcher.onDidChangeCellState(e => {346this._onDidChangeCellState.fire(e);347}));348349350this._register(_notebookService.onDidChangeOutputRenderers(() => {351this._updateOutputRenderers();352}));353354this._register(this.instantiationService.createInstance(NotebookEditorContextKeys, this));355356this._register(notebookKernelService.onDidChangeSelectedNotebooks(e => {357if (isEqual(e.notebook, this.viewModel?.uri)) {358this._loadKernelPreloads();359this._onDidChangeActiveKernel.fire();360}361}));362363this._scrollBeyondLastLine = this.configurationService.getValue<boolean>('editor.scrollBeyondLastLine');364365this._register(this.configurationService.onDidChangeConfiguration(e => {366if (e.affectsConfiguration('editor.scrollBeyondLastLine')) {367this._scrollBeyondLastLine = this.configurationService.getValue<boolean>('editor.scrollBeyondLastLine');368if (this._dimension && this._isVisible) {369this.layout(this._dimension);370}371}372}));373374this._register(this._notebookOptions.onDidChangeOptions(e => {375if (e.cellStatusBarVisibility || e.cellToolbarLocation || e.cellToolbarInteraction) {376this._updateForNotebookConfiguration();377}378379if (e.fontFamily) {380this._generateFontInfo();381}382383if (e.compactView384|| e.focusIndicator385|| e.insertToolbarPosition386|| e.cellToolbarLocation387|| e.dragAndDropEnabled388|| e.fontSize389|| e.markupFontSize390|| e.markdownLineHeight391|| e.fontFamily392|| e.insertToolbarAlignment393|| e.outputFontSize394|| e.outputLineHeight395|| e.outputFontFamily396|| e.outputWordWrap397|| e.outputScrolling398|| e.outputLinkifyFilePaths399|| e.minimalError400) {401this._styleElement?.remove();402this._createLayoutStyles();403this._webview?.updateOptions({404...this.notebookOptions.computeWebviewOptions(),405fontFamily: this._generateFontFamily()406});407}408409if (this._dimension && this._isVisible) {410this.layout(this._dimension);411}412}));413414const container = creationOptions.codeWindow ? this.layoutService.getContainer(creationOptions.codeWindow) : this.layoutService.mainContainer;415this._register(editorGroupsService.getPart(container).onDidScroll(e => {416if (!this._shadowElement || !this._isVisible) {417return;418}419420this.updateShadowElement(this._shadowElement, this._dimension);421this.layoutContainerOverShadowElement(this._dimension, this._position);422}));423424this.notebookEditorService.addNotebookEditor(this);425426const id = generateUuid();427this._overlayContainer.id = `notebook-${id}`;428this._overlayContainer.className = 'notebookOverlay';429this._overlayContainer.classList.add('notebook-editor');430this._overlayContainer.inert = true;431this._overlayContainer.style.visibility = 'hidden';432433container.appendChild(this._overlayContainer);434435this._createBody(this._overlayContainer);436this._generateFontInfo();437this._isVisible = true;438this._editorFocus = NOTEBOOK_EDITOR_FOCUSED.bindTo(this.scopedContextKeyService);439this._outputFocus = NOTEBOOK_OUTPUT_FOCUSED.bindTo(this.scopedContextKeyService);440this._outputInputFocus = NOTEBOOK_OUTPUT_INPUT_FOCUSED.bindTo(this.scopedContextKeyService);441this._editorEditable = NOTEBOOK_EDITOR_EDITABLE.bindTo(this.scopedContextKeyService);442this._cursorNavMode = NOTEBOOK_CURSOR_NAVIGATION_MODE.bindTo(this.scopedContextKeyService);443// Never display the native cut/copy context menu items in notebooks444new RawContextKey<boolean>(PreventDefaultContextMenuItemsContextKeyName, false).bindTo(this.scopedContextKeyService).set(true);445446this._editorEditable.set(!creationOptions.isReadOnly);447448let contributions: INotebookEditorContributionDescription[];449if (Array.isArray(this.creationOptions.contributions)) {450contributions = this.creationOptions.contributions;451} else {452contributions = NotebookEditorExtensionsRegistry.getEditorContributions();453}454for (const desc of contributions) {455let contribution: INotebookEditorContribution | undefined;456try {457contribution = this.instantiationService.createInstance(desc.ctor, this);458} catch (err) {459onUnexpectedError(err);460}461if (contribution) {462if (!this._contributions.has(desc.id)) {463this._contributions.set(desc.id, contribution);464} else {465contribution.dispose();466throw new Error(`DUPLICATE notebook editor contribution: '${desc.id}'`);467}468}469}470471this._updateForNotebookConfiguration();472}473474private _debugFlag: boolean = false;475476private _debug(...args: any[]) {477if (!this._debugFlag) {478return;479}480481notebookDebug(...args);482}483484/**485* EditorId486*/487public getId(): string {488return this._uuid;489}490491getViewModel(): NotebookViewModel | undefined {492return this.viewModel;493}494495getLength() {496return this.viewModel?.length ?? 0;497}498499getSelections() {500return this.viewModel?.getSelections() ?? [{ start: 0, end: 0 }];501}502503setSelections(selections: ICellRange[]) {504if (!this.viewModel) {505return;506}507508const focus = this.viewModel.getFocus();509this.viewModel.updateSelectionsState({510kind: SelectionStateType.Index,511focus: focus,512selections: selections513});514}515516getFocus() {517return this.viewModel?.getFocus() ?? { start: 0, end: 0 };518}519520setFocus(focus: ICellRange) {521if (!this.viewModel) {522return;523}524525const selections = this.viewModel.getSelections();526this.viewModel.updateSelectionsState({527kind: SelectionStateType.Index,528focus: focus,529selections: selections530});531}532533getSelectionViewModels(): ICellViewModel[] {534if (!this.viewModel) {535return [];536}537538const cellsSet = new Set<number>();539540return this.viewModel.getSelections().map(range => this.viewModel!.viewCells.slice(range.start, range.end)).reduce((a, b) => {541b.forEach(cell => {542if (!cellsSet.has(cell.handle)) {543cellsSet.add(cell.handle);544a.push(cell);545}546});547548return a;549}, [] as ICellViewModel[]);550}551552hasModel(): this is IActiveNotebookEditorDelegate {553return !!this._notebookViewModel;554}555556showProgress(): void {557this._currentProgress = this.editorProgressService.show(true);558}559560hideProgress(): void {561if (this._currentProgress) {562this._currentProgress.done();563this._currentProgress = undefined;564}565}566567//#region Editor Core568569getBaseCellEditorOptions(language: string): IBaseCellEditorOptions {570const existingOptions = this._baseCellEditorOptions.get(language);571572if (existingOptions) {573return existingOptions;574} else {575const options = new BaseCellEditorOptions(this, this.notebookOptions, this.configurationService, language);576this._baseCellEditorOptions.set(language, options);577return options;578}579}580581private _updateForNotebookConfiguration() {582if (!this._overlayContainer) {583return;584}585586this._overlayContainer.classList.remove('cell-title-toolbar-left');587this._overlayContainer.classList.remove('cell-title-toolbar-right');588this._overlayContainer.classList.remove('cell-title-toolbar-hidden');589const cellToolbarLocation = this._notebookOptions.computeCellToolbarLocation(this.viewModel?.viewType);590this._overlayContainer.classList.add(`cell-title-toolbar-${cellToolbarLocation}`);591592const cellToolbarInteraction = this._notebookOptions.getDisplayOptions().cellToolbarInteraction;593let cellToolbarInteractionState = 'hover';594this._overlayContainer.classList.remove('cell-toolbar-hover');595this._overlayContainer.classList.remove('cell-toolbar-click');596597if (cellToolbarInteraction === 'hover' || cellToolbarInteraction === 'click') {598cellToolbarInteractionState = cellToolbarInteraction;599}600this._overlayContainer.classList.add(`cell-toolbar-${cellToolbarInteractionState}`);601602}603604private _generateFontInfo(): void {605const editorOptions = this.configurationService.getValue<IEditorOptions>('editor');606const targetWindow = DOM.getWindow(this.getDomNode());607this._fontInfo = FontMeasurements.readFontInfo(targetWindow, BareFontInfo.createFromRawSettings(editorOptions, PixelRatio.getInstance(targetWindow).value));608}609610private _createBody(parent: HTMLElement): void {611this._notebookTopToolbarContainer = document.createElement('div');612this._notebookTopToolbarContainer.classList.add('notebook-toolbar-container');613this._notebookTopToolbarContainer.style.display = 'none';614DOM.append(parent, this._notebookTopToolbarContainer);615616this._notebookStickyScrollContainer = document.createElement('div');617this._notebookStickyScrollContainer.classList.add('notebook-sticky-scroll-container');618DOM.append(parent, this._notebookStickyScrollContainer);619620this._body = document.createElement('div');621DOM.append(parent, this._body);622623this._body.classList.add('cell-list-container');624this._createLayoutStyles();625this._createCellList();626627this._notebookOverviewRulerContainer = document.createElement('div');628this._notebookOverviewRulerContainer.classList.add('notebook-overview-ruler-container');629this._list.scrollableElement.appendChild(this._notebookOverviewRulerContainer);630this._registerNotebookOverviewRuler();631632this._register(this.instantiationService.createInstance(NotebookHorizontalTracker, this, this._list.scrollableElement));633634this._overflowContainer = document.createElement('div');635this._overflowContainer.classList.add('notebook-overflow-widget-container', 'monaco-editor');636DOM.append(parent, this._overflowContainer);637}638639private _generateFontFamily() {640return this._fontInfo?.fontFamily ?? `"SF Mono", Monaco, Menlo, Consolas, "Ubuntu Mono", "Liberation Mono", "DejaVu Sans Mono", "Courier New", monospace`;641}642643private _createLayoutStyles(): void {644this._styleElement = domStylesheets.createStyleSheet(this._body);645const {646cellRightMargin,647cellTopMargin,648cellRunGutter,649cellBottomMargin,650codeCellLeftMargin,651markdownCellGutter,652markdownCellLeftMargin,653markdownCellBottomMargin,654markdownCellTopMargin,655collapsedIndicatorHeight,656focusIndicator,657insertToolbarPosition,658outputFontSize,659focusIndicatorLeftMargin,660focusIndicatorGap661} = this._notebookOptions.getLayoutConfiguration();662663const {664insertToolbarAlignment,665compactView,666fontSize667} = this._notebookOptions.getDisplayOptions();668669const getCellEditorContainerLeftMargin = this._notebookOptions.getCellEditorContainerLeftMargin();670671const { bottomToolbarGap, bottomToolbarHeight } = this._notebookOptions.computeBottomToolbarDimensions(this.viewModel?.viewType);672673const styleSheets: string[] = [];674if (!this._fontInfo) {675this._generateFontInfo();676}677678const fontFamily = this._generateFontFamily();679680styleSheets.push(`681.notebook-editor {682--notebook-cell-output-font-size: ${outputFontSize}px;683--notebook-cell-input-preview-font-size: ${fontSize}px;684--notebook-cell-input-preview-font-family: ${fontFamily};685}686`);687688if (compactView) {689styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .markdown-cell-row div.cell.code { margin-left: ${getCellEditorContainerLeftMargin}px; }`);690} else {691styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .markdown-cell-row div.cell.code { margin-left: ${codeCellLeftMargin}px; }`);692}693694// focus indicator695if (focusIndicator === 'border') {696styleSheets.push(`697.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-top:before,698.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-bottom:before,699.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row .cell-inner-container:before,700.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row .cell-inner-container:after {701content: "";702position: absolute;703width: 100%;704height: 1px;705}706707.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-left:before,708.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-right:before {709content: "";710position: absolute;711width: 1px;712height: 100%;713z-index: 10;714}715716/* top border */717.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-top:before {718border-top: 1px solid transparent;719}720721/* left border */722.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-left:before {723border-left: 1px solid transparent;724}725726/* bottom border */727.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-bottom:before {728border-bottom: 1px solid transparent;729}730731/* right border */732.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-right:before {733border-right: 1px solid transparent;734}735`);736737// left and right border margins738styleSheets.push(`739.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row.focused .cell-focus-indicator-left:before,740.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row.focused .cell-focus-indicator-right:before,741.monaco-workbench .notebookOverlay .monaco-list.selection-multiple .monaco-list-row.code-cell-row.selected .cell-focus-indicator-left:before,742.monaco-workbench .notebookOverlay .monaco-list.selection-multiple .monaco-list-row.code-cell-row.selected .cell-focus-indicator-right:before {743top: -${cellTopMargin}px; height: calc(100% + ${cellTopMargin + cellBottomMargin}px)744}`);745} else {746styleSheets.push(`747.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-left .codeOutput-focus-indicator {748border-left: 3px solid transparent;749border-radius: 4px;750width: 0px;751margin-left: ${focusIndicatorLeftMargin}px;752border-color: var(--vscode-notebook-inactiveFocusedCellBorder) !important;753}754755.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-left .codeOutput-focus-indicator-container,756.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-output-hover .cell-focus-indicator-left .codeOutput-focus-indicator-container,757.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .markdown-cell-hover .cell-focus-indicator-left .codeOutput-focus-indicator-container,758.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row:hover .cell-focus-indicator-left .codeOutput-focus-indicator-container {759display: block;760}761762.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-left .codeOutput-focus-indicator-container:hover .codeOutput-focus-indicator {763border-left: 5px solid transparent;764margin-left: ${focusIndicatorLeftMargin - 1}px;765}766`);767768styleSheets.push(`769.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-inner-container.cell-output-focus .cell-focus-indicator-left .codeOutput-focus-indicator,770.monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-inner-container .cell-focus-indicator-left .codeOutput-focus-indicator {771border-color: var(--vscode-notebook-focusedCellBorder) !important;772}773774.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-inner-container .cell-focus-indicator-left .output-focus-indicator {775margin-top: ${focusIndicatorGap}px;776}777`);778}779780// between cell insert toolbar781if (insertToolbarPosition === 'betweenCells' || insertToolbarPosition === 'both') {782styleSheets.push(`.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container { display: flex; }`);783styleSheets.push(`.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .view-zones .cell-list-top-cell-toolbar-container { display: flex; }`);784} else {785styleSheets.push(`.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container { display: none; }`);786styleSheets.push(`.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .view-zones .cell-list-top-cell-toolbar-container { display: none; }`);787}788789if (insertToolbarAlignment === 'left') {790styleSheets.push(`791.monaco-workbench .notebookOverlay .cell-list-top-cell-toolbar-container .action-item:first-child,792.monaco-workbench .notebookOverlay .cell-list-top-cell-toolbar-container .action-item:first-child, .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container .action-item:first-child {793margin-right: 0px !important;794}`);795796styleSheets.push(`797.monaco-workbench .notebookOverlay .cell-list-top-cell-toolbar-container .monaco-toolbar .action-label,798.monaco-workbench .notebookOverlay .cell-list-top-cell-toolbar-container .monaco-toolbar .action-label, .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container .monaco-toolbar .action-label {799padding: 0px !important;800justify-content: center;801border-radius: 4px;802}`);803804styleSheets.push(`805.monaco-workbench .notebookOverlay .cell-list-top-cell-toolbar-container,806.monaco-workbench .notebookOverlay .cell-list-top-cell-toolbar-container, .monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container {807align-items: flex-start;808justify-content: left;809margin: 0 16px 0 ${8 + codeCellLeftMargin}px;810}`);811812styleSheets.push(`813.monaco-workbench .notebookOverlay .cell-list-top-cell-toolbar-container,814.notebookOverlay .cell-bottom-toolbar-container .action-item {815border: 0px;816}`);817}818819styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .code-cell-row div.cell.code { margin-left: ${getCellEditorContainerLeftMargin}px; }`);820// Chat Edit, deleted Cell Overlay821styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .view-zones .code-cell-row div.cell.code { margin-left: ${getCellEditorContainerLeftMargin}px; }`);822// Chat Edit, deleted Cell Overlay823styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .view-zones .code-cell-row div.cell { margin-right: ${cellRightMargin}px; }`);824styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row div.cell { margin-right: ${cellRightMargin}px; }`);825styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row > .cell-inner-container { padding-top: ${cellTopMargin}px; }`);826styleSheets.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; }`);827styleSheets.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; }`);828styleSheets.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; }`);829styleSheets.push(`.notebookOverlay .output { margin: 0px ${cellRightMargin}px 0px ${getCellEditorContainerLeftMargin}px; }`);830styleSheets.push(`.notebookOverlay .output { width: calc(100% - ${getCellEditorContainerLeftMargin + cellRightMargin}px); }`);831832// comment833styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-comment-container { left: ${getCellEditorContainerLeftMargin}px; }`);834styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-comment-container { width: calc(100% - ${getCellEditorContainerLeftMargin + cellRightMargin}px); }`);835836// output collapse button837styleSheets.push(`.monaco-workbench .notebookOverlay .output .output-collapse-container .expandButton { left: -${cellRunGutter}px; }`);838styleSheets.push(`.monaco-workbench .notebookOverlay .output .output-collapse-container .expandButton {839position: absolute;840width: ${cellRunGutter}px;841padding: 6px 0px;842}`);843844// show more container845styleSheets.push(`.notebookOverlay .output-show-more-container { margin: 0px ${cellRightMargin}px 0px ${getCellEditorContainerLeftMargin}px; }`);846styleSheets.push(`.notebookOverlay .output-show-more-container { width: calc(100% - ${getCellEditorContainerLeftMargin + cellRightMargin}px); }`);847848styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row div.cell.markdown { padding-left: ${cellRunGutter}px; }`);849styleSheets.push(`.monaco-workbench .notebookOverlay > .cell-list-container .notebook-folding-indicator { left: ${(markdownCellGutter - 20) / 2 + markdownCellLeftMargin}px; }`);850styleSheets.push(`.notebookOverlay > .cell-list-container .notebook-folded-hint { left: ${markdownCellGutter + markdownCellLeftMargin + 8}px; }`);851styleSheets.push(`.notebookOverlay .monaco-list .monaco-list-row :not(.webview-backed-markdown-cell) .cell-focus-indicator-top { height: ${cellTopMargin}px; }`);852styleSheets.push(`.notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-side { bottom: ${bottomToolbarGap}px; }`);853styleSheets.push(`.notebookOverlay .monaco-list .monaco-list-row.code-cell-row .cell-focus-indicator-left { width: ${getCellEditorContainerLeftMargin}px; }`);854styleSheets.push(`.notebookOverlay .monaco-list .monaco-list-row.markdown-cell-row .cell-focus-indicator-left { width: ${codeCellLeftMargin}px; }`);855styleSheets.push(`.notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator.cell-focus-indicator-right { width: ${cellRightMargin}px; }`);856styleSheets.push(`.notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-bottom { height: ${cellBottomMargin}px; }`);857styleSheets.push(`.notebookOverlay .monaco-list .monaco-list-row .cell-shadow-container-bottom { top: ${cellBottomMargin}px; }`);858859styleSheets.push(`860.notebookOverlay .monaco-list.selection-multiple .monaco-list-row:has(+ .monaco-list-row.selected) .cell-focus-indicator-bottom {861height: ${bottomToolbarGap + cellBottomMargin}px;862}863`);864865styleSheets.push(`866.notebookOverlay .monaco-list .monaco-list-row.code-cell-row.nb-multiCellHighlight:has(+ .monaco-list-row.nb-multiCellHighlight) .cell-focus-indicator-bottom {867height: ${bottomToolbarGap + cellBottomMargin}px;868background-color: var(--vscode-notebook-symbolHighlightBackground) !important;869}870871.notebookOverlay .monaco-list .monaco-list-row.markdown-cell-row.nb-multiCellHighlight:has(+ .monaco-list-row.nb-multiCellHighlight) .cell-focus-indicator-bottom {872height: ${bottomToolbarGap + cellBottomMargin - 6}px;873background-color: var(--vscode-notebook-symbolHighlightBackground) !important;874}875`);876877878styleSheets.push(`879.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .input-collapse-container .cell-collapse-preview {880line-height: ${collapsedIndicatorHeight}px;881}882883.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .input-collapse-container .cell-collapse-preview .monaco-tokenized-source {884max-height: ${collapsedIndicatorHeight}px;885}886`);887888styleSheets.push(`.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container .monaco-toolbar { height: ${bottomToolbarHeight}px }`);889styleSheets.push(`.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .view-zones .cell-list-top-cell-toolbar-container .monaco-toolbar { height: ${bottomToolbarHeight}px }`);890891// cell toolbar892styleSheets.push(`.monaco-workbench .notebookOverlay.cell-title-toolbar-right > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-title-toolbar {893right: ${cellRightMargin + 26}px;894}895.monaco-workbench .notebookOverlay.cell-title-toolbar-left > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-title-toolbar {896left: ${getCellEditorContainerLeftMargin + 16}px;897}898.monaco-workbench .notebookOverlay.cell-title-toolbar-hidden > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-title-toolbar {899display: none;900}`);901902// cell output innert container903styleSheets.push(`904.monaco-workbench .notebookOverlay .output > div.foreground.output-inner-container {905padding: ${OutputInnerContainerTopPadding}px 8px;906}907.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .output-collapse-container {908padding: ${OutputInnerContainerTopPadding}px 8px;909}910`);911912// chat913styleSheets.push(`914.monaco-workbench .notebookOverlay .cell-chat-part {915margin: 0 ${cellRightMargin}px 6px 4px;916}917`);918919this._styleElement.textContent = styleSheets.join('\n');920}921922private _createCellList(): void {923this._body.classList.add('cell-list-container');924this._dndController = this._register(new CellDragAndDropController(this, this._body));925const getScopedContextKeyService = (container: HTMLElement) => this._list.contextKeyService.createScoped(container);926this._editorPool = this._register(this.instantiationService.createInstance(NotebookCellEditorPool, this, getScopedContextKeyService));927const renderers = [928this.instantiationService.createInstance(CodeCellRenderer, this, this._renderedEditors, this._editorPool, this._dndController, getScopedContextKeyService),929this.instantiationService.createInstance(MarkupCellRenderer, this, this._dndController, this._renderedEditors, getScopedContextKeyService),930];931932renderers.forEach(renderer => {933this._register(renderer);934});935936this._listDelegate = this.instantiationService.createInstance(NotebookCellListDelegate, DOM.getWindow(this.getDomNode()));937this._register(this._listDelegate);938939const accessibilityProvider = this.instantiationService.createInstance(NotebookAccessibilityProvider, () => this.viewModel, this.isReplHistory);940this._register(accessibilityProvider);941942this._list = this.instantiationService.createInstance(943NotebookCellList,944'NotebookCellList',945this._body,946this._viewContext.notebookOptions,947this._listDelegate,948renderers,949this.scopedContextKeyService,950{951setRowLineHeight: false,952setRowHeight: false,953supportDynamicHeights: true,954horizontalScrolling: false,955keyboardSupport: false,956mouseSupport: true,957multipleSelectionSupport: true,958selectionNavigation: true,959typeNavigationEnabled: true,960paddingTop: 0,961paddingBottom: 0,962transformOptimization: false, //(isMacintosh && isNative) || getTitleBarStyle(this.configurationService, this.environmentService) === 'native',963initialSize: this._dimension,964styleController: (_suffix: string) => { return this._list; },965overrideStyles: {966listBackground: notebookEditorBackground,967listActiveSelectionBackground: notebookEditorBackground,968listActiveSelectionForeground: foreground,969listFocusAndSelectionBackground: notebookEditorBackground,970listFocusAndSelectionForeground: foreground,971listFocusBackground: notebookEditorBackground,972listFocusForeground: foreground,973listHoverForeground: foreground,974listHoverBackground: notebookEditorBackground,975listHoverOutline: focusBorder,976listFocusOutline: focusBorder,977listInactiveSelectionBackground: notebookEditorBackground,978listInactiveSelectionForeground: foreground,979listInactiveFocusBackground: notebookEditorBackground,980listInactiveFocusOutline: notebookEditorBackground,981},982accessibilityProvider983},984);985this._cellLayoutManager = new NotebookCellLayoutManager(this, this._list, this.logService);986this._dndController.setList(this._list);987988// create Webview989990this._register(this._list);991this._listViewInfoAccessor = new ListViewInfoAccessor(this._list);992this._register(this._listViewInfoAccessor);993994this._register(combinedDisposable(...renderers));995996// top cell toolbar997this._listTopCellToolbar = this._register(this.instantiationService.createInstance(ListTopCellToolbar, this, this.notebookOptions));998999// transparent cover1000this._webviewTransparentCover = DOM.append(this._list.rowsContainer, $('.webview-cover'));1001this._webviewTransparentCover.style.display = 'none';10021003this._register(DOM.addStandardDisposableGenericMouseDownListener(this._overlayContainer, (e: StandardMouseEvent) => {1004if (e.target.classList.contains('slider') && this._webviewTransparentCover) {1005this._webviewTransparentCover.style.display = 'block';1006}1007}));10081009this._register(DOM.addStandardDisposableGenericMouseUpListener(this._overlayContainer, () => {1010if (this._webviewTransparentCover) {1011// no matter when1012this._webviewTransparentCover.style.display = 'none';1013}1014}));10151016this._register(this._list.onMouseDown(e => {1017if (e.element) {1018this._onMouseDown.fire({ event: e.browserEvent, target: e.element });1019}1020}));10211022this._register(this._list.onMouseUp(e => {1023if (e.element) {1024this._onMouseUp.fire({ event: e.browserEvent, target: e.element });1025}1026}));10271028this._register(this._list.onDidChangeFocus(_e => {1029this._onDidChangeActiveEditor.fire(this);1030this._onDidChangeActiveCell.fire();1031this._onDidChangeFocus.fire();1032this._cursorNavMode.set(false);1033}));10341035this._register(this._list.onContextMenu(e => {1036this.showListContextMenu(e);1037}));10381039this._register(this._list.onDidChangeVisibleRanges(() => {1040this._onDidChangeVisibleRanges.fire();1041}));10421043this._register(this._list.onDidScroll((e) => {1044if (e.scrollTop !== e.oldScrollTop) {1045this._onDidScroll.fire();1046this.clearActiveCellWidgets();1047}10481049if (e.scrollTop === e.oldScrollTop && e.scrollHeightChanged) {1050this._onDidChangeLayout.fire();1051}1052}));10531054this._focusTracker = this._register(DOM.trackFocus(this.getDomNode()));1055this._register(this._focusTracker.onDidBlur(() => {1056this._editorFocus.set(false);1057this.viewModel?.setEditorFocus(false);1058this._onDidBlurEmitter.fire();1059}));1060this._register(this._focusTracker.onDidFocus(() => {1061this._editorFocus.set(true);1062this.viewModel?.setEditorFocus(true);1063this._onDidFocusEmitter.fire();1064}));10651066this._registerNotebookActionsToolbar();1067this._registerNotebookStickyScroll();10681069this._register(this.configurationService.onDidChangeConfiguration(e => {1070if (e.affectsConfiguration(accessibilityProvider.verbositySettingId)) {1071this._list.ariaLabel = accessibilityProvider?.getWidgetAriaLabel();1072}1073}));1074}10751076private showListContextMenu(e: IListContextMenuEvent<CellViewModel>) {1077this.contextMenuService.showContextMenu({1078menuId: MenuId.NotebookCellTitle,1079menuActionOptions: {1080shouldForwardArgs: true1081},1082contextKeyService: this.scopedContextKeyService,1083getAnchor: () => e.anchor,1084getActionsContext: () => {1085return {1086from: 'cellContainer'1087};1088}1089});1090}10911092private _registerNotebookOverviewRuler() {1093this._notebookOverviewRuler = this._register(this.instantiationService.createInstance(NotebookOverviewRuler, this, this._notebookOverviewRulerContainer));1094}10951096private _registerNotebookActionsToolbar() {1097this._notebookTopToolbar = this._register(this.instantiationService.createInstance(NotebookEditorWorkbenchToolbar, this, this.scopedContextKeyService, this._notebookOptions, this._notebookTopToolbarContainer));1098this._register(this._notebookTopToolbar.onDidChangeVisibility(() => {1099if (this._dimension && this._isVisible) {1100this.layout(this._dimension);1101}1102}));1103}11041105private _registerNotebookStickyScroll() {1106this._notebookStickyScroll = this._register(this.instantiationService.createInstance(NotebookStickyScroll, this._notebookStickyScrollContainer, this, this._list, (sizeDelta) => {1107if (this.isDisposed) {1108return;1109}11101111if (this._dimension && this._isVisible) {1112if (sizeDelta > 0) { // delta > 0 ==> sticky is growing, cell list shrinking1113this.layout(this._dimension);1114this.setScrollTop(this.scrollTop + sizeDelta);1115} else if (sizeDelta < 0) { // delta < 0 ==> sticky is shrinking, cell list growing1116this.setScrollTop(this.scrollTop + sizeDelta);1117this.layout(this._dimension);1118}1119}11201121this._onDidScroll.fire();1122}));1123}11241125private _updateOutputRenderers() {1126if (!this.viewModel || !this._webview) {1127return;1128}11291130this._webview.updateOutputRenderers();1131this.viewModel.viewCells.forEach(cell => {1132cell.outputsViewModels.forEach(output => {1133if (output.pickedMimeType?.rendererId === RENDERER_NOT_AVAILABLE) {1134output.resetRenderer();1135}1136});1137});1138}11391140getDomNode() {1141return this._overlayContainer;1142}11431144getOverflowContainerDomNode() {1145return this._overflowContainer;1146}11471148getInnerWebview(): IWebviewElement | undefined {1149return this._webview?.webview;1150}11511152setEditorProgressService(editorProgressService: IEditorProgressService): void {1153this.editorProgressService = editorProgressService;1154}11551156setParentContextKeyService(parentContextKeyService: IContextKeyService): void {1157this.scopedContextKeyService.updateParent(parentContextKeyService);1158}11591160async setModel(textModel: NotebookTextModel, viewState: INotebookEditorViewState | undefined, perf?: NotebookPerfMarks, viewType?: string): Promise<void> {1161if (this.viewModel === undefined || !this.viewModel.equal(textModel)) {1162const oldBottomToolbarDimensions = this._notebookOptions.computeBottomToolbarDimensions(this.viewModel?.viewType);1163this._detachModel();1164await this._attachModel(textModel, viewType ?? textModel.viewType, viewState, perf);1165const newBottomToolbarDimensions = this._notebookOptions.computeBottomToolbarDimensions(this.viewModel?.viewType);11661167if (oldBottomToolbarDimensions.bottomToolbarGap !== newBottomToolbarDimensions.bottomToolbarGap1168|| oldBottomToolbarDimensions.bottomToolbarHeight !== newBottomToolbarDimensions.bottomToolbarHeight) {1169this._styleElement?.remove();1170this._createLayoutStyles();1171this._webview?.updateOptions({1172...this.notebookOptions.computeWebviewOptions(),1173fontFamily: this._generateFontFamily()1174});1175}1176type WorkbenchNotebookOpenClassification = {1177owner: 'rebornix';1178comment: 'Identify the notebook editor view type';1179scheme: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'File system provider scheme for the resource' };1180ext: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'File extension for the resource' };1181viewType: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'View type of the notebook editor' };1182isRepl: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the notebook editor is within a REPL editor' };1183};11841185type WorkbenchNotebookOpenEvent = {1186scheme: string;1187ext: string;1188viewType: string;1189isRepl: boolean;1190};11911192this.telemetryService.publicLog2<WorkbenchNotebookOpenEvent, WorkbenchNotebookOpenClassification>('notebook/editorOpened', {1193scheme: textModel.uri.scheme,1194ext: extname(textModel.uri),1195viewType: textModel.viewType,1196isRepl: this.isReplHistory1197});1198} else {1199this.restoreListViewState(viewState);1200}12011202this._restoreSelectedKernel(viewState);12031204// load preloads for matching kernel1205this._loadKernelPreloads();12061207// clear state1208this._dndController?.clearGlobalDragState();12091210this._localStore.add(this._list.onDidChangeFocus(() => {1211this.updateContextKeysOnFocusChange();1212}));12131214this.updateContextKeysOnFocusChange();1215// render markdown top down on idle1216this._backgroundMarkdownRendering();1217}12181219private _backgroundMarkdownRenderRunning = false;1220private _backgroundMarkdownRendering() {1221if (this._backgroundMarkdownRenderRunning) {1222return;1223}12241225this._backgroundMarkdownRenderRunning = true;1226DOM.runWhenWindowIdle(DOM.getWindow(this.getDomNode()), (deadline) => {1227this._backgroundMarkdownRenderingWithDeadline(deadline);1228});1229}12301231private _backgroundMarkdownRenderingWithDeadline(deadline: IdleDeadline) {1232const endTime = Date.now() + deadline.timeRemaining();12331234const execute = () => {1235try {1236this._backgroundMarkdownRenderRunning = true;1237if (this._isDisposed) {1238return;1239}12401241if (!this.viewModel) {1242return;1243}12441245const firstMarkupCell = this.viewModel.viewCells.find(cell => cell.cellKind === CellKind.Markup && !this._webview?.markupPreviewMapping.has(cell.id) && !this.cellIsHidden(cell)) as MarkupCellViewModel | undefined;1246if (!firstMarkupCell) {1247return;1248}12491250this.createMarkupPreview(firstMarkupCell);1251} finally {1252this._backgroundMarkdownRenderRunning = false;1253}12541255if (Date.now() < endTime) {1256setTimeout0(execute);1257} else {1258this._backgroundMarkdownRendering();1259}1260};12611262execute();1263}12641265private updateContextKeysOnFocusChange() {1266if (!this.viewModel) {1267return;1268}12691270const focused = this._list.getFocusedElements()[0];1271if (focused) {1272if (!this._cellContextKeyManager) {1273this._cellContextKeyManager = this._localStore.add(this.instantiationService.createInstance(CellContextKeyManager, this, focused as CellViewModel));1274}12751276this._cellContextKeyManager.updateForElement(focused as CellViewModel);1277}1278}12791280async setOptions(options: INotebookEditorOptions | undefined) {1281if (options?.isReadOnly !== undefined) {1282this._readOnly = options?.isReadOnly;1283}12841285if (!this.viewModel) {1286return;1287}12881289this.viewModel.updateOptions({ isReadOnly: this._readOnly });1290this.notebookOptions.updateOptions(this._readOnly);12911292// reveal cell if editor options tell to do so1293const cellOptions = options?.cellOptions ?? this._parseIndexedCellOptions(options);1294if (cellOptions) {1295const cell = this.viewModel.viewCells.find(cell => cell.uri.toString() === cellOptions.resource.toString());1296if (cell) {1297this.focusElement(cell);1298const selection = cellOptions.options?.selection;1299if (selection) {1300cell.updateEditState(CellEditState.Editing, 'setOptions');1301cell.focusMode = CellFocusMode.Editor;1302await this.revealRangeInCenterIfOutsideViewportAsync(cell, new Range(selection.startLineNumber, selection.startColumn, selection.endLineNumber || selection.startLineNumber, selection.endColumn || selection.startColumn));1303} else {1304this._list.revealCell(cell, options?.cellRevealType ?? CellRevealType.CenterIfOutsideViewport);1305}13061307const editor = this._renderedEditors.get(cell)!;1308if (editor) {1309if (cellOptions.options?.selection) {1310const { selection } = cellOptions.options;1311const editorSelection = new Range(selection.startLineNumber, selection.startColumn, selection.endLineNumber || selection.startLineNumber, selection.endColumn || selection.startColumn);1312editor.setSelection(editorSelection);1313editor.revealPositionInCenterIfOutsideViewport({1314lineNumber: selection.startLineNumber,1315column: selection.startColumn1316});1317await this.revealRangeInCenterIfOutsideViewportAsync(cell, editorSelection);1318}1319if (!cellOptions.options?.preserveFocus) {1320editor.focus();1321}1322}1323}1324}13251326// select cells if options tell to do so1327// todo@rebornix https://github.com/microsoft/vscode/issues/118108 support selections not just focus1328// todo@rebornix support multipe selections1329if (options?.cellSelections) {1330const focusCellIndex = options.cellSelections[0].start;1331const focusedCell = this.viewModel.cellAt(focusCellIndex);1332if (focusedCell) {1333this.viewModel.updateSelectionsState({1334kind: SelectionStateType.Index,1335focus: { start: focusCellIndex, end: focusCellIndex + 1 },1336selections: options.cellSelections1337});1338this.revealInCenterIfOutsideViewport(focusedCell);1339}1340}13411342this._updateForOptions();1343this._onDidChangeOptions.fire();1344}13451346private _parseIndexedCellOptions(options: INotebookEditorOptions | undefined) {1347if (options?.indexedCellOptions) {1348// convert index based selections1349const cell = this.cellAt(options.indexedCellOptions.index);1350if (cell) {1351return {1352resource: cell.uri,1353options: {1354selection: options.indexedCellOptions.selection,1355preserveFocus: false1356}1357};1358}1359}13601361return undefined;1362}13631364private _detachModel() {1365this._localStore.clear();1366dispose(this._localCellStateListeners);1367this._list.detachViewModel();1368this.viewModel?.dispose();1369// avoid event1370this.viewModel = undefined;1371this._webview?.dispose();1372this._webview?.element.remove();1373this._webview = null;1374this._list.clear();1375}137613771378private _updateForOptions(): void {1379if (!this.viewModel) {1380return;1381}13821383this._editorEditable.set(!this.viewModel.options.isReadOnly);1384this._overflowContainer.classList.toggle('notebook-editor-editable', !this.viewModel.options.isReadOnly);1385this.getDomNode().classList.toggle('notebook-editor-editable', !this.viewModel.options.isReadOnly);1386}13871388private async _resolveWebview(): Promise<BackLayerWebView<ICommonCellInfo> | null> {1389if (!this.textModel) {1390return null;1391}13921393if (this._webviewResolvePromise) {1394return this._webviewResolvePromise;1395}13961397if (!this._webview) {1398this._ensureWebview(this.getId(), this.textModel.viewType, this.textModel.uri);1399}14001401this._webviewResolvePromise = (async () => {1402if (!this._webview) {1403throw new Error('Notebook output webview object is not created successfully.');1404}14051406await this._webview.createWebview(this.creationOptions.codeWindow ?? mainWindow);1407if (!this._webview.webview) {1408throw new Error('Notebook output webview element was not created successfully.');1409}14101411this._localStore.add(this._webview.webview.onDidBlur(() => {1412this._outputFocus.set(false);1413this._webviewFocused = false;14141415this.updateEditorFocus();1416this.updateCellFocusMode();1417}));14181419this._localStore.add(this._webview.webview.onDidFocus(() => {1420this._outputFocus.set(true);1421this.updateEditorFocus();1422this._webviewFocused = true;1423}));14241425this._localStore.add(this._webview.onMessage(e => {1426this._onDidReceiveMessage.fire(e);1427}));14281429return this._webview;1430})();14311432return this._webviewResolvePromise;1433}14341435private _ensureWebview(id: string, viewType: string, resource: URI) {1436if (this._webview) {1437return;1438}14391440const that = this;14411442this._webview = this.instantiationService.createInstance(BackLayerWebView, {1443get creationOptions() { return that.creationOptions; },1444setScrollTop(scrollTop: number) { that._list.scrollTop = scrollTop; },1445triggerScroll(event: IMouseWheelEvent) { that._list.triggerScrollFromMouseWheelEvent(event); },1446getCellByInfo: that.getCellByInfo.bind(that),1447getCellById: that._getCellById.bind(that),1448toggleNotebookCellSelection: that._toggleNotebookCellSelection.bind(that),1449focusNotebookCell: that.focusNotebookCell.bind(that),1450focusNextNotebookCell: that.focusNextNotebookCell.bind(that),1451updateOutputHeight: that._updateOutputHeight.bind(that),1452scheduleOutputHeightAck: that._scheduleOutputHeightAck.bind(that),1453updateMarkupCellHeight: that._updateMarkupCellHeight.bind(that),1454setMarkupCellEditState: that._setMarkupCellEditState.bind(that),1455didStartDragMarkupCell: that._didStartDragMarkupCell.bind(that),1456didDragMarkupCell: that._didDragMarkupCell.bind(that),1457didDropMarkupCell: that._didDropMarkupCell.bind(that),1458didEndDragMarkupCell: that._didEndDragMarkupCell.bind(that),1459didResizeOutput: that._didResizeOutput.bind(that),1460updatePerformanceMetadata: that._updatePerformanceMetadata.bind(that),1461didFocusOutputInputChange: that._didFocusOutputInputChange.bind(that),1462}, id, viewType, resource, {1463...this._notebookOptions.computeWebviewOptions(),1464fontFamily: this._generateFontFamily()1465}, this.notebookRendererMessaging.getScoped(this._uuid));14661467this._webview.element.style.width = '100%';14681469// attach the webview container to the DOM tree first1470this._list.attachWebview(this._webview.element);1471}14721473private async _attachModel(textModel: NotebookTextModel, viewType: string, viewState: INotebookEditorViewState | undefined, perf?: NotebookPerfMarks) {1474this._ensureWebview(this.getId(), textModel.viewType, textModel.uri);14751476this.viewModel = this.instantiationService.createInstance(NotebookViewModel, viewType, textModel, this._viewContext, this.getLayoutInfo(), { isReadOnly: this._readOnly });1477this._viewContext.eventDispatcher.emit([new NotebookLayoutChangedEvent({ width: true, fontInfo: true }, this.getLayoutInfo())]);1478this.notebookOptions.updateOptions(this._readOnly);14791480this._updateForOptions();1481this._updateForNotebookConfiguration();14821483// restore view states, including contributions14841485{1486// restore view state1487this.viewModel.restoreEditorViewState(viewState);14881489// contribution state restore14901491const contributionsState = viewState?.contributionsState || {};1492for (const [id, contribution] of this._contributions) {1493if (typeof contribution.restoreViewState === 'function') {1494contribution.restoreViewState(contributionsState[id]);1495}1496}1497}14981499this._localStore.add(this.viewModel.onDidChangeViewCells(e => {1500this._onDidChangeViewCells.fire(e);1501}));15021503this._localStore.add(this.viewModel.onDidChangeSelection(() => {1504this._onDidChangeSelection.fire();1505this.updateSelectedMarkdownPreviews();1506}));15071508this._localStore.add(this._list.onWillScroll(e => {1509if (this._webview?.isResolved()) {1510this._webviewTransparentCover!.style.transform = `translateY(${e.scrollTop})`;1511}1512}));15131514let hasPendingChangeContentHeight = false;1515this._localStore.add(this._list.onDidChangeContentHeight(() => {1516if (hasPendingChangeContentHeight) {1517return;1518}1519hasPendingChangeContentHeight = true;15201521this._localStore.add(DOM.scheduleAtNextAnimationFrame(DOM.getWindow(this.getDomNode()), () => {1522hasPendingChangeContentHeight = false;1523this._updateScrollHeight();1524}, 100));1525}));15261527this._localStore.add(this._list.onDidRemoveOutputs(outputs => {1528outputs.forEach(output => this.removeInset(output));1529}));1530this._localStore.add(this._list.onDidHideOutputs(outputs => {1531outputs.forEach(output => this.hideInset(output));1532}));1533this._localStore.add(this._list.onDidRemoveCellsFromView(cells => {1534const hiddenCells: MarkupCellViewModel[] = [];1535const deletedCells: MarkupCellViewModel[] = [];15361537for (const cell of cells) {1538if (cell.cellKind === CellKind.Markup) {1539const mdCell = cell as MarkupCellViewModel;1540if (this.viewModel?.viewCells.find(cell => cell.handle === mdCell.handle)) {1541// Cell has been folded but is still in model1542hiddenCells.push(mdCell);1543} else {1544// Cell was deleted1545deletedCells.push(mdCell);1546}1547}1548}15491550this.hideMarkupPreviews(hiddenCells);1551this.deleteMarkupPreviews(deletedCells);1552}));15531554// init rendering1555await this._warmupWithMarkdownRenderer(this.viewModel, viewState, perf);15561557perf?.mark('customMarkdownLoaded');15581559// model attached1560this._localCellStateListeners = this.viewModel.viewCells.map(cell => this._bindCellListener(cell));1561this._lastCellWithEditorFocus = this.viewModel.viewCells.find(viewCell => this.getActiveCell() === viewCell && viewCell.focusMode === CellFocusMode.Editor) ?? null;15621563this._localStore.add(this.viewModel.onDidChangeViewCells((e) => {1564if (this._isDisposed) {1565return;1566}15671568// update cell listener1569[...e.splices].reverse().forEach(splice => {1570const [start, deleted, newCells] = splice;1571const deletedCells = this._localCellStateListeners.splice(start, deleted, ...newCells.map(cell => this._bindCellListener(cell)));15721573dispose(deletedCells);1574});15751576if (e.splices.some(s => s[2].some(cell => cell.cellKind === CellKind.Markup))) {1577this._backgroundMarkdownRendering();1578}1579}));15801581if (this._dimension) {1582this._list.layout(this.getBodyHeight(this._dimension.height), this._dimension.width);1583} else {1584this._list.layout();1585}15861587this._dndController?.clearGlobalDragState();15881589// restore list state at last, it must be after list layout1590this.restoreListViewState(viewState);1591}15921593private _bindCellListener(cell: ICellViewModel) {1594const store = new DisposableStore();15951596store.add(cell.onDidChangeLayout(e => {1597// e.totalHeight will be false it's not changed1598if (e.totalHeight || e.outerWidth) {1599this.layoutNotebookCell(cell, cell.layoutInfo.totalHeight, e.context);1600}1601}));16021603if (cell.cellKind === CellKind.Code) {1604store.add((cell as CodeCellViewModel).onDidRemoveOutputs((outputs) => {1605outputs.forEach(output => this.removeInset(output));1606}));1607}16081609store.add((cell as CellViewModel).onDidChangeState(e => {1610if (e.inputCollapsedChanged && cell.isInputCollapsed && cell.cellKind === CellKind.Markup) {1611this.hideMarkupPreviews([(cell as MarkupCellViewModel)]);1612}16131614if (e.outputCollapsedChanged && cell.isOutputCollapsed && cell.cellKind === CellKind.Code) {1615cell.outputsViewModels.forEach(output => this.hideInset(output));1616}16171618if (e.focusModeChanged) {1619this._validateCellFocusMode(cell);1620}1621}));16221623store.add(cell.onCellDecorationsChanged(e => {1624e.added.forEach(options => {1625if (options.className) {1626this.deltaCellContainerClassNames(cell.id, [options.className], [], cell.cellKind);1627}16281629if (options.outputClassName) {1630this.deltaCellContainerClassNames(cell.id, [options.outputClassName], [], cell.cellKind);1631}1632});16331634e.removed.forEach(options => {1635if (options.className) {1636this.deltaCellContainerClassNames(cell.id, [], [options.className], cell.cellKind);1637}16381639if (options.outputClassName) {1640this.deltaCellContainerClassNames(cell.id, [], [options.outputClassName], cell.cellKind);1641}1642});1643}));16441645return store;1646}164716481649private _lastCellWithEditorFocus: ICellViewModel | null = null;1650private _validateCellFocusMode(cell: ICellViewModel) {1651if (cell.focusMode !== CellFocusMode.Editor) {1652return;1653}16541655if (this._lastCellWithEditorFocus && this._lastCellWithEditorFocus !== cell) {1656this._lastCellWithEditorFocus.focusMode = CellFocusMode.Container;1657}16581659this._lastCellWithEditorFocus = cell;1660}16611662private async _warmupWithMarkdownRenderer(viewModel: NotebookViewModel, viewState: INotebookEditorViewState | undefined, perf?: NotebookPerfMarks) {16631664this.logService.debug('NotebookEditorWidget', 'warmup ' + this.viewModel?.uri.toString());1665await this._resolveWebview();1666perf?.mark('webviewCommLoaded');16671668this.logService.debug('NotebookEditorWidget', 'warmup - webview resolved');16691670// make sure that the webview is not visible otherwise users will see pre-rendered markdown cells in wrong position as the list view doesn't have a correct `top` offset yet1671this._webview!.element.style.visibility = 'hidden';1672// warm up can take around 200ms to load markdown libraries, etc.1673await this._warmupViewportMarkdownCells(viewModel, viewState);1674this.logService.debug('NotebookEditorWidget', 'warmup - viewport warmed up');16751676// todo@rebornix @mjbvz, is this too complicated?16771678/* now the webview is ready, and requests to render markdown are fast enough1679* we can start rendering the list view1680* render1681* - markdown cell -> request to webview to (10ms, basically just latency between UI and iframe)1682* - code cell -> render in place1683*/1684this._list.layout(0, 0);1685this._list.attachViewModel(viewModel);16861687// now the list widget has a correct contentHeight/scrollHeight1688// setting scrollTop will work properly1689// after setting scroll top, the list view will update `top` of the scrollable element, e.g. `top: -584px`1690this._list.scrollTop = viewState?.scrollPosition?.top ?? 0;1691this._debug('finish initial viewport warmup and view state restore.');1692this._webview!.element.style.visibility = 'visible';1693this.logService.debug('NotebookEditorWidget', 'warmup - list view model attached, set to visible');1694this._onDidAttachViewModel.fire();1695}16961697private async _warmupViewportMarkdownCells(viewModel: NotebookViewModel, viewState: INotebookEditorViewState | undefined) {1698if (viewState && viewState.cellTotalHeights) {1699const totalHeightCache = viewState.cellTotalHeights;1700const scrollTop = viewState.scrollPosition?.top ?? 0;1701const scrollBottom = scrollTop + Math.max(this._dimension?.height ?? 0, 1080);17021703let offset = 0;1704const requests: [ICellViewModel, number][] = [];17051706for (let i = 0; i < viewModel.length; i++) {1707const cell = viewModel.cellAt(i)!;1708const cellHeight = totalHeightCache[i] ?? 0;17091710if (offset + cellHeight < scrollTop) {1711offset += cellHeight;1712continue;1713}17141715if (cell.cellKind === CellKind.Markup) {1716requests.push([cell, offset]);1717}17181719offset += cellHeight;17201721if (offset > scrollBottom) {1722break;1723}1724}17251726await this._webview!.initializeMarkup(requests.map(([model, offset]) => this.createMarkupCellInitialization(model, offset)));1727} else {1728const initRequests = viewModel.viewCells1729.filter(cell => cell.cellKind === CellKind.Markup)1730.slice(0, 5)1731.map(cell => this.createMarkupCellInitialization(cell, -10000));17321733await this._webview!.initializeMarkup(initRequests);17341735// no cached view state so we are rendering the first viewport1736// after above async call, we already get init height for markdown cells, we can update their offset1737let offset = 0;1738const offsetUpdateRequests: { id: string; top: number }[] = [];1739const scrollBottom = Math.max(this._dimension?.height ?? 0, 1080);1740for (const cell of viewModel.viewCells) {1741if (cell.cellKind === CellKind.Markup) {1742offsetUpdateRequests.push({ id: cell.id, top: offset });1743}17441745offset += cell.getHeight(this.getLayoutInfo().fontInfo.lineHeight);17461747if (offset > scrollBottom) {1748break;1749}1750}17511752this._webview?.updateScrollTops([], offsetUpdateRequests);1753}1754}17551756private createMarkupCellInitialization(model: ICellViewModel, offset: number): IMarkupCellInitialization {1757return ({1758mime: model.mime,1759cellId: model.id,1760cellHandle: model.handle,1761content: model.getText(),1762offset: offset,1763visible: false,1764metadata: model.metadata,1765});1766}17671768restoreListViewState(viewState: INotebookEditorViewState | undefined): void {1769if (!this.viewModel) {1770return;1771}17721773if (viewState?.scrollPosition !== undefined) {1774this._list.scrollTop = viewState.scrollPosition.top;1775this._list.scrollLeft = viewState.scrollPosition.left;1776} else {1777this._list.scrollTop = 0;1778this._list.scrollLeft = 0;1779}17801781const focusIdx = typeof viewState?.focus === 'number' ? viewState.focus : 0;1782if (focusIdx < this.viewModel.length) {1783const element = this.viewModel.cellAt(focusIdx);1784if (element) {1785this.viewModel?.updateSelectionsState({1786kind: SelectionStateType.Handle,1787primary: element.handle,1788selections: [element.handle]1789});1790}1791} else if (this._list.length > 0) {1792this.viewModel.updateSelectionsState({1793kind: SelectionStateType.Index,1794focus: { start: 0, end: 1 },1795selections: [{ start: 0, end: 1 }]1796});1797}17981799if (viewState?.editorFocused) {1800const cell = this.viewModel.cellAt(focusIdx);1801if (cell) {1802cell.focusMode = CellFocusMode.Editor;1803}1804}1805}18061807private _restoreSelectedKernel(viewState: INotebookEditorViewState | undefined): void {1808if (viewState?.selectedKernelId && this.textModel) {1809const matching = this.notebookKernelService.getMatchingKernel(this.textModel);1810const kernel = matching.all.find(k => k.id === viewState.selectedKernelId);1811// Selected kernel may have already been picked prior to the view state loading1812// If so, don't overwrite it with the saved kernel.1813if (kernel && !matching.selected) {1814this.notebookKernelService.selectKernelForNotebook(kernel, this.textModel);1815}1816}1817}18181819getEditorViewState(): INotebookEditorViewState {1820const state = this.viewModel?.getEditorViewState();1821if (!state) {1822return {1823editingCells: {},1824cellLineNumberStates: {},1825editorViewStates: {},1826collapsedInputCells: {},1827collapsedOutputCells: {},1828};1829}18301831if (this._list) {1832state.scrollPosition = { left: this._list.scrollLeft, top: this._list.scrollTop };1833const cellHeights: { [key: number]: number } = {};1834for (let i = 0; i < this.viewModel!.length; i++) {1835const elm = this.viewModel!.cellAt(i) as CellViewModel;1836cellHeights[i] = elm.layoutInfo.totalHeight;1837}18381839state.cellTotalHeights = cellHeights;18401841if (this.viewModel) {1842const focusRange = this.viewModel.getFocus();1843const element = this.viewModel.cellAt(focusRange.start);1844if (element) {1845const itemDOM = this._list.domElementOfElement(element);1846const editorFocused = element.getEditState() === CellEditState.Editing && !!(itemDOM && itemDOM.ownerDocument.activeElement && itemDOM.contains(itemDOM.ownerDocument.activeElement));18471848state.editorFocused = editorFocused;1849state.focus = focusRange.start;1850}1851}1852}18531854// Save contribution view states1855const contributionsState: { [key: string]: unknown } = {};1856for (const [id, contribution] of this._contributions) {1857if (typeof contribution.saveViewState === 'function') {1858contributionsState[id] = contribution.saveViewState();1859}1860}1861state.contributionsState = contributionsState;1862if (this.textModel?.uri.scheme === Schemas.untitled) {1863state.selectedKernelId = this.activeKernel?.id;1864}18651866return state;1867}18681869private _allowScrollBeyondLastLine() {1870return this._scrollBeyondLastLine && !this.isReplHistory;1871}18721873private getBodyHeight(dimensionHeight: number) {1874return Math.max(dimensionHeight - (this._notebookTopToolbar?.useGlobalToolbar ? /** Toolbar height */ 26 : 0), 0);1875}18761877layout(dimension: DOM.Dimension, shadowElement?: HTMLElement, position?: DOM.IDomPosition): void {1878if (!shadowElement && this._shadowElementViewInfo === null) {1879this._dimension = dimension;1880this._position = position;1881return;1882}18831884if (dimension.width <= 0 || dimension.height <= 0) {1885this.onWillHide();1886return;1887}18881889const whenContainerStylesLoaded = this.layoutService.whenContainerStylesLoaded(DOM.getWindow(this.getDomNode()));1890if (whenContainerStylesLoaded) {1891// In floating windows, we need to ensure that the1892// container is ready for us to compute certain1893// layout related properties.1894whenContainerStylesLoaded.then(() => this.layoutNotebook(dimension, shadowElement, position));1895} else {1896this.layoutNotebook(dimension, shadowElement, position);1897}18981899}19001901private layoutNotebook(dimension: DOM.Dimension, shadowElement?: HTMLElement, position?: DOM.IDomPosition) {1902if (shadowElement) {1903this.updateShadowElement(shadowElement, dimension, position);1904}19051906if (this._shadowElementViewInfo && this._shadowElementViewInfo.width <= 0 && this._shadowElementViewInfo.height <= 0) {1907this.onWillHide();1908return;1909}19101911this._dimension = dimension;1912this._position = position;1913const newBodyHeight = this.getBodyHeight(dimension.height) - this.getLayoutInfo().stickyHeight;1914DOM.size(this._body, dimension.width, newBodyHeight);19151916const newCellListHeight = newBodyHeight;1917if (this._list.getRenderHeight() < newCellListHeight) {1918// the new dimension is larger than the list viewport, update its additional height first, otherwise the list view will move down a bit (as the `scrollBottom` will move down)1919this._list.updateOptions({ paddingBottom: this._allowScrollBeyondLastLine() ? Math.max(0, (newCellListHeight - 50)) : 0, paddingTop: 0 });1920this._list.layout(newCellListHeight, dimension.width);1921} else {1922// the new dimension is smaller than the list viewport, if we update the additional height, the `scrollBottom` will move up, which moves the whole list view upwards a bit. So we run a layout first.1923this._list.layout(newCellListHeight, dimension.width);1924this._list.updateOptions({ paddingBottom: this._allowScrollBeyondLastLine() ? Math.max(0, (newCellListHeight - 50)) : 0, paddingTop: 0 });1925}19261927this._overlayContainer.inert = false;1928this._overlayContainer.style.visibility = 'visible';1929this._overlayContainer.style.display = 'block';1930this._overlayContainer.style.position = 'absolute';1931this._overlayContainer.style.overflow = 'hidden';19321933this.layoutContainerOverShadowElement(dimension, position);19341935if (this._webviewTransparentCover) {1936this._webviewTransparentCover.style.height = `${dimension.height}px`;1937this._webviewTransparentCover.style.width = `${dimension.width}px`;1938}19391940this._notebookTopToolbar.layout(this._dimension);1941this._notebookOverviewRuler.layout();19421943this._viewContext?.eventDispatcher.emit([new NotebookLayoutChangedEvent({ width: true, fontInfo: true }, this.getLayoutInfo())]);1944}19451946private updateShadowElement(shadowElement: HTMLElement, dimension?: IDimension, position?: DOM.IDomPosition) {1947this._shadowElement = shadowElement;1948if (dimension && position) {1949this._shadowElementViewInfo = {1950height: dimension.height,1951width: dimension.width,1952top: position.top,1953left: position.left,1954};1955} else {1956// We have to recompute position and size ourselves (which is slow)1957const containerRect = shadowElement.getBoundingClientRect();1958this._shadowElementViewInfo = {1959height: containerRect.height,1960width: containerRect.width,1961top: containerRect.top,1962left: containerRect.left1963};1964}1965}19661967private layoutContainerOverShadowElement(dimension?: DOM.Dimension, position?: DOM.IDomPosition): void {1968if (dimension && position) {1969this._overlayContainer.style.top = `${position.top}px`;1970this._overlayContainer.style.left = `${position.left}px`;1971this._overlayContainer.style.width = `${dimension.width}px`;1972this._overlayContainer.style.height = `${dimension.height}px`;1973return;1974}19751976if (!this._shadowElementViewInfo) {1977return;1978}19791980const elementContainerRect = this._overlayContainer.parentElement?.getBoundingClientRect();1981this._overlayContainer.style.top = `${this._shadowElementViewInfo.top - (elementContainerRect?.top || 0)}px`;1982this._overlayContainer.style.left = `${this._shadowElementViewInfo.left - (elementContainerRect?.left || 0)}px`;1983this._overlayContainer.style.width = `${dimension ? dimension.width : this._shadowElementViewInfo.width}px`;1984this._overlayContainer.style.height = `${dimension ? dimension.height : this._shadowElementViewInfo.height}px`;1985}19861987//#endregion19881989//#region Focus tracker1990focus() {1991this._isVisible = true;1992this._editorFocus.set(true);19931994if (this._webviewFocused) {1995this._webview?.focusWebview();1996} else {1997if (this.viewModel) {1998const focusRange = this.viewModel.getFocus();1999const element = this.viewModel.cellAt(focusRange.start);20002001// The notebook editor doesn't have focus yet2002if (!this.hasEditorFocus()) {2003this.focusContainer();2004// trigger editor to update as FocusTracker might not emit focus change event2005this.updateEditorFocus();2006}20072008if (element && element.focusMode === CellFocusMode.Editor) {2009element.updateEditState(CellEditState.Editing, 'editorWidget.focus');2010element.focusMode = CellFocusMode.Editor;2011this.focusEditor(element);2012return;2013}2014}20152016this._list.domFocus();2017}20182019if (this._currentProgress) {2020// The editor forces progress to hide when switching editors. So if progress should be visible, force it to show when the editor is focused.2021this.showProgress();2022}2023}20242025onShow() {2026this._isVisible = true;2027}20282029private focusEditor(activeElement: CellViewModel): void {2030for (const [element, editor] of this._renderedEditors.entries()) {2031if (element === activeElement) {2032editor.focus();2033return;2034}2035}2036}20372038focusContainer(clearSelection: boolean = false) {2039if (this._webviewFocused) {2040this._webview?.focusWebview();2041} else {2042this._list.focusContainer(clearSelection);2043}2044}20452046selectOutputContent(cell: ICellViewModel) {2047this._webview?.selectOutputContents(cell);2048}20492050selectInputContents(cell: ICellViewModel) {2051this._webview?.selectInputContents(cell);2052}20532054onWillHide() {2055this._isVisible = false;2056this._editorFocus.set(false);2057this._overlayContainer.inert = true;2058this._overlayContainer.style.visibility = 'hidden';2059this._overlayContainer.style.left = '-50000px';2060this._notebookTopToolbarContainer.style.display = 'none';2061this.clearActiveCellWidgets();2062}20632064private clearActiveCellWidgets() {2065this._renderedEditors.forEach((editor, cell) => {2066if (this.getActiveCell() === cell && editor) {2067SuggestController.get(editor)?.cancelSuggestWidget();2068DropIntoEditorController.get(editor)?.clearWidgets();2069CopyPasteController.get(editor)?.clearWidgets();2070}2071});20722073this._renderedEditors.forEach((editor, cell) => {2074const controller = InlineCompletionsController.get(editor);2075if (controller?.model.get()?.inlineEditState.get()) {2076editor.render(true);2077}2078});2079}20802081private editorHasDomFocus(): boolean {2082return DOM.isAncestorOfActiveElement(this.getDomNode());2083}20842085updateEditorFocus() {2086// Note - focus going to the webview will fire 'blur', but the webview element will be2087// a descendent of the notebook editor root.2088this._focusTracker.refreshState();2089const focused = this.editorHasDomFocus();2090this._editorFocus.set(focused);2091this.viewModel?.setEditorFocus(focused);2092}20932094updateCellFocusMode() {2095const activeCell = this.getActiveCell();20962097if (activeCell?.focusMode === CellFocusMode.Output && !this._webviewFocused) {2098// output previously has focus, but now it's blurred.2099activeCell.focusMode = CellFocusMode.Container;2100}2101}21022103hasEditorFocus() {2104// _editorFocus is driven by the FocusTracker, which is only guaranteed to _eventually_ fire blur.2105// If we need to know whether we have focus at this instant, we need to check the DOM manually.2106this.updateEditorFocus();2107return this.editorHasDomFocus();2108}21092110hasWebviewFocus() {2111return this._webviewFocused;2112}21132114hasOutputTextSelection() {2115if (!this.hasEditorFocus()) {2116return false;2117}21182119const windowSelection = DOM.getWindow(this.getDomNode()).getSelection();2120if (windowSelection?.rangeCount !== 1) {2121return false;2122}21232124const activeSelection = windowSelection.getRangeAt(0);2125if (activeSelection.startContainer === activeSelection.endContainer && activeSelection.endOffset - activeSelection.startOffset === 0) {2126return false;2127}21282129let container: any = activeSelection.commonAncestorContainer;21302131if (!this._body.contains(container)) {2132return false;2133}21342135while (container2136&&2137container !== this._body) {2138if ((container as HTMLElement).classList && (container as HTMLElement).classList.contains('output')) {2139return true;2140}21412142container = container.parentNode;2143}21442145return false;2146}21472148_didFocusOutputInputChange(hasFocus: boolean) {2149this._outputInputFocus.set(hasFocus);2150}21512152//#endregion21532154//#region Editor Features21552156focusElement(cell: ICellViewModel) {2157this.viewModel?.updateSelectionsState({2158kind: SelectionStateType.Handle,2159primary: cell.handle,2160selections: [cell.handle]2161});2162}21632164get scrollTop() {2165return this._list.scrollTop;2166}21672168get scrollBottom() {2169return this._list.scrollTop + this._list.getRenderHeight();2170}21712172getAbsoluteTopOfElement(cell: ICellViewModel) {2173return this._list.getCellViewScrollTop(cell);2174}21752176getAbsoluteBottomOfElement(cell: ICellViewModel) {2177return this._list.getCellViewScrollBottom(cell);2178}21792180getHeightOfElement(cell: ICellViewModel) {2181return this._list.elementHeight(cell);2182}21832184scrollToBottom() {2185this._list.scrollToBottom();2186}21872188setScrollTop(scrollTop: number): void {2189this._list.scrollTop = scrollTop;2190}21912192revealCellRangeInView(range: ICellRange) {2193return this._list.revealCells(range);2194}21952196revealInView(cell: ICellViewModel) {2197return this._list.revealCell(cell, CellRevealType.Default);2198}21992200revealInViewAtTop(cell: ICellViewModel) {2201this._list.revealCell(cell, CellRevealType.Top);2202}22032204revealInCenter(cell: ICellViewModel) {2205this._list.revealCell(cell, CellRevealType.Center);2206}22072208async revealInCenterIfOutsideViewport(cell: ICellViewModel) {2209await this._list.revealCell(cell, CellRevealType.CenterIfOutsideViewport);2210}22112212async revealFirstLineIfOutsideViewport(cell: ICellViewModel) {2213await this._list.revealCell(cell, CellRevealType.FirstLineIfOutsideViewport);2214}22152216async revealLineInViewAsync(cell: ICellViewModel, line: number): Promise<void> {2217return this._list.revealRangeInCell(cell, new Range(line, 1, line, 1), CellRevealRangeType.Default);2218}22192220async revealLineInCenterAsync(cell: ICellViewModel, line: number): Promise<void> {2221return this._list.revealRangeInCell(cell, new Range(line, 1, line, 1), CellRevealRangeType.Center);2222}22232224async revealLineInCenterIfOutsideViewportAsync(cell: ICellViewModel, line: number): Promise<void> {2225return this._list.revealRangeInCell(cell, new Range(line, 1, line, 1), CellRevealRangeType.CenterIfOutsideViewport);2226}22272228async revealRangeInViewAsync(cell: ICellViewModel, range: Selection | Range): Promise<void> {2229return this._list.revealRangeInCell(cell, range, CellRevealRangeType.Default);2230}22312232async revealRangeInCenterAsync(cell: ICellViewModel, range: Selection | Range): Promise<void> {2233return this._list.revealRangeInCell(cell, range, CellRevealRangeType.Center);2234}22352236async revealRangeInCenterIfOutsideViewportAsync(cell: ICellViewModel, range: Selection | Range): Promise<void> {2237return this._list.revealRangeInCell(cell, range, CellRevealRangeType.CenterIfOutsideViewport);2238}22392240revealCellOffsetInCenter(cell: ICellViewModel, offset: number) {2241return this._list.revealCellOffsetInCenter(cell, offset);2242}22432244revealOffsetInCenterIfOutsideViewport(offset: number) {2245return this._list.revealOffsetInCenterIfOutsideViewport(offset);2246}22472248getViewIndexByModelIndex(index: number): number {2249if (!this._listViewInfoAccessor) {2250return -1;2251}2252const cell = this.viewModel?.viewCells[index];2253if (!cell) {2254return -1;2255}22562257return this._listViewInfoAccessor.getViewIndex(cell);2258}22592260getViewHeight(cell: ICellViewModel): number {2261if (!this._listViewInfoAccessor) {2262return -1;2263}22642265return this._listViewInfoAccessor.getViewHeight(cell);2266}22672268getCellRangeFromViewRange(startIndex: number, endIndex: number): ICellRange | undefined {2269return this._listViewInfoAccessor.getCellRangeFromViewRange(startIndex, endIndex);2270}22712272getCellsInRange(range?: ICellRange): ReadonlyArray<ICellViewModel> {2273return this._listViewInfoAccessor.getCellsInRange(range);2274}22752276setCellEditorSelection(cell: ICellViewModel, range: Range): void {2277this._list.setCellEditorSelection(cell, range);2278}22792280setHiddenAreas(_ranges: ICellRange[]): boolean {2281return this._list.setHiddenAreas(_ranges, true);2282}22832284getVisibleRangesPlusViewportAboveAndBelow(): ICellRange[] {2285return this._listViewInfoAccessor.getVisibleRangesPlusViewportAboveAndBelow();2286}22872288//#endregion22892290//#region Decorations22912292deltaCellDecorations(oldDecorations: string[], newDecorations: INotebookDeltaDecoration[]): string[] {2293const ret = this.viewModel?.deltaCellDecorations(oldDecorations, newDecorations) || [];2294this._onDidChangeDecorations.fire();2295return ret;2296}22972298deltaCellContainerClassNames(cellId: string, added: string[], removed: string[], cellkind: CellKind): void {2299if (cellkind === CellKind.Markup) {2300this._webview?.deltaMarkupPreviewClassNames(cellId, added, removed);2301} else {2302this._webview?.deltaCellOutputContainerClassNames(cellId, added, removed);2303}2304}23052306changeModelDecorations<T>(callback: (changeAccessor: IModelDecorationsChangeAccessor) => T): T | null {2307return this.viewModel?.changeModelDecorations<T>(callback) || null;2308}23092310//#endregion23112312//#region View Zones2313changeViewZones(callback: (accessor: INotebookViewZoneChangeAccessor) => void): void {2314this._list.changeViewZones(callback);2315this._onDidChangeLayout.fire();2316}23172318getViewZoneLayoutInfo(id: string): { top: number; height: number } | null {2319return this._list.getViewZoneLayoutInfo(id);2320}2321//#endregion23222323//#region Overlay2324changeCellOverlays(callback: (accessor: INotebookCellOverlayChangeAccessor) => void): void {2325this._list.changeCellOverlays(callback);2326}2327//#endregion23282329//#region Kernel/Execution23302331private async _loadKernelPreloads(): Promise<void> {2332if (!this.hasModel()) {2333return;2334}2335const { selected } = this.notebookKernelService.getMatchingKernel(this.textModel);2336if (!this._webview?.isResolved()) {2337await this._resolveWebview();2338}2339this._webview?.updateKernelPreloads(selected);2340}23412342get activeKernel() {2343return this.textModel && this.notebookKernelService.getSelectedOrSuggestedKernel(this.textModel);2344}23452346async cancelNotebookCells(cells?: Iterable<ICellViewModel>): Promise<void> {2347if (!this.viewModel || !this.hasModel()) {2348return;2349}2350if (!cells) {2351cells = this.viewModel.viewCells;2352}2353return this.notebookExecutionService.cancelNotebookCellHandles(this.textModel, Array.from(cells).map(cell => cell.handle));2354}23552356async executeNotebookCells(cells?: Iterable<ICellViewModel>): Promise<void> {2357if (!this.viewModel || !this.hasModel()) {2358this.logService.info('notebookEditorWidget', 'No NotebookViewModel, cannot execute cells');2359return;2360}2361if (!cells) {2362cells = this.viewModel.viewCells;2363}2364return this.notebookExecutionService.executeNotebookCells(this.textModel, Array.from(cells).map(c => c.model), this.scopedContextKeyService);2365}23662367//#endregion23682369async layoutNotebookCell(cell: ICellViewModel, height: number, context?: CellLayoutContext): Promise<void> {2370return this._cellLayoutManager?.layoutNotebookCell(cell, height);2371}23722373getActiveCell() {2374const elements = this._list.getFocusedElements();23752376if (elements && elements.length) {2377return elements[0];2378}23792380return undefined;2381}23822383private _toggleNotebookCellSelection(selectedCell: ICellViewModel, selectFromPrevious: boolean): void {2384const currentSelections = this._list.getSelectedElements();2385const isSelected = currentSelections.includes(selectedCell);23862387const previousSelection = selectFromPrevious ? currentSelections[currentSelections.length - 1] ?? selectedCell : selectedCell;2388const selectedIndex = this._list.getViewIndex(selectedCell)!;2389const previousIndex = this._list.getViewIndex(previousSelection)!;23902391const cellsInSelectionRange = this.getCellsInViewRange(selectedIndex, previousIndex);2392if (isSelected) {2393// Deselect2394this._list.selectElements(currentSelections.filter(current => !cellsInSelectionRange.includes(current)));2395} else {2396// Add to selection2397this.focusElement(selectedCell);2398this._list.selectElements([...currentSelections.filter(current => !cellsInSelectionRange.includes(current)), ...cellsInSelectionRange]);2399}2400}24012402private getCellsInViewRange(fromInclusive: number, toInclusive: number): ICellViewModel[] {2403const selectedCellsInRange: ICellViewModel[] = [];2404for (let index = 0; index < this._list.length; ++index) {2405const cell = this._list.element(index);2406if (cell) {2407if ((index >= fromInclusive && index <= toInclusive) || (index >= toInclusive && index <= fromInclusive)) {2408selectedCellsInRange.push(cell);2409}2410}2411}2412return selectedCellsInRange;2413}24142415async focusNotebookCell(cell: ICellViewModel, focusItem: 'editor' | 'container' | 'output', options?: IFocusNotebookCellOptions) {2416if (this._isDisposed) {2417return;2418}24192420cell.focusedOutputId = undefined;24212422if (focusItem === 'editor') {2423cell.isInputCollapsed = false;2424this.focusElement(cell);2425this._list.focusView();24262427cell.updateEditState(CellEditState.Editing, 'focusNotebookCell');2428cell.focusMode = CellFocusMode.Editor;2429if (!options?.skipReveal) {2430if (typeof options?.focusEditorLine === 'number') {2431this._cursorNavMode.set(true);2432await this.revealLineInViewAsync(cell, options.focusEditorLine);2433const editor = this._renderedEditors.get(cell)!;2434const focusEditorLine = options.focusEditorLine;2435editor?.setSelection({2436startLineNumber: focusEditorLine,2437startColumn: 1,2438endLineNumber: focusEditorLine,2439endColumn: 12440});2441} else {2442const selectionsStartPosition = cell.getSelectionsStartPosition();2443if (selectionsStartPosition?.length) {2444const firstSelectionPosition = selectionsStartPosition[0];2445await this.revealRangeInViewAsync(cell, Range.fromPositions(firstSelectionPosition, firstSelectionPosition));2446} else {2447await this.revealInView(cell);2448}24492450}24512452}2453} else if (focusItem === 'output') {2454this.focusElement(cell);24552456if (!this.hasEditorFocus()) {2457this._list.focusView();2458}24592460if (!this._webview) {2461return;2462}24632464const firstOutputId = cell.outputsViewModels.find(o => o.model.alternativeOutputId)?.model.alternativeOutputId;2465const focusElementId = options?.outputId ?? firstOutputId ?? cell.id;2466this._webview.focusOutput(focusElementId, options?.altOutputId, options?.outputWebviewFocused || this._webviewFocused);24672468cell.updateEditState(CellEditState.Preview, 'focusNotebookCell');2469cell.focusMode = CellFocusMode.Output;2470cell.focusedOutputId = options?.outputId;2471this._outputFocus.set(true);2472if (!options?.skipReveal) {2473this.revealInCenterIfOutsideViewport(cell);2474}2475} else {2476// focus container2477const itemDOM = this._list.domElementOfElement(cell);2478if (itemDOM && itemDOM.ownerDocument.activeElement && itemDOM.contains(itemDOM.ownerDocument.activeElement)) {2479(itemDOM.ownerDocument.activeElement as HTMLElement).blur();2480}24812482this._webview?.blurOutput();24832484cell.updateEditState(CellEditState.Preview, 'focusNotebookCell');2485cell.focusMode = CellFocusMode.Container;24862487this.focusElement(cell);2488if (!options?.skipReveal) {2489if (typeof options?.focusEditorLine === 'number') {2490this._cursorNavMode.set(true);2491await this.revealInView(cell);2492} else if (options?.revealBehavior === ScrollToRevealBehavior.firstLine) {2493await this.revealFirstLineIfOutsideViewport(cell);2494} else if (options?.revealBehavior === ScrollToRevealBehavior.fullCell) {2495await this.revealInView(cell);2496} else {2497await this.revealInCenterIfOutsideViewport(cell);2498}2499}2500this._list.focusView();2501this.updateEditorFocus();2502}2503}25042505async focusNextNotebookCell(cell: ICellViewModel, focusItem: 'editor' | 'container' | 'output') {2506const idx = this.viewModel?.getCellIndex(cell);2507if (typeof idx !== 'number') {2508return;2509}25102511const newCell = this.viewModel?.cellAt(idx + 1);2512if (!newCell) {2513return;2514}25152516await this.focusNotebookCell(newCell, focusItem);2517}25182519//#endregion25202521//#region Find25222523private async _warmupCell(viewCell: CodeCellViewModel) {2524if (viewCell.isOutputCollapsed) {2525return;2526}25272528const outputs = viewCell.outputsViewModels;2529for (const output of outputs.slice(0, outputDisplayLimit)) {2530const [mimeTypes, pick] = output.resolveMimeTypes(this.textModel!, undefined);2531if (!mimeTypes.find(mimeType => mimeType.isTrusted) || mimeTypes.length === 0) {2532continue;2533}25342535const pickedMimeTypeRenderer = mimeTypes[pick];25362537if (!pickedMimeTypeRenderer) {2538return;2539}25402541const renderer = this._notebookService.getRendererInfo(pickedMimeTypeRenderer.rendererId);25422543if (!renderer) {2544return;2545}25462547const result: IInsetRenderOutput = { type: RenderOutputType.Extension, renderer, source: output, mimeType: pickedMimeTypeRenderer.mimeType };2548const inset = this._webview?.insetMapping.get(result.source);2549if (!inset || !inset.initialized) {2550const p = new Promise<void>(resolve => {2551this._register(Event.any(this.onDidRenderOutput, this.onDidRemoveOutput)(e => {2552if (e.model === result.source.model) {2553resolve();2554}2555}));2556});2557this.createOutput(viewCell, result, 0, false);2558await p;2559} else {2560// request to update its visibility2561this.createOutput(viewCell, result, 0, false);2562}25632564return;2565}25662567}25682569private async _warmupAll(includeOutput: boolean) {2570if (!this.hasModel() || !this.viewModel) {2571return;2572}25732574const cells = this.viewModel.viewCells;2575const requests = [];25762577for (let i = 0; i < cells.length; i++) {2578if (cells[i].cellKind === CellKind.Markup && !this._webview!.markupPreviewMapping.has(cells[i].id)) {2579requests.push(this.createMarkupPreview(cells[i]));2580}2581}25822583if (includeOutput && this._list) {2584for (let i = 0; i < this._list.length; i++) {2585const cell = this._list.element(i);25862587if (cell?.cellKind === CellKind.Code) {2588requests.push(this._warmupCell((cell as CodeCellViewModel)));2589}2590}2591}25922593return Promise.all(requests);2594}25952596private async _warmupSelection(includeOutput: boolean, selectedCellRanges: ICellRange[]) {2597if (!this.hasModel() || !this.viewModel) {2598return;2599}26002601const cells = this.viewModel.viewCells;2602const requests = [];26032604for (const range of selectedCellRanges) {2605for (let i = range.start; i < range.end; i++) {2606if (cells[i].cellKind === CellKind.Markup && !this._webview!.markupPreviewMapping.has(cells[i].id)) {2607requests.push(this.createMarkupPreview(cells[i]));2608}2609}2610}26112612if (includeOutput && this._list) {2613for (const range of selectedCellRanges) {2614for (let i = range.start; i < range.end; i++) {2615const cell = this._list.element(i);26162617if (cell?.cellKind === CellKind.Code) {2618requests.push(this._warmupCell((cell as CodeCellViewModel)));2619}2620}2621}2622}26232624return Promise.all(requests);2625}26262627async find(query: string, options: INotebookFindOptions, token: CancellationToken, skipWarmup: boolean = false, shouldGetSearchPreviewInfo = false, ownerID?: string): Promise<CellFindMatchWithIndex[]> {2628if (!this._notebookViewModel) {2629return [];2630}26312632if (!ownerID) {2633ownerID = this.getId();2634}26352636const findMatches = this._notebookViewModel.find(query, options).filter(match => match.length > 0);26372638if ((!options.includeMarkupPreview && !options.includeOutput) || options.findScope?.findScopeType === NotebookFindScopeType.Text) {2639this._webview?.findStop(ownerID);2640return findMatches;2641}26422643// search in webview enabled26442645const matchMap: { [key: string]: CellFindMatchWithIndex } = {};2646findMatches.forEach(match => {2647matchMap[match.cell.id] = match;2648});26492650if (this._webview) {2651// request all or some outputs to be rendered2652// measure perf2653const start = Date.now();2654if (options.findScope && options.findScope.findScopeType === NotebookFindScopeType.Cells && options.findScope.selectedCellRanges) {2655await this._warmupSelection(!!options.includeOutput, options.findScope.selectedCellRanges);2656} else {2657await this._warmupAll(!!options.includeOutput);2658}2659const end = Date.now();2660this.logService.debug('Find', `Warmup time: ${end - start}ms`);26612662if (token.isCancellationRequested) {2663return [];2664}26652666let findIds: string[] = [];2667if (options.findScope && options.findScope.findScopeType === NotebookFindScopeType.Cells && options.findScope.selectedCellRanges) {2668const selectedIndexes = cellRangesToIndexes(options.findScope.selectedCellRanges);2669findIds = selectedIndexes.map<string>(index => this._notebookViewModel?.viewCells[index].id ?? '');2670}26712672const webviewMatches = await this._webview.find(query, { caseSensitive: options.caseSensitive, wholeWord: options.wholeWord, includeMarkup: !!options.includeMarkupPreview, includeOutput: !!options.includeOutput, shouldGetSearchPreviewInfo, ownerID, findIds: findIds });26732674if (token.isCancellationRequested) {2675return [];2676}26772678// attach webview matches to model find matches2679webviewMatches.forEach(match => {2680const cell = this._notebookViewModel!.viewCells.find(cell => cell.id === match.cellId);26812682if (!cell) {2683return;2684}26852686if (match.type === 'preview') {2687// markup preview2688if (cell.getEditState() === CellEditState.Preview && !options.includeMarkupPreview) {2689return;2690}26912692if (cell.getEditState() === CellEditState.Editing && options.includeMarkupInput) {2693return;2694}2695} else {2696if (!options.includeOutput) {2697// skip outputs if not included2698return;2699}2700}27012702const exisitingMatch = matchMap[match.cellId];27032704if (exisitingMatch) {2705exisitingMatch.webviewMatches.push(match);2706} else {27072708matchMap[match.cellId] = new CellFindMatchModel(2709this._notebookViewModel!.viewCells.find(cell => cell.id === match.cellId)!,2710this._notebookViewModel!.viewCells.findIndex(cell => cell.id === match.cellId)!,2711[],2712[match]2713);2714}2715});2716}27172718const ret: CellFindMatchWithIndex[] = [];2719this._notebookViewModel.viewCells.forEach((cell, index) => {2720if (matchMap[cell.id]) {2721ret.push(new CellFindMatchModel(cell, index, matchMap[cell.id].contentMatches, matchMap[cell.id].webviewMatches));2722}2723});27242725return ret;2726}27272728async findHighlightCurrent(matchIndex: number, ownerID?: string): Promise<number> {2729if (!this._webview) {2730return 0;2731}27322733return this._webview?.findHighlightCurrent(matchIndex, ownerID ?? this.getId());2734}27352736async findUnHighlightCurrent(matchIndex: number, ownerID?: string): Promise<void> {2737if (!this._webview) {2738return;2739}27402741return this._webview?.findUnHighlightCurrent(matchIndex, ownerID ?? this.getId());2742}27432744findStop(ownerID?: string) {2745this._webview?.findStop(ownerID ?? this.getId());2746}27472748//#endregion27492750//#region MISC27512752getLayoutInfo(): NotebookLayoutInfo {2753if (!this._list) {2754throw new Error('Editor is not initalized successfully');2755}27562757if (!this._fontInfo) {2758this._generateFontInfo();2759}27602761let listViewOffset = 0;2762if (this._dimension) {2763listViewOffset = (this._notebookTopToolbar?.useGlobalToolbar ? /** Toolbar height */ 26 : 0) + (this._notebookStickyScroll?.getCurrentStickyHeight() ?? 0);2764}27652766return {2767width: this._dimension?.width ?? 0,2768height: this._dimension?.height ?? 0,2769scrollHeight: this._list?.getScrollHeight() ?? 0,2770fontInfo: this._fontInfo!,2771stickyHeight: this._notebookStickyScroll?.getCurrentStickyHeight() ?? 0,2772listViewOffsetTop: listViewOffset2773};2774}27752776async createMarkupPreview(cell: MarkupCellViewModel) {2777if (!this._webview) {2778return;2779}27802781if (!this._webview.isResolved()) {2782await this._resolveWebview();2783}27842785if (!this._webview || !this._list.webviewElement) {2786return;2787}27882789if (!this.viewModel || !this._list.viewModel) {2790return;2791}27922793if (this.viewModel.getCellIndex(cell) === -1) {2794return;2795}27962797if (this.cellIsHidden(cell)) {2798return;2799}28002801const webviewTop = parseInt(this._list.webviewElement.domNode.style.top, 10);2802const top = !!webviewTop ? (0 - webviewTop) : 0;28032804const cellTop = this._list.getCellViewScrollTop(cell);2805await this._webview.showMarkupPreview({2806mime: cell.mime,2807cellHandle: cell.handle,2808cellId: cell.id,2809content: cell.getText(),2810offset: cellTop + top,2811visible: true,2812metadata: cell.metadata,2813});2814}28152816private cellIsHidden(cell: ICellViewModel): boolean {2817const modelIndex = this.viewModel!.getCellIndex(cell);2818const foldedRanges = this.viewModel!.getHiddenRanges();2819return foldedRanges.some(range => modelIndex >= range.start && modelIndex <= range.end);2820}28212822async unhideMarkupPreviews(cells: readonly MarkupCellViewModel[]) {2823if (!this._webview) {2824return;2825}28262827if (!this._webview.isResolved()) {2828await this._resolveWebview();2829}28302831await this._webview?.unhideMarkupPreviews(cells.map(cell => cell.id));2832}28332834async hideMarkupPreviews(cells: readonly MarkupCellViewModel[]) {2835if (!this._webview || !cells.length) {2836return;2837}28382839if (!this._webview.isResolved()) {2840await this._resolveWebview();2841}28422843await this._webview?.hideMarkupPreviews(cells.map(cell => cell.id));2844}28452846async deleteMarkupPreviews(cells: readonly MarkupCellViewModel[]) {2847if (!this._webview) {2848return;2849}28502851if (!this._webview.isResolved()) {2852await this._resolveWebview();2853}28542855await this._webview?.deleteMarkupPreviews(cells.map(cell => cell.id));2856}28572858private async updateSelectedMarkdownPreviews(): Promise<void> {2859if (!this._webview) {2860return;2861}28622863if (!this._webview.isResolved()) {2864await this._resolveWebview();2865}28662867const selectedCells = this.getSelectionViewModels().map(cell => cell.id);28682869// Only show selection when there is more than 1 cell selected2870await this._webview?.updateMarkupPreviewSelections(selectedCells.length > 1 ? selectedCells : []);2871}28722873async createOutput(cell: CodeCellViewModel, output: IInsetRenderOutput, offset: number, createWhenIdle: boolean): Promise<void> {2874this._insetModifyQueueByOutputId.queue(output.source.model.outputId, async () => {2875if (this._isDisposed || !this._webview) {2876return;2877}28782879if (!this._webview.isResolved()) {2880await this._resolveWebview();2881}28822883if (!this._webview) {2884return;2885}28862887if (!this._list.webviewElement) {2888return;2889}28902891if (output.type === RenderOutputType.Extension) {2892this.notebookRendererMessaging.prepare(output.renderer.id);2893}28942895const webviewTop = parseInt(this._list.webviewElement.domNode.style.top, 10);2896const top = !!webviewTop ? (0 - webviewTop) : 0;28972898const cellTop = this._list.getCellViewScrollTop(cell) + top;28992900const existingOutput = this._webview.insetMapping.get(output.source);2901if (!existingOutput2902|| (!existingOutput.renderer && output.type === RenderOutputType.Extension)2903) {2904if (createWhenIdle) {2905this._webview.requestCreateOutputWhenWebviewIdle({ cellId: cell.id, cellHandle: cell.handle, cellUri: cell.uri, executionId: cell.internalMetadata.executionId }, output, cellTop, offset);2906} else {2907this._webview.createOutput({ cellId: cell.id, cellHandle: cell.handle, cellUri: cell.uri, executionId: cell.internalMetadata.executionId }, output, cellTop, offset);2908}2909} else if (existingOutput.renderer2910&& output.type === RenderOutputType.Extension2911&& existingOutput.renderer.id !== output.renderer.id) {2912// switch mimetype2913this._webview.removeInsets([output.source]);2914this._webview.createOutput({ cellId: cell.id, cellHandle: cell.handle, cellUri: cell.uri }, output, cellTop, offset);2915} else if (existingOutput.versionId !== output.source.model.versionId) {2916this._webview.updateOutput({ cellId: cell.id, cellHandle: cell.handle, cellUri: cell.uri, executionId: cell.internalMetadata.executionId }, output, cellTop, offset);2917} else {2918const outputIndex = cell.outputsViewModels.indexOf(output.source);2919const outputOffset = cell.getOutputOffset(outputIndex);2920this._webview.updateScrollTops([{2921cell,2922output: output.source,2923cellTop,2924outputOffset,2925forceDisplay: !cell.isOutputCollapsed,2926}], []);2927}2928});2929}29302931async updateOutput(cell: CodeCellViewModel, output: IInsetRenderOutput, offset: number): Promise<void> {2932this._insetModifyQueueByOutputId.queue(output.source.model.outputId, async () => {2933if (this._isDisposed || !this._webview || cell.isOutputCollapsed) {2934return;2935}29362937if (!this._webview.isResolved()) {2938await this._resolveWebview();2939}29402941if (!this._webview || !this._list.webviewElement) {2942return;2943}29442945if (!this._webview.insetMapping.has(output.source)) {2946return this.createOutput(cell, output, offset, false);2947}29482949if (output.type === RenderOutputType.Extension) {2950this.notebookRendererMessaging.prepare(output.renderer.id);2951}29522953const webviewTop = parseInt(this._list.webviewElement.domNode.style.top, 10);2954const top = !!webviewTop ? (0 - webviewTop) : 0;29552956const cellTop = this._list.getCellViewScrollTop(cell) + top;2957this._webview.updateOutput({ cellId: cell.id, cellHandle: cell.handle, cellUri: cell.uri }, output, cellTop, offset);2958});2959}29602961async copyOutputImage(cellOutput: ICellOutputViewModel): Promise<void> {2962this._webview?.copyImage(cellOutput);2963}29642965removeInset(output: ICellOutputViewModel) {2966this._insetModifyQueueByOutputId.queue(output.model.outputId, async () => {2967if (this._isDisposed || !this._webview) {2968return;2969}29702971if (this._webview?.isResolved()) {2972this._webview.removeInsets([output]);2973}29742975this._onDidRemoveOutput.fire(output);2976});2977}29782979hideInset(output: ICellOutputViewModel) {2980this._insetModifyQueueByOutputId.queue(output.model.outputId, async () => {2981if (this._isDisposed || !this._webview) {2982return;2983}29842985if (this._webview?.isResolved()) {2986this._webview.hideInset(output);2987}2988});2989}29902991//#region --- webview IPC ----2992postMessage(message: any) {2993if (this._webview?.isResolved()) {2994this._webview.postKernelMessage(message);2995}2996}29972998//#endregion29993000addClassName(className: string) {3001this._overlayContainer.classList.add(className);3002}30033004removeClassName(className: string) {3005this._overlayContainer.classList.remove(className);3006}30073008cellAt(index: number): ICellViewModel | undefined {3009return this.viewModel?.cellAt(index);3010}30113012getCellByInfo(cellInfo: ICommonCellInfo): ICellViewModel {3013const { cellHandle } = cellInfo;3014return this.viewModel?.viewCells.find(vc => vc.handle === cellHandle) as CodeCellViewModel;3015}30163017getCellByHandle(handle: number): ICellViewModel | undefined {3018return this.viewModel?.getCellByHandle(handle);3019}30203021getCellIndex(cell: ICellViewModel) {3022return this.viewModel?.getCellIndexByHandle(cell.handle);3023}30243025getNextVisibleCellIndex(index: number): number | undefined {3026return this.viewModel?.getNextVisibleCellIndex(index);3027}30283029getPreviousVisibleCellIndex(index: number): number | undefined {3030return this.viewModel?.getPreviousVisibleCellIndex(index);3031}30323033private _updateScrollHeight() {3034if (this._isDisposed || !this._webview?.isResolved()) {3035return;3036}30373038if (!this._list.webviewElement) {3039return;3040}30413042const scrollHeight = this._list.scrollHeight;3043this._webview.element.style.height = `${scrollHeight + NOTEBOOK_WEBVIEW_BOUNDARY * 2}px`;30443045const webviewTop = parseInt(this._list.webviewElement.domNode.style.top, 10);3046const top = !!webviewTop ? (0 - webviewTop) : 0;30473048const updateItems: IDisplayOutputLayoutUpdateRequest[] = [];3049const removedItems: ICellOutputViewModel[] = [];3050this._webview?.insetMapping.forEach((value, key) => {3051const cell = this.viewModel?.getCellByHandle(value.cellInfo.cellHandle);3052if (!cell || !(cell instanceof CodeCellViewModel)) {3053return;3054}30553056this.viewModel?.viewCells.find(cell => cell.handle === value.cellInfo.cellHandle);3057const viewIndex = this._list.getViewIndex(cell);30583059if (viewIndex === undefined) {3060return;3061}30623063if (cell.outputsViewModels.indexOf(key) < 0) {3064// output is already gone3065removedItems.push(key);3066}30673068const cellTop = this._list.getCellViewScrollTop(cell);3069const outputIndex = cell.outputsViewModels.indexOf(key);3070const outputOffset = cell.getOutputOffset(outputIndex);3071updateItems.push({3072cell,3073output: key,3074cellTop: cellTop + top,3075outputOffset,3076forceDisplay: false,3077});3078});30793080this._webview.removeInsets(removedItems);30813082const markdownUpdateItems: { id: string; top: number }[] = [];3083for (const cellId of this._webview.markupPreviewMapping.keys()) {3084const cell = this.viewModel?.viewCells.find(cell => cell.id === cellId);3085if (cell) {3086const cellTop = this._list.getCellViewScrollTop(cell);3087// markdownUpdateItems.push({ id: cellId, top: cellTop });3088markdownUpdateItems.push({ id: cellId, top: cellTop + top });3089}3090}30913092if (markdownUpdateItems.length || updateItems.length) {3093this._debug('_list.onDidChangeContentHeight/markdown', markdownUpdateItems);3094this._webview?.updateScrollTops(updateItems, markdownUpdateItems);3095}3096}30973098//#endregion30993100//#region BacklayerWebview delegate3101private _updateOutputHeight(cellInfo: ICommonCellInfo, output: ICellOutputViewModel, outputHeight: number, isInit: boolean, source?: string): void {3102const cell = this.viewModel?.viewCells.find(vc => vc.handle === cellInfo.cellHandle);3103if (cell && cell instanceof CodeCellViewModel) {3104const outputIndex = cell.outputsViewModels.indexOf(output);3105if (outputIndex > -1) {3106this._debug('update cell output', cell.handle, outputHeight);3107cell.updateOutputHeight(outputIndex, outputHeight, source);3108this.layoutNotebookCell(cell, cell.layoutInfo.totalHeight);31093110if (isInit) {3111this._onDidRenderOutput.fire(output);3112}3113} else {3114this._debug('tried to update cell output that does not exist');3115}3116}3117}31183119private readonly _pendingOutputHeightAcks = new Map</* outputId */ string, IAckOutputHeight>();31203121private _scheduleOutputHeightAck(cellInfo: ICommonCellInfo, outputId: string, height: number) {3122const wasEmpty = this._pendingOutputHeightAcks.size === 0;3123this._pendingOutputHeightAcks.set(outputId, { cellId: cellInfo.cellId, outputId, height });31243125if (wasEmpty) {3126DOM.scheduleAtNextAnimationFrame(DOM.getWindow(this.getDomNode()), () => {3127this._debug('ack height');3128this._updateScrollHeight();31293130this._webview?.ackHeight([...this._pendingOutputHeightAcks.values()]);31313132this._pendingOutputHeightAcks.clear();3133}, -1); // -1 priority because this depends on calls to layoutNotebookCell, and that may be called multiple times before this runs3134}3135}31363137private _getCellById(cellId: string): ICellViewModel | undefined {3138return this.viewModel?.viewCells.find(vc => vc.id === cellId);3139}31403141private _updateMarkupCellHeight(cellId: string, height: number, isInit: boolean) {3142const cell = this._getCellById(cellId);3143if (cell && cell instanceof MarkupCellViewModel) {3144const { bottomToolbarGap } = this._notebookOptions.computeBottomToolbarDimensions(this.viewModel?.viewType);3145this._debug('updateMarkdownCellHeight', cell.handle, height + bottomToolbarGap, isInit);3146cell.renderedMarkdownHeight = height;3147}3148}31493150private _setMarkupCellEditState(cellId: string, editState: CellEditState): void {3151const cell = this._getCellById(cellId);3152if (cell instanceof MarkupCellViewModel) {3153this.revealInView(cell);3154cell.updateEditState(editState, 'setMarkdownCellEditState');3155}3156}31573158private _didStartDragMarkupCell(cellId: string, event: { dragOffsetY: number }): void {3159const cell = this._getCellById(cellId);3160if (cell instanceof MarkupCellViewModel) {3161const webviewOffset = this._list.webviewElement ? -parseInt(this._list.webviewElement.domNode.style.top, 10) : 0;3162this._dndController?.startExplicitDrag(cell, event.dragOffsetY - webviewOffset);3163}3164}31653166private _didDragMarkupCell(cellId: string, event: { dragOffsetY: number }): void {3167const cell = this._getCellById(cellId);3168if (cell instanceof MarkupCellViewModel) {3169const webviewOffset = this._list.webviewElement ? -parseInt(this._list.webviewElement.domNode.style.top, 10) : 0;3170this._dndController?.explicitDrag(cell, event.dragOffsetY - webviewOffset);3171}3172}31733174private _didDropMarkupCell(cellId: string, event: { dragOffsetY: number; ctrlKey: boolean; altKey: boolean }): void {3175const cell = this._getCellById(cellId);3176if (cell instanceof MarkupCellViewModel) {3177const webviewOffset = this._list.webviewElement ? -parseInt(this._list.webviewElement.domNode.style.top, 10) : 0;3178event.dragOffsetY -= webviewOffset;3179this._dndController?.explicitDrop(cell, event);3180}3181}31823183private _didEndDragMarkupCell(cellId: string): void {3184const cell = this._getCellById(cellId);3185if (cell instanceof MarkupCellViewModel) {3186this._dndController?.endExplicitDrag(cell);3187}3188}31893190private _didResizeOutput(cellId: string): void {3191const cell = this._getCellById(cellId);3192if (cell) {3193this._onDidResizeOutputEmitter.fire(cell);3194}3195}31963197private _updatePerformanceMetadata(cellId: string, executionId: string, duration: number, rendererId: string): void {3198if (!this.hasModel()) {3199return;3200}32013202const cell = this._getCellById(cellId);3203const cellIndex = !cell ? undefined : this.getCellIndex(cell);3204if (cell?.internalMetadata.executionId === executionId && cellIndex !== undefined) {3205const renderDurationMap = cell.internalMetadata.renderDuration || {};3206renderDurationMap[rendererId] = (renderDurationMap[rendererId] ?? 0) + duration;32073208this.textModel.applyEdits([3209{3210editType: CellEditType.PartialInternalMetadata,3211index: cellIndex,3212internalMetadata: {3213executionId: executionId,3214renderDuration: renderDurationMap3215}3216}3217], true, undefined, () => undefined, undefined, false);32183219}3220}32213222//#endregion32233224//#region Editor Contributions3225getContribution<T extends INotebookEditorContribution>(id: string): T {3226return <T>(this._contributions.get(id) || null);3227}32283229//#endregion32303231override dispose() {3232this._isDisposed = true;3233// dispose webview first3234this._webview?.dispose();3235this._webview = null;32363237this.notebookEditorService.removeNotebookEditor(this);3238dispose(this._contributions.values());3239this._contributions.clear();32403241this._localStore.clear();3242dispose(this._localCellStateListeners);3243this._list.dispose();3244this._cellLayoutManager?.dispose();3245this._listTopCellToolbar?.dispose();32463247this._overlayContainer.remove();3248this.viewModel?.dispose();32493250this._renderedEditors.clear();3251this._baseCellEditorOptions.forEach(v => v.dispose());3252this._baseCellEditorOptions.clear();32533254this._notebookOverviewRulerContainer.remove();32553256super.dispose();32573258// unref3259this._webview = null;3260this._webviewResolvePromise = null;3261this._webviewTransparentCover = null;3262this._dndController = null;3263this._listTopCellToolbar = null;3264this._notebookViewModel = undefined;3265this._cellContextKeyManager = null;3266this._notebookTopToolbar = null!;3267this._list = null!;3268this._listViewInfoAccessor = null!;3269this._listDelegate = null;3270}32713272toJSON(): { notebookUri: URI | undefined } {3273return {3274notebookUri: this.viewModel?.uri,3275};3276}3277}32783279registerZIndex(ZIndex.Base, 5, 'notebook-progress-bar',);3280registerZIndex(ZIndex.Base, 10, 'notebook-list-insertion-indicator');3281registerZIndex(ZIndex.Base, 20, 'notebook-cell-editor-outline');3282registerZIndex(ZIndex.Base, 25, 'notebook-scrollbar');3283registerZIndex(ZIndex.Base, 26, 'notebook-cell-status');3284registerZIndex(ZIndex.Base, 26, 'notebook-folding-indicator');3285registerZIndex(ZIndex.Base, 27, 'notebook-output');3286registerZIndex(ZIndex.Base, 28, 'notebook-cell-bottom-toolbar-container');3287registerZIndex(ZIndex.Base, 29, 'notebook-run-button-container');3288registerZIndex(ZIndex.Base, 29, 'notebook-input-collapse-condicon');3289registerZIndex(ZIndex.Base, 30, 'notebook-cell-output-toolbar');3290registerZIndex(ZIndex.Sash, 1, 'notebook-cell-expand-part-button');3291registerZIndex(ZIndex.Sash, 2, 'notebook-cell-toolbar');3292registerZIndex(ZIndex.Sash, 3, 'notebook-cell-toolbar-dropdown-active');32933294export const notebookCellBorder = registerColor('notebook.cellBorderColor', {3295dark: transparent(listInactiveSelectionBackground, 1),3296light: transparent(listInactiveSelectionBackground, 1),3297hcDark: PANEL_BORDER,3298hcLight: PANEL_BORDER3299}, nls.localize('notebook.cellBorderColor', "The border color for notebook cells."));33003301export const focusedEditorBorderColor = registerColor('notebook.focusedEditorBorder', focusBorder, nls.localize('notebook.focusedEditorBorder', "The color of the notebook cell editor border."));33023303export const cellStatusIconSuccess = registerColor('notebookStatusSuccessIcon.foreground', debugIconStartForeground, nls.localize('notebookStatusSuccessIcon.foreground', "The error icon color of notebook cells in the cell status bar."));33043305export const runningCellRulerDecorationColor = registerColor('notebookEditorOverviewRuler.runningCellForeground', debugIconStartForeground, nls.localize('notebookEditorOverviewRuler.runningCellForeground', "The color of the running cell decoration in the notebook editor overview ruler."));33063307export const cellStatusIconError = registerColor('notebookStatusErrorIcon.foreground', errorForeground, nls.localize('notebookStatusErrorIcon.foreground', "The error icon color of notebook cells in the cell status bar."));33083309export const cellStatusIconRunning = registerColor('notebookStatusRunningIcon.foreground', foreground, nls.localize('notebookStatusRunningIcon.foreground', "The running icon color of notebook cells in the cell status bar."));33103311export const notebookOutputContainerBorderColor = registerColor('notebook.outputContainerBorderColor', null, nls.localize('notebook.outputContainerBorderColor', "The border color of the notebook output container."));33123313export const notebookOutputContainerColor = registerColor('notebook.outputContainerBackgroundColor', null, nls.localize('notebook.outputContainerBackgroundColor', "The color of the notebook output container background."));33143315// TODO@rebornix currently also used for toolbar border, if we keep all of this, pick a generic name3316export const CELL_TOOLBAR_SEPERATOR = registerColor('notebook.cellToolbarSeparator', {3317dark: Color.fromHex('#808080').transparent(0.35),3318light: Color.fromHex('#808080').transparent(0.35),3319hcDark: contrastBorder,3320hcLight: contrastBorder3321}, nls.localize('notebook.cellToolbarSeparator', "The color of the separator in the cell bottom toolbar"));33223323export const focusedCellBackground = registerColor('notebook.focusedCellBackground', null, nls.localize('focusedCellBackground', "The background color of a cell when the cell is focused."));33243325export const selectedCellBackground = registerColor('notebook.selectedCellBackground', {3326dark: listInactiveSelectionBackground,3327light: listInactiveSelectionBackground,3328hcDark: null,3329hcLight: null3330}, nls.localize('selectedCellBackground', "The background color of a cell when the cell is selected."));333133323333export const cellHoverBackground = registerColor('notebook.cellHoverBackground', {3334dark: transparent(focusedCellBackground, .5),3335light: transparent(focusedCellBackground, .7),3336hcDark: null,3337hcLight: null3338}, nls.localize('notebook.cellHoverBackground', "The background color of a cell when the cell is hovered."));33393340export const selectedCellBorder = registerColor('notebook.selectedCellBorder', {3341dark: notebookCellBorder,3342light: notebookCellBorder,3343hcDark: contrastBorder,3344hcLight: contrastBorder3345}, nls.localize('notebook.selectedCellBorder', "The color of the cell's top and bottom border when the cell is selected but not focused."));33463347export const inactiveSelectedCellBorder = registerColor('notebook.inactiveSelectedCellBorder', {3348dark: null,3349light: null,3350hcDark: focusBorder,3351hcLight: focusBorder3352}, nls.localize('notebook.inactiveSelectedCellBorder', "The color of the cell's borders when multiple cells are selected."));33533354export const focusedCellBorder = registerColor('notebook.focusedCellBorder', focusBorder, nls.localize('notebook.focusedCellBorder', "The color of the cell's focus indicator borders when the cell is focused."));33553356export const inactiveFocusedCellBorder = registerColor('notebook.inactiveFocusedCellBorder', notebookCellBorder, nls.localize('notebook.inactiveFocusedCellBorder', "The color of the cell's top and bottom border when a cell is focused while the primary focus is outside of the editor."));33573358export const cellStatusBarItemHover = registerColor('notebook.cellStatusBarItemHoverBackground', {3359light: new Color(new RGBA(0, 0, 0, 0.08)),3360dark: new Color(new RGBA(255, 255, 255, 0.15)),3361hcDark: new Color(new RGBA(255, 255, 255, 0.15)),3362hcLight: new Color(new RGBA(0, 0, 0, 0.08)),3363}, nls.localize('notebook.cellStatusBarItemHoverBackground', "The background color of notebook cell status bar items."));33643365export const cellInsertionIndicator = registerColor('notebook.cellInsertionIndicator', focusBorder, nls.localize('notebook.cellInsertionIndicator', "The color of the notebook cell insertion indicator."));33663367export const listScrollbarSliderBackground = registerColor('notebookScrollbarSlider.background', scrollbarSliderBackground, nls.localize('notebookScrollbarSliderBackground', "Notebook scrollbar slider background color."));33683369export const listScrollbarSliderHoverBackground = registerColor('notebookScrollbarSlider.hoverBackground', scrollbarSliderHoverBackground, nls.localize('notebookScrollbarSliderHoverBackground', "Notebook scrollbar slider background color when hovering."));33703371export const listScrollbarSliderActiveBackground = registerColor('notebookScrollbarSlider.activeBackground', scrollbarSliderActiveBackground, nls.localize('notebookScrollbarSliderActiveBackground', "Notebook scrollbar slider background color when clicked on."));33723373export const cellSymbolHighlight = registerColor('notebook.symbolHighlightBackground', {3374dark: Color.fromHex('#ffffff0b'),3375light: Color.fromHex('#fdff0033'),3376hcDark: null,3377hcLight: null3378}, nls.localize('notebook.symbolHighlightBackground', "Background color of highlighted cell"));33793380export const cellEditorBackground = registerColor('notebook.cellEditorBackground', {3381light: SIDE_BAR_BACKGROUND,3382dark: SIDE_BAR_BACKGROUND,3383hcDark: null,3384hcLight: null3385}, nls.localize('notebook.cellEditorBackground', "Cell editor background color."));33863387const notebookEditorBackground = registerColor('notebook.editorBackground', {3388light: EDITOR_PANE_BACKGROUND,3389dark: EDITOR_PANE_BACKGROUND,3390hcDark: null,3391hcLight: null3392}, nls.localize('notebook.editorBackground', "Notebook background color."));339333943395