Path: blob/main/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts
5243 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import './media/notebook.css';6import './media/notebookCellChat.css';7import './media/notebookCellEditorHint.css';8import './media/notebookCellInsertToolbar.css';9import './media/notebookCellStatusBar.css';10import './media/notebookCellTitleToolbar.css';11import './media/notebookFocusIndicator.css';12import './media/notebookToolbar.css';13import './media/notebookDnd.css';14import './media/notebookFolding.css';15import './media/notebookCellOutput.css';16import './media/notebookEditorStickyScroll.css';17import './media/notebookKernelActionViewItem.css';18import './media/notebookOutline.css';19import './media/notebookChatEditController.css';20import './media/notebookChatEditorOverlay.css';21import * as DOM from '../../../../base/browser/dom.js';22import * as domStylesheets from '../../../../base/browser/domStylesheets.js';23import { IMouseWheelEvent, StandardMouseEvent } from '../../../../base/browser/mouseEvent.js';24import { IListContextMenuEvent } from '../../../../base/browser/ui/list/list.js';25import { mainWindow } from '../../../../base/browser/window.js';26import { SequencerByKey } from '../../../../base/common/async.js';27import { CancellationToken } from '../../../../base/common/cancellation.js';28import { Color, RGBA } from '../../../../base/common/color.js';29import { onUnexpectedError } from '../../../../base/common/errors.js';30import { Emitter, Event } from '../../../../base/common/event.js';31import { combinedDisposable, Disposable, DisposableStore, dispose, MutableDisposable } from '../../../../base/common/lifecycle.js';32import { setTimeout0 } from '../../../../base/common/platform.js';33import { extname, isEqual } from '../../../../base/common/resources.js';34import { URI } from '../../../../base/common/uri.js';35import { generateUuid } from '../../../../base/common/uuid.js';36import { FontMeasurements } from '../../../../editor/browser/config/fontMeasurements.js';37import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js';38import { IEditorOptions } from '../../../../editor/common/config/editorOptions.js';39import { FontInfo } from '../../../../editor/common/config/fontInfo.js';40import { createBareFontInfoFromRawSettings } from '../../../../editor/common/config/fontInfoFromSettings.js';41import { Range } from '../../../../editor/common/core/range.js';42import { Selection } from '../../../../editor/common/core/selection.js';43import { SuggestController } from '../../../../editor/contrib/suggest/browser/suggestController.js';44import * as nls from '../../../../nls.js';45import { MenuId } from '../../../../platform/actions/common/actions.js';46import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';47import { IContextKey, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js';48import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js';49import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';50import { ServiceCollection } from '../../../../platform/instantiation/common/serviceCollection.js';51import { ILayoutService } from '../../../../platform/layout/browser/layoutService.js';52import { registerZIndex, ZIndex } from '../../../../platform/layout/browser/zIndexRegistry.js';53import { IEditorProgressService, IProgressRunner } from '../../../../platform/progress/common/progress.js';54import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js';55import { contrastBorder, errorForeground, focusBorder, foreground, listInactiveSelectionBackground, registerColor, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, transparent } from '../../../../platform/theme/common/colorRegistry.js';56import { EDITOR_PANE_BACKGROUND, PANEL_BORDER, SIDE_BAR_BACKGROUND } from '../../../common/theme.js';57import { debugIconStartForeground } from '../../debug/browser/debugColors.js';58import { 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';59import { NotebookEditorExtensionsRegistry } from './notebookEditorExtensions.js';60import { INotebookEditorService } from './services/notebookEditorService.js';61import { notebookDebug } from './notebookLogger.js';62import { NotebookCellStateChangedEvent, NotebookLayoutChangedEvent, NotebookLayoutInfo } from './notebookViewEvents.js';63import { CellContextKeyManager } from './view/cellParts/cellContextKeys.js';64import { CellDragAndDropController } from './view/cellParts/cellDnd.js';65import { ListViewInfoAccessor, NotebookCellList, NOTEBOOK_WEBVIEW_BOUNDARY } from './view/notebookCellList.js';66import { INotebookCellList } from './view/notebookRenderingCommon.js';67import { BackLayerWebView } from './view/renderers/backLayerWebView.js';68import { CodeCellRenderer, MarkupCellRenderer, NotebookCellListDelegate } from './view/renderers/cellRenderer.js';69import { IAckOutputHeight, IMarkupCellInitialization } from './view/renderers/webviewMessages.js';70import { CodeCellViewModel, outputDisplayLimit } from './viewModel/codeCellViewModel.js';71import { NotebookEventDispatcher } from './viewModel/eventDispatcher.js';72import { MarkupCellViewModel } from './viewModel/markupCellViewModel.js';73import { CellViewModel, NotebookViewModel } from './viewModel/notebookViewModelImpl.js';74import { ViewContext } from './viewModel/viewContext.js';75import { NotebookEditorWorkbenchToolbar } from './viewParts/notebookEditorToolbar.js';76import { NotebookEditorContextKeys } from './viewParts/notebookEditorWidgetContextKeys.js';77import { NotebookOverviewRuler } from './viewParts/notebookOverviewRuler.js';78import { ListTopCellToolbar } from './viewParts/notebookTopCellToolbar.js';79import { NotebookTextModel } from '../common/model/notebookTextModel.js';80import { CellEditType, CellKind, INotebookFindOptions, NotebookFindScopeType, RENDERER_NOT_AVAILABLE, SelectionStateType } from '../common/notebookCommon.js';81import { NOTEBOOK_CURSOR_NAVIGATION_MODE, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_OUTPUT_FOCUSED, NOTEBOOK_OUTPUT_INPUT_FOCUSED } from '../common/notebookContextKeys.js';82import { INotebookExecutionService } from '../common/notebookExecutionService.js';83import { INotebookKernelService } from '../common/notebookKernelService.js';84import { NotebookOptions, OutputInnerContainerTopPadding } from './notebookOptions.js';85import { cellRangesToIndexes, ICellRange } from '../common/notebookRange.js';86import { INotebookRendererMessagingService } from '../common/notebookRendererMessagingService.js';87import { INotebookService } from '../common/notebookService.js';88import { IWebviewElement } from '../../webview/browser/webview.js';89import { EditorExtensionsRegistry } from '../../../../editor/browser/editorExtensions.js';90import { IEditorGroupsService } from '../../../services/editor/common/editorGroupsService.js';91import { NotebookPerfMarks } from '../common/notebookPerformance.js';92import { BaseCellEditorOptions } from './viewModel/cellEditorOptions.js';93import { FloatingEditorClickMenu } from '../../../browser/codeeditor.js';94import { IDimension } from '../../../../editor/common/core/2d/dimension.js';95import { CellFindMatchModel } from './contrib/find/findModel.js';96import { INotebookLoggingService } from '../common/notebookLoggingService.js';97import { Schemas } from '../../../../base/common/network.js';98import { DropIntoEditorController } from '../../../../editor/contrib/dropOrPasteInto/browser/dropIntoEditorController.js';99import { CopyPasteController } from '../../../../editor/contrib/dropOrPasteInto/browser/copyPasteController.js';100import { NotebookStickyScroll } from './viewParts/notebookEditorStickyScroll.js';101import { PixelRatio } from '../../../../base/browser/pixelRatio.js';102import { PreventDefaultContextMenuItemsContextKeyName } from '../../webview/browser/webview.contribution.js';103import { NotebookAccessibilityProvider } from './notebookAccessibilityProvider.js';104import { NotebookHorizontalTracker } from './viewParts/notebookHorizontalTracker.js';105import { NotebookCellEditorPool } from './view/notebookCellEditorPool.js';106import { InlineCompletionsController } from '../../../../editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.js';107import { NotebookCellLayoutManager } from './notebookCellLayoutManager.js';108import { FloatingEditorToolbar } from '../../../../editor/contrib/floatingMenu/browser/floatingMenu.js';109110const $ = DOM.$;111112export function getDefaultNotebookCreationOptions(): INotebookEditorCreationOptions {113// We inlined the id to avoid loading comment contrib in tests114const skipContributions = [115'editor.contrib.review',116FloatingEditorClickMenu.ID,117FloatingEditorToolbar.ID,118'editor.contrib.dirtydiff',119'editor.contrib.testingOutputPeek',120'editor.contrib.testingDecorations',121'store.contrib.stickyScrollController',122'editor.contrib.findController',123'editor.contrib.emptyTextEditorHint'124];125const contributions = EditorExtensionsRegistry.getEditorContributions().filter(c => skipContributions.indexOf(c.id) === -1);126127return {128menuIds: {129notebookToolbar: MenuId.NotebookToolbar,130cellTitleToolbar: MenuId.NotebookCellTitle,131cellDeleteToolbar: MenuId.NotebookCellDelete,132cellInsertToolbar: MenuId.NotebookCellBetween,133cellTopInsertToolbar: MenuId.NotebookCellListTop,134cellExecuteToolbar: MenuId.NotebookCellExecute,135cellExecutePrimary: MenuId.NotebookCellExecutePrimary,136},137cellEditorContributions: contributions138};139}140141export class NotebookEditorWidget extends Disposable implements INotebookEditorDelegate, INotebookEditor {142//#region Eventing143private readonly _onDidChangeCellState = this._register(new Emitter<NotebookCellStateChangedEvent>());144readonly onDidChangeCellState = this._onDidChangeCellState.event;145private readonly _onDidChangeViewCells = this._register(new Emitter<INotebookViewCellsUpdateEvent>());146readonly onDidChangeViewCells: Event<INotebookViewCellsUpdateEvent> = this._onDidChangeViewCells.event;147private readonly _onWillChangeModel = this._register(new Emitter<NotebookTextModel | undefined>());148readonly onWillChangeModel: Event<NotebookTextModel | undefined> = this._onWillChangeModel.event;149private readonly _onDidChangeModel = this._register(new Emitter<NotebookTextModel | undefined>());150readonly onDidChangeModel: Event<NotebookTextModel | undefined> = this._onDidChangeModel.event;151private readonly _onDidAttachViewModel = this._register(new Emitter<void>());152readonly onDidAttachViewModel: Event<void> = this._onDidAttachViewModel.event;153private readonly _onDidChangeOptions = this._register(new Emitter<void>());154readonly onDidChangeOptions: Event<void> = this._onDidChangeOptions.event;155private readonly _onDidChangeDecorations = this._register(new Emitter<void>());156readonly onDidChangeDecorations: Event<void> = this._onDidChangeDecorations.event;157private readonly _onDidScroll = this._register(new Emitter<void>());158readonly onDidScroll: Event<void> = this._onDidScroll.event;159private readonly _onDidChangeLayout = this._register(new Emitter<void>());160readonly onDidChangeLayout: Event<void> = this._onDidChangeLayout.event;161private readonly _onDidChangeActiveCell = this._register(new Emitter<void>());162readonly onDidChangeActiveCell: Event<void> = this._onDidChangeActiveCell.event;163private readonly _onDidChangeFocus = this._register(new Emitter<void>());164readonly onDidChangeFocus: Event<void> = this._onDidChangeFocus.event;165private readonly _onDidChangeSelection = this._register(new Emitter<void>());166readonly onDidChangeSelection: Event<void> = this._onDidChangeSelection.event;167private readonly _onDidChangeVisibleRanges = this._register(new Emitter<void>());168readonly onDidChangeVisibleRanges: Event<void> = this._onDidChangeVisibleRanges.event;169private readonly _onDidFocusEmitter = this._register(new Emitter<void>());170readonly onDidFocusWidget = this._onDidFocusEmitter.event;171private readonly _onDidBlurEmitter = this._register(new Emitter<void>());172readonly onDidBlurWidget = this._onDidBlurEmitter.event;173private readonly _onDidChangeActiveEditor = this._register(new Emitter<this>());174readonly onDidChangeActiveEditor: Event<this> = this._onDidChangeActiveEditor.event;175private readonly _onDidChangeActiveKernel = this._register(new Emitter<void>());176readonly onDidChangeActiveKernel: Event<void> = this._onDidChangeActiveKernel.event;177private readonly _onMouseUp: Emitter<INotebookEditorMouseEvent> = this._register(new Emitter<INotebookEditorMouseEvent>());178readonly onMouseUp: Event<INotebookEditorMouseEvent> = this._onMouseUp.event;179private readonly _onMouseDown: Emitter<INotebookEditorMouseEvent> = this._register(new Emitter<INotebookEditorMouseEvent>());180readonly onMouseDown: Event<INotebookEditorMouseEvent> = this._onMouseDown.event;181private readonly _onDidReceiveMessage = this._register(new Emitter<INotebookWebviewMessage>());182readonly onDidReceiveMessage: Event<INotebookWebviewMessage> = this._onDidReceiveMessage.event;183private readonly _onDidRenderOutput = this._register(new Emitter<ICellOutputViewModel>());184private readonly onDidRenderOutput = this._onDidRenderOutput.event;185private readonly _onDidRemoveOutput = this._register(new Emitter<ICellOutputViewModel>());186private readonly onDidRemoveOutput = this._onDidRemoveOutput.event;187private readonly _onDidResizeOutputEmitter = this._register(new Emitter<ICellViewModel>());188readonly onDidResizeOutput = this._onDidResizeOutputEmitter.event;189190//#endregion191private _overlayContainer!: HTMLElement;192private _notebookTopToolbarContainer!: HTMLElement;193private _notebookTopToolbar!: NotebookEditorWorkbenchToolbar;194private _notebookStickyScrollContainer!: HTMLElement;195private _notebookStickyScroll!: NotebookStickyScroll;196private _notebookOverviewRulerContainer!: HTMLElement;197private _notebookOverviewRuler!: NotebookOverviewRuler;198private _body!: HTMLElement;199private _styleElement!: HTMLStyleElement;200private _overflowContainer!: HTMLElement;201private _webview: BackLayerWebView<ICommonCellInfo> | null = null;202private _webviewResolvePromise: Promise<BackLayerWebView<ICommonCellInfo> | null> | null = null;203private _webviewTransparentCover: HTMLElement | null = null;204private _listDelegate: NotebookCellListDelegate | null = null;205private _list!: INotebookCellList;206private _listViewInfoAccessor!: ListViewInfoAccessor;207private _dndController: CellDragAndDropController | null = null;208private _listTopCellToolbar: ListTopCellToolbar | null = null;209private _renderedEditors: Map<ICellViewModel, ICodeEditor> = new Map();210private _editorPool!: NotebookCellEditorPool;211private _viewContext: ViewContext;212private _notebookViewModel: NotebookViewModel | undefined;213private readonly _localStore: DisposableStore = this._register(new DisposableStore());214private _localCellStateListeners: DisposableStore[] = [];215private _fontInfo: FontInfo | undefined;216private _dimension?: DOM.Dimension;217private _position?: DOM.IDomPosition;218private _shadowElement?: HTMLElement;219private _shadowElementViewInfo: { height: number; width: number; top: number; left: number } | null = null;220private _cellLayoutManager: NotebookCellLayoutManager | undefined;221222private readonly _editorFocus: IContextKey<boolean>;223private readonly _outputFocus: IContextKey<boolean>;224private readonly _editorEditable: IContextKey<boolean>;225private readonly _cursorNavMode: IContextKey<boolean>;226private readonly _outputInputFocus: IContextKey<boolean>;227protected readonly _contributions = new Map<string, INotebookEditorContribution>();228private _scrollBeyondLastLine: boolean;229private readonly _insetModifyQueueByOutputId = new SequencerByKey<string>();230private _cellContextKeyManager: CellContextKeyManager | null = null;231private readonly _uuid = generateUuid();232private _focusTracker!: DOM.IFocusTracker;233private _webviewFocused: boolean = false;234private _isVisible = false;235get isVisible() {236return this._isVisible;237}238239private _isDisposed: boolean = false;240241get isDisposed() {242return this._isDisposed;243}244245set viewModel(newModel: NotebookViewModel | undefined) {246this._onWillChangeModel.fire(this._notebookViewModel?.notebookDocument);247this._notebookViewModel = newModel;248this._onDidChangeModel.fire(newModel?.notebookDocument);249}250251get viewModel() {252return this._notebookViewModel;253}254255get textModel() {256return this._notebookViewModel?.notebookDocument;257}258259get isReadOnly() {260return this._notebookViewModel?.options.isReadOnly ?? false;261}262263get activeCodeEditor(): ICodeEditor | undefined {264if (this._isDisposed) {265return;266}267268const [focused] = this._list.getFocusedElements();269return this._renderedEditors.get(focused);270}271272get activeCellAndCodeEditor(): [ICellViewModel, ICodeEditor] | undefined {273if (this._isDisposed) {274return;275}276277const [focused] = this._list.getFocusedElements();278const editor = this._renderedEditors.get(focused);279if (!editor) {280return;281}282return [focused, editor];283}284285get codeEditors(): [ICellViewModel, ICodeEditor][] {286return [...this._renderedEditors];287}288289get visibleRanges() {290return this._list ? (this._list.visibleRanges || []) : [];291}292293private _baseCellEditorOptions = new Map<string, IBaseCellEditorOptions>();294295readonly isReplHistory: boolean;296private _readOnly: boolean;297298public readonly scopedContextKeyService: IContextKeyService;299private readonly instantiationService: IInstantiationService;300private readonly _notebookOptions: NotebookOptions;301302private _currentProgress: IProgressRunner | undefined;303304get notebookOptions() {305return this._notebookOptions;306}307308constructor(309readonly creationOptions: INotebookEditorCreationOptions,310dimension: DOM.Dimension | undefined,311@IInstantiationService instantiationService: IInstantiationService,312@IEditorGroupsService editorGroupsService: IEditorGroupsService,313@INotebookRendererMessagingService private readonly notebookRendererMessaging: INotebookRendererMessagingService,314@INotebookEditorService private readonly notebookEditorService: INotebookEditorService,315@INotebookKernelService private readonly notebookKernelService: INotebookKernelService,316@INotebookService private readonly _notebookService: INotebookService,317@IConfigurationService private readonly configurationService: IConfigurationService,318@IContextKeyService contextKeyService: IContextKeyService,319@ILayoutService private readonly layoutService: ILayoutService,320@IContextMenuService private readonly contextMenuService: IContextMenuService,321@ITelemetryService private readonly telemetryService: ITelemetryService,322@INotebookExecutionService private readonly notebookExecutionService: INotebookExecutionService,323@IEditorProgressService private editorProgressService: IEditorProgressService,324@INotebookLoggingService private readonly logService: INotebookLoggingService,325) {326super();327328this._dimension = dimension;329330this.isReplHistory = creationOptions.isReplHistory ?? false;331this._readOnly = creationOptions.isReadOnly ?? false;332333this._overlayContainer = document.createElement('div');334this.scopedContextKeyService = this._register(contextKeyService.createScoped(this._overlayContainer));335this.instantiationService = this._register(instantiationService.createChild(new ServiceCollection([IContextKeyService, this.scopedContextKeyService])));336337this._notebookOptions = creationOptions.options ??338this.instantiationService.createInstance(NotebookOptions, this.creationOptions?.codeWindow ?? mainWindow, this._readOnly, undefined);339this._register(this._notebookOptions);340const eventDispatcher = this._register(new NotebookEventDispatcher());341this._viewContext = new ViewContext(342this._notebookOptions,343eventDispatcher,344language => this.getBaseCellEditorOptions(language));345this._register(this._viewContext.eventDispatcher.onDidChangeLayout(() => {346this._onDidChangeLayout.fire();347}));348this._register(this._viewContext.eventDispatcher.onDidChangeCellState(e => {349this._onDidChangeCellState.fire(e);350}));351352353this._register(_notebookService.onDidChangeOutputRenderers(() => {354this._updateOutputRenderers();355}));356357this._register(this.instantiationService.createInstance(NotebookEditorContextKeys, this));358359this._register(notebookKernelService.onDidChangeSelectedNotebooks(e => {360if (isEqual(e.notebook, this.viewModel?.uri)) {361this._loadKernelPreloads();362this._onDidChangeActiveKernel.fire();363}364}));365366this._scrollBeyondLastLine = this.configurationService.getValue<boolean>('editor.scrollBeyondLastLine');367368this._register(this.configurationService.onDidChangeConfiguration(e => {369if (e.affectsConfiguration('editor.scrollBeyondLastLine')) {370this._scrollBeyondLastLine = this.configurationService.getValue<boolean>('editor.scrollBeyondLastLine');371if (this._dimension && this._isVisible) {372this.layout(this._dimension);373}374}375}));376377this._register(this._notebookOptions.onDidChangeOptions(e => {378if (e.cellStatusBarVisibility || e.cellToolbarLocation || e.cellToolbarInteraction) {379this._updateForNotebookConfiguration();380}381382if (e.fontFamily) {383this._generateFontInfo();384}385386if (e.compactView387|| e.focusIndicator388|| e.insertToolbarPosition389|| e.cellToolbarLocation390|| e.dragAndDropEnabled391|| e.fontSize392|| e.markupFontSize393|| e.markdownLineHeight394|| e.fontFamily395|| e.insertToolbarAlignment396|| e.outputFontSize397|| e.outputLineHeight398|| e.outputFontFamily399|| e.outputWordWrap400|| e.outputScrolling401|| e.outputLinkifyFilePaths402|| e.minimalError403) {404this._styleElement?.remove();405this._createLayoutStyles();406this._webview?.updateOptions({407...this.notebookOptions.computeWebviewOptions(),408fontFamily: this._generateFontFamily()409});410}411412if (this._dimension && this._isVisible) {413this.layout(this._dimension);414}415}));416417const container = creationOptions.codeWindow ? this.layoutService.getContainer(creationOptions.codeWindow) : this.layoutService.mainContainer;418this._register(editorGroupsService.getPart(container).onDidScroll(e => {419if (!this._shadowElement || !this._isVisible) {420return;421}422423this.updateShadowElement(this._shadowElement, this._dimension);424this.layoutContainerOverShadowElement(this._dimension, this._position);425}));426427this.notebookEditorService.addNotebookEditor(this);428429const id = generateUuid();430this._overlayContainer.id = `notebook-${id}`;431this._overlayContainer.className = 'notebookOverlay';432this._overlayContainer.classList.add('notebook-editor');433this._overlayContainer.inert = true;434this._overlayContainer.style.visibility = 'hidden';435436container.appendChild(this._overlayContainer);437438this._createBody(this._overlayContainer);439this._generateFontInfo();440this._isVisible = true;441this._editorFocus = NOTEBOOK_EDITOR_FOCUSED.bindTo(this.scopedContextKeyService);442this._outputFocus = NOTEBOOK_OUTPUT_FOCUSED.bindTo(this.scopedContextKeyService);443this._outputInputFocus = NOTEBOOK_OUTPUT_INPUT_FOCUSED.bindTo(this.scopedContextKeyService);444this._editorEditable = NOTEBOOK_EDITOR_EDITABLE.bindTo(this.scopedContextKeyService);445this._cursorNavMode = NOTEBOOK_CURSOR_NAVIGATION_MODE.bindTo(this.scopedContextKeyService);446// Never display the native cut/copy context menu items in notebooks447new RawContextKey<boolean>(PreventDefaultContextMenuItemsContextKeyName, false).bindTo(this.scopedContextKeyService).set(true);448449this._editorEditable.set(!creationOptions.isReadOnly);450451let contributions: INotebookEditorContributionDescription[];452if (Array.isArray(this.creationOptions.contributions)) {453contributions = this.creationOptions.contributions;454} else {455contributions = NotebookEditorExtensionsRegistry.getEditorContributions();456}457for (const desc of contributions) {458let contribution: INotebookEditorContribution | undefined;459try {460contribution = this.instantiationService.createInstance(desc.ctor, this);461} catch (err) {462onUnexpectedError(err);463}464if (contribution) {465if (!this._contributions.has(desc.id)) {466this._contributions.set(desc.id, contribution);467} else {468contribution.dispose();469throw new Error(`DUPLICATE notebook editor contribution: '${desc.id}'`);470}471}472}473474this._updateForNotebookConfiguration();475}476477private _debugFlag: boolean = false;478479private _debug(...args: unknown[]) {480if (!this._debugFlag) {481return;482}483484notebookDebug(...args);485}486487/**488* EditorId489*/490public getId(): string {491return this._uuid;492}493494getViewModel(): NotebookViewModel | undefined {495return this.viewModel;496}497498getLength() {499return this.viewModel?.length ?? 0;500}501502getSelections() {503return this.viewModel?.getSelections() ?? [{ start: 0, end: 0 }];504}505506setSelections(selections: ICellRange[]) {507if (!this.viewModel) {508return;509}510511const focus = this.viewModel.getFocus();512this.viewModel.updateSelectionsState({513kind: SelectionStateType.Index,514focus: focus,515selections: selections516});517}518519getFocus() {520return this.viewModel?.getFocus() ?? { start: 0, end: 0 };521}522523setFocus(focus: ICellRange) {524if (!this.viewModel) {525return;526}527528const selections = this.viewModel.getSelections();529this.viewModel.updateSelectionsState({530kind: SelectionStateType.Index,531focus: focus,532selections: selections533});534}535536getSelectionViewModels(): ICellViewModel[] {537if (!this.viewModel) {538return [];539}540541const cellsSet = new Set<number>();542543return this.viewModel.getSelections().map(range => this.viewModel!.viewCells.slice(range.start, range.end)).reduce((a, b) => {544b.forEach(cell => {545if (!cellsSet.has(cell.handle)) {546cellsSet.add(cell.handle);547a.push(cell);548}549});550551return a;552}, [] as ICellViewModel[]);553}554555hasModel(): this is IActiveNotebookEditorDelegate {556return !!this._notebookViewModel;557}558559showProgress(): void {560this._currentProgress = this.editorProgressService.show(true);561}562563hideProgress(): void {564if (this._currentProgress) {565this._currentProgress.done();566this._currentProgress = undefined;567}568}569570//#region Editor Core571572getBaseCellEditorOptions(language: string): IBaseCellEditorOptions {573const existingOptions = this._baseCellEditorOptions.get(language);574575if (existingOptions) {576return existingOptions;577} else {578const options = new BaseCellEditorOptions(this, this.notebookOptions, this.configurationService, language);579this._baseCellEditorOptions.set(language, options);580return options;581}582}583584private _updateForNotebookConfiguration() {585if (!this._overlayContainer) {586return;587}588589this._overlayContainer.classList.remove('cell-title-toolbar-left');590this._overlayContainer.classList.remove('cell-title-toolbar-right');591this._overlayContainer.classList.remove('cell-title-toolbar-hidden');592const cellToolbarLocation = this._notebookOptions.computeCellToolbarLocation(this.viewModel?.viewType);593this._overlayContainer.classList.add(`cell-title-toolbar-${cellToolbarLocation}`);594595const cellToolbarInteraction = this._notebookOptions.getDisplayOptions().cellToolbarInteraction;596let cellToolbarInteractionState = 'hover';597this._overlayContainer.classList.remove('cell-toolbar-hover');598this._overlayContainer.classList.remove('cell-toolbar-click');599600if (cellToolbarInteraction === 'hover' || cellToolbarInteraction === 'click') {601cellToolbarInteractionState = cellToolbarInteraction;602}603this._overlayContainer.classList.add(`cell-toolbar-${cellToolbarInteractionState}`);604605}606607private _generateFontInfo(): void {608const editorOptions = this.configurationService.getValue<IEditorOptions>('editor');609const targetWindow = DOM.getWindow(this.getDomNode());610this._fontInfo = FontMeasurements.readFontInfo(targetWindow, createBareFontInfoFromRawSettings(editorOptions, PixelRatio.getInstance(targetWindow).value));611}612613private _createBody(parent: HTMLElement): void {614this._notebookTopToolbarContainer = document.createElement('div');615this._notebookTopToolbarContainer.classList.add('notebook-toolbar-container');616this._notebookTopToolbarContainer.style.display = 'none';617DOM.append(parent, this._notebookTopToolbarContainer);618619this._notebookStickyScrollContainer = document.createElement('div');620this._notebookStickyScrollContainer.classList.add('notebook-sticky-scroll-container');621DOM.append(parent, this._notebookStickyScrollContainer);622623this._body = document.createElement('div');624DOM.append(parent, this._body);625626this._body.classList.add('cell-list-container');627this._createLayoutStyles();628this._createCellList();629630this._notebookOverviewRulerContainer = document.createElement('div');631this._notebookOverviewRulerContainer.classList.add('notebook-overview-ruler-container');632this._list.scrollableElement.appendChild(this._notebookOverviewRulerContainer);633this._registerNotebookOverviewRuler();634635this._register(this.instantiationService.createInstance(NotebookHorizontalTracker, this, this._list.scrollableElement));636637this._overflowContainer = document.createElement('div');638this._overflowContainer.classList.add('notebook-overflow-widget-container', 'monaco-editor');639DOM.append(parent, this._overflowContainer);640}641642private _generateFontFamily() {643return this._fontInfo?.fontFamily ?? `"SF Mono", Monaco, Menlo, Consolas, "Ubuntu Mono", "Liberation Mono", "DejaVu Sans Mono", "Courier New", monospace`;644}645646private _createLayoutStyles(): void {647this._styleElement = domStylesheets.createStyleSheet(this._body);648const {649cellRightMargin,650cellTopMargin,651cellRunGutter,652cellBottomMargin,653codeCellLeftMargin,654markdownCellGutter,655markdownCellLeftMargin,656markdownCellBottomMargin,657markdownCellTopMargin,658collapsedIndicatorHeight,659focusIndicator,660insertToolbarPosition,661outputFontSize,662focusIndicatorLeftMargin,663focusIndicatorGap664} = this._notebookOptions.getLayoutConfiguration();665666const {667insertToolbarAlignment,668compactView,669fontSize670} = this._notebookOptions.getDisplayOptions();671672const getCellEditorContainerLeftMargin = this._notebookOptions.getCellEditorContainerLeftMargin();673674const { bottomToolbarGap, bottomToolbarHeight } = this._notebookOptions.computeBottomToolbarDimensions(this.viewModel?.viewType);675676const styleSheets: string[] = [];677if (!this._fontInfo) {678this._generateFontInfo();679}680681const fontFamily = this._generateFontFamily();682683styleSheets.push(`684.notebook-editor {685--notebook-cell-output-font-size: ${outputFontSize}px;686--notebook-cell-input-preview-font-size: ${fontSize}px;687--notebook-cell-input-preview-font-family: ${fontFamily};688}689`);690691if (compactView) {692styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .markdown-cell-row div.cell.code { margin-left: ${getCellEditorContainerLeftMargin}px; }`);693} else {694styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .markdown-cell-row div.cell.code { margin-left: ${codeCellLeftMargin}px; }`);695}696697// focus indicator698if (focusIndicator === 'border') {699styleSheets.push(`700.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-top:before,701.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-bottom:before,702.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row .cell-inner-container:before,703.monaco-workbench .notebookOverlay .monaco-list .markdown-cell-row .cell-inner-container:after {704content: "";705position: absolute;706width: 100%;707height: 1px;708}709710.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-left:before,711.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-right:before {712content: "";713position: absolute;714width: 1px;715height: 100%;716z-index: 10;717}718719/* top border */720.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-top:before {721border-top: 1px solid transparent;722}723724/* left border */725.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-left:before {726border-left: 1px solid transparent;727}728729/* bottom border */730.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-bottom:before {731border-bottom: 1px solid transparent;732}733734/* right border */735.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-right:before {736border-right: 1px solid transparent;737}738`);739740// left and right border margins741styleSheets.push(`742.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row.focused .cell-focus-indicator-left:before,743.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row.focused .cell-focus-indicator-right:before,744.monaco-workbench .notebookOverlay .monaco-list.selection-multiple .monaco-list-row.code-cell-row.selected .cell-focus-indicator-left:before,745.monaco-workbench .notebookOverlay .monaco-list.selection-multiple .monaco-list-row.code-cell-row.selected .cell-focus-indicator-right:before {746top: -${cellTopMargin}px; height: calc(100% + ${cellTopMargin + cellBottomMargin}px)747}`);748} else {749styleSheets.push(`750.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-left .codeOutput-focus-indicator {751border-left: 3px solid transparent;752border-radius: 4px;753width: 0px;754margin-left: ${focusIndicatorLeftMargin}px;755border-color: var(--vscode-notebook-inactiveFocusedCellBorder) !important;756}757758.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-left .codeOutput-focus-indicator-container,759.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-output-hover .cell-focus-indicator-left .codeOutput-focus-indicator-container,760.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .markdown-cell-hover .cell-focus-indicator-left .codeOutput-focus-indicator-container,761.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row:hover .cell-focus-indicator-left .codeOutput-focus-indicator-container {762display: block;763}764765.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-left .codeOutput-focus-indicator-container:hover .codeOutput-focus-indicator {766border-left: 5px solid transparent;767margin-left: ${focusIndicatorLeftMargin - 1}px;768}769`);770771styleSheets.push(`772.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-inner-container.cell-output-focus .cell-focus-indicator-left .codeOutput-focus-indicator,773.monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row.focused .cell-inner-container .cell-focus-indicator-left .codeOutput-focus-indicator {774border-color: var(--vscode-notebook-focusedCellBorder) !important;775}776777.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-inner-container .cell-focus-indicator-left .output-focus-indicator {778margin-top: ${focusIndicatorGap}px;779}780`);781}782783// between cell insert toolbar784if (insertToolbarPosition === 'betweenCells' || insertToolbarPosition === 'both') {785styleSheets.push(`.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container { display: flex; }`);786styleSheets.push(`.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .view-zones .cell-list-top-cell-toolbar-container { display: flex; }`);787} else {788styleSheets.push(`.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container { display: none; }`);789styleSheets.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; }`);790}791792if (insertToolbarAlignment === 'left') {793styleSheets.push(`794.monaco-workbench .notebookOverlay .cell-list-top-cell-toolbar-container .action-item:first-child,795.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 {796margin-right: 0px !important;797}`);798799styleSheets.push(`800.monaco-workbench .notebookOverlay .cell-list-top-cell-toolbar-container .monaco-toolbar .action-label,801.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 {802padding: 0px !important;803justify-content: center;804border-radius: 4px;805}`);806807styleSheets.push(`808.monaco-workbench .notebookOverlay .cell-list-top-cell-toolbar-container,809.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 {810align-items: flex-start;811justify-content: left;812margin: 0 16px 0 ${8 + codeCellLeftMargin}px;813}`);814815styleSheets.push(`816.monaco-workbench .notebookOverlay .cell-list-top-cell-toolbar-container,817.notebookOverlay .cell-bottom-toolbar-container .action-item {818border: 0px;819}`);820}821822styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .code-cell-row div.cell.code { margin-left: ${getCellEditorContainerLeftMargin}px; }`);823// Chat Edit, deleted Cell Overlay824styleSheets.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; }`);825// Chat Edit, deleted Cell Overlay826styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .view-zones .code-cell-row div.cell { margin-right: ${cellRightMargin}px; }`);827styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row div.cell { margin-right: ${cellRightMargin}px; }`);828styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row > .cell-inner-container { padding-top: ${cellTopMargin}px; }`);829styleSheets.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; }`);830styleSheets.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; }`);831styleSheets.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; }`);832styleSheets.push(`.notebookOverlay .output { margin: 0px ${cellRightMargin}px 0px ${getCellEditorContainerLeftMargin}px; }`);833styleSheets.push(`.notebookOverlay .output { width: calc(100% - ${getCellEditorContainerLeftMargin + cellRightMargin}px); }`);834835// comment836styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-comment-container { left: ${getCellEditorContainerLeftMargin}px; }`);837styleSheets.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); }`);838839// output collapse button840styleSheets.push(`.monaco-workbench .notebookOverlay .output .output-collapse-container .expandButton { left: -${cellRunGutter}px; }`);841styleSheets.push(`.monaco-workbench .notebookOverlay .output .output-collapse-container .expandButton {842position: absolute;843width: ${cellRunGutter}px;844padding: 6px 0px;845}`);846847// show more container848styleSheets.push(`.notebookOverlay .output-show-more-container { margin: 0px ${cellRightMargin}px 0px ${getCellEditorContainerLeftMargin}px; }`);849styleSheets.push(`.notebookOverlay .output-show-more-container { width: calc(100% - ${getCellEditorContainerLeftMargin + cellRightMargin}px); }`);850851styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row div.cell.markdown { padding-left: ${cellRunGutter}px; }`);852styleSheets.push(`.monaco-workbench .notebookOverlay > .cell-list-container .notebook-folding-indicator { left: ${(markdownCellGutter - 20) / 2 + markdownCellLeftMargin}px; }`);853styleSheets.push(`.notebookOverlay > .cell-list-container .notebook-folded-hint { left: ${markdownCellGutter + markdownCellLeftMargin + 8}px; }`);854styleSheets.push(`.notebookOverlay .monaco-list .monaco-list-row :not(.webview-backed-markdown-cell) .cell-focus-indicator-top { height: ${cellTopMargin}px; }`);855styleSheets.push(`.notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-side { bottom: ${bottomToolbarGap}px; }`);856styleSheets.push(`.notebookOverlay .monaco-list .monaco-list-row.code-cell-row .cell-focus-indicator-left { width: ${getCellEditorContainerLeftMargin}px; }`);857styleSheets.push(`.notebookOverlay .monaco-list .monaco-list-row.markdown-cell-row .cell-focus-indicator-left { width: ${codeCellLeftMargin}px; }`);858styleSheets.push(`.notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator.cell-focus-indicator-right { width: ${cellRightMargin}px; }`);859styleSheets.push(`.notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-bottom { height: ${cellBottomMargin}px; }`);860styleSheets.push(`.notebookOverlay .monaco-list .monaco-list-row .cell-shadow-container-bottom { top: ${cellBottomMargin}px; }`);861862styleSheets.push(`863.notebookOverlay .monaco-list.selection-multiple .monaco-list-row:has(+ .monaco-list-row.selected) .cell-focus-indicator-bottom {864height: ${bottomToolbarGap + cellBottomMargin}px;865}866`);867868styleSheets.push(`869.notebookOverlay .monaco-list .monaco-list-row.code-cell-row.nb-multiCellHighlight:has(+ .monaco-list-row.nb-multiCellHighlight) .cell-focus-indicator-bottom {870height: ${bottomToolbarGap + cellBottomMargin}px;871background-color: var(--vscode-notebook-symbolHighlightBackground) !important;872}873874.notebookOverlay .monaco-list .monaco-list-row.markdown-cell-row.nb-multiCellHighlight:has(+ .monaco-list-row.nb-multiCellHighlight) .cell-focus-indicator-bottom {875height: ${bottomToolbarGap + cellBottomMargin - 6}px;876background-color: var(--vscode-notebook-symbolHighlightBackground) !important;877}878`);879880881styleSheets.push(`882.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .input-collapse-container .cell-collapse-preview {883line-height: ${collapsedIndicatorHeight}px;884}885886.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 {887max-height: ${collapsedIndicatorHeight}px;888}889`);890891styleSheets.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 }`);892styleSheets.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 }`);893894// cell toolbar895styleSheets.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 {896right: ${cellRightMargin + 26}px;897}898.monaco-workbench .notebookOverlay.cell-title-toolbar-left > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-title-toolbar {899left: ${getCellEditorContainerLeftMargin + 16}px;900}901.monaco-workbench .notebookOverlay.cell-title-toolbar-hidden > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-title-toolbar {902display: none;903}`);904905// cell output innert container906styleSheets.push(`907.monaco-workbench .notebookOverlay .output > div.foreground.output-inner-container {908padding: ${OutputInnerContainerTopPadding}px 8px;909}910.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .output-collapse-container {911padding: ${OutputInnerContainerTopPadding}px 8px;912}913`);914915// chat916styleSheets.push(`917.monaco-workbench .notebookOverlay .cell-chat-part {918margin: 0 ${cellRightMargin}px 6px 4px;919}920`);921922this._styleElement.textContent = styleSheets.join('\n');923}924925private _createCellList(): void {926this._body.classList.add('cell-list-container');927this._dndController = this._register(new CellDragAndDropController(this, this._body));928const getScopedContextKeyService = (container: HTMLElement) => this._list.contextKeyService.createScoped(container);929this._editorPool = this._register(this.instantiationService.createInstance(NotebookCellEditorPool, this, getScopedContextKeyService));930const renderers = [931this.instantiationService.createInstance(CodeCellRenderer, this, this._renderedEditors, this._editorPool, this._dndController, getScopedContextKeyService),932this.instantiationService.createInstance(MarkupCellRenderer, this, this._dndController, this._renderedEditors, getScopedContextKeyService),933];934935renderers.forEach(renderer => {936this._register(renderer);937});938939this._listDelegate = this.instantiationService.createInstance(NotebookCellListDelegate, DOM.getWindow(this.getDomNode()));940this._register(this._listDelegate);941942const accessibilityProvider = this.instantiationService.createInstance(NotebookAccessibilityProvider, () => this.viewModel, this.isReplHistory);943this._register(accessibilityProvider);944945this._list = this.instantiationService.createInstance(946NotebookCellList,947'NotebookCellList',948this._body,949this._viewContext.notebookOptions,950this._listDelegate,951renderers,952this.scopedContextKeyService,953{954setRowLineHeight: false,955setRowHeight: false,956supportDynamicHeights: true,957horizontalScrolling: false,958keyboardSupport: false,959mouseSupport: true,960multipleSelectionSupport: true,961selectionNavigation: true,962typeNavigationEnabled: true,963paddingTop: 0,964paddingBottom: 0,965transformOptimization: false, //(isMacintosh && isNative) || getTitleBarStyle(this.configurationService, this.environmentService) === 'native',966initialSize: this._dimension,967styleController: (_suffix: string) => { return this._list; },968overrideStyles: {969listBackground: notebookEditorBackground,970listActiveSelectionBackground: notebookEditorBackground,971listActiveSelectionForeground: foreground,972listFocusAndSelectionBackground: notebookEditorBackground,973listFocusAndSelectionForeground: foreground,974listFocusBackground: notebookEditorBackground,975listFocusForeground: foreground,976listHoverForeground: foreground,977listHoverBackground: notebookEditorBackground,978listHoverOutline: focusBorder,979listFocusOutline: focusBorder,980listInactiveSelectionBackground: notebookEditorBackground,981listInactiveSelectionForeground: foreground,982listInactiveFocusBackground: notebookEditorBackground,983listInactiveFocusOutline: notebookEditorBackground,984},985accessibilityProvider986},987);988this._cellLayoutManager = new NotebookCellLayoutManager(this, this._list, this.logService);989this._dndController.setList(this._list);990991// create Webview992993this._register(this._list);994this._listViewInfoAccessor = new ListViewInfoAccessor(this._list);995this._register(this._listViewInfoAccessor);996997this._register(combinedDisposable(...renderers));998999// top cell toolbar1000this._listTopCellToolbar = this._register(this.instantiationService.createInstance(ListTopCellToolbar, this, this.notebookOptions));10011002// transparent cover1003this._webviewTransparentCover = DOM.append(this._list.rowsContainer, $('.webview-cover'));1004this._webviewTransparentCover.style.display = 'none';10051006this._register(DOM.addStandardDisposableGenericMouseDownListener(this._overlayContainer, (e: StandardMouseEvent) => {1007if (e.target.classList.contains('slider') && this._webviewTransparentCover) {1008this._webviewTransparentCover.style.display = 'block';1009}1010}));10111012this._register(DOM.addStandardDisposableGenericMouseUpListener(this._overlayContainer, () => {1013if (this._webviewTransparentCover) {1014// no matter when1015this._webviewTransparentCover.style.display = 'none';1016}1017}));10181019this._register(this._list.onMouseDown(e => {1020if (e.element) {1021this._onMouseDown.fire({ event: e.browserEvent, target: e.element });1022}1023}));10241025this._register(this._list.onMouseUp(e => {1026if (e.element) {1027this._onMouseUp.fire({ event: e.browserEvent, target: e.element });1028}1029}));10301031this._register(this._list.onDidChangeFocus(_e => {1032this._onDidChangeActiveEditor.fire(this);1033this._onDidChangeActiveCell.fire();1034this._onDidChangeFocus.fire();1035this._cursorNavMode.set(false);1036}));10371038this._register(this._list.onContextMenu(e => {1039this.showListContextMenu(e);1040}));10411042this._register(this._list.onDidChangeVisibleRanges(() => {1043this._onDidChangeVisibleRanges.fire();1044}));10451046this._register(this._list.onDidScroll((e) => {1047if (e.scrollTop !== e.oldScrollTop) {1048this._onDidScroll.fire();1049this.clearActiveCellWidgets();1050}10511052if (e.scrollTop === e.oldScrollTop && e.scrollHeightChanged) {1053this._onDidChangeLayout.fire();1054}1055}));10561057this._focusTracker = this._register(DOM.trackFocus(this.getDomNode()));1058this._register(this._focusTracker.onDidBlur(() => {1059this._editorFocus.set(false);1060this.viewModel?.setEditorFocus(false);1061this._onDidBlurEmitter.fire();1062}));1063this._register(this._focusTracker.onDidFocus(() => {1064this._editorFocus.set(true);1065this.viewModel?.setEditorFocus(true);1066this._onDidFocusEmitter.fire();1067}));10681069this._registerNotebookActionsToolbar();1070this._registerNotebookStickyScroll();10711072this._register(this.configurationService.onDidChangeConfiguration(e => {1073if (e.affectsConfiguration(accessibilityProvider.verbositySettingId)) {1074this._list.ariaLabel = accessibilityProvider?.getWidgetAriaLabel();1075}1076}));1077}10781079private showListContextMenu(e: IListContextMenuEvent<CellViewModel>) {1080this.contextMenuService.showContextMenu({1081menuId: MenuId.NotebookCellTitle,1082menuActionOptions: {1083shouldForwardArgs: true1084},1085contextKeyService: this.scopedContextKeyService,1086getAnchor: () => e.anchor,1087getActionsContext: () => {1088return {1089from: 'cellContainer'1090};1091}1092});1093}10941095private _registerNotebookOverviewRuler() {1096this._notebookOverviewRuler = this._register(this.instantiationService.createInstance(NotebookOverviewRuler, this, this._notebookOverviewRulerContainer));1097}10981099private _registerNotebookActionsToolbar() {1100this._notebookTopToolbar = this._register(this.instantiationService.createInstance(NotebookEditorWorkbenchToolbar, this, this.scopedContextKeyService, this._notebookOptions, this._notebookTopToolbarContainer));1101this._register(this._notebookTopToolbar.onDidChangeVisibility(() => {1102if (this._dimension && this._isVisible) {1103this.layout(this._dimension);1104}1105}));1106}11071108private _registerNotebookStickyScroll() {1109this._notebookStickyScroll = this._register(this.instantiationService.createInstance(NotebookStickyScroll, this._notebookStickyScrollContainer, this, this._list, (sizeDelta) => {1110if (this.isDisposed) {1111return;1112}11131114if (this._dimension && this._isVisible) {1115if (sizeDelta > 0) { // delta > 0 ==> sticky is growing, cell list shrinking1116this.layout(this._dimension);1117this.setScrollTop(this.scrollTop + sizeDelta);1118} else if (sizeDelta < 0) { // delta < 0 ==> sticky is shrinking, cell list growing1119this.setScrollTop(this.scrollTop + sizeDelta);1120this.layout(this._dimension);1121}1122}11231124this._onDidScroll.fire();1125}));1126}11271128private _updateOutputRenderers() {1129if (!this.viewModel || !this._webview) {1130return;1131}11321133this._webview.updateOutputRenderers();1134this.viewModel.viewCells.forEach(cell => {1135cell.outputsViewModels.forEach(output => {1136if (output.pickedMimeType?.rendererId === RENDERER_NOT_AVAILABLE) {1137output.resetRenderer();1138}1139});1140});1141}11421143getDomNode() {1144return this._overlayContainer;1145}11461147getOverflowContainerDomNode() {1148return this._overflowContainer;1149}11501151getInnerWebview(): IWebviewElement | undefined {1152return this._webview?.webview;1153}11541155setEditorProgressService(editorProgressService: IEditorProgressService): void {1156this.editorProgressService = editorProgressService;1157}11581159setParentContextKeyService(parentContextKeyService: IContextKeyService): void {1160this.scopedContextKeyService.updateParent(parentContextKeyService);1161}11621163async setModel(textModel: NotebookTextModel, viewState: INotebookEditorViewState | undefined, perf?: NotebookPerfMarks, viewType?: string): Promise<void> {1164if (this.viewModel === undefined || !this.viewModel.equal(textModel)) {1165const oldBottomToolbarDimensions = this._notebookOptions.computeBottomToolbarDimensions(this.viewModel?.viewType);1166this._detachModel();1167await this._attachModel(textModel, viewType ?? textModel.viewType, viewState, perf);1168const newBottomToolbarDimensions = this._notebookOptions.computeBottomToolbarDimensions(this.viewModel?.viewType);11691170if (oldBottomToolbarDimensions.bottomToolbarGap !== newBottomToolbarDimensions.bottomToolbarGap1171|| oldBottomToolbarDimensions.bottomToolbarHeight !== newBottomToolbarDimensions.bottomToolbarHeight) {1172this._styleElement?.remove();1173this._createLayoutStyles();1174this._webview?.updateOptions({1175...this.notebookOptions.computeWebviewOptions(),1176fontFamily: this._generateFontFamily()1177});1178}1179type WorkbenchNotebookOpenClassification = {1180owner: 'rebornix';1181comment: 'Identify the notebook editor view type';1182scheme: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'File system provider scheme for the resource' };1183ext: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'File extension for the resource' };1184viewType: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'View type of the notebook editor' };1185isRepl: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the notebook editor is within a REPL editor' };1186};11871188type WorkbenchNotebookOpenEvent = {1189scheme: string;1190ext: string;1191viewType: string;1192isRepl: boolean;1193};11941195this.telemetryService.publicLog2<WorkbenchNotebookOpenEvent, WorkbenchNotebookOpenClassification>('notebook/editorOpened', {1196scheme: textModel.uri.scheme,1197ext: extname(textModel.uri),1198viewType: textModel.viewType,1199isRepl: this.isReplHistory1200});1201} else {1202this.restoreListViewState(viewState);1203}12041205this._restoreSelectedKernel(viewState);12061207// load preloads for matching kernel1208this._loadKernelPreloads();12091210// clear state1211this._dndController?.clearGlobalDragState();12121213this._localStore.add(this._list.onDidChangeFocus(() => {1214this.updateContextKeysOnFocusChange();1215}));12161217this.updateContextKeysOnFocusChange();1218// render markdown top down on idle1219this._backgroundMarkdownRendering();1220}12211222private _backgroundMarkdownRenderRunning = false;1223private _backgroundMarkdownRendering() {1224if (this._backgroundMarkdownRenderRunning) {1225return;1226}12271228this._backgroundMarkdownRenderRunning = true;1229DOM.runWhenWindowIdle(DOM.getWindow(this.getDomNode()), (deadline) => {1230this._backgroundMarkdownRenderingWithDeadline(deadline);1231});1232}12331234private _backgroundMarkdownRenderingWithDeadline(deadline: IdleDeadline) {1235const endTime = Date.now() + deadline.timeRemaining();12361237const execute = () => {1238try {1239this._backgroundMarkdownRenderRunning = true;1240if (this._isDisposed) {1241return;1242}12431244if (!this.viewModel) {1245return;1246}12471248const firstMarkupCell = this.viewModel.viewCells.find(cell => cell.cellKind === CellKind.Markup && !this._webview?.markupPreviewMapping.has(cell.id) && !this.cellIsHidden(cell)) as MarkupCellViewModel | undefined;1249if (!firstMarkupCell) {1250return;1251}12521253this.createMarkupPreview(firstMarkupCell);1254} finally {1255this._backgroundMarkdownRenderRunning = false;1256}12571258if (Date.now() < endTime) {1259setTimeout0(execute);1260} else {1261this._backgroundMarkdownRendering();1262}1263};12641265execute();1266}12671268private updateContextKeysOnFocusChange() {1269if (!this.viewModel) {1270return;1271}12721273const focused = this._list.getFocusedElements()[0];1274if (focused) {1275if (!this._cellContextKeyManager) {1276this._cellContextKeyManager = this._localStore.add(this.instantiationService.createInstance(CellContextKeyManager, this, focused as CellViewModel));1277}12781279this._cellContextKeyManager.updateForElement(focused as CellViewModel);1280}1281}12821283async setOptions(options: INotebookEditorOptions | undefined) {1284if (options?.isReadOnly !== undefined) {1285this._readOnly = options?.isReadOnly;1286}12871288if (!this.viewModel) {1289return;1290}12911292this.viewModel.updateOptions({ isReadOnly: this._readOnly });1293this.notebookOptions.updateOptions(this._readOnly);12941295// reveal cell if editor options tell to do so1296const cellOptions = options?.cellOptions ?? this._parseIndexedCellOptions(options);1297if (cellOptions) {1298const cell = this.viewModel.viewCells.find(cell => cell.uri.toString() === cellOptions.resource.toString());1299if (cell) {1300this.focusElement(cell);1301const selection = cellOptions.options?.selection;1302if (selection) {1303cell.updateEditState(CellEditState.Editing, 'setOptions');1304cell.focusMode = CellFocusMode.Editor;1305await this.revealRangeInCenterIfOutsideViewportAsync(cell, new Range(selection.startLineNumber, selection.startColumn, selection.endLineNumber || selection.startLineNumber, selection.endColumn || selection.startColumn));1306} else {1307this._list.revealCell(cell, options?.cellRevealType ?? CellRevealType.CenterIfOutsideViewport);1308}13091310const editor = this._renderedEditors.get(cell)!;1311if (editor) {1312if (cellOptions.options?.selection) {1313const { selection } = cellOptions.options;1314const editorSelection = new Range(selection.startLineNumber, selection.startColumn, selection.endLineNumber || selection.startLineNumber, selection.endColumn || selection.startColumn);1315editor.setSelection(editorSelection);1316editor.revealPositionInCenterIfOutsideViewport({1317lineNumber: selection.startLineNumber,1318column: selection.startColumn1319});1320await this.revealRangeInCenterIfOutsideViewportAsync(cell, editorSelection);1321}1322if (!cellOptions.options?.preserveFocus) {1323editor.focus();1324}1325}1326}1327}13281329// select cells if options tell to do so1330// todo@rebornix https://github.com/microsoft/vscode/issues/118108 support selections not just focus1331// todo@rebornix support multipe selections1332if (options?.cellSelections) {1333const focusCellIndex = options.cellSelections[0].start;1334const focusedCell = this.viewModel.cellAt(focusCellIndex);1335if (focusedCell) {1336this.viewModel.updateSelectionsState({1337kind: SelectionStateType.Index,1338focus: { start: focusCellIndex, end: focusCellIndex + 1 },1339selections: options.cellSelections1340});1341this.revealInCenterIfOutsideViewport(focusedCell);1342}1343}13441345this._updateForOptions();1346this._onDidChangeOptions.fire();1347}13481349private _parseIndexedCellOptions(options: INotebookEditorOptions | undefined) {1350if (options?.indexedCellOptions) {1351// convert index based selections1352const cell = this.cellAt(options.indexedCellOptions.index);1353if (cell) {1354return {1355resource: cell.uri,1356options: {1357selection: options.indexedCellOptions.selection,1358preserveFocus: false1359}1360};1361}1362}13631364return undefined;1365}13661367private _detachModel() {1368this._localStore.clear();1369dispose(this._localCellStateListeners);1370this._list.detachViewModel();1371this.viewModel?.dispose();1372// avoid event1373this.viewModel = undefined;1374this._webview?.dispose();1375this._webview?.element.remove();1376this._webview = null;1377this._list.clear();1378}137913801381private _updateForOptions(): void {1382if (!this.viewModel) {1383return;1384}13851386this._editorEditable.set(!this.viewModel.options.isReadOnly);1387this._overflowContainer.classList.toggle('notebook-editor-editable', !this.viewModel.options.isReadOnly);1388this.getDomNode().classList.toggle('notebook-editor-editable', !this.viewModel.options.isReadOnly);1389}13901391private async _resolveWebview(): Promise<BackLayerWebView<ICommonCellInfo> | null> {1392if (!this.textModel) {1393return null;1394}13951396if (this._webviewResolvePromise) {1397return this._webviewResolvePromise;1398}13991400if (!this._webview) {1401this._ensureWebview(this.getId(), this.textModel.viewType, this.textModel.uri);1402}14031404this._webviewResolvePromise = (async () => {1405if (!this._webview) {1406throw new Error('Notebook output webview object is not created successfully.');1407}14081409await this._webview.createWebview(this.creationOptions.codeWindow ?? mainWindow);1410if (!this._webview.webview) {1411throw new Error('Notebook output webview element was not created successfully.');1412}14131414this._localStore.add(this._webview.webview.onDidBlur(() => {1415this._outputFocus.set(false);1416this._webviewFocused = false;14171418this.updateEditorFocus();1419this.updateCellFocusMode();1420}));14211422this._localStore.add(this._webview.webview.onDidFocus(() => {1423this._outputFocus.set(true);1424this.updateEditorFocus();1425this._webviewFocused = true;1426}));14271428this._localStore.add(this._webview.onMessage(e => {1429this._onDidReceiveMessage.fire(e);1430}));14311432return this._webview;1433})();14341435return this._webviewResolvePromise;1436}14371438private _ensureWebview(id: string, viewType: string, resource: URI) {1439if (this._webview) {1440return;1441}14421443const that = this;14441445this._webview = this.instantiationService.createInstance(BackLayerWebView, {1446get creationOptions() { return that.creationOptions; },1447setScrollTop(scrollTop: number) { that._list.scrollTop = scrollTop; },1448triggerScroll(event: IMouseWheelEvent) { that._list.triggerScrollFromMouseWheelEvent(event); },1449getCellByInfo: that.getCellByInfo.bind(that),1450getCellById: that._getCellById.bind(that),1451toggleNotebookCellSelection: that._toggleNotebookCellSelection.bind(that),1452focusNotebookCell: that.focusNotebookCell.bind(that),1453focusNextNotebookCell: that.focusNextNotebookCell.bind(that),1454updateOutputHeight: that._updateOutputHeight.bind(that),1455scheduleOutputHeightAck: that._scheduleOutputHeightAck.bind(that),1456updateMarkupCellHeight: that._updateMarkupCellHeight.bind(that),1457setMarkupCellEditState: that._setMarkupCellEditState.bind(that),1458didStartDragMarkupCell: that._didStartDragMarkupCell.bind(that),1459didDragMarkupCell: that._didDragMarkupCell.bind(that),1460didDropMarkupCell: that._didDropMarkupCell.bind(that),1461didEndDragMarkupCell: that._didEndDragMarkupCell.bind(that),1462didResizeOutput: that._didResizeOutput.bind(that),1463updatePerformanceMetadata: that._updatePerformanceMetadata.bind(that),1464didFocusOutputInputChange: that._didFocusOutputInputChange.bind(that),1465}, id, viewType, resource, {1466...this._notebookOptions.computeWebviewOptions(),1467fontFamily: this._generateFontFamily()1468}, this.notebookRendererMessaging.getScoped(this._uuid));14691470this._webview.element.style.width = '100%';14711472// attach the webview container to the DOM tree first1473this._list.attachWebview(this._webview.element);1474}14751476private async _attachModel(textModel: NotebookTextModel, viewType: string, viewState: INotebookEditorViewState | undefined, perf?: NotebookPerfMarks) {1477this._ensureWebview(this.getId(), textModel.viewType, textModel.uri);14781479this.viewModel = this.instantiationService.createInstance(NotebookViewModel, viewType, textModel, this._viewContext, this.getLayoutInfo(), { isReadOnly: this._readOnly });1480this._viewContext.eventDispatcher.emit([new NotebookLayoutChangedEvent({ width: true, fontInfo: true }, this.getLayoutInfo())]);1481this.notebookOptions.updateOptions(this._readOnly);14821483this._updateForOptions();1484this._updateForNotebookConfiguration();14851486// restore view states, including contributions14871488{1489// restore view state1490this.viewModel.restoreEditorViewState(viewState);14911492// contribution state restore14931494const contributionsState = viewState?.contributionsState || {};1495for (const [id, contribution] of this._contributions) {1496if (typeof contribution.restoreViewState === 'function') {1497contribution.restoreViewState(contributionsState[id]);1498}1499}1500}15011502this._localStore.add(this.viewModel.onDidChangeViewCells(e => {1503this._onDidChangeViewCells.fire(e);1504}));15051506this._localStore.add(this.viewModel.onDidChangeSelection(() => {1507this._onDidChangeSelection.fire();1508this.updateSelectedMarkdownPreviews();1509}));15101511this._localStore.add(this._list.onWillScroll(e => {1512if (this._webview?.isResolved()) {1513this._webviewTransparentCover!.style.transform = `translateY(${e.scrollTop})`;1514}1515}));15161517let hasPendingChangeContentHeight = false;1518const renderScrollHeightDisposable = this._localStore.add(new MutableDisposable());1519this._localStore.add(this._list.onDidChangeContentHeight(() => {1520if (hasPendingChangeContentHeight) {1521return;1522}1523hasPendingChangeContentHeight = true;15241525renderScrollHeightDisposable.value = DOM.scheduleAtNextAnimationFrame(DOM.getWindow(this.getDomNode()), () => {1526hasPendingChangeContentHeight = false;1527this._updateScrollHeight();1528}, 100);1529}));15301531this._localStore.add(this._list.onDidRemoveOutputs(outputs => {1532outputs.forEach(output => this.removeInset(output));1533}));1534this._localStore.add(this._list.onDidHideOutputs(outputs => {1535outputs.forEach(output => this.hideInset(output));1536}));1537this._localStore.add(this._list.onDidRemoveCellsFromView(cells => {1538const hiddenCells: MarkupCellViewModel[] = [];1539const deletedCells: MarkupCellViewModel[] = [];15401541for (const cell of cells) {1542if (cell.cellKind === CellKind.Markup) {1543const mdCell = cell as MarkupCellViewModel;1544if (this.viewModel?.viewCells.find(cell => cell.handle === mdCell.handle)) {1545// Cell has been folded but is still in model1546hiddenCells.push(mdCell);1547} else {1548// Cell was deleted1549deletedCells.push(mdCell);1550}1551}1552}15531554this.hideMarkupPreviews(hiddenCells);1555this.deleteMarkupPreviews(deletedCells);1556}));15571558// init rendering1559await this._warmupWithMarkdownRenderer(this.viewModel, viewState, perf);15601561perf?.mark('customMarkdownLoaded');15621563// model attached1564this._localCellStateListeners = this.viewModel.viewCells.map(cell => this._bindCellListener(cell));1565this._lastCellWithEditorFocus = this.viewModel.viewCells.find(viewCell => this.getActiveCell() === viewCell && viewCell.focusMode === CellFocusMode.Editor) ?? null;15661567this._localStore.add(this.viewModel.onDidChangeViewCells((e) => {1568if (this._isDisposed) {1569return;1570}15711572// update cell listener1573[...e.splices].reverse().forEach(splice => {1574const [start, deleted, newCells] = splice;1575const deletedCells = this._localCellStateListeners.splice(start, deleted, ...newCells.map(cell => this._bindCellListener(cell)));15761577dispose(deletedCells);1578});15791580if (e.splices.some(s => s[2].some(cell => cell.cellKind === CellKind.Markup))) {1581this._backgroundMarkdownRendering();1582}1583}));15841585if (this._dimension) {1586this._list.layout(this.getBodyHeight(this._dimension.height), this._dimension.width);1587} else {1588this._list.layout();1589}15901591this._dndController?.clearGlobalDragState();15921593// restore list state at last, it must be after list layout1594this.restoreListViewState(viewState);1595}15961597private _bindCellListener(cell: ICellViewModel) {1598const store = new DisposableStore();15991600store.add(cell.onDidChangeLayout(e => {1601// e.totalHeight will be false it's not changed1602if (e.totalHeight || e.outerWidth) {1603this.layoutNotebookCell(cell, cell.layoutInfo.totalHeight, e.context);1604}1605}));16061607if (cell.cellKind === CellKind.Code) {1608store.add((cell as CodeCellViewModel).onDidRemoveOutputs((outputs) => {1609outputs.forEach(output => this.removeInset(output));1610}));1611}16121613store.add((cell as CellViewModel).onDidChangeState(e => {1614if (e.inputCollapsedChanged && cell.isInputCollapsed && cell.cellKind === CellKind.Markup) {1615this.hideMarkupPreviews([(cell as MarkupCellViewModel)]);1616}16171618if (e.outputCollapsedChanged && cell.isOutputCollapsed && cell.cellKind === CellKind.Code) {1619cell.outputsViewModels.forEach(output => this.hideInset(output));1620}16211622if (e.focusModeChanged) {1623this._validateCellFocusMode(cell);1624}1625}));16261627store.add(cell.onCellDecorationsChanged(e => {1628e.added.forEach(options => {1629if (options.className) {1630this.deltaCellContainerClassNames(cell.id, [options.className], [], cell.cellKind);1631}16321633if (options.outputClassName) {1634this.deltaCellContainerClassNames(cell.id, [options.outputClassName], [], cell.cellKind);1635}1636});16371638e.removed.forEach(options => {1639if (options.className) {1640this.deltaCellContainerClassNames(cell.id, [], [options.className], cell.cellKind);1641}16421643if (options.outputClassName) {1644this.deltaCellContainerClassNames(cell.id, [], [options.outputClassName], cell.cellKind);1645}1646});1647}));16481649return store;1650}165116521653private _lastCellWithEditorFocus: ICellViewModel | null = null;1654private _validateCellFocusMode(cell: ICellViewModel) {1655if (cell.focusMode !== CellFocusMode.Editor) {1656return;1657}16581659if (this._lastCellWithEditorFocus && this._lastCellWithEditorFocus !== cell) {1660this._lastCellWithEditorFocus.focusMode = CellFocusMode.Container;1661}16621663this._lastCellWithEditorFocus = cell;1664}16651666private async _warmupWithMarkdownRenderer(viewModel: NotebookViewModel, viewState: INotebookEditorViewState | undefined, perf?: NotebookPerfMarks) {16671668this.logService.debug('NotebookEditorWidget', 'warmup ' + this.viewModel?.uri.toString());1669await this._resolveWebview();1670perf?.mark('webviewCommLoaded');16711672this.logService.debug('NotebookEditorWidget', 'warmup - webview resolved');16731674// 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 yet1675this._webview!.element.style.visibility = 'hidden';1676// warm up can take around 200ms to load markdown libraries, etc.1677await this._warmupViewportMarkdownCells(viewModel, viewState);1678this.logService.debug('NotebookEditorWidget', 'warmup - viewport warmed up');16791680// todo@rebornix @mjbvz, is this too complicated?16811682/* now the webview is ready, and requests to render markdown are fast enough1683* we can start rendering the list view1684* render1685* - markdown cell -> request to webview to (10ms, basically just latency between UI and iframe)1686* - code cell -> render in place1687*/1688this._list.layout(0, 0);1689this._list.attachViewModel(viewModel);16901691// now the list widget has a correct contentHeight/scrollHeight1692// setting scrollTop will work properly1693// after setting scroll top, the list view will update `top` of the scrollable element, e.g. `top: -584px`1694this._list.scrollTop = viewState?.scrollPosition?.top ?? 0;1695this._debug('finish initial viewport warmup and view state restore.');1696this._webview!.element.style.visibility = 'visible';1697this.logService.debug('NotebookEditorWidget', 'warmup - list view model attached, set to visible');1698this._onDidAttachViewModel.fire();1699}17001701private async _warmupViewportMarkdownCells(viewModel: NotebookViewModel, viewState: INotebookEditorViewState | undefined) {1702if (viewState && viewState.cellTotalHeights) {1703const totalHeightCache = viewState.cellTotalHeights;1704const scrollTop = viewState.scrollPosition?.top ?? 0;1705const scrollBottom = scrollTop + Math.max(this._dimension?.height ?? 0, 1080);17061707let offset = 0;1708const requests: [ICellViewModel, number][] = [];17091710for (let i = 0; i < viewModel.length; i++) {1711const cell = viewModel.cellAt(i)!;1712const cellHeight = totalHeightCache[i] ?? 0;17131714if (offset + cellHeight < scrollTop) {1715offset += cellHeight;1716continue;1717}17181719if (cell.cellKind === CellKind.Markup) {1720requests.push([cell, offset]);1721}17221723offset += cellHeight;17241725if (offset > scrollBottom) {1726break;1727}1728}17291730await this._webview!.initializeMarkup(requests.map(([model, offset]) => this.createMarkupCellInitialization(model, offset)));1731} else {1732const initRequests = viewModel.viewCells1733.filter(cell => cell.cellKind === CellKind.Markup)1734.slice(0, 5)1735.map(cell => this.createMarkupCellInitialization(cell, -10000));17361737await this._webview!.initializeMarkup(initRequests);17381739// no cached view state so we are rendering the first viewport1740// after above async call, we already get init height for markdown cells, we can update their offset1741let offset = 0;1742const offsetUpdateRequests: { id: string; top: number }[] = [];1743const scrollBottom = Math.max(this._dimension?.height ?? 0, 1080);1744for (const cell of viewModel.viewCells) {1745if (cell.cellKind === CellKind.Markup) {1746offsetUpdateRequests.push({ id: cell.id, top: offset });1747}17481749offset += cell.getHeight(this.getLayoutInfo().fontInfo.lineHeight);17501751if (offset > scrollBottom) {1752break;1753}1754}17551756this._webview?.updateScrollTops([], offsetUpdateRequests);1757}1758}17591760private createMarkupCellInitialization(model: ICellViewModel, offset: number): IMarkupCellInitialization {1761return ({1762mime: model.mime,1763cellId: model.id,1764cellHandle: model.handle,1765content: model.getText(),1766offset: offset,1767visible: false,1768metadata: model.metadata,1769});1770}17711772restoreListViewState(viewState: INotebookEditorViewState | undefined): void {1773if (!this.viewModel) {1774return;1775}17761777if (viewState?.scrollPosition !== undefined) {1778this._list.scrollTop = viewState.scrollPosition.top;1779this._list.scrollLeft = viewState.scrollPosition.left;1780} else {1781this._list.scrollTop = 0;1782this._list.scrollLeft = 0;1783}17841785const focusIdx = typeof viewState?.focus === 'number' ? viewState.focus : 0;1786if (focusIdx < this.viewModel.length) {1787const element = this.viewModel.cellAt(focusIdx);1788if (element) {1789this.viewModel?.updateSelectionsState({1790kind: SelectionStateType.Handle,1791primary: element.handle,1792selections: [element.handle]1793});1794}1795} else if (this._list.length > 0) {1796this.viewModel.updateSelectionsState({1797kind: SelectionStateType.Index,1798focus: { start: 0, end: 1 },1799selections: [{ start: 0, end: 1 }]1800});1801}18021803if (viewState?.editorFocused) {1804const cell = this.viewModel.cellAt(focusIdx);1805if (cell) {1806cell.focusMode = CellFocusMode.Editor;1807}1808}1809}18101811private _restoreSelectedKernel(viewState: INotebookEditorViewState | undefined): void {1812if (viewState?.selectedKernelId && this.textModel) {1813const matching = this.notebookKernelService.getMatchingKernel(this.textModel);1814const kernel = matching.all.find(k => k.id === viewState.selectedKernelId);1815// Selected kernel may have already been picked prior to the view state loading1816// If so, don't overwrite it with the saved kernel.1817if (kernel && !matching.selected) {1818this.notebookKernelService.selectKernelForNotebook(kernel, this.textModel);1819}1820}1821}18221823getEditorViewState(): INotebookEditorViewState {1824const state = this.viewModel?.getEditorViewState();1825if (!state) {1826return {1827editingCells: {},1828cellLineNumberStates: {},1829editorViewStates: {},1830collapsedInputCells: {},1831collapsedOutputCells: {},1832};1833}18341835if (this._list) {1836state.scrollPosition = { left: this._list.scrollLeft, top: this._list.scrollTop };1837const cellHeights: { [key: number]: number } = {};1838for (let i = 0; i < this.viewModel!.length; i++) {1839const elm = this.viewModel!.cellAt(i) as CellViewModel;1840cellHeights[i] = elm.layoutInfo.totalHeight;1841}18421843state.cellTotalHeights = cellHeights;18441845if (this.viewModel) {1846const focusRange = this.viewModel.getFocus();1847const element = this.viewModel.cellAt(focusRange.start);1848if (element) {1849const itemDOM = this._list.domElementOfElement(element);1850const editorFocused = element.getEditState() === CellEditState.Editing && !!(itemDOM && itemDOM.ownerDocument.activeElement && itemDOM.contains(itemDOM.ownerDocument.activeElement));18511852state.editorFocused = editorFocused;1853state.focus = focusRange.start;1854}1855}1856}18571858// Save contribution view states1859const contributionsState: { [key: string]: unknown } = {};1860for (const [id, contribution] of this._contributions) {1861if (typeof contribution.saveViewState === 'function') {1862contributionsState[id] = contribution.saveViewState();1863}1864}1865state.contributionsState = contributionsState;1866if (this.textModel?.uri.scheme === Schemas.untitled) {1867state.selectedKernelId = this.activeKernel?.id;1868}18691870return state;1871}18721873private _allowScrollBeyondLastLine() {1874return this._scrollBeyondLastLine && !this.isReplHistory;1875}18761877private getBodyHeight(dimensionHeight: number) {1878return Math.max(dimensionHeight - (this._notebookTopToolbar?.useGlobalToolbar ? /** Toolbar height */ 26 : 0), 0);1879}18801881layout(dimension: DOM.Dimension, shadowElement?: HTMLElement, position?: DOM.IDomPosition): void {1882if (!shadowElement && this._shadowElementViewInfo === null) {1883this._dimension = dimension;1884this._position = position;1885return;1886}18871888if (dimension.width <= 0 || dimension.height <= 0) {1889this.onWillHide();1890return;1891}18921893const whenContainerStylesLoaded = this.layoutService.whenContainerStylesLoaded(DOM.getWindow(this.getDomNode()));1894if (whenContainerStylesLoaded) {1895// In floating windows, we need to ensure that the1896// container is ready for us to compute certain1897// layout related properties.1898whenContainerStylesLoaded.then(() => this.layoutNotebook(dimension, shadowElement, position));1899} else {1900this.layoutNotebook(dimension, shadowElement, position);1901}19021903}19041905private layoutNotebook(dimension: DOM.Dimension, shadowElement?: HTMLElement, position?: DOM.IDomPosition) {1906if (shadowElement) {1907this.updateShadowElement(shadowElement, dimension, position);1908}19091910if (this._shadowElementViewInfo && this._shadowElementViewInfo.width <= 0 && this._shadowElementViewInfo.height <= 0) {1911this.onWillHide();1912return;1913}19141915this._dimension = dimension;1916this._position = position;1917const newBodyHeight = this.getBodyHeight(dimension.height) - this.getLayoutInfo().stickyHeight;1918DOM.size(this._body, dimension.width, newBodyHeight);19191920const newCellListHeight = newBodyHeight;1921if (this._list.getRenderHeight() < newCellListHeight) {1922// 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)1923this._list.updateOptions({ paddingBottom: this._allowScrollBeyondLastLine() ? Math.max(0, (newCellListHeight - 50)) : 0, paddingTop: 0 });1924this._list.layout(newCellListHeight, dimension.width);1925} else {1926// 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.1927this._list.layout(newCellListHeight, dimension.width);1928this._list.updateOptions({ paddingBottom: this._allowScrollBeyondLastLine() ? Math.max(0, (newCellListHeight - 50)) : 0, paddingTop: 0 });1929}19301931this._overlayContainer.inert = false;1932this._overlayContainer.style.visibility = 'visible';1933this._overlayContainer.style.display = 'block';1934this._overlayContainer.style.position = 'absolute';1935this._overlayContainer.style.overflow = 'hidden';19361937this.layoutContainerOverShadowElement(dimension, position);19381939if (this._webviewTransparentCover) {1940this._webviewTransparentCover.style.height = `${dimension.height}px`;1941this._webviewTransparentCover.style.width = `${dimension.width}px`;1942}19431944this._notebookTopToolbar.layout(this._dimension);1945this._notebookOverviewRuler.layout();19461947this._viewContext?.eventDispatcher.emit([new NotebookLayoutChangedEvent({ width: true, fontInfo: true }, this.getLayoutInfo())]);1948}19491950private updateShadowElement(shadowElement: HTMLElement, dimension?: IDimension, position?: DOM.IDomPosition) {1951this._shadowElement = shadowElement;1952if (dimension && position) {1953this._shadowElementViewInfo = {1954height: dimension.height,1955width: dimension.width,1956top: position.top,1957left: position.left,1958};1959} else {1960// We have to recompute position and size ourselves (which is slow)1961const containerRect = shadowElement.getBoundingClientRect();1962this._shadowElementViewInfo = {1963height: containerRect.height,1964width: containerRect.width,1965top: containerRect.top,1966left: containerRect.left1967};1968}1969}19701971private layoutContainerOverShadowElement(dimension?: DOM.Dimension, position?: DOM.IDomPosition): void {1972if (dimension && position) {1973this._overlayContainer.style.top = `${position.top}px`;1974this._overlayContainer.style.left = `${position.left}px`;1975this._overlayContainer.style.width = `${dimension.width}px`;1976this._overlayContainer.style.height = `${dimension.height}px`;1977return;1978}19791980if (!this._shadowElementViewInfo) {1981return;1982}19831984const elementContainerRect = this._overlayContainer.parentElement?.getBoundingClientRect();1985this._overlayContainer.style.top = `${this._shadowElementViewInfo.top - (elementContainerRect?.top || 0)}px`;1986this._overlayContainer.style.left = `${this._shadowElementViewInfo.left - (elementContainerRect?.left || 0)}px`;1987this._overlayContainer.style.width = `${dimension ? dimension.width : this._shadowElementViewInfo.width}px`;1988this._overlayContainer.style.height = `${dimension ? dimension.height : this._shadowElementViewInfo.height}px`;1989}19901991//#endregion19921993//#region Focus tracker1994focus() {1995this._isVisible = true;1996this._editorFocus.set(true);19971998if (this._webviewFocused) {1999this._webview?.focusWebview();2000} else {2001if (this.viewModel) {2002const focusRange = this.viewModel.getFocus();2003const element = this.viewModel.cellAt(focusRange.start);20042005// The notebook editor doesn't have focus yet2006if (!this.hasEditorFocus()) {2007this.focusContainer();2008// trigger editor to update as FocusTracker might not emit focus change event2009this.updateEditorFocus();2010}20112012if (element && element.focusMode === CellFocusMode.Editor) {2013element.updateEditState(CellEditState.Editing, 'editorWidget.focus');2014element.focusMode = CellFocusMode.Editor;2015this.focusEditor(element);2016return;2017}2018}20192020this._list.domFocus();2021}20222023if (this._currentProgress) {2024// The editor forces progress to hide when switching editors. So if progress should be visible, force it to show when the editor is focused.2025this.showProgress();2026}2027}20282029onShow() {2030this._isVisible = true;2031}20322033private focusEditor(activeElement: CellViewModel): void {2034for (const [element, editor] of this._renderedEditors.entries()) {2035if (element === activeElement) {2036editor.focus();2037return;2038}2039}2040}20412042focusContainer(clearSelection: boolean = false) {2043if (this._webviewFocused) {2044this._webview?.focusWebview();2045} else {2046this._list.focusContainer(clearSelection);2047}2048}20492050selectOutputContent(cell: ICellViewModel) {2051this._webview?.selectOutputContents(cell);2052}20532054selectInputContents(cell: ICellViewModel) {2055this._webview?.selectInputContents(cell);2056}20572058onWillHide() {2059this._isVisible = false;2060this._editorFocus.set(false);2061this._overlayContainer.inert = true;2062this._overlayContainer.style.visibility = 'hidden';2063this._overlayContainer.style.left = '-50000px';2064this._notebookTopToolbarContainer.style.display = 'none';2065this.clearActiveCellWidgets();2066}20672068private clearActiveCellWidgets() {2069this._renderedEditors.forEach((editor, cell) => {2070if (this.getActiveCell() === cell && editor) {2071SuggestController.get(editor)?.cancelSuggestWidget();2072DropIntoEditorController.get(editor)?.clearWidgets();2073CopyPasteController.get(editor)?.clearWidgets();2074}2075});20762077this._renderedEditors.forEach((editor, cell) => {2078const controller = InlineCompletionsController.get(editor);2079if (controller?.model.get()?.inlineEditState.get()) {2080editor.render(true);2081}2082});2083}20842085private editorHasDomFocus(): boolean {2086return DOM.isAncestorOfActiveElement(this.getDomNode());2087}20882089updateEditorFocus() {2090// Note - focus going to the webview will fire 'blur', but the webview element will be2091// a descendent of the notebook editor root.2092this._focusTracker.refreshState();2093const focused = this.editorHasDomFocus();2094this._editorFocus.set(focused);2095this.viewModel?.setEditorFocus(focused);2096}20972098updateCellFocusMode() {2099const activeCell = this.getActiveCell();21002101if (activeCell?.focusMode === CellFocusMode.Output && !this._webviewFocused) {2102// output previously has focus, but now it's blurred.2103activeCell.focusMode = CellFocusMode.Container;2104}2105}21062107hasEditorFocus() {2108// _editorFocus is driven by the FocusTracker, which is only guaranteed to _eventually_ fire blur.2109// If we need to know whether we have focus at this instant, we need to check the DOM manually.2110this.updateEditorFocus();2111return this.editorHasDomFocus();2112}21132114hasWebviewFocus() {2115return this._webviewFocused;2116}21172118hasOutputTextSelection() {2119if (!this.hasEditorFocus()) {2120return false;2121}21222123const windowSelection = DOM.getWindow(this.getDomNode()).getSelection();2124if (windowSelection?.rangeCount !== 1) {2125return false;2126}21272128const activeSelection = windowSelection.getRangeAt(0);2129if (activeSelection.startContainer === activeSelection.endContainer && activeSelection.endOffset - activeSelection.startOffset === 0) {2130return false;2131}21322133let container: Node | null = activeSelection.commonAncestorContainer;21342135if (!this._body.contains(container)) {2136return false;2137}21382139while (container2140&&2141container !== this._body) {2142if ((container as HTMLElement).classList && (container as HTMLElement).classList.contains('output')) {2143return true;2144}21452146container = container.parentNode;2147}21482149return false;2150}21512152_didFocusOutputInputChange(hasFocus: boolean) {2153this._outputInputFocus.set(hasFocus);2154}21552156//#endregion21572158//#region Editor Features21592160focusElement(cell: ICellViewModel) {2161this.viewModel?.updateSelectionsState({2162kind: SelectionStateType.Handle,2163primary: cell.handle,2164selections: [cell.handle]2165});2166}21672168get scrollTop() {2169return this._list.scrollTop;2170}21712172get scrollBottom() {2173return this._list.scrollTop + this._list.getRenderHeight();2174}21752176getAbsoluteTopOfElement(cell: ICellViewModel) {2177return this._list.getCellViewScrollTop(cell);2178}21792180getAbsoluteBottomOfElement(cell: ICellViewModel) {2181return this._list.getCellViewScrollBottom(cell);2182}21832184getHeightOfElement(cell: ICellViewModel) {2185return this._list.elementHeight(cell);2186}21872188scrollToBottom() {2189this._list.scrollToBottom();2190}21912192setScrollTop(scrollTop: number): void {2193this._list.scrollTop = scrollTop;2194}21952196revealCellRangeInView(range: ICellRange) {2197return this._list.revealCells(range);2198}21992200revealInView(cell: ICellViewModel) {2201return this._list.revealCell(cell, CellRevealType.Default);2202}22032204revealInViewAtTop(cell: ICellViewModel) {2205this._list.revealCell(cell, CellRevealType.Top);2206}22072208revealInCenter(cell: ICellViewModel) {2209this._list.revealCell(cell, CellRevealType.Center);2210}22112212async revealInCenterIfOutsideViewport(cell: ICellViewModel) {2213await this._list.revealCell(cell, CellRevealType.CenterIfOutsideViewport);2214}22152216async revealFirstLineIfOutsideViewport(cell: ICellViewModel) {2217await this._list.revealCell(cell, CellRevealType.FirstLineIfOutsideViewport);2218}22192220async revealLineInViewAsync(cell: ICellViewModel, line: number): Promise<void> {2221return this._list.revealRangeInCell(cell, new Range(line, 1, line, 1), CellRevealRangeType.Default);2222}22232224async revealLineInCenterAsync(cell: ICellViewModel, line: number): Promise<void> {2225return this._list.revealRangeInCell(cell, new Range(line, 1, line, 1), CellRevealRangeType.Center);2226}22272228async revealLineInCenterIfOutsideViewportAsync(cell: ICellViewModel, line: number): Promise<void> {2229return this._list.revealRangeInCell(cell, new Range(line, 1, line, 1), CellRevealRangeType.CenterIfOutsideViewport);2230}22312232async revealRangeInViewAsync(cell: ICellViewModel, range: Selection | Range): Promise<void> {2233return this._list.revealRangeInCell(cell, range, CellRevealRangeType.Default);2234}22352236async revealRangeInCenterAsync(cell: ICellViewModel, range: Selection | Range): Promise<void> {2237return this._list.revealRangeInCell(cell, range, CellRevealRangeType.Center);2238}22392240async revealRangeInCenterIfOutsideViewportAsync(cell: ICellViewModel, range: Selection | Range): Promise<void> {2241return this._list.revealRangeInCell(cell, range, CellRevealRangeType.CenterIfOutsideViewport);2242}22432244revealCellOffsetInCenter(cell: ICellViewModel, offset: number) {2245return this._list.revealCellOffsetInCenter(cell, offset);2246}22472248revealOffsetInCenterIfOutsideViewport(offset: number) {2249return this._list.revealOffsetInCenterIfOutsideViewport(offset);2250}22512252getViewIndexByModelIndex(index: number): number {2253if (!this._listViewInfoAccessor) {2254return -1;2255}2256const cell = this.viewModel?.viewCells[index];2257if (!cell) {2258return -1;2259}22602261return this._listViewInfoAccessor.getViewIndex(cell);2262}22632264getViewHeight(cell: ICellViewModel): number {2265if (!this._listViewInfoAccessor) {2266return -1;2267}22682269return this._listViewInfoAccessor.getViewHeight(cell);2270}22712272getCellRangeFromViewRange(startIndex: number, endIndex: number): ICellRange | undefined {2273return this._listViewInfoAccessor.getCellRangeFromViewRange(startIndex, endIndex);2274}22752276getCellsInRange(range?: ICellRange): ReadonlyArray<ICellViewModel> {2277return this._listViewInfoAccessor.getCellsInRange(range);2278}22792280setCellEditorSelection(cell: ICellViewModel, range: Range): void {2281this._list.setCellEditorSelection(cell, range);2282}22832284setHiddenAreas(_ranges: ICellRange[]): boolean {2285return this._list.setHiddenAreas(_ranges, true);2286}22872288getVisibleRangesPlusViewportAboveAndBelow(): ICellRange[] {2289return this._listViewInfoAccessor.getVisibleRangesPlusViewportAboveAndBelow();2290}22912292//#endregion22932294//#region Decorations22952296deltaCellDecorations(oldDecorations: string[], newDecorations: INotebookDeltaDecoration[]): string[] {2297const ret = this.viewModel?.deltaCellDecorations(oldDecorations, newDecorations) || [];2298this._onDidChangeDecorations.fire();2299return ret;2300}23012302deltaCellContainerClassNames(cellId: string, added: string[], removed: string[], cellkind: CellKind): void {2303if (cellkind === CellKind.Markup) {2304this._webview?.deltaMarkupPreviewClassNames(cellId, added, removed);2305} else {2306this._webview?.deltaCellOutputContainerClassNames(cellId, added, removed);2307}2308}23092310changeModelDecorations<T>(callback: (changeAccessor: IModelDecorationsChangeAccessor) => T): T | null {2311return this.viewModel?.changeModelDecorations<T>(callback) || null;2312}23132314//#endregion23152316//#region View Zones2317changeViewZones(callback: (accessor: INotebookViewZoneChangeAccessor) => void): void {2318this._list.changeViewZones(callback);2319this._onDidChangeLayout.fire();2320}23212322getViewZoneLayoutInfo(id: string): { top: number; height: number } | null {2323return this._list.getViewZoneLayoutInfo(id);2324}2325//#endregion23262327//#region Overlay2328changeCellOverlays(callback: (accessor: INotebookCellOverlayChangeAccessor) => void): void {2329this._list.changeCellOverlays(callback);2330}2331//#endregion23322333//#region Kernel/Execution23342335private async _loadKernelPreloads(): Promise<void> {2336if (!this.hasModel()) {2337return;2338}2339const { selected } = this.notebookKernelService.getMatchingKernel(this.textModel);2340if (!this._webview?.isResolved()) {2341await this._resolveWebview();2342}2343this._webview?.updateKernelPreloads(selected);2344}23452346get activeKernel() {2347return this.textModel && this.notebookKernelService.getSelectedOrSuggestedKernel(this.textModel);2348}23492350async cancelNotebookCells(cells?: Iterable<ICellViewModel>): Promise<void> {2351if (!this.viewModel || !this.hasModel()) {2352return;2353}2354if (!cells) {2355cells = this.viewModel.viewCells;2356}2357return this.notebookExecutionService.cancelNotebookCellHandles(this.textModel, Array.from(cells).map(cell => cell.handle));2358}23592360async executeNotebookCells(cells?: Iterable<ICellViewModel>): Promise<void> {2361if (!this.viewModel || !this.hasModel()) {2362this.logService.info('notebookEditorWidget', 'No NotebookViewModel, cannot execute cells');2363return;2364}2365if (!cells) {2366cells = this.viewModel.viewCells;2367}2368return this.notebookExecutionService.executeNotebookCells(this.textModel, Array.from(cells).map(c => c.model), this.scopedContextKeyService);2369}23702371//#endregion23722373async layoutNotebookCell(cell: ICellViewModel, height: number, context?: CellLayoutContext): Promise<void> {2374return this._cellLayoutManager?.layoutNotebookCell(cell, height);2375}23762377getActiveCell() {2378const elements = this._list.getFocusedElements();23792380if (elements && elements.length) {2381return elements[0];2382}23832384return undefined;2385}23862387private _toggleNotebookCellSelection(selectedCell: ICellViewModel, selectFromPrevious: boolean): void {2388const currentSelections = this._list.getSelectedElements();2389const isSelected = currentSelections.includes(selectedCell);23902391const previousSelection = selectFromPrevious ? currentSelections[currentSelections.length - 1] ?? selectedCell : selectedCell;2392const selectedIndex = this._list.getViewIndex(selectedCell)!;2393const previousIndex = this._list.getViewIndex(previousSelection)!;23942395const cellsInSelectionRange = this.getCellsInViewRange(selectedIndex, previousIndex);2396if (isSelected) {2397// Deselect2398this._list.selectElements(currentSelections.filter(current => !cellsInSelectionRange.includes(current)));2399} else {2400// Add to selection2401this.focusElement(selectedCell);2402this._list.selectElements([...currentSelections.filter(current => !cellsInSelectionRange.includes(current)), ...cellsInSelectionRange]);2403}2404}24052406private getCellsInViewRange(fromInclusive: number, toInclusive: number): ICellViewModel[] {2407const selectedCellsInRange: ICellViewModel[] = [];2408for (let index = 0; index < this._list.length; ++index) {2409const cell = this._list.element(index);2410if (cell) {2411if ((index >= fromInclusive && index <= toInclusive) || (index >= toInclusive && index <= fromInclusive)) {2412selectedCellsInRange.push(cell);2413}2414}2415}2416return selectedCellsInRange;2417}24182419async focusNotebookCell(cell: ICellViewModel, focusItem: 'editor' | 'container' | 'output', options?: IFocusNotebookCellOptions) {2420if (this._isDisposed) {2421return;2422}24232424cell.focusedOutputId = undefined;24252426if (focusItem === 'editor') {2427cell.isInputCollapsed = false;2428this.focusElement(cell);2429this._list.focusView();24302431cell.updateEditState(CellEditState.Editing, 'focusNotebookCell');2432cell.focusMode = CellFocusMode.Editor;2433if (!options?.skipReveal) {2434if (typeof options?.focusEditorLine === 'number') {2435this._cursorNavMode.set(true);2436await this.revealLineInViewAsync(cell, options.focusEditorLine);2437const editor = this._renderedEditors.get(cell)!;2438const focusEditorLine = options.focusEditorLine;2439editor?.setSelection({2440startLineNumber: focusEditorLine,2441startColumn: 1,2442endLineNumber: focusEditorLine,2443endColumn: 12444});2445} else {2446const selectionsStartPosition = cell.getSelectionsStartPosition();2447if (selectionsStartPosition?.length) {2448const firstSelectionPosition = selectionsStartPosition[0];2449await this.revealRangeInViewAsync(cell, Range.fromPositions(firstSelectionPosition, firstSelectionPosition));2450} else {2451await this.revealInView(cell);2452}24532454}24552456}2457} else if (focusItem === 'output') {2458this.focusElement(cell);24592460if (!this.hasEditorFocus()) {2461this._list.focusView();2462}24632464if (!this._webview) {2465return;2466}24672468const firstOutputId = cell.outputsViewModels.find(o => o.model.alternativeOutputId)?.model.alternativeOutputId;2469const focusElementId = options?.outputId ?? firstOutputId ?? cell.id;2470this._webview.focusOutput(focusElementId, options?.altOutputId, options?.outputWebviewFocused || this._webviewFocused);24712472cell.updateEditState(CellEditState.Preview, 'focusNotebookCell');2473cell.focusMode = CellFocusMode.Output;2474cell.focusedOutputId = options?.outputId;2475this._outputFocus.set(true);2476if (!options?.skipReveal) {2477this.revealInCenterIfOutsideViewport(cell);2478}2479} else {2480// focus container2481const itemDOM = this._list.domElementOfElement(cell);2482if (itemDOM && itemDOM.ownerDocument.activeElement && itemDOM.contains(itemDOM.ownerDocument.activeElement)) {2483(itemDOM.ownerDocument.activeElement as HTMLElement).blur();2484}24852486this._webview?.blurOutput();24872488cell.updateEditState(CellEditState.Preview, 'focusNotebookCell');2489cell.focusMode = CellFocusMode.Container;24902491this.focusElement(cell);2492if (!options?.skipReveal) {2493if (typeof options?.focusEditorLine === 'number') {2494this._cursorNavMode.set(true);2495await this.revealInView(cell);2496} else if (options?.revealBehavior === ScrollToRevealBehavior.firstLine) {2497await this.revealFirstLineIfOutsideViewport(cell);2498} else if (options?.revealBehavior === ScrollToRevealBehavior.fullCell) {2499await this.revealInView(cell);2500} else {2501await this.revealInCenterIfOutsideViewport(cell);2502}2503}2504this._list.focusView();2505this.updateEditorFocus();2506}2507}25082509async focusNextNotebookCell(cell: ICellViewModel, focusItem: 'editor' | 'container' | 'output') {2510const idx = this.viewModel?.getCellIndex(cell);2511if (typeof idx !== 'number') {2512return;2513}25142515const newCell = this.viewModel?.cellAt(idx + 1);2516if (!newCell) {2517return;2518}25192520await this.focusNotebookCell(newCell, focusItem);2521}25222523//#endregion25242525//#region Find25262527private async _warmupCell(viewCell: CodeCellViewModel) {2528if (viewCell.isOutputCollapsed) {2529return;2530}25312532const outputs = viewCell.outputsViewModels;2533for (const output of outputs.slice(0, outputDisplayLimit)) {2534const [mimeTypes, pick] = output.resolveMimeTypes(this.textModel!, undefined);2535if (!mimeTypes.find(mimeType => mimeType.isTrusted) || mimeTypes.length === 0) {2536continue;2537}25382539const pickedMimeTypeRenderer = mimeTypes[pick];25402541if (!pickedMimeTypeRenderer) {2542return;2543}25442545const renderer = this._notebookService.getRendererInfo(pickedMimeTypeRenderer.rendererId);25462547if (!renderer) {2548return;2549}25502551const result: IInsetRenderOutput = { type: RenderOutputType.Extension, renderer, source: output, mimeType: pickedMimeTypeRenderer.mimeType };2552const inset = this._webview?.insetMapping.get(result.source);2553if (!inset || !inset.initialized) {2554const p = new Promise<void>(resolve => {2555this._register(Event.any(this.onDidRenderOutput, this.onDidRemoveOutput)(e => {2556if (e.model === result.source.model) {2557resolve();2558}2559}));2560});2561this.createOutput(viewCell, result, 0, false);2562await p;2563} else {2564// request to update its visibility2565this.createOutput(viewCell, result, 0, false);2566}25672568return;2569}25702571}25722573private async _warmupAll(includeOutput: boolean) {2574if (!this.hasModel() || !this.viewModel) {2575return;2576}25772578const cells = this.viewModel.viewCells;2579const requests = [];25802581for (let i = 0; i < cells.length; i++) {2582if (cells[i].cellKind === CellKind.Markup && !this._webview!.markupPreviewMapping.has(cells[i].id)) {2583requests.push(this.createMarkupPreview(cells[i]));2584}2585}25862587if (includeOutput && this._list) {2588for (let i = 0; i < this._list.length; i++) {2589const cell = this._list.element(i);25902591if (cell?.cellKind === CellKind.Code) {2592requests.push(this._warmupCell((cell as CodeCellViewModel)));2593}2594}2595}25962597return Promise.all(requests);2598}25992600private async _warmupSelection(includeOutput: boolean, selectedCellRanges: ICellRange[]) {2601if (!this.hasModel() || !this.viewModel) {2602return;2603}26042605const cells = this.viewModel.viewCells;2606const requests = [];26072608for (const range of selectedCellRanges) {2609for (let i = range.start; i < range.end; i++) {2610if (cells[i].cellKind === CellKind.Markup && !this._webview!.markupPreviewMapping.has(cells[i].id)) {2611requests.push(this.createMarkupPreview(cells[i]));2612}2613}2614}26152616if (includeOutput && this._list) {2617for (const range of selectedCellRanges) {2618for (let i = range.start; i < range.end; i++) {2619const cell = this._list.element(i);26202621if (cell?.cellKind === CellKind.Code) {2622requests.push(this._warmupCell((cell as CodeCellViewModel)));2623}2624}2625}2626}26272628return Promise.all(requests);2629}26302631async find(query: string, options: INotebookFindOptions, token: CancellationToken, skipWarmup: boolean = false, shouldGetSearchPreviewInfo = false, ownerID?: string): Promise<CellFindMatchWithIndex[]> {2632if (!this._notebookViewModel) {2633return [];2634}26352636if (!ownerID) {2637ownerID = this.getId();2638}26392640const findMatches = this._notebookViewModel.find(query, options).filter(match => match.length > 0);26412642if ((!options.includeMarkupPreview && !options.includeOutput) || options.findScope?.findScopeType === NotebookFindScopeType.Text) {2643this._webview?.findStop(ownerID);2644return findMatches;2645}26462647// search in webview enabled26482649const matchMap: { [key: string]: CellFindMatchWithIndex } = {};2650findMatches.forEach(match => {2651matchMap[match.cell.id] = match;2652});26532654if (this._webview) {2655// request all or some outputs to be rendered2656// measure perf2657const start = Date.now();2658if (options.findScope && options.findScope.findScopeType === NotebookFindScopeType.Cells && options.findScope.selectedCellRanges) {2659await this._warmupSelection(!!options.includeOutput, options.findScope.selectedCellRanges);2660} else {2661await this._warmupAll(!!options.includeOutput);2662}2663const end = Date.now();2664this.logService.debug('Find', `Warmup time: ${end - start}ms`);26652666if (token.isCancellationRequested) {2667return [];2668}26692670let findIds: string[] = [];2671if (options.findScope && options.findScope.findScopeType === NotebookFindScopeType.Cells && options.findScope.selectedCellRanges) {2672const selectedIndexes = cellRangesToIndexes(options.findScope.selectedCellRanges);2673findIds = selectedIndexes.map<string>(index => this._notebookViewModel?.viewCells[index].id ?? '');2674}26752676const webviewMatches = await this._webview.find(query, { caseSensitive: options.caseSensitive, wholeWord: options.wholeWord, includeMarkup: !!options.includeMarkupPreview, includeOutput: !!options.includeOutput, shouldGetSearchPreviewInfo, ownerID, findIds: findIds });26772678if (token.isCancellationRequested) {2679return [];2680}26812682// attach webview matches to model find matches2683webviewMatches.forEach(match => {2684const cell = this._notebookViewModel!.viewCells.find(cell => cell.id === match.cellId);26852686if (!cell) {2687return;2688}26892690if (match.type === 'preview') {2691// markup preview2692if (cell.getEditState() === CellEditState.Preview && !options.includeMarkupPreview) {2693return;2694}26952696if (cell.getEditState() === CellEditState.Editing && options.includeMarkupInput) {2697return;2698}2699} else {2700if (!options.includeOutput) {2701// skip outputs if not included2702return;2703}2704}27052706const exisitingMatch = matchMap[match.cellId];27072708if (exisitingMatch) {2709exisitingMatch.webviewMatches.push(match);2710} else {27112712matchMap[match.cellId] = new CellFindMatchModel(2713this._notebookViewModel!.viewCells.find(cell => cell.id === match.cellId)!,2714this._notebookViewModel!.viewCells.findIndex(cell => cell.id === match.cellId)!,2715[],2716[match]2717);2718}2719});2720}27212722const ret: CellFindMatchWithIndex[] = [];2723this._notebookViewModel.viewCells.forEach((cell, index) => {2724if (matchMap[cell.id]) {2725ret.push(new CellFindMatchModel(cell, index, matchMap[cell.id].contentMatches, matchMap[cell.id].webviewMatches));2726}2727});27282729return ret;2730}27312732async findHighlightCurrent(matchIndex: number, ownerID?: string): Promise<number> {2733if (!this._webview) {2734return 0;2735}27362737return this._webview?.findHighlightCurrent(matchIndex, ownerID ?? this.getId());2738}27392740async findUnHighlightCurrent(matchIndex: number, ownerID?: string): Promise<void> {2741if (!this._webview) {2742return;2743}27442745return this._webview?.findUnHighlightCurrent(matchIndex, ownerID ?? this.getId());2746}27472748findStop(ownerID?: string) {2749this._webview?.findStop(ownerID ?? this.getId());2750}27512752//#endregion27532754//#region MISC27552756getLayoutInfo(): NotebookLayoutInfo {2757if (!this._list) {2758throw new Error('Editor is not initalized successfully');2759}27602761if (!this._fontInfo) {2762this._generateFontInfo();2763}27642765let listViewOffset = 0;2766if (this._dimension) {2767listViewOffset = (this._notebookTopToolbar?.useGlobalToolbar ? /** Toolbar height */ 26 : 0) + (this._notebookStickyScroll?.getCurrentStickyHeight() ?? 0);2768}27692770return {2771width: this._dimension?.width ?? 0,2772height: this._dimension?.height ?? 0,2773scrollHeight: this._list?.getScrollHeight() ?? 0,2774fontInfo: this._fontInfo!,2775stickyHeight: this._notebookStickyScroll?.getCurrentStickyHeight() ?? 0,2776listViewOffsetTop: listViewOffset2777};2778}27792780async createMarkupPreview(cell: MarkupCellViewModel) {2781if (!this._webview) {2782return;2783}27842785if (!this._webview.isResolved()) {2786await this._resolveWebview();2787}27882789if (!this._webview || !this._list.webviewElement) {2790return;2791}27922793if (!this.viewModel || !this._list.viewModel) {2794return;2795}27962797if (this.viewModel.getCellIndex(cell) === -1) {2798return;2799}28002801if (this.cellIsHidden(cell)) {2802return;2803}28042805const webviewTop = parseInt(this._list.webviewElement.domNode.style.top, 10);2806const top = !!webviewTop ? (0 - webviewTop) : 0;28072808const cellTop = this._list.getCellViewScrollTop(cell);2809await this._webview.showMarkupPreview({2810mime: cell.mime,2811cellHandle: cell.handle,2812cellId: cell.id,2813content: cell.getText(),2814offset: cellTop + top,2815visible: true,2816metadata: cell.metadata,2817});2818}28192820private cellIsHidden(cell: ICellViewModel): boolean {2821const modelIndex = this.viewModel!.getCellIndex(cell);2822const foldedRanges = this.viewModel!.getHiddenRanges();2823return foldedRanges.some(range => modelIndex >= range.start && modelIndex <= range.end);2824}28252826async unhideMarkupPreviews(cells: readonly MarkupCellViewModel[]) {2827if (!this._webview) {2828return;2829}28302831if (!this._webview.isResolved()) {2832await this._resolveWebview();2833}28342835await this._webview?.unhideMarkupPreviews(cells.map(cell => cell.id));2836}28372838async hideMarkupPreviews(cells: readonly MarkupCellViewModel[]) {2839if (!this._webview || !cells.length) {2840return;2841}28422843if (!this._webview.isResolved()) {2844await this._resolveWebview();2845}28462847await this._webview?.hideMarkupPreviews(cells.map(cell => cell.id));2848}28492850async deleteMarkupPreviews(cells: readonly MarkupCellViewModel[]) {2851if (!this._webview) {2852return;2853}28542855if (!this._webview.isResolved()) {2856await this._resolveWebview();2857}28582859await this._webview?.deleteMarkupPreviews(cells.map(cell => cell.id));2860}28612862private async updateSelectedMarkdownPreviews(): Promise<void> {2863if (!this._webview) {2864return;2865}28662867if (!this._webview.isResolved()) {2868await this._resolveWebview();2869}28702871const selectedCells = this.getSelectionViewModels().map(cell => cell.id);28722873// Only show selection when there is more than 1 cell selected2874await this._webview?.updateMarkupPreviewSelections(selectedCells.length > 1 ? selectedCells : []);2875}28762877async createOutput(cell: CodeCellViewModel, output: IInsetRenderOutput, offset: number, createWhenIdle: boolean): Promise<void> {2878this._insetModifyQueueByOutputId.queue(output.source.model.outputId, async () => {2879if (this._isDisposed || !this._webview) {2880return;2881}28822883if (!this._webview.isResolved()) {2884await this._resolveWebview();2885}28862887if (!this._webview) {2888return;2889}28902891if (!this._list.webviewElement) {2892return;2893}28942895if (output.type === RenderOutputType.Extension) {2896this.notebookRendererMessaging.prepare(output.renderer.id);2897}28982899const webviewTop = parseInt(this._list.webviewElement.domNode.style.top, 10);2900const top = !!webviewTop ? (0 - webviewTop) : 0;29012902const cellTop = this._list.getCellViewScrollTop(cell) + top;29032904const existingOutput = this._webview.insetMapping.get(output.source);2905if (!existingOutput2906|| (!existingOutput.renderer && output.type === RenderOutputType.Extension)2907) {2908if (createWhenIdle) {2909this._webview.requestCreateOutputWhenWebviewIdle({ cellId: cell.id, cellHandle: cell.handle, cellUri: cell.uri, executionId: cell.internalMetadata.executionId }, output, cellTop, offset);2910} else {2911this._webview.createOutput({ cellId: cell.id, cellHandle: cell.handle, cellUri: cell.uri, executionId: cell.internalMetadata.executionId }, output, cellTop, offset);2912}2913} else if (existingOutput.renderer2914&& output.type === RenderOutputType.Extension2915&& existingOutput.renderer.id !== output.renderer.id) {2916// switch mimetype2917this._webview.removeInsets([output.source]);2918this._webview.createOutput({ cellId: cell.id, cellHandle: cell.handle, cellUri: cell.uri }, output, cellTop, offset);2919} else if (existingOutput.versionId !== output.source.model.versionId) {2920this._webview.updateOutput({ cellId: cell.id, cellHandle: cell.handle, cellUri: cell.uri, executionId: cell.internalMetadata.executionId }, output, cellTop, offset);2921} else {2922const outputIndex = cell.outputsViewModels.indexOf(output.source);2923const outputOffset = cell.getOutputOffset(outputIndex);2924this._webview.updateScrollTops([{2925cell,2926output: output.source,2927cellTop,2928outputOffset,2929forceDisplay: !cell.isOutputCollapsed,2930}], []);2931}2932});2933}29342935async updateOutput(cell: CodeCellViewModel, output: IInsetRenderOutput, offset: number): Promise<void> {2936this._insetModifyQueueByOutputId.queue(output.source.model.outputId, async () => {2937if (this._isDisposed || !this._webview || cell.isOutputCollapsed) {2938return;2939}29402941if (!this._webview.isResolved()) {2942await this._resolveWebview();2943}29442945if (!this._webview || !this._list.webviewElement) {2946return;2947}29482949if (!this._webview.insetMapping.has(output.source)) {2950return this.createOutput(cell, output, offset, false);2951}29522953if (output.type === RenderOutputType.Extension) {2954this.notebookRendererMessaging.prepare(output.renderer.id);2955}29562957const webviewTop = parseInt(this._list.webviewElement.domNode.style.top, 10);2958const top = !!webviewTop ? (0 - webviewTop) : 0;29592960const cellTop = this._list.getCellViewScrollTop(cell) + top;2961this._webview.updateOutput({ cellId: cell.id, cellHandle: cell.handle, cellUri: cell.uri }, output, cellTop, offset);2962});2963}29642965async copyOutputImage(cellOutput: ICellOutputViewModel): Promise<void> {2966this._webview?.copyImage(cellOutput);2967}29682969removeInset(output: ICellOutputViewModel) {2970this._insetModifyQueueByOutputId.queue(output.model.outputId, async () => {2971if (this._isDisposed || !this._webview) {2972return;2973}29742975if (this._webview?.isResolved()) {2976this._webview.removeInsets([output]);2977}29782979this._onDidRemoveOutput.fire(output);2980});2981}29822983hideInset(output: ICellOutputViewModel) {2984this._insetModifyQueueByOutputId.queue(output.model.outputId, async () => {2985if (this._isDisposed || !this._webview) {2986return;2987}29882989if (this._webview?.isResolved()) {2990this._webview.hideInset(output);2991}2992});2993}29942995//#region --- webview IPC ----2996postMessage(message: unknown) {2997if (this._webview?.isResolved()) {2998this._webview.postKernelMessage(message);2999}3000}30013002//#endregion30033004addClassName(className: string) {3005this._overlayContainer.classList.add(className);3006}30073008removeClassName(className: string) {3009this._overlayContainer.classList.remove(className);3010}30113012cellAt(index: number): ICellViewModel | undefined {3013return this.viewModel?.cellAt(index);3014}30153016getCellByInfo(cellInfo: ICommonCellInfo): ICellViewModel {3017const { cellHandle } = cellInfo;3018return this.viewModel?.viewCells.find(vc => vc.handle === cellHandle) as CodeCellViewModel;3019}30203021getCellByHandle(handle: number): ICellViewModel | undefined {3022return this.viewModel?.getCellByHandle(handle);3023}30243025getCellIndex(cell: ICellViewModel) {3026return this.viewModel?.getCellIndexByHandle(cell.handle);3027}30283029getNextVisibleCellIndex(index: number): number | undefined {3030return this.viewModel?.getNextVisibleCellIndex(index);3031}30323033getPreviousVisibleCellIndex(index: number): number | undefined {3034return this.viewModel?.getPreviousVisibleCellIndex(index);3035}30363037private _updateScrollHeight() {3038if (this._isDisposed || !this._webview?.isResolved()) {3039return;3040}30413042if (!this._list.webviewElement) {3043return;3044}30453046const scrollHeight = this._list.scrollHeight;3047this._webview.element.style.height = `${scrollHeight + NOTEBOOK_WEBVIEW_BOUNDARY * 2}px`;30483049const webviewTop = parseInt(this._list.webviewElement.domNode.style.top, 10);3050const top = !!webviewTop ? (0 - webviewTop) : 0;30513052const updateItems: IDisplayOutputLayoutUpdateRequest[] = [];3053const removedItems: ICellOutputViewModel[] = [];3054this._webview?.insetMapping.forEach((value, key) => {3055const cell = this.viewModel?.getCellByHandle(value.cellInfo.cellHandle);3056if (!cell || !(cell instanceof CodeCellViewModel)) {3057return;3058}30593060this.viewModel?.viewCells.find(cell => cell.handle === value.cellInfo.cellHandle);3061const viewIndex = this._list.getViewIndex(cell);30623063if (viewIndex === undefined) {3064return;3065}30663067if (cell.outputsViewModels.indexOf(key) < 0) {3068// output is already gone3069removedItems.push(key);3070}30713072const cellTop = this._list.getCellViewScrollTop(cell);3073const outputIndex = cell.outputsViewModels.indexOf(key);3074const outputOffset = cell.getOutputOffset(outputIndex);3075updateItems.push({3076cell,3077output: key,3078cellTop: cellTop + top,3079outputOffset,3080forceDisplay: false,3081});3082});30833084this._webview.removeInsets(removedItems);30853086const markdownUpdateItems: { id: string; top: number }[] = [];3087for (const cellId of this._webview.markupPreviewMapping.keys()) {3088const cell = this.viewModel?.viewCells.find(cell => cell.id === cellId);3089if (cell) {3090const cellTop = this._list.getCellViewScrollTop(cell);3091// markdownUpdateItems.push({ id: cellId, top: cellTop });3092markdownUpdateItems.push({ id: cellId, top: cellTop + top });3093}3094}30953096if (markdownUpdateItems.length || updateItems.length) {3097this._debug('_list.onDidChangeContentHeight/markdown', markdownUpdateItems);3098this._webview?.updateScrollTops(updateItems, markdownUpdateItems);3099}3100}31013102//#endregion31033104//#region BacklayerWebview delegate3105private _updateOutputHeight(cellInfo: ICommonCellInfo, output: ICellOutputViewModel, outputHeight: number, isInit: boolean, source?: string): void {3106const cell = this.viewModel?.viewCells.find(vc => vc.handle === cellInfo.cellHandle);3107if (cell && cell instanceof CodeCellViewModel) {3108const outputIndex = cell.outputsViewModels.indexOf(output);3109if (outputIndex > -1) {3110this._debug('update cell output', cell.handle, outputHeight);3111cell.updateOutputHeight(outputIndex, outputHeight, source);3112this.layoutNotebookCell(cell, cell.layoutInfo.totalHeight);31133114if (isInit) {3115this._onDidRenderOutput.fire(output);3116}3117} else {3118this._debug('tried to update cell output that does not exist');3119}3120}3121}31223123private readonly _pendingOutputHeightAcks = new Map</* outputId */ string, IAckOutputHeight>();31243125private _scheduleOutputHeightAck(cellInfo: ICommonCellInfo, outputId: string, height: number) {3126const wasEmpty = this._pendingOutputHeightAcks.size === 0;3127this._pendingOutputHeightAcks.set(outputId, { cellId: cellInfo.cellId, outputId, height });31283129if (wasEmpty) {3130DOM.scheduleAtNextAnimationFrame(DOM.getWindow(this.getDomNode()), () => {3131this._debug('ack height');3132this._updateScrollHeight();31333134this._webview?.ackHeight([...this._pendingOutputHeightAcks.values()]);31353136this._pendingOutputHeightAcks.clear();3137}, -1); // -1 priority because this depends on calls to layoutNotebookCell, and that may be called multiple times before this runs3138}3139}31403141private _getCellById(cellId: string): ICellViewModel | undefined {3142return this.viewModel?.viewCells.find(vc => vc.id === cellId);3143}31443145private _updateMarkupCellHeight(cellId: string, height: number, isInit: boolean) {3146const cell = this._getCellById(cellId);3147if (cell && cell instanceof MarkupCellViewModel) {3148const { bottomToolbarGap } = this._notebookOptions.computeBottomToolbarDimensions(this.viewModel?.viewType);3149this._debug('updateMarkdownCellHeight', cell.handle, height + bottomToolbarGap, isInit);3150cell.renderedMarkdownHeight = height;3151}3152}31533154private _setMarkupCellEditState(cellId: string, editState: CellEditState): void {3155const cell = this._getCellById(cellId);3156if (cell instanceof MarkupCellViewModel) {3157this.revealInView(cell);3158cell.updateEditState(editState, 'setMarkdownCellEditState');3159}3160}31613162private _didStartDragMarkupCell(cellId: string, event: { dragOffsetY: number }): void {3163const cell = this._getCellById(cellId);3164if (cell instanceof MarkupCellViewModel) {3165const webviewOffset = this._list.webviewElement ? -parseInt(this._list.webviewElement.domNode.style.top, 10) : 0;3166this._dndController?.startExplicitDrag(cell, event.dragOffsetY - webviewOffset);3167}3168}31693170private _didDragMarkupCell(cellId: string, event: { dragOffsetY: number }): void {3171const cell = this._getCellById(cellId);3172if (cell instanceof MarkupCellViewModel) {3173const webviewOffset = this._list.webviewElement ? -parseInt(this._list.webviewElement.domNode.style.top, 10) : 0;3174this._dndController?.explicitDrag(cell, event.dragOffsetY - webviewOffset);3175}3176}31773178private _didDropMarkupCell(cellId: string, event: { dragOffsetY: number; ctrlKey: boolean; altKey: boolean }): void {3179const cell = this._getCellById(cellId);3180if (cell instanceof MarkupCellViewModel) {3181const webviewOffset = this._list.webviewElement ? -parseInt(this._list.webviewElement.domNode.style.top, 10) : 0;3182event.dragOffsetY -= webviewOffset;3183this._dndController?.explicitDrop(cell, event);3184}3185}31863187private _didEndDragMarkupCell(cellId: string): void {3188const cell = this._getCellById(cellId);3189if (cell instanceof MarkupCellViewModel) {3190this._dndController?.endExplicitDrag(cell);3191}3192}31933194private _didResizeOutput(cellId: string): void {3195const cell = this._getCellById(cellId);3196if (cell) {3197this._onDidResizeOutputEmitter.fire(cell);3198}3199}32003201private _updatePerformanceMetadata(cellId: string, executionId: string, duration: number, rendererId: string): void {3202if (!this.hasModel()) {3203return;3204}32053206const cell = this._getCellById(cellId);3207const cellIndex = !cell ? undefined : this.getCellIndex(cell);3208if (cell?.internalMetadata.executionId === executionId && cellIndex !== undefined) {3209const renderDurationMap = cell.internalMetadata.renderDuration || {};3210renderDurationMap[rendererId] = (renderDurationMap[rendererId] ?? 0) + duration;32113212this.textModel.applyEdits([3213{3214editType: CellEditType.PartialInternalMetadata,3215index: cellIndex,3216internalMetadata: {3217executionId: executionId,3218renderDuration: renderDurationMap3219}3220}3221], true, undefined, () => undefined, undefined, false);32223223}3224}32253226//#endregion32273228//#region Editor Contributions3229getContribution<T extends INotebookEditorContribution>(id: string): T {3230return <T>(this._contributions.get(id) || null);3231}32323233//#endregion32343235override dispose() {3236this._isDisposed = true;3237// dispose webview first3238this._webview?.dispose();3239this._webview = null;32403241this.notebookEditorService.removeNotebookEditor(this);3242dispose(this._contributions.values());3243this._contributions.clear();32443245this._localStore.clear();3246dispose(this._localCellStateListeners);3247this._list.dispose();3248this._cellLayoutManager?.dispose();3249this._listTopCellToolbar?.dispose();32503251this._overlayContainer.remove();3252this.viewModel?.dispose();32533254this._renderedEditors.clear();3255this._baseCellEditorOptions.forEach(v => v.dispose());3256this._baseCellEditorOptions.clear();32573258this._notebookOverviewRulerContainer.remove();32593260super.dispose();32613262// unref3263this._webview = null;3264this._webviewResolvePromise = null;3265this._webviewTransparentCover = null;3266this._dndController = null;3267this._listTopCellToolbar = null;3268this._notebookViewModel = undefined;3269this._cellContextKeyManager = null;3270this._notebookTopToolbar = null!;3271this._list = null!;3272this._listViewInfoAccessor = null!;3273this._listDelegate = null;3274}32753276toJSON(): { notebookUri: URI | undefined } {3277return {3278notebookUri: this.viewModel?.uri,3279};3280}3281}32823283registerZIndex(ZIndex.Base, 5, 'notebook-progress-bar',);3284registerZIndex(ZIndex.Base, 10, 'notebook-list-insertion-indicator');3285registerZIndex(ZIndex.Base, 20, 'notebook-cell-editor-outline');3286registerZIndex(ZIndex.Base, 25, 'notebook-scrollbar');3287registerZIndex(ZIndex.Base, 26, 'notebook-cell-status');3288registerZIndex(ZIndex.Base, 26, 'notebook-folding-indicator');3289registerZIndex(ZIndex.Base, 27, 'notebook-output');3290registerZIndex(ZIndex.Base, 28, 'notebook-cell-bottom-toolbar-container');3291registerZIndex(ZIndex.Base, 29, 'notebook-run-button-container');3292registerZIndex(ZIndex.Base, 29, 'notebook-input-collapse-condicon');3293registerZIndex(ZIndex.Base, 30, 'notebook-cell-output-toolbar');3294registerZIndex(ZIndex.Sash, 1, 'notebook-cell-expand-part-button');3295registerZIndex(ZIndex.Sash, 2, 'notebook-cell-toolbar');3296registerZIndex(ZIndex.Sash, 3, 'notebook-cell-toolbar-dropdown-active');32973298export const notebookCellBorder = registerColor('notebook.cellBorderColor', {3299dark: transparent(listInactiveSelectionBackground, 1),3300light: transparent(listInactiveSelectionBackground, 1),3301hcDark: PANEL_BORDER,3302hcLight: PANEL_BORDER3303}, nls.localize('notebook.cellBorderColor', "The border color for notebook cells."));33043305export const focusedEditorBorderColor = registerColor('notebook.focusedEditorBorder', focusBorder, nls.localize('notebook.focusedEditorBorder', "The color of the notebook cell editor border."));33063307export const cellStatusIconSuccess = registerColor('notebookStatusSuccessIcon.foreground', debugIconStartForeground, nls.localize('notebookStatusSuccessIcon.foreground', "The error icon color of notebook cells in the cell status bar."));33083309export const runningCellRulerDecorationColor = registerColor('notebookEditorOverviewRuler.runningCellForeground', debugIconStartForeground, nls.localize('notebookEditorOverviewRuler.runningCellForeground', "The color of the running cell decoration in the notebook editor overview ruler."));33103311export const cellStatusIconError = registerColor('notebookStatusErrorIcon.foreground', errorForeground, nls.localize('notebookStatusErrorIcon.foreground', "The error icon color of notebook cells in the cell status bar."));33123313export const cellStatusIconRunning = registerColor('notebookStatusRunningIcon.foreground', foreground, nls.localize('notebookStatusRunningIcon.foreground', "The running icon color of notebook cells in the cell status bar."));33143315export const notebookOutputContainerBorderColor = registerColor('notebook.outputContainerBorderColor', null, nls.localize('notebook.outputContainerBorderColor', "The border color of the notebook output container."));33163317export const notebookOutputContainerColor = registerColor('notebook.outputContainerBackgroundColor', null, nls.localize('notebook.outputContainerBackgroundColor', "The color of the notebook output container background."));33183319// TODO@rebornix currently also used for toolbar border, if we keep all of this, pick a generic name3320export const CELL_TOOLBAR_SEPERATOR = registerColor('notebook.cellToolbarSeparator', {3321dark: Color.fromHex('#808080').transparent(0.35),3322light: Color.fromHex('#808080').transparent(0.35),3323hcDark: contrastBorder,3324hcLight: contrastBorder3325}, nls.localize('notebook.cellToolbarSeparator', "The color of the separator in the cell bottom toolbar"));33263327export const focusedCellBackground = registerColor('notebook.focusedCellBackground', null, nls.localize('focusedCellBackground', "The background color of a cell when the cell is focused."));33283329export const selectedCellBackground = registerColor('notebook.selectedCellBackground', {3330dark: listInactiveSelectionBackground,3331light: listInactiveSelectionBackground,3332hcDark: null,3333hcLight: null3334}, nls.localize('selectedCellBackground', "The background color of a cell when the cell is selected."));333533363337export const cellHoverBackground = registerColor('notebook.cellHoverBackground', {3338dark: transparent(focusedCellBackground, .5),3339light: transparent(focusedCellBackground, .7),3340hcDark: null,3341hcLight: null3342}, nls.localize('notebook.cellHoverBackground', "The background color of a cell when the cell is hovered."));33433344export const selectedCellBorder = registerColor('notebook.selectedCellBorder', {3345dark: notebookCellBorder,3346light: notebookCellBorder,3347hcDark: contrastBorder,3348hcLight: contrastBorder3349}, nls.localize('notebook.selectedCellBorder', "The color of the cell's top and bottom border when the cell is selected but not focused."));33503351export const inactiveSelectedCellBorder = registerColor('notebook.inactiveSelectedCellBorder', {3352dark: null,3353light: null,3354hcDark: focusBorder,3355hcLight: focusBorder3356}, nls.localize('notebook.inactiveSelectedCellBorder', "The color of the cell's borders when multiple cells are selected."));33573358export const focusedCellBorder = registerColor('notebook.focusedCellBorder', focusBorder, nls.localize('notebook.focusedCellBorder', "The color of the cell's focus indicator borders when the cell is focused."));33593360export 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."));33613362export const cellStatusBarItemHover = registerColor('notebook.cellStatusBarItemHoverBackground', {3363light: new Color(new RGBA(0, 0, 0, 0.08)),3364dark: new Color(new RGBA(255, 255, 255, 0.15)),3365hcDark: new Color(new RGBA(255, 255, 255, 0.15)),3366hcLight: new Color(new RGBA(0, 0, 0, 0.08)),3367}, nls.localize('notebook.cellStatusBarItemHoverBackground', "The background color of notebook cell status bar items."));33683369export const cellInsertionIndicator = registerColor('notebook.cellInsertionIndicator', focusBorder, nls.localize('notebook.cellInsertionIndicator', "The color of the notebook cell insertion indicator."));33703371export const listScrollbarSliderBackground = registerColor('notebookScrollbarSlider.background', scrollbarSliderBackground, nls.localize('notebookScrollbarSliderBackground', "Notebook scrollbar slider background color."));33723373export const listScrollbarSliderHoverBackground = registerColor('notebookScrollbarSlider.hoverBackground', scrollbarSliderHoverBackground, nls.localize('notebookScrollbarSliderHoverBackground', "Notebook scrollbar slider background color when hovering."));33743375export const listScrollbarSliderActiveBackground = registerColor('notebookScrollbarSlider.activeBackground', scrollbarSliderActiveBackground, nls.localize('notebookScrollbarSliderActiveBackground', "Notebook scrollbar slider background color when clicked on."));33763377export const cellSymbolHighlight = registerColor('notebook.symbolHighlightBackground', {3378dark: Color.fromHex('#ffffff0b'),3379light: Color.fromHex('#fdff0033'),3380hcDark: null,3381hcLight: null3382}, nls.localize('notebook.symbolHighlightBackground', "Background color of highlighted cell"));33833384export const cellEditorBackground = registerColor('notebook.cellEditorBackground', {3385light: SIDE_BAR_BACKGROUND,3386dark: SIDE_BAR_BACKGROUND,3387hcDark: null,3388hcLight: null3389}, nls.localize('notebook.cellEditorBackground', "Cell editor background color."));33903391const notebookEditorBackground = registerColor('notebook.editorBackground', {3392light: EDITOR_PANE_BACKGROUND,3393dark: EDITOR_PANE_BACKGROUND,3394hcDark: null,3395hcLight: null3396}, nls.localize('notebook.editorBackground', "Notebook background color."));339733983399