Path: blob/main/extensions/copilot/test/simulation/fixtures/codeMapper/notebookEditorWidget.ts
13399 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 * as DOM from '../../../../base/browser/dom.js';6import * as domStylesheets from '../../../../base/browser/domStylesheets.js';7import { IMouseWheelEvent, StandardMouseEvent } from '../../../../base/browser/mouseEvent.js';8import { PixelRatio } from '../../../../base/browser/pixelRatio.js';9import { IListContextMenuEvent } from '../../../../base/browser/ui/list/list.js';10import { mainWindow } from '../../../../base/browser/window.js';11import { DeferredPromise, SequencerByKey } from '../../../../base/common/async.js';12import { CancellationToken } from '../../../../base/common/cancellation.js';13import { Color, RGBA } from '../../../../base/common/color.js';14import { onUnexpectedError } from '../../../../base/common/errors.js';15import { Emitter, Event } from '../../../../base/common/event.js';16import { combinedDisposable, Disposable, DisposableStore, dispose, IDisposable, toDisposable } from '../../../../base/common/lifecycle.js';17import { Schemas } from '../../../../base/common/network.js';18import { setTimeout0 } from '../../../../base/common/platform.js';19import { extname, isEqual } from '../../../../base/common/resources.js';20import { URI } from '../../../../base/common/uri.js';21import { generateUuid } from '../../../../base/common/uuid.js';22import { FontMeasurements } from '../../../../editor/browser/config/fontMeasurements.js';23import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js';24import { EditorExtensionsRegistry } from '../../../../editor/browser/editorExtensions.js';25import { IEditorOptions } from '../../../../editor/common/config/editorOptions.js';26import { BareFontInfo, FontInfo } from '../../../../editor/common/config/fontInfo.js';27import { IDimension } from '../../../../editor/common/core/dimension.js';28import { Range } from '../../../../editor/common/core/range.js';29import { Selection } from '../../../../editor/common/core/selection.js';30import { CopyPasteController } from '../../../../editor/contrib/dropOrPasteInto/browser/copyPasteController.js';31import { DropIntoEditorController } from '../../../../editor/contrib/dropOrPasteInto/browser/dropIntoEditorController.js';32import { InlineCompletionsController } from '../../../../editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.js';33import { SuggestController } from '../../../../editor/contrib/suggest/browser/suggestController.js';34import * as nls from '../../../../nls.js';35import { MenuId } from '../../../../platform/actions/common/actions.js';36import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';37import { IContextKey, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js';38import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js';39import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';40import { ServiceCollection } from '../../../../platform/instantiation/common/serviceCollection.js';41import { ILayoutService } from '../../../../platform/layout/browser/layoutService.js';42import { registerZIndex, ZIndex } from '../../../../platform/layout/browser/zIndexRegistry.js';43import { IEditorProgressService, IProgressRunner } from '../../../../platform/progress/common/progress.js';44import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js';45import { contrastBorder, errorForeground, focusBorder, foreground, listInactiveSelectionBackground, registerColor, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, transparent } from '../../../../platform/theme/common/colorRegistry.js';46import { FloatingEditorClickMenu } from '../../../browser/codeeditor.js';47import { EDITOR_PANE_BACKGROUND, PANEL_BORDER, SIDE_BAR_BACKGROUND } from '../../../common/theme.js';48import { IEditorGroupsService } from '../../../services/editor/common/editorGroupsService.js';49import { debugIconStartForeground } from '../../debug/browser/debugColors.js';50import { PreventDefaultContextMenuItemsContextKeyName } from '../../webview/browser/webview.contribution.js';51import { IWebviewElement } from '../../webview/browser/webview.js';52import { NotebookTextModel } from '../common/model/notebookTextModel.js';53import { CellEditType, CellKind, INotebookFindOptions, NotebookFindScopeType, RENDERER_NOT_AVAILABLE, SelectionStateType } from '../common/notebookCommon.js';54import { NOTEBOOK_CURSOR_NAVIGATION_MODE, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_OUTPUT_FOCUSED, NOTEBOOK_OUTPUT_INPUT_FOCUSED } from '../common/notebookContextKeys.js';55import { INotebookExecutionService } from '../common/notebookExecutionService.js';56import { INotebookKernelService } from '../common/notebookKernelService.js';57import { INotebookLoggingService } from '../common/notebookLoggingService.js';58import { NotebookPerfMarks } from '../common/notebookPerformance.js';59import { cellRangesToIndexes, ICellRange } from '../common/notebookRange.js';60import { INotebookRendererMessagingService } from '../common/notebookRendererMessagingService.js';61import { INotebookService } from '../common/notebookService.js';62import { CellFindMatchModel } from './contrib/find/findModel.js';63import './media/notebook.css';64import './media/notebookCellChat.css';65import './media/notebookCellEditorHint.css';66import './media/notebookCellInsertToolbar.css';67import './media/notebookCellOutput.css';68import './media/notebookCellStatusBar.css';69import './media/notebookCellTitleToolbar.css';70import './media/notebookChatEditController.css';71import './media/notebookChatEditorOverlay.css';72import './media/notebookDnd.css';73import './media/notebookEditorStickyScroll.css';74import './media/notebookFocusIndicator.css';75import './media/notebookFolding.css';76import './media/notebookKernelActionViewItem.css';77import './media/notebookOutline.css';78import './media/notebookToolbar.css';79import { NotebookAccessibilityProvider } from './notebookAccessibilityProvider.js';80import { 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';81import { NotebookEditorExtensionsRegistry } from './notebookEditorExtensions.js';82import { notebookDebug } from './notebookLogger.js';83import { NotebookOptions, OutputInnerContainerTopPadding } from './notebookOptions.js';84import { NotebookCellStateChangedEvent, NotebookLayoutChangedEvent, NotebookLayoutInfo } from './notebookViewEvents.js';85import { INotebookEditorService } from './services/notebookEditorService.js';86import { CellContextKeyManager } from './view/cellParts/cellContextKeys.js';87import { CellDragAndDropController } from './view/cellParts/cellDnd.js';88import { NotebookCellEditorPool } from './view/notebookCellEditorPool.js';89import { ListViewInfoAccessor, NOTEBOOK_WEBVIEW_BOUNDARY, NotebookCellList } from './view/notebookCellList.js';90import { INotebookCellList } from './view/notebookRenderingCommon.js';91import { BackLayerWebView } from './view/renderers/backLayerWebView.js';92import { CodeCellRenderer, MarkupCellRenderer, NotebookCellListDelegate } from './view/renderers/cellRenderer.js';93import { IAckOutputHeight, IMarkupCellInitialization } from './view/renderers/webviewMessages.js';94import { BaseCellEditorOptions } from './viewModel/cellEditorOptions.js';95import { CodeCellViewModel, outputDisplayLimit } from './viewModel/codeCellViewModel.js';96import { NotebookEventDispatcher } from './viewModel/eventDispatcher.js';97import { MarkupCellViewModel } from './viewModel/markupCellViewModel.js';98import { CellViewModel, NotebookViewModel } from './viewModel/notebookViewModelImpl.js';99import { ViewContext } from './viewModel/viewContext.js';100import { NotebookStickyScroll } from './viewParts/notebookEditorStickyScroll.js';101import { NotebookEditorWorkbenchToolbar } from './viewParts/notebookEditorToolbar.js';102import { NotebookEditorContextKeys } from './viewParts/notebookEditorWidgetContextKeys.js';103import { NotebookHorizontalTracker } from './viewParts/notebookHorizontalTracker.js';104import { NotebookOverviewRuler } from './viewParts/notebookOverviewRuler.js';105import { ListTopCellToolbar } from './viewParts/notebookTopCellToolbar.js';106107const $ = DOM.$;108109export function getDefaultNotebookCreationOptions(): INotebookEditorCreationOptions {110// We inlined the id to avoid loading comment contrib in tests111const skipContributions = [112'editor.contrib.review',113FloatingEditorClickMenu.ID,114'editor.contrib.dirtydiff',115'editor.contrib.testingOutputPeek',116'editor.contrib.testingDecorations',117'store.contrib.stickyScrollController',118'editor.contrib.findController',119'editor.contrib.emptyTextEditorHint'120];121const contributions = EditorExtensionsRegistry.getEditorContributions().filter(c => skipContributions.indexOf(c.id) === -1);122123return {124menuIds: {125notebookToolbar: MenuId.NotebookToolbar,126cellTitleToolbar: MenuId.NotebookCellTitle,127cellDeleteToolbar: MenuId.NotebookCellDelete,128cellInsertToolbar: MenuId.NotebookCellBetween,129cellTopInsertToolbar: MenuId.NotebookCellListTop,130cellExecuteToolbar: MenuId.NotebookCellExecute,131cellExecutePrimary: MenuId.NotebookCellExecutePrimary,132},133cellEditorContributions: contributions134};135}136137export class NotebookEditorWidget extends Disposable implements INotebookEditorDelegate, INotebookEditor {138//#region Eventing139private readonly _onDidChangeCellState = this._register(new Emitter<NotebookCellStateChangedEvent>());140readonly onDidChangeCellState = this._onDidChangeCellState.event;141private readonly _onDidChangeViewCells = this._register(new Emitter<INotebookViewCellsUpdateEvent>());142readonly onDidChangeViewCells: Event<INotebookViewCellsUpdateEvent> = this._onDidChangeViewCells.event;143private readonly _onWillChangeModel = this._register(new Emitter<NotebookTextModel | undefined>());144readonly onWillChangeModel: Event<NotebookTextModel | undefined> = this._onWillChangeModel.event;145private readonly _onDidChangeModel = this._register(new Emitter<NotebookTextModel | undefined>());146readonly onDidChangeModel: Event<NotebookTextModel | undefined> = this._onDidChangeModel.event;147private readonly _onDidAttachViewModel = this._register(new Emitter<void>());148readonly onDidAttachViewModel: Event<void> = this._onDidAttachViewModel.event;149private readonly _onDidChangeOptions = this._register(new Emitter<void>());150readonly onDidChangeOptions: Event<void> = this._onDidChangeOptions.event;151private readonly _onDidChangeDecorations = this._register(new Emitter<void>());152readonly onDidChangeDecorations: Event<void> = this._onDidChangeDecorations.event;153private readonly _onDidScroll = this._register(new Emitter<void>());154readonly onDidScroll: Event<void> = this._onDidScroll.event;155private readonly _onDidChangeLayout = this._register(new Emitter<void>());156readonly onDidChangeLayout: Event<void> = this._onDidChangeLayout.event;157private readonly _onDidChangeActiveCell = this._register(new Emitter<void>());158readonly onDidChangeActiveCell: Event<void> = this._onDidChangeActiveCell.event;159private readonly _onDidChangeFocus = this._register(new Emitter<void>());160readonly onDidChangeFocus: Event<void> = this._onDidChangeFocus.event;161private readonly _onDidChangeSelection = this._register(new Emitter<void>());162readonly onDidChangeSelection: Event<void> = this._onDidChangeSelection.event;163private readonly _onDidChangeVisibleRanges = this._register(new Emitter<void>());164readonly onDidChangeVisibleRanges: Event<void> = this._onDidChangeVisibleRanges.event;165private readonly _onDidFocusEmitter = this._register(new Emitter<void>());166readonly onDidFocusWidget = this._onDidFocusEmitter.event;167private readonly _onDidBlurEmitter = this._register(new Emitter<void>());168readonly onDidBlurWidget = this._onDidBlurEmitter.event;169private readonly _onDidChangeActiveEditor = this._register(new Emitter<this>());170readonly onDidChangeActiveEditor: Event<this> = this._onDidChangeActiveEditor.event;171private readonly _onDidChangeActiveKernel = this._register(new Emitter<void>());172readonly onDidChangeActiveKernel: Event<void> = this._onDidChangeActiveKernel.event;173private readonly _onMouseUp: Emitter<INotebookEditorMouseEvent> = this._register(new Emitter<INotebookEditorMouseEvent>());174readonly onMouseUp: Event<INotebookEditorMouseEvent> = this._onMouseUp.event;175private readonly _onMouseDown: Emitter<INotebookEditorMouseEvent> = this._register(new Emitter<INotebookEditorMouseEvent>());176readonly onMouseDown: Event<INotebookEditorMouseEvent> = this._onMouseDown.event;177private readonly _onDidReceiveMessage = this._register(new Emitter<INotebookWebviewMessage>());178readonly onDidReceiveMessage: Event<INotebookWebviewMessage> = this._onDidReceiveMessage.event;179private readonly _onDidRenderOutput = this._register(new Emitter<ICellOutputViewModel>());180private readonly onDidRenderOutput = this._onDidRenderOutput.event;181private readonly _onDidRemoveOutput = this._register(new Emitter<ICellOutputViewModel>());182private readonly onDidRemoveOutput = this._onDidRemoveOutput.event;183private readonly _onDidResizeOutputEmitter = this._register(new Emitter<ICellViewModel>());184readonly onDidResizeOutput = this._onDidResizeOutputEmitter.event;185186//#endregion187private _overlayContainer!: HTMLElement;188private _notebookTopToolbarContainer!: HTMLElement;189private _notebookTopToolbar!: NotebookEditorWorkbenchToolbar;190private _notebookStickyScrollContainer!: HTMLElement;191private _notebookStickyScroll!: NotebookStickyScroll;192private _notebookOverviewRulerContainer!: HTMLElement;193private _notebookOverviewRuler!: NotebookOverviewRuler;194private _body!: HTMLElement;195private _styleElement!: HTMLStyleElement;196private _overflowContainer!: HTMLElement;197private _webview: BackLayerWebView<ICommonCellInfo> | null = null;198private _webviewResolvePromise: Promise<BackLayerWebView<ICommonCellInfo> | null> | null = null;199private _webviewTransparentCover: HTMLElement | null = null;200private _listDelegate: NotebookCellListDelegate | null = null;201private _list!: INotebookCellList;202private _listViewInfoAccessor!: ListViewInfoAccessor;203private _dndController: CellDragAndDropController | null = null;204private _listTopCellToolbar: ListTopCellToolbar | null = null;205private _renderedEditors: Map<ICellViewModel, ICodeEditor> = new Map();206private _editorPool!: NotebookCellEditorPool;207private _viewContext: ViewContext;208private _notebookViewModel: NotebookViewModel | undefined;209private readonly _localStore: DisposableStore = this._register(new DisposableStore());210private _localCellStateListeners: DisposableStore[] = [];211private _fontInfo: FontInfo | undefined;212private _dimension?: DOM.Dimension;213private _position?: DOM.IDomPosition;214private _shadowElement?: HTMLElement;215private _shadowElementViewInfo: { height: number; width: number; top: number; left: number } | null = null;216217private readonly _editorFocus: IContextKey<boolean>;218private readonly _outputFocus: IContextKey<boolean>;219private readonly _editorEditable: IContextKey<boolean>;220private readonly _cursorNavMode: IContextKey<boolean>;221private readonly _outputInputFocus: IContextKey<boolean>;222protected readonly _contributions = new Map<string, INotebookEditorContribution>();223private _scrollBeyondLastLine: boolean;224private readonly _insetModifyQueueByOutputId = new SequencerByKey<string>();225private _cellContextKeyManager: CellContextKeyManager | null = null;226private readonly _uuid = generateUuid();227private _focusTracker!: DOM.IFocusTracker;228private _webviewFocused: boolean = false;229private _isVisible = false;230get isVisible() {231return this._isVisible;232}233234private _isDisposed: boolean = false;235236get isDisposed() {237return this._isDisposed;238}239240set viewModel(newModel: NotebookViewModel | undefined) {241this._onWillChangeModel.fire(this._notebookViewModel?.notebookDocument);242this._notebookViewModel = newModel;243this._onDidChangeModel.fire(newModel?.notebookDocument);244}245246get viewModel() {247return this._notebookViewModel;248}249250get textModel() {251return this._notebookViewModel?.notebookDocument;252}253254get isReadOnly() {255return this._notebookViewModel?.options.isReadOnly ?? false;256}257258get activeCodeEditor(): ICodeEditor | undefined {259if (this._isDisposed) {260return;261}262263const [focused] = this._list.getFocusedElements();264return this._renderedEditors.get(focused);265}266267get activeCellAndCodeEditor(): [ICellViewModel, ICodeEditor] | undefined {268if (this._isDisposed) {269return;270}271272const [focused] = this._list.getFocusedElements();273const editor = this._renderedEditors.get(focused);274if (!editor) {275return;276}277return [focused, editor];278}279280get codeEditors(): [ICellViewModel, ICodeEditor][] {281return [...this._renderedEditors];282}283284get visibleRanges() {285return this._list ? (this._list.visibleRanges || []) : [];286}287288private _baseCellEditorOptions = new Map<string, IBaseCellEditorOptions>();289290readonly isReplHistory: boolean;291private _readOnly: boolean;292293public readonly scopedContextKeyService: IContextKeyService;294private readonly instantiationService: IInstantiationService;295private readonly _notebookOptions: NotebookOptions;296297private _currentProgress: IProgressRunner | undefined;298299get notebookOptions() {300return this._notebookOptions;301}302303constructor(304readonly creationOptions: INotebookEditorCreationOptions,305dimension: DOM.Dimension | undefined,306@IInstantiationService instantiationService: IInstantiationService,307@IEditorGroupsService editorGroupsService: IEditorGroupsService,308@INotebookRendererMessagingService private readonly notebookRendererMessaging: INotebookRendererMessagingService,309@INotebookEditorService private readonly notebookEditorService: INotebookEditorService,310@INotebookKernelService private readonly notebookKernelService: INotebookKernelService,311@INotebookService private readonly _notebookService: INotebookService,312@IConfigurationService private readonly configurationService: IConfigurationService,313@IContextKeyService contextKeyService: IContextKeyService,314@ILayoutService private readonly layoutService: ILayoutService,315@IContextMenuService private readonly contextMenuService: IContextMenuService,316@ITelemetryService private readonly telemetryService: ITelemetryService,317@INotebookExecutionService private readonly notebookExecutionService: INotebookExecutionService,318@IEditorProgressService private editorProgressService: IEditorProgressService,319@INotebookLoggingService private readonly logService: INotebookLoggingService,320) {321super();322323this._dimension = dimension;324325this.isReplHistory = creationOptions.isReplHistory ?? false;326this._readOnly = creationOptions.isReadOnly ?? false;327328this._overlayContainer = document.createElement('div');329this.scopedContextKeyService = this._register(contextKeyService.createScoped(this._overlayContainer));330this.instantiationService = this._register(instantiationService.createChild(new ServiceCollection([IContextKeyService, this.scopedContextKeyService])));331332this._notebookOptions = creationOptions.options ??333this.instantiationService.createInstance(NotebookOptions, this.creationOptions?.codeWindow ?? mainWindow, this._readOnly, undefined);334this._register(this._notebookOptions);335const eventDispatcher = this._register(new NotebookEventDispatcher());336this._viewContext = new ViewContext(337this._notebookOptions,338eventDispatcher,339language => this.getBaseCellEditorOptions(language));340this._register(this._viewContext.eventDispatcher.onDidChangeLayout(() => {341this._onDidChangeLayout.fire();342}));343this._register(this._viewContext.eventDispatcher.onDidChangeCellState(e => {344this._onDidChangeCellState.fire(e);345}));346347348this._register(_notebookService.onDidChangeOutputRenderers(() => {349this._updateOutputRenderers();350}));351352this._register(this.instantiationService.createInstance(NotebookEditorContextKeys, this));353354this._register(notebookKernelService.onDidChangeSelectedNotebooks(e => {355if (isEqual(e.notebook, this.viewModel?.uri)) {356this._loadKernelPreloads();357this._onDidChangeActiveKernel.fire();358}359}));360361this._scrollBeyondLastLine = this.configurationService.getValue<boolean>('editor.scrollBeyondLastLine');362363this._register(this.configurationService.onDidChangeConfiguration(e => {364if (e.affectsConfiguration('editor.scrollBeyondLastLine')) {365this._scrollBeyondLastLine = this.configurationService.getValue<boolean>('editor.scrollBeyondLastLine');366if (this._dimension && this._isVisible) {367this.layout(this._dimension);368}369}370}));371372this._register(this._notebookOptions.onDidChangeOptions(e => {373if (e.cellStatusBarVisibility || e.cellToolbarLocation || e.cellToolbarInteraction) {374this._updateForNotebookConfiguration();375}376377if (e.fontFamily) {378this._generateFontInfo();379}380381if (e.compactView382|| e.focusIndicator383|| e.insertToolbarPosition384|| e.cellToolbarLocation385|| e.dragAndDropEnabled386|| e.fontSize387|| e.markupFontSize388|| e.markdownLineHeight389|| e.fontFamily390|| e.insertToolbarAlignment391|| e.outputFontSize392|| e.outputLineHeight393|| e.outputFontFamily394|| e.outputWordWrap395|| e.outputScrolling396|| e.outputLinkifyFilePaths397|| e.minimalError398) {399this._styleElement?.remove();400this._createLayoutStyles();401this._webview?.updateOptions({402...this.notebookOptions.computeWebviewOptions(),403fontFamily: this._generateFontFamily()404});405}406407if (this._dimension && this._isVisible) {408this.layout(this._dimension);409}410}));411412const container = creationOptions.codeWindow ? this.layoutService.getContainer(creationOptions.codeWindow) : this.layoutService.mainContainer;413this._register(editorGroupsService.getPart(container).onDidScroll(e => {414if (!this._shadowElement || !this._isVisible) {415return;416}417418this.updateShadowElement(this._shadowElement, this._dimension);419this.layoutContainerOverShadowElement(this._dimension, this._position);420}));421422this.notebookEditorService.addNotebookEditor(this);423424const id = generateUuid();425this._overlayContainer.id = `notebook-${id}`;426this._overlayContainer.className = 'notebookOverlay';427this._overlayContainer.classList.add('notebook-editor');428this._overlayContainer.inert = true;429this._overlayContainer.style.visibility = 'hidden';430431container.appendChild(this._overlayContainer);432433this._createBody(this._overlayContainer);434this._generateFontInfo();435this._isVisible = true;436this._editorFocus = NOTEBOOK_EDITOR_FOCUSED.bindTo(this.scopedContextKeyService);437this._outputFocus = NOTEBOOK_OUTPUT_FOCUSED.bindTo(this.scopedContextKeyService);438this._outputInputFocus = NOTEBOOK_OUTPUT_INPUT_FOCUSED.bindTo(this.scopedContextKeyService);439this._editorEditable = NOTEBOOK_EDITOR_EDITABLE.bindTo(this.scopedContextKeyService);440this._cursorNavMode = NOTEBOOK_CURSOR_NAVIGATION_MODE.bindTo(this.scopedContextKeyService);441// Never display the native cut/copy context menu items in notebooks442new RawContextKey<boolean>(PreventDefaultContextMenuItemsContextKeyName, false).bindTo(this.scopedContextKeyService).set(true);443444this._editorEditable.set(!creationOptions.isReadOnly);445446let contributions: INotebookEditorContributionDescription[];447if (Array.isArray(this.creationOptions.contributions)) {448contributions = this.creationOptions.contributions;449} else {450contributions = NotebookEditorExtensionsRegistry.getEditorContributions();451}452for (const desc of contributions) {453let contribution: INotebookEditorContribution | undefined;454try {455contribution = this.instantiationService.createInstance(desc.ctor, this);456} catch (err) {457onUnexpectedError(err);458}459if (contribution) {460if (!this._contributions.has(desc.id)) {461this._contributions.set(desc.id, contribution);462} else {463contribution.dispose();464throw new Error(`DUPLICATE notebook editor contribution: '${desc.id}'`);465}466}467}468469this._updateForNotebookConfiguration();470}471472private _debugFlag: boolean = false;473474private _debug(...args: any[]) {475if (!this._debugFlag) {476return;477}478479notebookDebug(...args);480}481482/**483* EditorId484*/485public getId(): string {486return this._uuid;487}488489getViewModel(): NotebookViewModel | undefined {490return this.viewModel;491}492493getLength() {494return this.viewModel?.length ?? 0;495}496497getSelections() {498return this.viewModel?.getSelections() ?? [];499}500501setSelections(selections: ICellRange[]) {502if (!this.viewModel) {503return;504}505506const focus = this.viewModel.getFocus();507this.viewModel.updateSelectionsState({508kind: SelectionStateType.Index,509focus: focus,510selections: selections511});512}513514getFocus() {515return this.viewModel?.getFocus() ?? { start: 0, end: 0 };516}517518setFocus(focus: ICellRange) {519if (!this.viewModel) {520return;521}522523const selections = this.viewModel.getSelections();524this.viewModel.updateSelectionsState({525kind: SelectionStateType.Index,526focus: focus,527selections: selections528});529}530531getSelectionViewModels(): ICellViewModel[] {532if (!this.viewModel) {533return [];534}535536const cellsSet = new Set<number>();537538return this.viewModel.getSelections().map(range => this.viewModel!.viewCells.slice(range.start, range.end)).reduce((a, b) => {539b.forEach(cell => {540if (!cellsSet.has(cell.handle)) {541cellsSet.add(cell.handle);542a.push(cell);543}544});545546return a;547}, [] as ICellViewModel[]);548}549550hasModel(): this is IActiveNotebookEditorDelegate {551return !!this._notebookViewModel;552}553554showProgress(): void {555this._currentProgress = this.editorProgressService.show(true);556}557558hideProgress(): void {559if (this._currentProgress) {560this._currentProgress.done();561this._currentProgress = undefined;562}563}564565//#region Editor Core566567getBaseCellEditorOptions(language: string): IBaseCellEditorOptions {568const existingOptions = this._baseCellEditorOptions.get(language);569570if (existingOptions) {571return existingOptions;572} else {573const options = new BaseCellEditorOptions(this, this.notebookOptions, this.configurationService, language);574this._baseCellEditorOptions.set(language, options);575return options;576}577}578579private _updateForNotebookConfiguration() {580if (!this._overlayContainer) {581return;582}583584this._overlayContainer.classList.remove('cell-title-toolbar-left');585this._overlayContainer.classList.remove('cell-title-toolbar-right');586this._overlayContainer.classList.remove('cell-title-toolbar-hidden');587const cellToolbarLocation = this._notebookOptions.computeCellToolbarLocation(this.viewModel?.viewType);588this._overlayContainer.classList.add(`cell-title-toolbar-${cellToolbarLocation}`);589590const cellToolbarInteraction = this._notebookOptions.getDisplayOptions().cellToolbarInteraction;591let cellToolbarInteractionState = 'hover';592this._overlayContainer.classList.remove('cell-toolbar-hover');593this._overlayContainer.classList.remove('cell-toolbar-click');594595if (cellToolbarInteraction === 'hover' || cellToolbarInteraction === 'click') {596cellToolbarInteractionState = cellToolbarInteraction;597}598this._overlayContainer.classList.add(`cell-toolbar-${cellToolbarInteractionState}`);599600}601602private _generateFontInfo(): void {603const editorOptions = this.configurationService.getValue<IEditorOptions>('editor');604const targetWindow = DOM.getWindow(this.getDomNode());605this._fontInfo = FontMeasurements.readFontInfo(targetWindow, BareFontInfo.createFromRawSettings(editorOptions, PixelRatio.getInstance(targetWindow).value));606}607608private _createBody(parent: HTMLElement): void {609this._notebookTopToolbarContainer = document.createElement('div');610this._notebookTopToolbarContainer.classList.add('notebook-toolbar-container');611this._notebookTopToolbarContainer.style.display = 'none';612DOM.append(parent, this._notebookTopToolbarContainer);613614this._notebookStickyScrollContainer = document.createElement('div');615this._notebookStickyScrollContainer.classList.add('notebook-sticky-scroll-container');616DOM.append(parent, this._notebookStickyScrollContainer);617618this._body = document.createElement('div');619DOM.append(parent, this._body);620621this._body.classList.add('cell-list-container');622this._createLayoutStyles();623this._createCellList();624625this._notebookOverviewRulerContainer = document.createElement('div');626this._notebookOverviewRulerContainer.classList.add('notebook-overview-ruler-container');627this._list.scrollableElement.appendChild(this._notebookOverviewRulerContainer);628this._registerNotebookOverviewRuler();629630this._register(this.instantiationService.createInstance(NotebookHorizontalTracker, this, this._list.scrollableElement));631632this._overflowContainer = document.createElement('div');633this._overflowContainer.classList.add('notebook-overflow-widget-container', 'monaco-editor');634DOM.append(parent, this._overflowContainer);635}636637private _generateFontFamily() {638return this._fontInfo?.fontFamily ?? `"SF Mono", Monaco, Menlo, Consolas, "Ubuntu Mono", "Liberation Mono", "DejaVu Sans Mono", "Courier New", monospace`;639}640641private _createLayoutStyles(): void {642this._styleElement = domStylesheets.createStyleSheet(this._body);643const {644cellRightMargin,645cellTopMargin,646cellRunGutter,647cellBottomMargin,648codeCellLeftMargin,649markdownCellGutter,650markdownCellLeftMargin,651markdownCellBottomMargin,652markdownCellTopMargin,653collapsedIndicatorHeight,654focusIndicator,655insertToolbarPosition,656outputFontSize,657focusIndicatorLeftMargin,658focusIndicatorGap659} = this._notebookOptions.getLayoutConfiguration();660661const {662insertToolbarAlignment,663compactView,664fontSize665} = this._notebookOptions.getDisplayOptions();666667const getCellEditorContainerLeftMargin = this._notebookOptions.getCellEditorContainerLeftMargin();668669const { bottomToolbarGap, bottomToolbarHeight } = this._notebookOptions.computeBottomToolbarDimensions(this.viewModel?.viewType);670671const styleSheets: string[] = [];672if (!this._fontInfo) {673this._generateFontInfo();674}675676const fontFamily = this._generateFontFamily();677678styleSheets.push(`679.notebook-editor {680--notebook-cell-output-font-size: ${outputFontSize}px;681--notebook-cell-input-preview-font-size: ${fontSize}px;682--notebook-cell-input-preview-font-family: ${fontFamily};683}684`);685686if (compactView) {687styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .markdown-cell-row div.cell.code { margin-left: ${getCellEditorContainerLeftMargin}px; }`);688} else {689styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .markdown-cell-row div.cell.code { margin-left: ${codeCellLeftMargin}px; }`);690}691692// focus indicator693if (focusIndicator === 'border') {694styleSheets.push(`695.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-top:before,696.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-bottom:before,697.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row .cell-inner-container:before,698.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row .cell-inner-container:after {699content: "";700position: absolute;701width: 100%;702height: 1px;703}704705.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-left:before,706.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-right:before {707content: "";708position: absolute;709width: 1px;710height: 100%;711z-index: 10;712}713714/* top border */715.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-top:before {716border-top: 1px solid transparent;717}718719/* left border */720.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-left:before {721border-left: 1px solid transparent;722}723724/* bottom border */725.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-bottom:before {726border-bottom: 1px solid transparent;727}728729/* right border */730.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-right:before {731border-right: 1px solid transparent;732}733`);734735// left and right border margins736styleSheets.push(`737.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row.focused .cell-focus-indicator-left:before,738.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row.focused .cell-focus-indicator-right:before,739.monaco-workbench .notebookOverlay .monaco-list.selection-multiple .monaco-list-row.code-cell-row.selected .cell-focus-indicator-left:before,740.monaco-workbench .notebookOverlay .monaco-list.selection-multiple .monaco-list-row.code-cell-row.selected .cell-focus-indicator-right:before {741top: -${cellTopMargin}px; height: calc(100% + ${cellTopMargin + cellBottomMargin}px)742}`);743} else {744styleSheets.push(`745.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-left .codeOutput-focus-indicator {746border-left: 3px solid transparent;747border-radius: 4px;748width: 0px;749margin-left: ${focusIndicatorLeftMargin}px;750border-color: var(--vscode-notebook-inactiveFocusedCellBorder) !important;751}752753.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-left .codeOutput-focus-indicator-container,754.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-output-hover .cell-focus-indicator-left .codeOutput-focus-indicator-container,755.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .markdown-cell-hover .cell-focus-indicator-left .codeOutput-focus-indicator-container,756.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row:hover .cell-focus-indicator-left .codeOutput-focus-indicator-container {757display: block;758}759760.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-left .codeOutput-focus-indicator-container:hover .codeOutput-focus-indicator {761border-left: 5px solid transparent;762margin-left: ${focusIndicatorLeftMargin - 1}px;763}764`);765766styleSheets.push(`767.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-inner-container.cell-output-focus .cell-focus-indicator-left .codeOutput-focus-indicator,768.monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-inner-container .cell-focus-indicator-left .codeOutput-focus-indicator {769border-color: var(--vscode-notebook-focusedCellBorder) !important;770}771772.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-inner-container .cell-focus-indicator-left .output-focus-indicator {773margin-top: ${focusIndicatorGap}px;774}775`);776}777778// between cell insert toolbar779if (insertToolbarPosition === 'betweenCells' || insertToolbarPosition === 'both') {780styleSheets.push(`.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container { display: flex; }`);781styleSheets.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; }`);782} else {783styleSheets.push(`.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container { display: none; }`);784styleSheets.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; }`);785}786787if (insertToolbarAlignment === 'left') {788styleSheets.push(`789.monaco-workbench .notebookOverlay .cell-list-top-cell-toolbar-container .action-item:first-child,790.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 {791margin-right: 0px !important;792}`);793794styleSheets.push(`795.monaco-workbench .notebookOverlay .cell-list-top-cell-toolbar-container .monaco-toolbar .action-label,796.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 {797padding: 0px !important;798justify-content: center;799border-radius: 4px;800}`);801802styleSheets.push(`803.monaco-workbench .notebookOverlay .cell-list-top-cell-toolbar-container,804.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 {805align-items: flex-start;806justify-content: left;807margin: 0 16px 0 ${8 + codeCellLeftMargin}px;808}`);809810styleSheets.push(`811.monaco-workbench .notebookOverlay .cell-list-top-cell-toolbar-container,812.notebookOverlay .cell-bottom-toolbar-container .action-item {813border: 0px;814}`);815}816817styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .code-cell-row div.cell.code { margin-left: ${getCellEditorContainerLeftMargin}px; }`);818// Chat Edit, deleted Cell Overlay819styleSheets.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; }`);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 { margin-right: ${cellRightMargin}px; }`);822styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row div.cell { margin-right: ${cellRightMargin}px; }`);823styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row > .cell-inner-container { padding-top: ${cellTopMargin}px; }`);824styleSheets.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; }`);825styleSheets.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; }`);826styleSheets.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; }`);827styleSheets.push(`.notebookOverlay .output { margin: 0px ${cellRightMargin}px 0px ${getCellEditorContainerLeftMargin}px; }`);828styleSheets.push(`.notebookOverlay .output { width: calc(100% - ${getCellEditorContainerLeftMargin + cellRightMargin}px); }`);829830// comment831styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-comment-container { left: ${getCellEditorContainerLeftMargin}px; }`);832styleSheets.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); }`);833834// output collapse button835styleSheets.push(`.monaco-workbench .notebookOverlay .output .output-collapse-container .expandButton { left: -${cellRunGutter}px; }`);836styleSheets.push(`.monaco-workbench .notebookOverlay .output .output-collapse-container .expandButton {837position: absolute;838width: ${cellRunGutter}px;839padding: 6px 0px;840}`);841842// show more container843styleSheets.push(`.notebookOverlay .output-show-more-container { margin: 0px ${cellRightMargin}px 0px ${getCellEditorContainerLeftMargin}px; }`);844styleSheets.push(`.notebookOverlay .output-show-more-container { width: calc(100% - ${getCellEditorContainerLeftMargin + cellRightMargin}px); }`);845846styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row div.cell.markdown { padding-left: ${cellRunGutter}px; }`);847styleSheets.push(`.monaco-workbench .notebookOverlay > .cell-list-container .notebook-folding-indicator { left: ${(markdownCellGutter - 20) / 2 + markdownCellLeftMargin}px; }`);848styleSheets.push(`.notebookOverlay > .cell-list-container .notebook-folded-hint { left: ${markdownCellGutter + markdownCellLeftMargin + 8}px; }`);849styleSheets.push(`.notebookOverlay .monaco-list .monaco-list-row :not(.webview-backed-markdown-cell) .cell-focus-indicator-top { height: ${cellTopMargin}px; }`);850styleSheets.push(`.notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-side { bottom: ${bottomToolbarGap}px; }`);851styleSheets.push(`.notebookOverlay .monaco-list .monaco-list-row.code-cell-row .cell-focus-indicator-left { width: ${getCellEditorContainerLeftMargin}px; }`);852styleSheets.push(`.notebookOverlay .monaco-list .monaco-list-row.markdown-cell-row .cell-focus-indicator-left { width: ${codeCellLeftMargin}px; }`);853styleSheets.push(`.notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator.cell-focus-indicator-right { width: ${cellRightMargin}px; }`);854styleSheets.push(`.notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-bottom { height: ${cellBottomMargin}px; }`);855styleSheets.push(`.notebookOverlay .monaco-list .monaco-list-row .cell-shadow-container-bottom { top: ${cellBottomMargin}px; }`);856857styleSheets.push(`858.notebookOverlay .monaco-list.selection-multiple .monaco-list-row:has(+ .monaco-list-row.selected) .cell-focus-indicator-bottom {859height: ${bottomToolbarGap + cellBottomMargin}px;860}861`);862863styleSheets.push(`864.notebookOverlay .monaco-list .monaco-list-row.code-cell-row.nb-multiCellHighlight:has(+ .monaco-list-row.nb-multiCellHighlight) .cell-focus-indicator-bottom {865height: ${bottomToolbarGap + cellBottomMargin}px;866background-color: var(--vscode-notebook-symbolHighlightBackground) !important;867}868869.notebookOverlay .monaco-list .monaco-list-row.markdown-cell-row.nb-multiCellHighlight:has(+ .monaco-list-row.nb-multiCellHighlight) .cell-focus-indicator-bottom {870height: ${bottomToolbarGap + cellBottomMargin - 6}px;871background-color: var(--vscode-notebook-symbolHighlightBackground) !important;872}873`);874875876styleSheets.push(`877.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .input-collapse-container .cell-collapse-preview {878line-height: ${collapsedIndicatorHeight}px;879}880881.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 {882max-height: ${collapsedIndicatorHeight}px;883}884`);885886styleSheets.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 }`);887styleSheets.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 }`);888889// cell toolbar890styleSheets.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 {891right: ${cellRightMargin + 26}px;892}893.monaco-workbench .notebookOverlay.cell-title-toolbar-left > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-title-toolbar {894left: ${getCellEditorContainerLeftMargin + 16}px;895}896.monaco-workbench .notebookOverlay.cell-title-toolbar-hidden > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-title-toolbar {897display: none;898}`);899900// cell output innert container901styleSheets.push(`902.monaco-workbench .notebookOverlay .output > div.foreground.output-inner-container {903padding: ${OutputInnerContainerTopPadding}px 8px;904}905.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .output-collapse-container {906padding: ${OutputInnerContainerTopPadding}px 8px;907}908`);909910// chat911styleSheets.push(`912.monaco-workbench .notebookOverlay .cell-chat-part {913margin: 0 ${cellRightMargin}px 6px 4px;914}915`);916917this._styleElement.textContent = styleSheets.join('\n');918}919920private _createCellList(): void {921this._body.classList.add('cell-list-container');922this._dndController = this._register(new CellDragAndDropController(this, this._body));923const getScopedContextKeyService = (container: HTMLElement) => this._list.contextKeyService.createScoped(container);924this._editorPool = this._register(this.instantiationService.createInstance(NotebookCellEditorPool, this, getScopedContextKeyService));925const renderers = [926this.instantiationService.createInstance(CodeCellRenderer, this, this._renderedEditors, this._editorPool, this._dndController, getScopedContextKeyService),927this.instantiationService.createInstance(MarkupCellRenderer, this, this._dndController, this._renderedEditors, getScopedContextKeyService),928];929930renderers.forEach(renderer => {931this._register(renderer);932});933934this._listDelegate = this.instantiationService.createInstance(NotebookCellListDelegate, DOM.getWindow(this.getDomNode()));935this._register(this._listDelegate);936937const accessibilityProvider = this.instantiationService.createInstance(NotebookAccessibilityProvider, () => this.viewModel, this.isReplHistory);938this._register(accessibilityProvider);939940this._list = this.instantiationService.createInstance(941NotebookCellList,942'NotebookCellList',943this._body,944this._viewContext.notebookOptions,945this._listDelegate,946renderers,947this.scopedContextKeyService,948{949setRowLineHeight: false,950setRowHeight: false,951supportDynamicHeights: true,952horizontalScrolling: false,953keyboardSupport: false,954mouseSupport: true,955multipleSelectionSupport: true,956selectionNavigation: true,957typeNavigationEnabled: true,958paddingTop: 0,959paddingBottom: 0,960transformOptimization: false, //(isMacintosh && isNative) || getTitleBarStyle(this.configurationService, this.environmentService) === 'native',961initialSize: this._dimension,962styleController: (_suffix: string) => { return this._list; },963overrideStyles: {964listBackground: notebookEditorBackground,965listActiveSelectionBackground: notebookEditorBackground,966listActiveSelectionForeground: foreground,967listFocusAndSelectionBackground: notebookEditorBackground,968listFocusAndSelectionForeground: foreground,969listFocusBackground: notebookEditorBackground,970listFocusForeground: foreground,971listHoverForeground: foreground,972listHoverBackground: notebookEditorBackground,973listHoverOutline: focusBorder,974listFocusOutline: focusBorder,975listInactiveSelectionBackground: notebookEditorBackground,976listInactiveSelectionForeground: foreground,977listInactiveFocusBackground: notebookEditorBackground,978listInactiveFocusOutline: notebookEditorBackground,979},980accessibilityProvider981},982);983this._dndController.setList(this._list);984985// create Webview986987this._register(this._list);988this._listViewInfoAccessor = new ListViewInfoAccessor(this._list);989this._register(this._listViewInfoAccessor);990991this._register(combinedDisposable(...renderers));992993// top cell toolbar994this._listTopCellToolbar = this._register(this.instantiationService.createInstance(ListTopCellToolbar, this, this.notebookOptions));995996// transparent cover997this._webviewTransparentCover = DOM.append(this._list.rowsContainer, $('.webview-cover'));998this._webviewTransparentCover.style.display = 'none';9991000this._register(DOM.addStandardDisposableGenericMouseDownListener(this._overlayContainer, (e: StandardMouseEvent) => {1001if (e.target.classList.contains('slider') && this._webviewTransparentCover) {1002this._webviewTransparentCover.style.display = 'block';1003}1004}));10051006this._register(DOM.addStandardDisposableGenericMouseUpListener(this._overlayContainer, () => {1007if (this._webviewTransparentCover) {1008// no matter when1009this._webviewTransparentCover.style.display = 'none';1010}1011}));10121013this._register(this._list.onMouseDown(e => {1014if (e.element) {1015this._onMouseDown.fire({ event: e.browserEvent, target: e.element });1016}1017}));10181019this._register(this._list.onMouseUp(e => {1020if (e.element) {1021this._onMouseUp.fire({ event: e.browserEvent, target: e.element });1022}1023}));10241025this._register(this._list.onDidChangeFocus(_e => {1026this._onDidChangeActiveEditor.fire(this);1027this._onDidChangeActiveCell.fire();1028this._onDidChangeFocus.fire();1029this._cursorNavMode.set(false);1030}));10311032this._register(this._list.onContextMenu(e => {1033this.showListContextMenu(e);1034}));10351036this._register(this._list.onDidChangeVisibleRanges(() => {1037this._onDidChangeVisibleRanges.fire();1038}));10391040this._register(this._list.onDidScroll((e) => {1041if (e.scrollTop !== e.oldScrollTop) {1042this._onDidScroll.fire();1043this.clearActiveCellWidgets();1044}10451046if (e.scrollTop === e.oldScrollTop && e.scrollHeightChanged) {1047this._onDidChangeLayout.fire();1048}1049}));10501051this._focusTracker = this._register(DOM.trackFocus(this.getDomNode()));1052this._register(this._focusTracker.onDidBlur(() => {1053this._editorFocus.set(false);1054this.viewModel?.setEditorFocus(false);1055this._onDidBlurEmitter.fire();1056}));1057this._register(this._focusTracker.onDidFocus(() => {1058this._editorFocus.set(true);1059this.viewModel?.setEditorFocus(true);1060this._onDidFocusEmitter.fire();1061}));10621063this._registerNotebookActionsToolbar();1064this._registerNotebookStickyScroll();10651066this._register(this.configurationService.onDidChangeConfiguration(e => {1067if (e.affectsConfiguration(accessibilityProvider.verbositySettingId)) {1068this._list.ariaLabel = accessibilityProvider?.getWidgetAriaLabel();1069}1070}));1071}10721073private showListContextMenu(e: IListContextMenuEvent<CellViewModel>) {1074this.contextMenuService.showContextMenu({1075menuId: MenuId.NotebookCellTitle,1076menuActionOptions: {1077shouldForwardArgs: true1078},1079contextKeyService: this.scopedContextKeyService,1080getAnchor: () => e.anchor,1081getActionsContext: () => {1082return {1083from: 'cellContainer'1084};1085}1086});1087}10881089private _registerNotebookOverviewRuler() {1090this._notebookOverviewRuler = this._register(this.instantiationService.createInstance(NotebookOverviewRuler, this, this._notebookOverviewRulerContainer));1091}10921093private _registerNotebookActionsToolbar() {1094this._notebookTopToolbar = this._register(this.instantiationService.createInstance(NotebookEditorWorkbenchToolbar, this, this.scopedContextKeyService, this._notebookOptions, this._notebookTopToolbarContainer));1095this._register(this._notebookTopToolbar.onDidChangeVisibility(() => {1096if (this._dimension && this._isVisible) {1097this.layout(this._dimension);1098}1099}));1100}11011102private _registerNotebookStickyScroll() {1103this._notebookStickyScroll = this._register(this.instantiationService.createInstance(NotebookStickyScroll, this._notebookStickyScrollContainer, this, this._list, (sizeDelta) => {1104if (this.isDisposed) {1105return;1106}11071108if (this._dimension && this._isVisible) {1109if (sizeDelta > 0) { // delta > 0 ==> sticky is growing, cell list shrinking1110this.layout(this._dimension);1111this.setScrollTop(this.scrollTop + sizeDelta);1112} else if (sizeDelta < 0) { // delta < 0 ==> sticky is shrinking, cell list growing1113this.setScrollTop(this.scrollTop + sizeDelta);1114this.layout(this._dimension);1115}1116}11171118this._onDidScroll.fire();1119}));1120}11211122private _updateOutputRenderers() {1123if (!this.viewModel || !this._webview) {1124return;1125}11261127this._webview.updateOutputRenderers();1128this.viewModel.viewCells.forEach(cell => {1129cell.outputsViewModels.forEach(output => {1130if (output.pickedMimeType?.rendererId === RENDERER_NOT_AVAILABLE) {1131output.resetRenderer();1132}1133});1134});1135}11361137getDomNode() {1138return this._overlayContainer;1139}11401141getOverflowContainerDomNode() {1142return this._overflowContainer;1143}11441145getInnerWebview(): IWebviewElement | undefined {1146return this._webview?.webview;1147}11481149setEditorProgressService(editorProgressService: IEditorProgressService): void {1150this.editorProgressService = editorProgressService;1151}11521153setParentContextKeyService(parentContextKeyService: IContextKeyService): void {1154this.scopedContextKeyService.updateParent(parentContextKeyService);1155}11561157async setModel(textModel: NotebookTextModel, viewState: INotebookEditorViewState | undefined, perf?: NotebookPerfMarks, viewType?: string): Promise<void> {1158if (this.viewModel === undefined || !this.viewModel.equal(textModel)) {1159const oldBottomToolbarDimensions = this._notebookOptions.computeBottomToolbarDimensions(this.viewModel?.viewType);1160this._detachModel();1161await this._attachModel(textModel, viewType ?? textModel.viewType, viewState, perf);1162const newBottomToolbarDimensions = this._notebookOptions.computeBottomToolbarDimensions(this.viewModel?.viewType);11631164if (oldBottomToolbarDimensions.bottomToolbarGap !== newBottomToolbarDimensions.bottomToolbarGap1165|| oldBottomToolbarDimensions.bottomToolbarHeight !== newBottomToolbarDimensions.bottomToolbarHeight) {1166this._styleElement?.remove();1167this._createLayoutStyles();1168this._webview?.updateOptions({1169...this.notebookOptions.computeWebviewOptions(),1170fontFamily: this._generateFontFamily()1171});1172}1173type WorkbenchNotebookOpenClassification = {1174owner: 'rebornix';1175comment: 'Identify the notebook editor view type';1176scheme: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'File system provider scheme for the resource' };1177ext: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'File extension for the resource' };1178viewType: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'View type of the notebook editor' };1179isRepl: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the notebook editor is within a REPL editor' };1180};11811182type WorkbenchNotebookOpenEvent = {1183scheme: string;1184ext: string;1185viewType: string;1186isRepl: boolean;1187};11881189this.telemetryService.publicLog2<WorkbenchNotebookOpenEvent, WorkbenchNotebookOpenClassification>('notebook/editorOpened', {1190scheme: textModel.uri.scheme,1191ext: extname(textModel.uri),1192viewType: textModel.viewType,1193isRepl: this.isReplHistory1194});1195} else {1196this.restoreListViewState(viewState);1197}11981199this._restoreSelectedKernel(viewState);12001201// load preloads for matching kernel1202this._loadKernelPreloads();12031204// clear state1205this._dndController?.clearGlobalDragState();12061207this._localStore.add(this._list.onDidChangeFocus(() => {1208this.updateContextKeysOnFocusChange();1209}));12101211this.updateContextKeysOnFocusChange();1212// render markdown top down on idle1213this._backgroundMarkdownRendering();1214}12151216private _backgroundMarkdownRenderRunning = false;1217private _backgroundMarkdownRendering() {1218if (this._backgroundMarkdownRenderRunning) {1219return;1220}12211222this._backgroundMarkdownRenderRunning = true;1223DOM.runWhenWindowIdle(DOM.getWindow(this.getDomNode()), (deadline) => {1224this._backgroundMarkdownRenderingWithDeadline(deadline);1225});1226}12271228private _backgroundMarkdownRenderingWithDeadline(deadline: IdleDeadline) {1229const endTime = Date.now() + deadline.timeRemaining();12301231const execute = () => {1232try {1233this._backgroundMarkdownRenderRunning = true;1234if (this._isDisposed) {1235return;1236}12371238if (!this.viewModel) {1239return;1240}12411242const firstMarkupCell = this.viewModel.viewCells.find(cell => cell.cellKind === CellKind.Markup && !this._webview?.markupPreviewMapping.has(cell.id) && !this.cellIsHidden(cell)) as MarkupCellViewModel | undefined;1243if (!firstMarkupCell) {1244return;1245}12461247this.createMarkupPreview(firstMarkupCell);1248} finally {1249this._backgroundMarkdownRenderRunning = false;1250}12511252if (Date.now() < endTime) {1253setTimeout0(execute);1254} else {1255this._backgroundMarkdownRendering();1256}1257};12581259execute();1260}12611262private updateContextKeysOnFocusChange() {1263if (!this.viewModel) {1264return;1265}12661267const focused = this._list.getFocusedElements()[0];1268if (focused) {1269if (!this._cellContextKeyManager) {1270this._cellContextKeyManager = this._localStore.add(this.instantiationService.createInstance(CellContextKeyManager, this, focused as CellViewModel));1271}12721273this._cellContextKeyManager.updateForElement(focused as CellViewModel);1274}1275}12761277async setOptions(options: INotebookEditorOptions | undefined) {1278if (options?.isReadOnly !== undefined) {1279this._readOnly = options?.isReadOnly;1280}12811282if (!this.viewModel) {1283return;1284}12851286this.viewModel.updateOptions({ isReadOnly: this._readOnly });1287this.notebookOptions.updateOptions(this._readOnly);12881289// reveal cell if editor options tell to do so1290const cellOptions = options?.cellOptions ?? this._parseIndexedCellOptions(options);1291if (cellOptions) {1292const cell = this.viewModel.viewCells.find(cell => cell.uri.toString() === cellOptions.resource.toString());1293if (cell) {1294this.focusElement(cell);1295const selection = cellOptions.options?.selection;1296if (selection) {1297cell.updateEditState(CellEditState.Editing, 'setOptions');1298cell.focusMode = CellFocusMode.Editor;1299await this.revealRangeInCenterIfOutsideViewportAsync(cell, new Range(selection.startLineNumber, selection.startColumn, selection.endLineNumber || selection.startLineNumber, selection.endColumn || selection.startColumn));1300} else {1301this._list.revealCell(cell, options?.cellRevealType ?? CellRevealType.CenterIfOutsideViewport);1302}13031304const editor = this._renderedEditors.get(cell)!;1305if (editor) {1306if (cellOptions.options?.selection) {1307const { selection } = cellOptions.options;1308const editorSelection = new Range(selection.startLineNumber, selection.startColumn, selection.endLineNumber || selection.startLineNumber, selection.endColumn || selection.startColumn);1309editor.setSelection(editorSelection);1310editor.revealPositionInCenterIfOutsideViewport({1311lineNumber: selection.startLineNumber,1312column: selection.startColumn1313});1314await this.revealRangeInCenterIfOutsideViewportAsync(cell, editorSelection);1315}1316if (!cellOptions.options?.preserveFocus) {1317editor.focus();1318}1319}1320}1321}13221323// select cells if options tell to do so1324// todo@rebornix https://github.com/microsoft/vscode/issues/118108 support selections not just focus1325// todo@rebornix support multipe selections1326if (options?.cellSelections) {1327const focusCellIndex = options.cellSelections[0].start;1328const focusedCell = this.viewModel.cellAt(focusCellIndex);1329if (focusedCell) {1330this.viewModel.updateSelectionsState({1331kind: SelectionStateType.Index,1332focus: { start: focusCellIndex, end: focusCellIndex + 1 },1333selections: options.cellSelections1334});1335this.revealInCenterIfOutsideViewport(focusedCell);1336}1337}13381339this._updateForOptions();1340this._onDidChangeOptions.fire();1341}13421343private _parseIndexedCellOptions(options: INotebookEditorOptions | undefined) {1344if (options?.indexedCellOptions) {1345// convert index based selections1346const cell = this.cellAt(options.indexedCellOptions.index);1347if (cell) {1348return {1349resource: cell.uri,1350options: {1351selection: options.indexedCellOptions.selection,1352preserveFocus: false1353}1354};1355}1356}13571358return undefined;1359}13601361private _detachModel() {1362this._localStore.clear();1363dispose(this._localCellStateListeners);1364this._list.detachViewModel();1365this.viewModel?.dispose();1366// avoid event1367this.viewModel = undefined;1368this._webview?.dispose();1369this._webview?.element.remove();1370this._webview = null;1371this._list.clear();1372}137313741375private _updateForOptions(): void {1376if (!this.viewModel) {1377return;1378}13791380this._editorEditable.set(!this.viewModel.options.isReadOnly);1381this._overflowContainer.classList.toggle('notebook-editor-editable', !this.viewModel.options.isReadOnly);1382this.getDomNode().classList.toggle('notebook-editor-editable', !this.viewModel.options.isReadOnly);1383}13841385private async _resolveWebview(): Promise<BackLayerWebView<ICommonCellInfo> | null> {1386if (!this.textModel) {1387return null;1388}13891390if (this._webviewResolvePromise) {1391return this._webviewResolvePromise;1392}13931394if (!this._webview) {1395this._ensureWebview(this.getId(), this.textModel.viewType, this.textModel.uri);1396}13971398this._webviewResolvePromise = (async () => {1399if (!this._webview) {1400throw new Error('Notebook output webview object is not created successfully.');1401}14021403await this._webview.createWebview(this.creationOptions.codeWindow ?? mainWindow);1404if (!this._webview.webview) {1405throw new Error('Notebook output webview element was not created successfully.');1406}14071408this._localStore.add(this._webview.webview.onDidBlur(() => {1409this._outputFocus.set(false);1410this._webviewFocused = false;14111412this.updateEditorFocus();1413this.updateCellFocusMode();1414}));14151416this._localStore.add(this._webview.webview.onDidFocus(() => {1417this._outputFocus.set(true);1418this.updateEditorFocus();1419this._webviewFocused = true;1420}));14211422this._localStore.add(this._webview.onMessage(e => {1423this._onDidReceiveMessage.fire(e);1424}));14251426return this._webview;1427})();14281429return this._webviewResolvePromise;1430}14311432private _ensureWebview(id: string, viewType: string, resource: URI) {1433if (this._webview) {1434return;1435}14361437const that = this;14381439this._webview = this.instantiationService.createInstance(BackLayerWebView, {1440get creationOptions() { return that.creationOptions; },1441setScrollTop(scrollTop: number) { that._list.scrollTop = scrollTop; },1442triggerScroll(event: IMouseWheelEvent) { that._list.triggerScrollFromMouseWheelEvent(event); },1443getCellByInfo: that.getCellByInfo.bind(that),1444getCellById: that._getCellById.bind(that),1445toggleNotebookCellSelection: that._toggleNotebookCellSelection.bind(that),1446focusNotebookCell: that.focusNotebookCell.bind(that),1447focusNextNotebookCell: that.focusNextNotebookCell.bind(that),1448updateOutputHeight: that._updateOutputHeight.bind(that),1449scheduleOutputHeightAck: that._scheduleOutputHeightAck.bind(that),1450updateMarkupCellHeight: that._updateMarkupCellHeight.bind(that),1451setMarkupCellEditState: that._setMarkupCellEditState.bind(that),1452didStartDragMarkupCell: that._didStartDragMarkupCell.bind(that),1453didDragMarkupCell: that._didDragMarkupCell.bind(that),1454didDropMarkupCell: that._didDropMarkupCell.bind(that),1455didEndDragMarkupCell: that._didEndDragMarkupCell.bind(that),1456didResizeOutput: that._didResizeOutput.bind(that),1457updatePerformanceMetadata: that._updatePerformanceMetadata.bind(that),1458didFocusOutputInputChange: that._didFocusOutputInputChange.bind(that),1459}, id, viewType, resource, {1460...this._notebookOptions.computeWebviewOptions(),1461fontFamily: this._generateFontFamily()1462}, this.notebookRendererMessaging.getScoped(this._uuid));14631464this._webview.element.style.width = '100%';14651466// attach the webview container to the DOM tree first1467this._list.attachWebview(this._webview.element);1468}14691470private async _attachModel(textModel: NotebookTextModel, viewType: string, viewState: INotebookEditorViewState | undefined, perf?: NotebookPerfMarks) {1471this._ensureWebview(this.getId(), textModel.viewType, textModel.uri);14721473this.viewModel = this.instantiationService.createInstance(NotebookViewModel, viewType, textModel, this._viewContext, this.getLayoutInfo(), { isReadOnly: this._readOnly });1474this._viewContext.eventDispatcher.emit([new NotebookLayoutChangedEvent({ width: true, fontInfo: true }, this.getLayoutInfo())]);1475this.notebookOptions.updateOptions(this._readOnly);14761477this._updateForOptions();1478this._updateForNotebookConfiguration();14791480// restore view states, including contributions14811482{1483// restore view state1484this.viewModel.restoreEditorViewState(viewState);14851486// contribution state restore14871488const contributionsState = viewState?.contributionsState || {};1489for (const [id, contribution] of this._contributions) {1490if (typeof contribution.restoreViewState === 'function') {1491contribution.restoreViewState(contributionsState[id]);1492}1493}1494}14951496this._localStore.add(this.viewModel.onDidChangeViewCells(e => {1497this._onDidChangeViewCells.fire(e);1498}));14991500this._localStore.add(this.viewModel.onDidChangeSelection(() => {1501this._onDidChangeSelection.fire();1502this.updateSelectedMarkdownPreviews();1503}));15041505this._localStore.add(this._list.onWillScroll(e => {1506if (this._webview?.isResolved()) {1507this._webviewTransparentCover!.style.transform = `translateY(${e.scrollTop})`;1508}1509}));15101511let hasPendingChangeContentHeight = false;1512this._localStore.add(this._list.onDidChangeContentHeight(() => {1513if (hasPendingChangeContentHeight) {1514return;1515}1516hasPendingChangeContentHeight = true;15171518this._localStore.add(DOM.scheduleAtNextAnimationFrame(DOM.getWindow(this.getDomNode()), () => {1519hasPendingChangeContentHeight = false;1520this._updateScrollHeight();1521}, 100));1522}));15231524this._localStore.add(this._list.onDidRemoveOutputs(outputs => {1525outputs.forEach(output => this.removeInset(output));1526}));1527this._localStore.add(this._list.onDidHideOutputs(outputs => {1528outputs.forEach(output => this.hideInset(output));1529}));1530this._localStore.add(this._list.onDidRemoveCellsFromView(cells => {1531const hiddenCells: MarkupCellViewModel[] = [];1532const deletedCells: MarkupCellViewModel[] = [];15331534for (const cell of cells) {1535if (cell.cellKind === CellKind.Markup) {1536const mdCell = cell as MarkupCellViewModel;1537if (this.viewModel?.viewCells.find(cell => cell.handle === mdCell.handle)) {1538// Cell has been folded but is still in model1539hiddenCells.push(mdCell);1540} else {1541// Cell was deleted1542deletedCells.push(mdCell);1543}1544}1545}15461547this.hideMarkupPreviews(hiddenCells);1548this.deleteMarkupPreviews(deletedCells);1549}));15501551// init rendering1552await this._warmupWithMarkdownRenderer(this.viewModel, viewState, perf);15531554perf?.mark('customMarkdownLoaded');15551556// model attached1557this._localCellStateListeners = this.viewModel.viewCells.map(cell => this._bindCellListener(cell));1558this._lastCellWithEditorFocus = this.viewModel.viewCells.find(viewCell => this.getActiveCell() === viewCell && viewCell.focusMode === CellFocusMode.Editor) ?? null;15591560this._localStore.add(this.viewModel.onDidChangeViewCells((e) => {1561if (this._isDisposed) {1562return;1563}15641565// update cell listener1566[...e.splices].reverse().forEach(splice => {1567const [start, deleted, newCells] = splice;1568const deletedCells = this._localCellStateListeners.splice(start, deleted, ...newCells.map(cell => this._bindCellListener(cell)));15691570dispose(deletedCells);1571});15721573if (e.splices.some(s => s[2].some(cell => cell.cellKind === CellKind.Markup))) {1574this._backgroundMarkdownRendering();1575}1576}));15771578if (this._dimension) {1579this._list.layout(this.getBodyHeight(this._dimension.height), this._dimension.width);1580} else {1581this._list.layout();1582}15831584this._dndController?.clearGlobalDragState();15851586// restore list state at last, it must be after list layout1587this.restoreListViewState(viewState);1588}15891590private _bindCellListener(cell: ICellViewModel) {1591const store = new DisposableStore();15921593store.add(cell.onDidChangeLayout(e => {1594// e.totalHeight will be false it's not changed1595if (e.totalHeight || e.outerWidth) {1596this.layoutNotebookCell(cell, cell.layoutInfo.totalHeight, e.context);1597}1598}));15991600if (cell.cellKind === CellKind.Code) {1601store.add((cell as CodeCellViewModel).onDidRemoveOutputs((outputs) => {1602outputs.forEach(output => this.removeInset(output));1603}));1604}16051606store.add((cell as CellViewModel).onDidChangeState(e => {1607if (e.inputCollapsedChanged && cell.isInputCollapsed && cell.cellKind === CellKind.Markup) {1608this.hideMarkupPreviews([(cell as MarkupCellViewModel)]);1609}16101611if (e.outputCollapsedChanged && cell.isOutputCollapsed && cell.cellKind === CellKind.Code) {1612cell.outputsViewModels.forEach(output => this.hideInset(output));1613}16141615if (e.focusModeChanged) {1616this._validateCellFocusMode(cell);1617}1618}));16191620store.add(cell.onCellDecorationsChanged(e => {1621e.added.forEach(options => {1622if (options.className) {1623this.deltaCellContainerClassNames(cell.id, [options.className], [], cell.cellKind);1624}16251626if (options.outputClassName) {1627this.deltaCellContainerClassNames(cell.id, [options.outputClassName], [], cell.cellKind);1628}1629});16301631e.removed.forEach(options => {1632if (options.className) {1633this.deltaCellContainerClassNames(cell.id, [], [options.className], cell.cellKind);1634}16351636if (options.outputClassName) {1637this.deltaCellContainerClassNames(cell.id, [], [options.outputClassName], cell.cellKind);1638}1639});1640}));16411642return store;1643}164416451646private _lastCellWithEditorFocus: ICellViewModel | null = null;1647private _validateCellFocusMode(cell: ICellViewModel) {1648if (cell.focusMode !== CellFocusMode.Editor) {1649return;1650}16511652if (this._lastCellWithEditorFocus && this._lastCellWithEditorFocus !== cell) {1653this._lastCellWithEditorFocus.focusMode = CellFocusMode.Container;1654}16551656this._lastCellWithEditorFocus = cell;1657}16581659private async _warmupWithMarkdownRenderer(viewModel: NotebookViewModel, viewState: INotebookEditorViewState | undefined, perf?: NotebookPerfMarks) {16601661this.logService.debug('NotebookEditorWidget', 'warmup ' + this.viewModel?.uri.toString());1662await this._resolveWebview();1663perf?.mark('webviewCommLoaded');16641665this.logService.debug('NotebookEditorWidget', 'warmup - webview resolved');16661667// 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 yet1668this._webview!.element.style.visibility = 'hidden';1669// warm up can take around 200ms to load markdown libraries, etc.1670await this._warmupViewportMarkdownCells(viewModel, viewState);1671this.logService.debug('NotebookEditorWidget', 'warmup - viewport warmed up');16721673// todo@rebornix @mjbvz, is this too complicated?16741675/* now the webview is ready, and requests to render markdown are fast enough1676* we can start rendering the list view1677* render1678* - markdown cell -> request to webview to (10ms, basically just latency between UI and iframe)1679* - code cell -> render in place1680*/1681this._list.layout(0, 0);1682this._list.attachViewModel(viewModel);16831684// now the list widget has a correct contentHeight/scrollHeight1685// setting scrollTop will work properly1686// after setting scroll top, the list view will update `top` of the scrollable element, e.g. `top: -584px`1687this._list.scrollTop = viewState?.scrollPosition?.top ?? 0;1688this._debug('finish initial viewport warmup and view state restore.');1689this._webview!.element.style.visibility = 'visible';1690this.logService.debug('NotebookEditorWidget', 'warmup - list view model attached, set to visible');1691this._onDidAttachViewModel.fire();1692}16931694private async _warmupViewportMarkdownCells(viewModel: NotebookViewModel, viewState: INotebookEditorViewState | undefined) {1695if (viewState && viewState.cellTotalHeights) {1696const totalHeightCache = viewState.cellTotalHeights;1697const scrollTop = viewState.scrollPosition?.top ?? 0;1698const scrollBottom = scrollTop + Math.max(this._dimension?.height ?? 0, 1080);16991700let offset = 0;1701const requests: [ICellViewModel, number][] = [];17021703for (let i = 0; i < viewModel.length; i++) {1704const cell = viewModel.cellAt(i)!;1705const cellHeight = totalHeightCache[i] ?? 0;17061707if (offset + cellHeight < scrollTop) {1708offset += cellHeight;1709continue;1710}17111712if (cell.cellKind === CellKind.Markup) {1713requests.push([cell, offset]);1714}17151716offset += cellHeight;17171718if (offset > scrollBottom) {1719break;1720}1721}17221723await this._webview!.initializeMarkup(requests.map(([model, offset]) => this.createMarkupCellInitialization(model, offset)));1724} else {1725const initRequests = viewModel.viewCells1726.filter(cell => cell.cellKind === CellKind.Markup)1727.slice(0, 5)1728.map(cell => this.createMarkupCellInitialization(cell, -10000));17291730await this._webview!.initializeMarkup(initRequests);17311732// no cached view state so we are rendering the first viewport1733// after above async call, we already get init height for markdown cells, we can update their offset1734let offset = 0;1735const offsetUpdateRequests: { id: string; top: number }[] = [];1736const scrollBottom = Math.max(this._dimension?.height ?? 0, 1080);1737for (const cell of viewModel.viewCells) {1738if (cell.cellKind === CellKind.Markup) {1739offsetUpdateRequests.push({ id: cell.id, top: offset });1740}17411742offset += cell.getHeight(this.getLayoutInfo().fontInfo.lineHeight);17431744if (offset > scrollBottom) {1745break;1746}1747}17481749this._webview?.updateScrollTops([], offsetUpdateRequests);1750}1751}17521753private createMarkupCellInitialization(model: ICellViewModel, offset: number): IMarkupCellInitialization {1754return ({1755mime: model.mime,1756cellId: model.id,1757cellHandle: model.handle,1758content: model.getText(),1759offset: offset,1760visible: false,1761metadata: model.metadata,1762});1763}17641765restoreListViewState(viewState: INotebookEditorViewState | undefined): void {1766if (!this.viewModel) {1767return;1768}17691770if (viewState?.scrollPosition !== undefined) {1771this._list.scrollTop = viewState.scrollPosition.top;1772this._list.scrollLeft = viewState.scrollPosition.left;1773} else {1774this._list.scrollTop = 0;1775this._list.scrollLeft = 0;1776}17771778const focusIdx = typeof viewState?.focus === 'number' ? viewState.focus : 0;1779if (focusIdx < this.viewModel.length) {1780const element = this.viewModel.cellAt(focusIdx);1781if (element) {1782this.viewModel?.updateSelectionsState({1783kind: SelectionStateType.Handle,1784primary: element.handle,1785selections: [element.handle]1786});1787}1788} else if (this._list.length > 0) {1789this.viewModel.updateSelectionsState({1790kind: SelectionStateType.Index,1791focus: { start: 0, end: 1 },1792selections: [{ start: 0, end: 1 }]1793});1794}17951796if (viewState?.editorFocused) {1797const cell = this.viewModel.cellAt(focusIdx);1798if (cell) {1799cell.focusMode = CellFocusMode.Editor;1800}1801}1802}18031804private _restoreSelectedKernel(viewState: INotebookEditorViewState | undefined): void {1805if (viewState?.selectedKernelId && this.textModel) {1806const matching = this.notebookKernelService.getMatchingKernel(this.textModel);1807const kernel = matching.all.find(k => k.id === viewState.selectedKernelId);1808// Selected kernel may have already been picked prior to the view state loading1809// If so, don't overwrite it with the saved kernel.1810if (kernel && !matching.selected) {1811this.notebookKernelService.selectKernelForNotebook(kernel, this.textModel);1812}1813}1814}18151816getEditorViewState(): INotebookEditorViewState {1817const state = this.viewModel?.getEditorViewState();1818if (!state) {1819return {1820editingCells: {},1821cellLineNumberStates: {},1822editorViewStates: {},1823collapsedInputCells: {},1824collapsedOutputCells: {},1825};1826}18271828if (this._list) {1829state.scrollPosition = { left: this._list.scrollLeft, top: this._list.scrollTop };1830const cellHeights: { [key: number]: number } = {};1831for (let i = 0; i < this.viewModel!.length; i++) {1832const elm = this.viewModel!.cellAt(i) as CellViewModel;1833cellHeights[i] = elm.layoutInfo.totalHeight;1834}18351836state.cellTotalHeights = cellHeights;18371838if (this.viewModel) {1839const focusRange = this.viewModel.getFocus();1840const element = this.viewModel.cellAt(focusRange.start);1841if (element) {1842const itemDOM = this._list.domElementOfElement(element);1843const editorFocused = element.getEditState() === CellEditState.Editing && !!(itemDOM && itemDOM.ownerDocument.activeElement && itemDOM.contains(itemDOM.ownerDocument.activeElement));18441845state.editorFocused = editorFocused;1846state.focus = focusRange.start;1847}1848}1849}18501851// Save contribution view states1852const contributionsState: { [key: string]: unknown } = {};1853for (const [id, contribution] of this._contributions) {1854if (typeof contribution.saveViewState === 'function') {1855contributionsState[id] = contribution.saveViewState();1856}1857}1858state.contributionsState = contributionsState;1859if (this.textModel?.uri.scheme === Schemas.untitled) {1860state.selectedKernelId = this.activeKernel?.id;1861}18621863return state;1864}18651866private _allowScrollBeyondLastLine() {1867return this._scrollBeyondLastLine && !this.isReplHistory;1868}18691870private getBodyHeight(dimensionHeight: number) {1871return Math.max(dimensionHeight - (this._notebookTopToolbar?.useGlobalToolbar ? /** Toolbar height */ 26 : 0), 0);1872}18731874layout(dimension: DOM.Dimension, shadowElement?: HTMLElement, position?: DOM.IDomPosition): void {1875if (!shadowElement && this._shadowElementViewInfo === null) {1876this._dimension = dimension;1877this._position = position;1878return;1879}18801881if (dimension.width <= 0 || dimension.height <= 0) {1882this.onWillHide();1883return;1884}18851886const whenContainerStylesLoaded = this.layoutService.whenContainerStylesLoaded(DOM.getWindow(this.getDomNode()));1887if (whenContainerStylesLoaded) {1888// In floating windows, we need to ensure that the1889// container is ready for us to compute certain1890// layout related properties.1891whenContainerStylesLoaded.then(() => this.layoutNotebook(dimension, shadowElement, position));1892} else {1893this.layoutNotebook(dimension, shadowElement, position);1894}18951896}18971898private layoutNotebook(dimension: DOM.Dimension, shadowElement?: HTMLElement, position?: DOM.IDomPosition) {1899if (shadowElement) {1900this.updateShadowElement(shadowElement, dimension, position);1901}19021903if (this._shadowElementViewInfo && this._shadowElementViewInfo.width <= 0 && this._shadowElementViewInfo.height <= 0) {1904this.onWillHide();1905return;1906}19071908this._dimension = dimension;1909this._position = position;1910const newBodyHeight = this.getBodyHeight(dimension.height) - this.getLayoutInfo().stickyHeight;1911DOM.size(this._body, dimension.width, newBodyHeight);19121913const newCellListHeight = newBodyHeight;1914if (this._list.getRenderHeight() < newCellListHeight) {1915// 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)1916this._list.updateOptions({ paddingBottom: this._allowScrollBeyondLastLine() ? Math.max(0, (newCellListHeight - 50)) : 0, paddingTop: 0 });1917this._list.layout(newCellListHeight, dimension.width);1918} else {1919// 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.1920this._list.layout(newCellListHeight, dimension.width);1921this._list.updateOptions({ paddingBottom: this._allowScrollBeyondLastLine() ? Math.max(0, (newCellListHeight - 50)) : 0, paddingTop: 0 });1922}19231924this._overlayContainer.inert = false;1925this._overlayContainer.style.visibility = 'visible';1926this._overlayContainer.style.display = 'block';1927this._overlayContainer.style.position = 'absolute';1928this._overlayContainer.style.overflow = 'hidden';19291930this.layoutContainerOverShadowElement(dimension, position);19311932if (this._webviewTransparentCover) {1933this._webviewTransparentCover.style.height = `${dimension.height}px`;1934this._webviewTransparentCover.style.width = `${dimension.width}px`;1935}19361937this._notebookTopToolbar.layout(this._dimension);1938this._notebookOverviewRuler.layout();19391940this._viewContext?.eventDispatcher.emit([new NotebookLayoutChangedEvent({ width: true, fontInfo: true }, this.getLayoutInfo())]);1941}19421943private updateShadowElement(shadowElement: HTMLElement, dimension?: IDimension, position?: DOM.IDomPosition) {1944this._shadowElement = shadowElement;1945if (dimension && position) {1946this._shadowElementViewInfo = {1947height: dimension.height,1948width: dimension.width,1949top: position.top,1950left: position.left,1951};1952} else {1953// We have to recompute position and size ourselves (which is slow)1954const containerRect = shadowElement.getBoundingClientRect();1955this._shadowElementViewInfo = {1956height: containerRect.height,1957width: containerRect.width,1958top: containerRect.top,1959left: containerRect.left1960};1961}1962}19631964private layoutContainerOverShadowElement(dimension?: DOM.Dimension, position?: DOM.IDomPosition): void {1965if (dimension && position) {1966this._overlayContainer.style.top = `${position.top}px`;1967this._overlayContainer.style.left = `${position.left}px`;1968this._overlayContainer.style.width = `${dimension.width}px`;1969this._overlayContainer.style.height = `${dimension.height}px`;1970return;1971}19721973if (!this._shadowElementViewInfo) {1974return;1975}19761977const elementContainerRect = this._overlayContainer.parentElement?.getBoundingClientRect();1978this._overlayContainer.style.top = `${this._shadowElementViewInfo.top - (elementContainerRect?.top || 0)}px`;1979this._overlayContainer.style.left = `${this._shadowElementViewInfo.left - (elementContainerRect?.left || 0)}px`;1980this._overlayContainer.style.width = `${dimension ? dimension.width : this._shadowElementViewInfo.width}px`;1981this._overlayContainer.style.height = `${dimension ? dimension.height : this._shadowElementViewInfo.height}px`;1982}19831984//#endregion19851986//#region Focus tracker1987focus() {1988this._isVisible = true;1989this._editorFocus.set(true);19901991if (this._webviewFocused) {1992this._webview?.focusWebview();1993} else {1994if (this.viewModel) {1995const focusRange = this.viewModel.getFocus();1996const element = this.viewModel.cellAt(focusRange.start);19971998// The notebook editor doesn't have focus yet1999if (!this.hasEditorFocus()) {2000this.focusContainer();2001// trigger editor to update as FocusTracker might not emit focus change event2002this.updateEditorFocus();2003}20042005if (element && element.focusMode === CellFocusMode.Editor) {2006element.updateEditState(CellEditState.Editing, 'editorWidget.focus');2007element.focusMode = CellFocusMode.Editor;2008this.focusEditor(element);2009return;2010}2011}20122013this._list.domFocus();2014}20152016if (this._currentProgress) {2017// The editor forces progress to hide when switching editors. So if progress should be visible, force it to show when the editor is focused.2018this.showProgress();2019}2020}20212022onShow() {2023this._isVisible = true;2024}20252026private focusEditor(activeElement: CellViewModel): void {2027for (const [element, editor] of this._renderedEditors.entries()) {2028if (element === activeElement) {2029editor.focus();2030return;2031}2032}2033}20342035focusContainer(clearSelection: boolean = false) {2036if (this._webviewFocused) {2037this._webview?.focusWebview();2038} else {2039this._list.focusContainer(clearSelection);2040}2041}20422043selectOutputContent(cell: ICellViewModel) {2044this._webview?.selectOutputContents(cell);2045}20462047selectInputContents(cell: ICellViewModel) {2048this._webview?.selectInputContents(cell);2049}20502051onWillHide() {2052this._isVisible = false;2053this._editorFocus.set(false);2054this._overlayContainer.inert = true;2055this._overlayContainer.style.visibility = 'hidden';2056this._overlayContainer.style.left = '-50000px';2057this._notebookTopToolbarContainer.style.display = 'none';2058this.clearActiveCellWidgets();2059}20602061private clearActiveCellWidgets() {2062this._renderedEditors.forEach((editor, cell) => {2063if (this.getActiveCell() === cell && editor) {2064SuggestController.get(editor)?.cancelSuggestWidget();2065DropIntoEditorController.get(editor)?.clearWidgets();2066CopyPasteController.get(editor)?.clearWidgets();2067}2068});20692070this._renderedEditors.forEach((editor, cell) => {2071const controller = InlineCompletionsController.get(editor);2072if (controller?.model.get()?.inlineEditState.get()) {2073editor.render(true);2074}2075});2076}20772078private editorHasDomFocus(): boolean {2079return DOM.isAncestorOfActiveElement(this.getDomNode());2080}20812082updateEditorFocus() {2083// Note - focus going to the webview will fire 'blur', but the webview element will be2084// a descendent of the notebook editor root.2085this._focusTracker.refreshState();2086const focused = this.editorHasDomFocus();2087this._editorFocus.set(focused);2088this.viewModel?.setEditorFocus(focused);2089}20902091updateCellFocusMode() {2092const activeCell = this.getActiveCell();20932094if (activeCell?.focusMode === CellFocusMode.Output && !this._webviewFocused) {2095// output previously has focus, but now it's blurred.2096activeCell.focusMode = CellFocusMode.Container;2097}2098}20992100hasEditorFocus() {2101// _editorFocus is driven by the FocusTracker, which is only guaranteed to _eventually_ fire blur.2102// If we need to know whether we have focus at this instant, we need to check the DOM manually.2103this.updateEditorFocus();2104return this.editorHasDomFocus();2105}21062107hasWebviewFocus() {2108return this._webviewFocused;2109}21102111hasOutputTextSelection() {2112if (!this.hasEditorFocus()) {2113return false;2114}21152116const windowSelection = DOM.getWindow(this.getDomNode()).getSelection();2117if (windowSelection?.rangeCount !== 1) {2118return false;2119}21202121const activeSelection = windowSelection.getRangeAt(0);2122if (activeSelection.startContainer === activeSelection.endContainer && activeSelection.endOffset - activeSelection.startOffset === 0) {2123return false;2124}21252126let container: any = activeSelection.commonAncestorContainer;21272128if (!this._body.contains(container)) {2129return false;2130}21312132while (container2133&&2134container !== this._body) {2135if ((container as HTMLElement).classList && (container as HTMLElement).classList.contains('output')) {2136return true;2137}21382139container = container.parentNode;2140}21412142return false;2143}21442145_didFocusOutputInputChange(hasFocus: boolean) {2146this._outputInputFocus.set(hasFocus);2147}21482149//#endregion21502151//#region Editor Features21522153focusElement(cell: ICellViewModel) {2154this.viewModel?.updateSelectionsState({2155kind: SelectionStateType.Handle,2156primary: cell.handle,2157selections: [cell.handle]2158});2159}21602161get scrollTop() {2162return this._list.scrollTop;2163}21642165get scrollBottom() {2166return this._list.scrollTop + this._list.getRenderHeight();2167}21682169getAbsoluteTopOfElement(cell: ICellViewModel) {2170return this._list.getCellViewScrollTop(cell);2171}21722173getHeightOfElement(cell: ICellViewModel) {2174return this._list.elementHeight(cell);2175}21762177scrollToBottom() {2178this._list.scrollToBottom();2179}21802181setScrollTop(scrollTop: number): void {2182this._list.scrollTop = scrollTop;2183}21842185revealCellRangeInView(range: ICellRange) {2186return this._list.revealCells(range);2187}21882189revealInView(cell: ICellViewModel) {2190return this._list.revealCell(cell, CellRevealType.Default);2191}21922193revealInViewAtTop(cell: ICellViewModel) {2194this._list.revealCell(cell, CellRevealType.Top);2195}21962197revealInCenter(cell: ICellViewModel) {2198this._list.revealCell(cell, CellRevealType.Center);2199}22002201async revealInCenterIfOutsideViewport(cell: ICellViewModel) {2202await this._list.revealCell(cell, CellRevealType.CenterIfOutsideViewport);2203}22042205async revealFirstLineIfOutsideViewport(cell: ICellViewModel) {2206await this._list.revealCell(cell, CellRevealType.FirstLineIfOutsideViewport);2207}22082209async revealLineInViewAsync(cell: ICellViewModel, line: number): Promise<void> {2210return this._list.revealRangeInCell(cell, new Range(line, 1, line, 1), CellRevealRangeType.Default);2211}22122213async revealLineInCenterAsync(cell: ICellViewModel, line: number): Promise<void> {2214return this._list.revealRangeInCell(cell, new Range(line, 1, line, 1), CellRevealRangeType.Center);2215}22162217async revealLineInCenterIfOutsideViewportAsync(cell: ICellViewModel, line: number): Promise<void> {2218return this._list.revealRangeInCell(cell, new Range(line, 1, line, 1), CellRevealRangeType.CenterIfOutsideViewport);2219}22202221async revealRangeInViewAsync(cell: ICellViewModel, range: Selection | Range): Promise<void> {2222return this._list.revealRangeInCell(cell, range, CellRevealRangeType.Default);2223}22242225async revealRangeInCenterAsync(cell: ICellViewModel, range: Selection | Range): Promise<void> {2226return this._list.revealRangeInCell(cell, range, CellRevealRangeType.Center);2227}22282229async revealRangeInCenterIfOutsideViewportAsync(cell: ICellViewModel, range: Selection | Range): Promise<void> {2230return this._list.revealRangeInCell(cell, range, CellRevealRangeType.CenterIfOutsideViewport);2231}22322233revealCellOffsetInCenter(cell: ICellViewModel, offset: number) {2234return this._list.revealCellOffsetInCenter(cell, offset);2235}22362237revealOffsetInCenterIfOutsideViewport(offset: number) {2238return this._list.revealOffsetInCenterIfOutsideViewport(offset);2239}22402241getViewIndexByModelIndex(index: number): number {2242if (!this._listViewInfoAccessor) {2243return -1;2244}2245const cell = this.viewModel?.viewCells[index];2246if (!cell) {2247return -1;2248}22492250return this._listViewInfoAccessor.getViewIndex(cell);2251}22522253getViewHeight(cell: ICellViewModel): number {2254if (!this._listViewInfoAccessor) {2255return -1;2256}22572258return this._listViewInfoAccessor.getViewHeight(cell);2259}22602261getCellRangeFromViewRange(startIndex: number, endIndex: number): ICellRange | undefined {2262return this._listViewInfoAccessor.getCellRangeFromViewRange(startIndex, endIndex);2263}22642265getCellsInRange(range?: ICellRange): ReadonlyArray<ICellViewModel> {2266return this._listViewInfoAccessor.getCellsInRange(range);2267}22682269setCellEditorSelection(cell: ICellViewModel, range: Range): void {2270this._list.setCellEditorSelection(cell, range);2271}22722273setHiddenAreas(_ranges: ICellRange[]): boolean {2274return this._list.setHiddenAreas(_ranges, true);2275}22762277getVisibleRangesPlusViewportAboveAndBelow(): ICellRange[] {2278return this._listViewInfoAccessor.getVisibleRangesPlusViewportAboveAndBelow();2279}22802281//#endregion22822283//#region Decorations22842285deltaCellDecorations(oldDecorations: string[], newDecorations: INotebookDeltaDecoration[]): string[] {2286const ret = this.viewModel?.deltaCellDecorations(oldDecorations, newDecorations) || [];2287this._onDidChangeDecorations.fire();2288return ret;2289}22902291deltaCellContainerClassNames(cellId: string, added: string[], removed: string[], cellkind: CellKind): void {2292if (cellkind === CellKind.Markup) {2293this._webview?.deltaMarkupPreviewClassNames(cellId, added, removed);2294} else {2295this._webview?.deltaCellOutputContainerClassNames(cellId, added, removed);2296}2297}22982299changeModelDecorations<T>(callback: (changeAccessor: IModelDecorationsChangeAccessor) => T): T | null {2300return this.viewModel?.changeModelDecorations<T>(callback) || null;2301}23022303//#endregion23042305//#region View Zones2306changeViewZones(callback: (accessor: INotebookViewZoneChangeAccessor) => void): void {2307this._list.changeViewZones(callback);2308this._onDidChangeLayout.fire();2309}23102311getViewZoneLayoutInfo(id: string): { top: number; height: number } | null {2312return this._list.getViewZoneLayoutInfo(id);2313}2314//#endregion23152316//#region Overlay2317changeCellOverlays(callback: (accessor: INotebookCellOverlayChangeAccessor) => void): void {2318this._list.changeCellOverlays(callback);2319}2320//#endregion23212322//#region Kernel/Execution23232324private async _loadKernelPreloads(): Promise<void> {2325if (!this.hasModel()) {2326return;2327}2328const { selected } = this.notebookKernelService.getMatchingKernel(this.textModel);2329if (!this._webview?.isResolved()) {2330await this._resolveWebview();2331}2332this._webview?.updateKernelPreloads(selected);2333}23342335get activeKernel() {2336return this.textModel && this.notebookKernelService.getSelectedOrSuggestedKernel(this.textModel);2337}23382339async cancelNotebookCells(cells?: Iterable<ICellViewModel>): Promise<void> {2340if (!this.viewModel || !this.hasModel()) {2341return;2342}2343if (!cells) {2344cells = this.viewModel.viewCells;2345}2346return this.notebookExecutionService.cancelNotebookCellHandles(this.textModel, Array.from(cells).map(cell => cell.handle));2347}23482349async executeNotebookCells(cells?: Iterable<ICellViewModel>): Promise<void> {2350if (!this.viewModel || !this.hasModel()) {2351this.logService.info('notebookEditorWidget', 'No NotebookViewModel, cannot execute cells');2352return;2353}2354if (!cells) {2355cells = this.viewModel.viewCells;2356}2357return this.notebookExecutionService.executeNotebookCells(this.textModel, Array.from(cells).map(c => c.model), this.scopedContextKeyService);2358}23592360//#endregion23612362//#region Cell operations/layout API2363private _pendingLayouts: WeakMap<ICellViewModel, IDisposable> | null = new WeakMap<ICellViewModel, IDisposable>();2364private _layoutDisposables: Set<IDisposable> = new Set<IDisposable>();2365async layoutNotebookCell(cell: ICellViewModel, height: number, context?: CellLayoutContext): Promise<void> {2366this._debug('layout cell', cell.handle, height);2367const viewIndex = this._list.getViewIndex(cell);2368if (viewIndex === undefined) {2369// the cell is hidden2370return;2371}23722373if (this._pendingLayouts?.has(cell)) {2374this._pendingLayouts?.get(cell)!.dispose();2375}23762377const deferred = new DeferredPromise<void>();2378const doLayout = () => {2379if (this._isDisposed) {2380return;2381}23822383if (!this.viewModel?.hasCell(cell)) {2384// Cell removed in the meantime?2385return;2386}23872388if (this._list.getViewIndex(cell) === undefined) {2389// Cell can be hidden2390return;2391}23922393if (this._list.elementHeight(cell) === height) {2394return;2395}23962397const pendingLayout = this._pendingLayouts?.get(cell);2398this._pendingLayouts?.delete(cell);23992400if (!this.hasEditorFocus()) {2401// Do not scroll inactive notebook2402// https://github.com/microsoft/vscode/issues/1453402403const cellIndex = this.viewModel?.getCellIndex(cell);2404const visibleRanges = this.visibleRanges;2405if (cellIndex !== undefined2406&& visibleRanges && visibleRanges.length && visibleRanges[0].start === cellIndex2407// cell is partially visible2408&& this._list.scrollTop > this.getAbsoluteTopOfElement(cell)2409) {2410return this._list.updateElementHeight2(cell, height, Math.min(cellIndex + 1, this.getLength() - 1));2411}2412}24132414this._list.updateElementHeight2(cell, height);2415deferred.complete(undefined);2416if (pendingLayout) {2417pendingLayout.dispose();2418this._layoutDisposables.delete(pendingLayout);2419}2420};24212422if (this._list.inRenderingTransaction) {2423const layoutDisposable = DOM.scheduleAtNextAnimationFrame(DOM.getWindow(this.getDomNode()), doLayout);24242425const disposable = toDisposable(() => {2426layoutDisposable.dispose();2427deferred.complete(undefined);2428});2429this._pendingLayouts?.set(cell, disposable);2430this._layoutDisposables.add(disposable);2431} else {2432doLayout();2433}24342435return deferred.p;2436}24372438getActiveCell() {2439const elements = this._list.getFocusedElements();24402441if (elements && elements.length) {2442return elements[0];2443}24442445return undefined;2446}24472448private _toggleNotebookCellSelection(selectedCell: ICellViewModel, selectFromPrevious: boolean): void {2449const currentSelections = this._list.getSelectedElements();2450const isSelected = currentSelections.includes(selectedCell);24512452const previousSelection = selectFromPrevious ? currentSelections[currentSelections.length - 1] ?? selectedCell : selectedCell;2453const selectedIndex = this._list.getViewIndex(selectedCell)!;2454const previousIndex = this._list.getViewIndex(previousSelection)!;24552456const cellsInSelectionRange = this.getCellsInViewRange(selectedIndex, previousIndex);2457if (isSelected) {2458// Deselect2459this._list.selectElements(currentSelections.filter(current => !cellsInSelectionRange.includes(current)));2460} else {2461// Add to selection2462this.focusElement(selectedCell);2463this._list.selectElements([...currentSelections.filter(current => !cellsInSelectionRange.includes(current)), ...cellsInSelectionRange]);2464}2465}24662467private getCellsInViewRange(fromInclusive: number, toInclusive: number): ICellViewModel[] {2468const selectedCellsInRange: ICellViewModel[] = [];2469for (let index = 0; index < this._list.length; ++index) {2470const cell = this._list.element(index);2471if (cell) {2472if ((index >= fromInclusive && index <= toInclusive) || (index >= toInclusive && index <= fromInclusive)) {2473selectedCellsInRange.push(cell);2474}2475}2476}2477return selectedCellsInRange;2478}24792480async focusNotebookCell(cell: ICellViewModel, focusItem: 'editor' | 'container' | 'output', options?: IFocusNotebookCellOptions) {2481if (this._isDisposed) {2482return;2483}24842485cell.focusedOutputId = undefined;24862487if (focusItem === 'editor') {2488cell.isInputCollapsed = false;2489this.focusElement(cell);2490this._list.focusView();24912492cell.updateEditState(CellEditState.Editing, 'focusNotebookCell');2493cell.focusMode = CellFocusMode.Editor;2494if (!options?.skipReveal) {2495if (typeof options?.focusEditorLine === 'number') {2496this._cursorNavMode.set(true);2497await this.revealLineInViewAsync(cell, options.focusEditorLine);2498const editor = this._renderedEditors.get(cell)!;2499const focusEditorLine = options.focusEditorLine;2500editor?.setSelection({2501startLineNumber: focusEditorLine,2502startColumn: 1,2503endLineNumber: focusEditorLine,2504endColumn: 12505});2506} else {2507const selectionsStartPosition = cell.getSelectionsStartPosition();2508if (selectionsStartPosition?.length) {2509const firstSelectionPosition = selectionsStartPosition[0];2510await this.revealRangeInViewAsync(cell, Range.fromPositions(firstSelectionPosition, firstSelectionPosition));2511} else {2512await this.revealInView(cell);2513}25142515}25162517}2518} else if (focusItem === 'output') {2519this.focusElement(cell);25202521if (!this.hasEditorFocus()) {2522this._list.focusView();2523}25242525if (!this._webview) {2526return;2527}25282529const firstOutputId = cell.outputsViewModels.find(o => o.model.alternativeOutputId)?.model.alternativeOutputId;2530const focusElementId = options?.outputId ?? firstOutputId ?? cell.id;2531this._webview.focusOutput(focusElementId, options?.altOutputId, options?.outputWebviewFocused || this._webviewFocused);25322533cell.updateEditState(CellEditState.Preview, 'focusNotebookCell');2534cell.focusMode = CellFocusMode.Output;2535cell.focusedOutputId = options?.outputId;2536this._outputFocus.set(true);2537if (!options?.skipReveal) {2538this.revealInCenterIfOutsideViewport(cell);2539}2540} else {2541// focus container2542const itemDOM = this._list.domElementOfElement(cell);2543if (itemDOM && itemDOM.ownerDocument.activeElement && itemDOM.contains(itemDOM.ownerDocument.activeElement)) {2544(itemDOM.ownerDocument.activeElement as HTMLElement).blur();2545}25462547this._webview?.blurOutput();25482549cell.updateEditState(CellEditState.Preview, 'focusNotebookCell');2550cell.focusMode = CellFocusMode.Container;25512552this.focusElement(cell);2553if (!options?.skipReveal) {2554if (typeof options?.focusEditorLine === 'number') {2555this._cursorNavMode.set(true);2556await this.revealInView(cell);2557} else if (options?.revealBehavior === ScrollToRevealBehavior.firstLine) {2558await this.revealFirstLineIfOutsideViewport(cell);2559} else if (options?.revealBehavior === ScrollToRevealBehavior.fullCell) {2560await this.revealInView(cell);2561} else {2562await this.revealInCenterIfOutsideViewport(cell);2563}2564}2565this._list.focusView();2566this.updateEditorFocus();2567}2568}25692570async focusNextNotebookCell(cell: ICellViewModel, focusItem: 'editor' | 'container' | 'output') {2571const idx = this.viewModel?.getCellIndex(cell);2572if (typeof idx !== 'number') {2573return;2574}25752576const newCell = this.viewModel?.cellAt(idx + 1);2577if (!newCell) {2578return;2579}25802581await this.focusNotebookCell(newCell, focusItem);2582}25832584//#endregion25852586//#region Find25872588private async _warmupCell(viewCell: CodeCellViewModel) {2589if (viewCell.isOutputCollapsed) {2590return;2591}25922593const outputs = viewCell.outputsViewModels;2594for (const output of outputs.slice(0, outputDisplayLimit)) {2595const [mimeTypes, pick] = output.resolveMimeTypes(this.textModel!, undefined);2596if (!mimeTypes.find(mimeType => mimeType.isTrusted) || mimeTypes.length === 0) {2597continue;2598}25992600const pickedMimeTypeRenderer = mimeTypes[pick];26012602if (!pickedMimeTypeRenderer) {2603return;2604}26052606const renderer = this._notebookService.getRendererInfo(pickedMimeTypeRenderer.rendererId);26072608if (!renderer) {2609return;2610}26112612const result: IInsetRenderOutput = { type: RenderOutputType.Extension, renderer, source: output, mimeType: pickedMimeTypeRenderer.mimeType };2613const inset = this._webview?.insetMapping.get(result.source);2614if (!inset || !inset.initialized) {2615const p = new Promise<void>(resolve => {2616this._register(Event.any(this.onDidRenderOutput, this.onDidRemoveOutput)(e => {2617if (e.model === result.source.model) {2618resolve();2619}2620}));2621});2622this.createOutput(viewCell, result, 0, false);2623await p;2624} else {2625// request to update its visibility2626this.createOutput(viewCell, result, 0, false);2627}26282629return;2630}26312632}26332634private async _warmupAll(includeOutput: boolean) {2635if (!this.hasModel() || !this.viewModel) {2636return;2637}26382639const cells = this.viewModel.viewCells;2640const requests = [];26412642for (let i = 0; i < cells.length; i++) {2643if (cells[i].cellKind === CellKind.Markup && !this._webview!.markupPreviewMapping.has(cells[i].id)) {2644requests.push(this.createMarkupPreview(cells[i]));2645}2646}26472648if (includeOutput && this._list) {2649for (let i = 0; i < this._list.length; i++) {2650const cell = this._list.element(i);26512652if (cell?.cellKind === CellKind.Code) {2653requests.push(this._warmupCell((cell as CodeCellViewModel)));2654}2655}2656}26572658return Promise.all(requests);2659}26602661private async _warmupSelection(includeOutput: boolean, selectedCellRanges: ICellRange[]) {2662if (!this.hasModel() || !this.viewModel) {2663return;2664}26652666const cells = this.viewModel.viewCells;2667const requests = [];26682669for (const range of selectedCellRanges) {2670for (let i = range.start; i < range.end; i++) {2671if (cells[i].cellKind === CellKind.Markup && !this._webview!.markupPreviewMapping.has(cells[i].id)) {2672requests.push(this.createMarkupPreview(cells[i]));2673}2674}2675}26762677if (includeOutput && this._list) {2678for (const range of selectedCellRanges) {2679for (let i = range.start; i < range.end; i++) {2680const cell = this._list.element(i);26812682if (cell?.cellKind === CellKind.Code) {2683requests.push(this._warmupCell((cell as CodeCellViewModel)));2684}2685}2686}2687}26882689return Promise.all(requests);2690}26912692async find(query: string, options: INotebookFindOptions, token: CancellationToken, skipWarmup: boolean = false, shouldGetSearchPreviewInfo = false, ownerID?: string): Promise<CellFindMatchWithIndex[]> {2693if (!this._notebookViewModel) {2694return [];2695}26962697if (!ownerID) {2698ownerID = this.getId();2699}27002701const findMatches = this._notebookViewModel.find(query, options).filter(match => match.length > 0);27022703if ((!options.includeMarkupPreview && !options.includeOutput) || options.findScope?.findScopeType === NotebookFindScopeType.Text) {2704this._webview?.findStop(ownerID);2705return findMatches;2706}27072708// search in webview enabled27092710const matchMap: { [key: string]: CellFindMatchWithIndex } = {};2711findMatches.forEach(match => {2712matchMap[match.cell.id] = match;2713});27142715if (this._webview) {2716// request all or some outputs to be rendered2717// measure perf2718const start = Date.now();2719if (options.findScope && options.findScope.findScopeType === NotebookFindScopeType.Cells && options.findScope.selectedCellRanges) {2720await this._warmupSelection(!!options.includeOutput, options.findScope.selectedCellRanges);2721} else {2722await this._warmupAll(!!options.includeOutput);2723}2724const end = Date.now();2725this.logService.debug('Find', `Warmup time: ${end - start}ms`);27262727if (token.isCancellationRequested) {2728return [];2729}27302731let findIds: string[] = [];2732if (options.findScope && options.findScope.findScopeType === NotebookFindScopeType.Cells && options.findScope.selectedCellRanges) {2733const selectedIndexes = cellRangesToIndexes(options.findScope.selectedCellRanges);2734findIds = selectedIndexes.map<string>(index => this._notebookViewModel?.viewCells[index].id ?? '');2735}27362737const webviewMatches = await this._webview.find(query, { caseSensitive: options.caseSensitive, wholeWord: options.wholeWord, includeMarkup: !!options.includeMarkupPreview, includeOutput: !!options.includeOutput, shouldGetSearchPreviewInfo, ownerID, findIds: findIds });27382739if (token.isCancellationRequested) {2740return [];2741}27422743// attach webview matches to model find matches2744webviewMatches.forEach(match => {2745const cell = this._notebookViewModel!.viewCells.find(cell => cell.id === match.cellId);27462747if (!cell) {2748return;2749}27502751if (match.type === 'preview') {2752// markup preview2753if (cell.getEditState() === CellEditState.Preview && !options.includeMarkupPreview) {2754return;2755}27562757if (cell.getEditState() === CellEditState.Editing && options.includeMarkupInput) {2758return;2759}2760} else {2761if (!options.includeOutput) {2762// skip outputs if not included2763return;2764}2765}27662767const exisitingMatch = matchMap[match.cellId];27682769if (exisitingMatch) {2770exisitingMatch.webviewMatches.push(match);2771} else {27722773matchMap[match.cellId] = new CellFindMatchModel(2774this._notebookViewModel!.viewCells.find(cell => cell.id === match.cellId)!,2775this._notebookViewModel!.viewCells.findIndex(cell => cell.id === match.cellId)!,2776[],2777[match]2778);2779}2780});2781}27822783const ret: CellFindMatchWithIndex[] = [];2784this._notebookViewModel.viewCells.forEach((cell, index) => {2785if (matchMap[cell.id]) {2786ret.push(new CellFindMatchModel(cell, index, matchMap[cell.id].contentMatches, matchMap[cell.id].webviewMatches));2787}2788});27892790return ret;2791}27922793async findHighlightCurrent(matchIndex: number, ownerID?: string): Promise<number> {2794if (!this._webview) {2795return 0;2796}27972798return this._webview?.findHighlightCurrent(matchIndex, ownerID ?? this.getId());2799}28002801async findUnHighlightCurrent(matchIndex: number, ownerID?: string): Promise<void> {2802if (!this._webview) {2803return;2804}28052806return this._webview?.findUnHighlightCurrent(matchIndex, ownerID ?? this.getId());2807}28082809findStop(ownerID?: string) {2810this._webview?.findStop(ownerID ?? this.getId());2811}28122813//#endregion28142815//#region MISC28162817getLayoutInfo(): NotebookLayoutInfo {2818if (!this._list) {2819throw new Error('Editor is not initalized successfully');2820}28212822if (!this._fontInfo) {2823this._generateFontInfo();2824}28252826return {2827width: this._dimension?.width ?? 0,2828height: this._dimension?.height ?? 0,2829scrollHeight: this._list?.getScrollHeight() ?? 0,2830fontInfo: this._fontInfo!,2831stickyHeight: this._notebookStickyScroll?.getCurrentStickyHeight() ?? 02832};2833}28342835async createMarkupPreview(cell: MarkupCellViewModel) {2836if (!this._webview) {2837return;2838}28392840if (!this._webview.isResolved()) {2841await this._resolveWebview();2842}28432844if (!this._webview || !this._list.webviewElement) {2845return;2846}28472848if (!this.viewModel || !this._list.viewModel) {2849return;2850}28512852if (this.viewModel.getCellIndex(cell) === -1) {2853return;2854}28552856if (this.cellIsHidden(cell)) {2857return;2858}28592860const webviewTop = parseInt(this._list.webviewElement.domNode.style.top, 10);2861const top = !!webviewTop ? (0 - webviewTop) : 0;28622863const cellTop = this._list.getCellViewScrollTop(cell);2864await this._webview.showMarkupPreview({2865mime: cell.mime,2866cellHandle: cell.handle,2867cellId: cell.id,2868content: cell.getText(),2869offset: cellTop + top,2870visible: true,2871metadata: cell.metadata,2872});2873}28742875private cellIsHidden(cell: ICellViewModel): boolean {2876const modelIndex = this.viewModel!.getCellIndex(cell);2877const foldedRanges = this.viewModel!.getHiddenRanges();2878return foldedRanges.some(range => modelIndex >= range.start && modelIndex <= range.end);2879}28802881async unhideMarkupPreviews(cells: readonly MarkupCellViewModel[]) {2882if (!this._webview) {2883return;2884}28852886if (!this._webview.isResolved()) {2887await this._resolveWebview();2888}28892890await this._webview?.unhideMarkupPreviews(cells.map(cell => cell.id));2891}28922893async hideMarkupPreviews(cells: readonly MarkupCellViewModel[]) {2894if (!this._webview || !cells.length) {2895return;2896}28972898if (!this._webview.isResolved()) {2899await this._resolveWebview();2900}29012902await this._webview?.hideMarkupPreviews(cells.map(cell => cell.id));2903}29042905async deleteMarkupPreviews(cells: readonly MarkupCellViewModel[]) {2906if (!this._webview) {2907return;2908}29092910if (!this._webview.isResolved()) {2911await this._resolveWebview();2912}29132914await this._webview?.deleteMarkupPreviews(cells.map(cell => cell.id));2915}29162917private async updateSelectedMarkdownPreviews(): Promise<void> {2918if (!this._webview) {2919return;2920}29212922if (!this._webview.isResolved()) {2923await this._resolveWebview();2924}29252926const selectedCells = this.getSelectionViewModels().map(cell => cell.id);29272928// Only show selection when there is more than 1 cell selected2929await this._webview?.updateMarkupPreviewSelections(selectedCells.length > 1 ? selectedCells : []);2930}29312932async createOutput(cell: CodeCellViewModel, output: IInsetRenderOutput, offset: number, createWhenIdle: boolean): Promise<void> {2933this._insetModifyQueueByOutputId.queue(output.source.model.outputId, async () => {2934if (this._isDisposed || !this._webview) {2935return;2936}29372938if (!this._webview.isResolved()) {2939await this._resolveWebview();2940}29412942if (!this._webview) {2943return;2944}29452946if (!this._list.webviewElement) {2947return;2948}29492950if (output.type === RenderOutputType.Extension) {2951this.notebookRendererMessaging.prepare(output.renderer.id);2952}29532954const webviewTop = parseInt(this._list.webviewElement.domNode.style.top, 10);2955const top = !!webviewTop ? (0 - webviewTop) : 0;29562957const cellTop = this._list.getCellViewScrollTop(cell) + top;29582959const existingOutput = this._webview.insetMapping.get(output.source);2960if (!existingOutput2961|| (!existingOutput.renderer && output.type === RenderOutputType.Extension)2962) {2963if (createWhenIdle) {2964this._webview.requestCreateOutputWhenWebviewIdle({ cellId: cell.id, cellHandle: cell.handle, cellUri: cell.uri, executionId: cell.internalMetadata.executionId }, output, cellTop, offset);2965} else {2966this._webview.createOutput({ cellId: cell.id, cellHandle: cell.handle, cellUri: cell.uri, executionId: cell.internalMetadata.executionId }, output, cellTop, offset);2967}2968} else if (existingOutput.renderer2969&& output.type === RenderOutputType.Extension2970&& existingOutput.renderer.id !== output.renderer.id) {2971// switch mimetype2972this._webview.removeInsets([output.source]);2973this._webview.createOutput({ cellId: cell.id, cellHandle: cell.handle, cellUri: cell.uri }, output, cellTop, offset);2974} else if (existingOutput.versionId !== output.source.model.versionId) {2975this._webview.updateOutput({ cellId: cell.id, cellHandle: cell.handle, cellUri: cell.uri, executionId: cell.internalMetadata.executionId }, output, cellTop, offset);2976} else {2977const outputIndex = cell.outputsViewModels.indexOf(output.source);2978const outputOffset = cell.getOutputOffset(outputIndex);2979this._webview.updateScrollTops([{2980cell,2981output: output.source,2982cellTop,2983outputOffset,2984forceDisplay: !cell.isOutputCollapsed,2985}], []);2986}2987});2988}29892990async updateOutput(cell: CodeCellViewModel, output: IInsetRenderOutput, offset: number): Promise<void> {2991this._insetModifyQueueByOutputId.queue(output.source.model.outputId, async () => {2992if (this._isDisposed || !this._webview || cell.isOutputCollapsed) {2993return;2994}29952996if (!this._webview.isResolved()) {2997await this._resolveWebview();2998}29993000if (!this._webview || !this._list.webviewElement) {3001return;3002}30033004if (!this._webview.insetMapping.has(output.source)) {3005return this.createOutput(cell, output, offset, false);3006}30073008if (output.type === RenderOutputType.Extension) {3009this.notebookRendererMessaging.prepare(output.renderer.id);3010}30113012const webviewTop = parseInt(this._list.webviewElement.domNode.style.top, 10);3013const top = !!webviewTop ? (0 - webviewTop) : 0;30143015const cellTop = this._list.getCellViewScrollTop(cell) + top;3016this._webview.updateOutput({ cellId: cell.id, cellHandle: cell.handle, cellUri: cell.uri }, output, cellTop, offset);3017});3018}30193020async copyOutputImage(cellOutput: ICellOutputViewModel): Promise<void> {3021this._webview?.copyImage(cellOutput);3022}30233024removeInset(output: ICellOutputViewModel) {3025this._insetModifyQueueByOutputId.queue(output.model.outputId, async () => {3026if (this._isDisposed || !this._webview) {3027return;3028}30293030if (this._webview?.isResolved()) {3031this._webview.removeInsets([output]);3032}30333034this._onDidRemoveOutput.fire(output);3035});3036}30373038hideInset(output: ICellOutputViewModel) {3039this._insetModifyQueueByOutputId.queue(output.model.outputId, async () => {3040if (this._isDisposed || !this._webview) {3041return;3042}30433044if (this._webview?.isResolved()) {3045this._webview.hideInset(output);3046}3047});3048}30493050//#region --- webview IPC ----3051postMessage(message: any) {3052if (this._webview?.isResolved()) {3053this._webview.postKernelMessage(message);3054}3055}30563057//#endregion30583059addClassName(className: string) {3060this._overlayContainer.classList.add(className);3061}30623063removeClassName(className: string) {3064this._overlayContainer.classList.remove(className);3065}30663067cellAt(index: number): ICellViewModel | undefined {3068return this.viewModel?.cellAt(index);3069}30703071getCellByInfo(cellInfo: ICommonCellInfo): ICellViewModel {3072const { cellHandle } = cellInfo;3073return this.viewModel?.viewCells.find(vc => vc.handle === cellHandle) as CodeCellViewModel;3074}30753076getCellByHandle(handle: number): ICellViewModel | undefined {3077return this.viewModel?.getCellByHandle(handle);3078}30793080getCellIndex(cell: ICellViewModel) {3081return this.viewModel?.getCellIndexByHandle(cell.handle);3082}30833084getNextVisibleCellIndex(index: number): number | undefined {3085return this.viewModel?.getNextVisibleCellIndex(index);3086}30873088getPreviousVisibleCellIndex(index: number): number | undefined {3089return this.viewModel?.getPreviousVisibleCellIndex(index);3090}30913092private _updateScrollHeight() {3093if (this._isDisposed || !this._webview?.isResolved()) {3094return;3095}30963097if (!this._list.webviewElement) {3098return;3099}31003101const scrollHeight = this._list.scrollHeight;3102this._webview.element.style.height = `${scrollHeight + NOTEBOOK_WEBVIEW_BOUNDARY * 2}px`;31033104const webviewTop = parseInt(this._list.webviewElement.domNode.style.top, 10);3105const top = !!webviewTop ? (0 - webviewTop) : 0;31063107const updateItems: IDisplayOutputLayoutUpdateRequest[] = [];3108const removedItems: ICellOutputViewModel[] = [];3109this._webview?.insetMapping.forEach((value, key) => {3110const cell = this.viewModel?.getCellByHandle(value.cellInfo.cellHandle);3111if (!cell || !(cell instanceof CodeCellViewModel)) {3112return;3113}31143115this.viewModel?.viewCells.find(cell => cell.handle === value.cellInfo.cellHandle);3116const viewIndex = this._list.getViewIndex(cell);31173118if (viewIndex === undefined) {3119return;3120}31213122if (cell.outputsViewModels.indexOf(key) < 0) {3123// output is already gone3124removedItems.push(key);3125}31263127const cellTop = this._list.getCellViewScrollTop(cell);3128const outputIndex = cell.outputsViewModels.indexOf(key);3129const outputOffset = cell.getOutputOffset(outputIndex);3130updateItems.push({3131cell,3132output: key,3133cellTop: cellTop + top,3134outputOffset,3135forceDisplay: false,3136});3137});31383139this._webview.removeInsets(removedItems);31403141const markdownUpdateItems: { id: string; top: number }[] = [];3142for (const cellId of this._webview.markupPreviewMapping.keys()) {3143const cell = this.viewModel?.viewCells.find(cell => cell.id === cellId);3144if (cell) {3145const cellTop = this._list.getCellViewScrollTop(cell);3146// markdownUpdateItems.push({ id: cellId, top: cellTop });3147markdownUpdateItems.push({ id: cellId, top: cellTop + top });3148}3149}31503151if (markdownUpdateItems.length || updateItems.length) {3152this._debug('_list.onDidChangeContentHeight/markdown', markdownUpdateItems);3153this._webview?.updateScrollTops(updateItems, markdownUpdateItems);3154}3155}31563157//#endregion31583159//#region BacklayerWebview delegate3160private _updateOutputHeight(cellInfo: ICommonCellInfo, output: ICellOutputViewModel, outputHeight: number, isInit: boolean, source?: string): void {3161const cell = this.viewModel?.viewCells.find(vc => vc.handle === cellInfo.cellHandle);3162if (cell && cell instanceof CodeCellViewModel) {3163const outputIndex = cell.outputsViewModels.indexOf(output);3164if (outputIndex > -1) {3165this._debug('update cell output', cell.handle, outputHeight);3166cell.updateOutputHeight(outputIndex, outputHeight, source);3167this.layoutNotebookCell(cell, cell.layoutInfo.totalHeight);31683169if (isInit) {3170this._onDidRenderOutput.fire(output);3171}3172} else {3173this._debug('tried to update cell output that does not exist');3174}3175}3176}31773178private readonly _pendingOutputHeightAcks = new Map</* outputId */ string, IAckOutputHeight>();31793180private _scheduleOutputHeightAck(cellInfo: ICommonCellInfo, outputId: string, height: number) {3181const wasEmpty = this._pendingOutputHeightAcks.size === 0;3182this._pendingOutputHeightAcks.set(outputId, { cellId: cellInfo.cellId, outputId, height });31833184if (wasEmpty) {3185DOM.scheduleAtNextAnimationFrame(DOM.getWindow(this.getDomNode()), () => {3186this._debug('ack height');3187this._updateScrollHeight();31883189this._webview?.ackHeight([...this._pendingOutputHeightAcks.values()]);31903191this._pendingOutputHeightAcks.clear();3192}, -1); // -1 priority because this depends on calls to layoutNotebookCell, and that may be called multiple times before this runs3193}3194}31953196private _getCellById(cellId: string): ICellViewModel | undefined {3197return this.viewModel?.viewCells.find(vc => vc.id === cellId);3198}31993200private _updateMarkupCellHeight(cellId: string, height: number, isInit: boolean) {3201const cell = this._getCellById(cellId);3202if (cell && cell instanceof MarkupCellViewModel) {3203const { bottomToolbarGap } = this._notebookOptions.computeBottomToolbarDimensions(this.viewModel?.viewType);3204this._debug('updateMarkdownCellHeight', cell.handle, height + bottomToolbarGap, isInit);3205cell.renderedMarkdownHeight = height;3206}3207}32083209private _setMarkupCellEditState(cellId: string, editState: CellEditState): void {3210const cell = this._getCellById(cellId);3211if (cell instanceof MarkupCellViewModel) {3212this.revealInView(cell);3213cell.updateEditState(editState, 'setMarkdownCellEditState');3214}3215}32163217private _didStartDragMarkupCell(cellId: string, event: { dragOffsetY: number }): void {3218const cell = this._getCellById(cellId);3219if (cell instanceof MarkupCellViewModel) {3220const webviewOffset = this._list.webviewElement ? -parseInt(this._list.webviewElement.domNode.style.top, 10) : 0;3221this._dndController?.startExplicitDrag(cell, event.dragOffsetY - webviewOffset);3222}3223}32243225private _didDragMarkupCell(cellId: string, event: { dragOffsetY: number }): void {3226const cell = this._getCellById(cellId);3227if (cell instanceof MarkupCellViewModel) {3228const webviewOffset = this._list.webviewElement ? -parseInt(this._list.webviewElement.domNode.style.top, 10) : 0;3229this._dndController?.explicitDrag(cell, event.dragOffsetY - webviewOffset);3230}3231}32323233private _didDropMarkupCell(cellId: string, event: { dragOffsetY: number; ctrlKey: boolean; altKey: boolean }): void {3234const cell = this._getCellById(cellId);3235if (cell instanceof MarkupCellViewModel) {3236const webviewOffset = this._list.webviewElement ? -parseInt(this._list.webviewElement.domNode.style.top, 10) : 0;3237event.dragOffsetY -= webviewOffset;3238this._dndController?.explicitDrop(cell, event);3239}3240}32413242private _didEndDragMarkupCell(cellId: string): void {3243const cell = this._getCellById(cellId);3244if (cell instanceof MarkupCellViewModel) {3245this._dndController?.endExplicitDrag(cell);3246}3247}32483249private _didResizeOutput(cellId: string): void {3250const cell = this._getCellById(cellId);3251if (cell) {3252this._onDidResizeOutputEmitter.fire(cell);3253}3254}32553256private _updatePerformanceMetadata(cellId: string, executionId: string, duration: number, rendererId: string): void {3257if (!this.hasModel()) {3258return;3259}32603261const cell = this._getCellById(cellId);3262const cellIndex = !cell ? undefined : this.getCellIndex(cell);3263if (cell?.internalMetadata.executionId === executionId && cellIndex !== undefined) {3264const renderDurationMap = cell.internalMetadata.renderDuration || {};3265renderDurationMap[rendererId] = (renderDurationMap[rendererId] ?? 0) + duration;32663267this.textModel.applyEdits([3268{3269editType: CellEditType.PartialInternalMetadata,3270index: cellIndex,3271internalMetadata: {3272executionId: executionId,3273renderDuration: renderDurationMap3274}3275}3276], true, undefined, () => undefined, undefined, false);32773278}3279}32803281//#endregion32823283//#region Editor Contributions3284getContribution<T extends INotebookEditorContribution>(id: string): T {3285return <T>(this._contributions.get(id) || null);3286}32873288//#endregion32893290override dispose() {3291this._isDisposed = true;3292// dispose webview first3293this._webview?.dispose();3294this._webview = null;32953296this._layoutDisposables.forEach(d => d.dispose());32973298this.notebookEditorService.removeNotebookEditor(this);3299dispose(this._contributions.values());3300this._contributions.clear();33013302this._localStore.clear();3303dispose(this._localCellStateListeners);3304this._list.dispose();3305this._listTopCellToolbar?.dispose();33063307this._overlayContainer.remove();3308this.viewModel?.dispose();33093310this._renderedEditors.clear();3311this._baseCellEditorOptions.forEach(v => v.dispose());3312this._baseCellEditorOptions.clear();33133314this._notebookOverviewRulerContainer.remove();33153316super.dispose();33173318// unref3319this._webview = null;3320this._webviewResolvePromise = null;3321this._webviewTransparentCover = null;3322this._dndController = null;3323this._listTopCellToolbar = null;3324this._notebookViewModel = undefined;3325this._cellContextKeyManager = null;3326this._notebookTopToolbar = null!;3327this._list = null!;3328this._listViewInfoAccessor = null!;3329this._pendingLayouts = null;3330this._listDelegate = null;3331}33323333toJSON(): { notebookUri: URI | undefined } {3334return {3335notebookUri: this.viewModel?.uri,3336};3337}3338}33393340registerZIndex(ZIndex.Base, 5, 'notebook-progress-bar',);3341registerZIndex(ZIndex.Base, 10, 'notebook-list-insertion-indicator');3342registerZIndex(ZIndex.Base, 20, 'notebook-cell-editor-outline');3343registerZIndex(ZIndex.Base, 25, 'notebook-scrollbar');3344registerZIndex(ZIndex.Base, 26, 'notebook-cell-status');3345registerZIndex(ZIndex.Base, 26, 'notebook-folding-indicator');3346registerZIndex(ZIndex.Base, 27, 'notebook-output');3347registerZIndex(ZIndex.Base, 28, 'notebook-cell-bottom-toolbar-container');3348registerZIndex(ZIndex.Base, 29, 'notebook-run-button-container');3349registerZIndex(ZIndex.Base, 29, 'notebook-input-collapse-condicon');3350registerZIndex(ZIndex.Base, 30, 'notebook-cell-output-toolbar');3351registerZIndex(ZIndex.Sash, 1, 'notebook-cell-expand-part-button');3352registerZIndex(ZIndex.Sash, 2, 'notebook-cell-toolbar');3353registerZIndex(ZIndex.Sash, 3, 'notebook-cell-toolbar-dropdown-active');33543355export const notebookCellBorder = registerColor('notebook.cellBorderColor', {3356dark: transparent(listInactiveSelectionBackground, 1),3357light: transparent(listInactiveSelectionBackground, 1),3358hcDark: PANEL_BORDER,3359hcLight: PANEL_BORDER3360}, nls.localize('notebook.cellBorderColor', "The border color for notebook cells."));33613362export const focusedEditorBorderColor = registerColor('notebook.focusedEditorBorder', focusBorder, nls.localize('notebook.focusedEditorBorder', "The color of the notebook cell editor border."));33633364export const cellStatusIconSuccess = registerColor('notebookStatusSuccessIcon.foreground', debugIconStartForeground, nls.localize('notebookStatusSuccessIcon.foreground', "The error icon color of notebook cells in the cell status bar."));33653366export const runningCellRulerDecorationColor = registerColor('notebookEditorOverviewRuler.runningCellForeground', debugIconStartForeground, nls.localize('notebookEditorOverviewRuler.runningCellForeground', "The color of the running cell decoration in the notebook editor overview ruler."));33673368export const cellStatusIconError = registerColor('notebookStatusErrorIcon.foreground', errorForeground, nls.localize('notebookStatusErrorIcon.foreground', "The error icon color of notebook cells in the cell status bar."));33693370export const cellStatusIconRunning = registerColor('notebookStatusRunningIcon.foreground', foreground, nls.localize('notebookStatusRunningIcon.foreground', "The running icon color of notebook cells in the cell status bar."));33713372export const notebookOutputContainerBorderColor = registerColor('notebook.outputContainerBorderColor', null, nls.localize('notebook.outputContainerBorderColor', "The border color of the notebook output container."));33733374export const notebookOutputContainerColor = registerColor('notebook.outputContainerBackgroundColor', null, nls.localize('notebook.outputContainerBackgroundColor', "The color of the notebook output container background."));33753376// TODO@rebornix currently also used for toolbar border, if we keep all of this, pick a generic name3377export const CELL_TOOLBAR_SEPERATOR = registerColor('notebook.cellToolbarSeparator', {3378dark: Color.fromHex('#808080').transparent(0.35),3379light: Color.fromHex('#808080').transparent(0.35),3380hcDark: contrastBorder,3381hcLight: contrastBorder3382}, nls.localize('notebook.cellToolbarSeparator', "The color of the separator in the cell bottom toolbar"));33833384export const focusedCellBackground = registerColor('notebook.focusedCellBackground', null, nls.localize('focusedCellBackground', "The background color of a cell when the cell is focused."));33853386export const selectedCellBackground = registerColor('notebook.selectedCellBackground', {3387dark: listInactiveSelectionBackground,3388light: listInactiveSelectionBackground,3389hcDark: null,3390hcLight: null3391}, nls.localize('selectedCellBackground', "The background color of a cell when the cell is selected."));339233933394export const cellHoverBackground = registerColor('notebook.cellHoverBackground', {3395dark: transparent(focusedCellBackground, .5),3396light: transparent(focusedCellBackground, .7),3397hcDark: null,3398hcLight: null3399}, nls.localize('notebook.cellHoverBackground', "The background color of a cell when the cell is hovered."));34003401export const selectedCellBorder = registerColor('notebook.selectedCellBorder', {3402dark: notebookCellBorder,3403light: notebookCellBorder,3404hcDark: contrastBorder,3405hcLight: contrastBorder3406}, nls.localize('notebook.selectedCellBorder', "The color of the cell's top and bottom border when the cell is selected but not focused."));34073408export const inactiveSelectedCellBorder = registerColor('notebook.inactiveSelectedCellBorder', {3409dark: null,3410light: null,3411hcDark: focusBorder,3412hcLight: focusBorder3413}, nls.localize('notebook.inactiveSelectedCellBorder', "The color of the cell's borders when multiple cells are selected."));34143415export const focusedCellBorder = registerColor('notebook.focusedCellBorder', focusBorder, nls.localize('notebook.focusedCellBorder', "The color of the cell's focus indicator borders when the cell is focused."));34163417export 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."));34183419export const cellStatusBarItemHover = registerColor('notebook.cellStatusBarItemHoverBackground', {3420light: new Color(new RGBA(0, 0, 0, 0.08)),3421dark: new Color(new RGBA(255, 255, 255, 0.15)),3422hcDark: new Color(new RGBA(255, 255, 255, 0.15)),3423hcLight: new Color(new RGBA(0, 0, 0, 0.08)),3424}, nls.localize('notebook.cellStatusBarItemHoverBackground', "The background color of notebook cell status bar items."));34253426export const cellInsertionIndicator = registerColor('notebook.cellInsertionIndicator', focusBorder, nls.localize('notebook.cellInsertionIndicator', "The color of the notebook cell insertion indicator."));34273428export const listScrollbarSliderBackground = registerColor('notebookScrollbarSlider.background', scrollbarSliderBackground, nls.localize('notebookScrollbarSliderBackground', "Notebook scrollbar slider background color."));34293430export const listScrollbarSliderHoverBackground = registerColor('notebookScrollbarSlider.hoverBackground', scrollbarSliderHoverBackground, nls.localize('notebookScrollbarSliderHoverBackground', "Notebook scrollbar slider background color when hovering."));34313432export const listScrollbarSliderActiveBackground = registerColor('notebookScrollbarSlider.activeBackground', scrollbarSliderActiveBackground, nls.localize('notebookScrollbarSliderActiveBackground', "Notebook scrollbar slider background color when clicked on."));34333434export const cellSymbolHighlight = registerColor('notebook.symbolHighlightBackground', {3435dark: Color.fromHex('#ffffff0b'),3436light: Color.fromHex('#fdff0033'),3437hcDark: null,3438hcLight: null3439}, nls.localize('notebook.symbolHighlightBackground', "Background color of highlighted cell"));34403441export const cellEditorBackground = registerColor('notebook.cellEditorBackground', {3442light: SIDE_BAR_BACKGROUND,3443dark: SIDE_BAR_BACKGROUND,3444hcDark: null,3445hcLight: null3446}, nls.localize('notebook.cellEditorBackground', "Cell editor background color."));34473448const notebookEditorBackground = registerColor('notebook.editorBackground', {3449light: EDITOR_PANE_BACKGROUND,3450dark: EDITOR_PANE_BACKGROUND,3451hcDark: null,3452hcLight: null3453}, nls.localize('notebook.editorBackground', "Notebook background color."));345434553456