Path: blob/main/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import * as DOM from '../../../../../base/browser/dom.js';6import { Disposable, DisposableStore } from '../../../../../base/common/lifecycle.js';7import { Schemas } from '../../../../../base/common/network.js';8import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js';9import { DiffElementCellViewModelBase, getFormattedOutputJSON, OutputComparison, outputEqual, OUTPUT_EDITOR_HEIGHT_MAGIC, PropertyFoldingState, SideBySideDiffElementViewModel, SingleSideDiffElementViewModel, DiffElementPlaceholderViewModel, IDiffElementViewModelBase, NotebookDocumentMetadataViewModel } from './diffElementViewModel.js';10import { CellDiffSideBySideRenderTemplate, CellDiffSingleSideRenderTemplate, DiffSide, DIFF_CELL_MARGIN, INotebookTextDiffEditor, NOTEBOOK_DIFF_CELL_INPUT, NOTEBOOK_DIFF_CELL_PROPERTY, NOTEBOOK_DIFF_CELL_PROPERTY_EXPANDED, CellDiffPlaceholderRenderTemplate, IDiffCellMarginOverlay, NOTEBOOK_DIFF_CELL_IGNORE_WHITESPACE, NotebookDocumentDiffElementRenderTemplate, NOTEBOOK_DIFF_METADATA } from './notebookDiffEditorBrowser.js';11import { CodeEditorWidget, ICodeEditorWidgetOptions } from '../../../../../editor/browser/widget/codeEditor/codeEditorWidget.js';12import { IModelService } from '../../../../../editor/common/services/model.js';13import { ILanguageService } from '../../../../../editor/common/languages/language.js';14import { CellEditType, CellUri, NotebookCellMetadata } from '../../common/notebookCommon.js';15import { ToolBar } from '../../../../../base/browser/ui/toolbar/toolbar.js';16import { IContextMenuService } from '../../../../../platform/contextview/browser/contextView.js';17import { IMenu, IMenuService, MenuId, MenuItemAction } from '../../../../../platform/actions/common/actions.js';18import { IKeybindingService } from '../../../../../platform/keybinding/common/keybinding.js';19import { INotificationService } from '../../../../../platform/notification/common/notification.js';20import { getFlatActionBarActions } from '../../../../../platform/actions/browser/menuEntryActionViewItem.js';21import { IContextKey, IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js';22import { CodiconActionViewItem } from '../view/cellParts/cellActionView.js';23import { collapsedIcon, expandedIcon } from '../notebookIcons.js';24import { OutputContainer } from './diffElementOutputs.js';25import { EditorExtensionsRegistry } from '../../../../../editor/browser/editorExtensions.js';26import { ContextMenuController } from '../../../../../editor/contrib/contextmenu/browser/contextmenu.js';27import { SnippetController2 } from '../../../../../editor/contrib/snippet/browser/snippetController2.js';28import { SuggestController } from '../../../../../editor/contrib/suggest/browser/suggestController.js';29import { MenuPreventer } from '../../../codeEditor/browser/menuPreventer.js';30import { SelectionClipboardContributionID } from '../../../codeEditor/browser/selectionClipboard.js';31import { TabCompletionController } from '../../../snippets/browser/tabCompletion.js';32import { renderIcon, renderLabelWithIcons } from '../../../../../base/browser/ui/iconLabel/iconLabels.js';33import * as editorCommon from '../../../../../editor/common/editorCommon.js';34import { ITextModelService } from '../../../../../editor/common/services/resolverService.js';35import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js';36import { IThemeService } from '../../../../../platform/theme/common/themeService.js';37import { WorkbenchToolBar } from '../../../../../platform/actions/browser/toolbar.js';38import { ITelemetryService } from '../../../../../platform/telemetry/common/telemetry.js';39import { fixedDiffEditorOptions, fixedEditorOptions, getEditorPadding } from './diffCellEditorOptions.js';40import { AccessibilityVerbositySettingId } from '../../../accessibility/browser/accessibilityConfiguration.js';41import { IAccessibilityService } from '../../../../../platform/accessibility/common/accessibility.js';42import { DiffEditorWidget } from '../../../../../editor/browser/widget/diffEditor/diffEditorWidget.js';43import { ICommandService } from '../../../../../platform/commands/common/commands.js';44import { DiffNestedCellViewModel } from './diffNestedCellViewModel.js';45import { localize } from '../../../../../nls.js';46import { Emitter } from '../../../../../base/common/event.js';47import { ITextResourceConfigurationService } from '../../../../../editor/common/services/textResourceConfiguration.js';48import { getFormattedMetadataJSON } from '../../common/model/notebookCellTextModel.js';49import { IDiffEditorOptions } from '../../../../../editor/common/config/editorOptions.js';50import { getUnchangedRegionSettings } from './unchangedEditorRegions.js';5152export function getOptimizedNestedCodeEditorWidgetOptions(): ICodeEditorWidgetOptions {53return {54isSimpleWidget: false,55contributions: EditorExtensionsRegistry.getSomeEditorContributions([56MenuPreventer.ID,57SelectionClipboardContributionID,58ContextMenuController.ID,59SuggestController.ID,60SnippetController2.ID,61TabCompletionController.ID,62])63};64}6566export class CellDiffPlaceholderElement extends Disposable {67constructor(68placeholder: DiffElementPlaceholderViewModel,69templateData: CellDiffPlaceholderRenderTemplate,70) {71super();72templateData.body.classList.remove('left', 'right', 'full');73const text = (placeholder.hiddenCells.length === 1) ?74localize('hiddenCell', '{0} hidden cell', placeholder.hiddenCells.length) :75localize('hiddenCells', '{0} hidden cells', placeholder.hiddenCells.length);76templateData.placeholder.innerText = text;7778this._register(DOM.addDisposableListener(templateData.placeholder, 'dblclick', (e: MouseEvent) => {79if (e.button !== 0) {80return;81}82e.preventDefault();83placeholder.showHiddenCells();84}));85this._register(templateData.marginOverlay.onAction(() => placeholder.showHiddenCells()));86templateData.marginOverlay.show();87}88}8990class PropertyHeader extends Disposable {91protected _foldingIndicator!: HTMLElement;92protected _statusSpan!: HTMLElement;93protected _description!: HTMLElement;94protected _toolbar!: WorkbenchToolBar;95protected _menu!: IMenu;96protected _propertyExpanded?: IContextKey<boolean>;97protected _propertyChanged?: IContextKey<boolean>;9899constructor(100readonly cell: IDiffElementViewModelBase,101readonly propertyHeaderContainer: HTMLElement,102readonly notebookEditor: INotebookTextDiffEditor,103readonly accessor: {104updateInfoRendering: (renderOutput: boolean) => void;105checkIfModified: () => false | { reason: string | undefined };106getFoldingState: () => PropertyFoldingState;107updateFoldingState: (newState: PropertyFoldingState) => void;108unChangedLabel: string;109changedLabel: string;110prefix: string;111menuId: MenuId;112},113@IContextMenuService private readonly contextMenuService: IContextMenuService,114@IKeybindingService private readonly keybindingService: IKeybindingService,115@ICommandService private readonly commandService: ICommandService,116@INotificationService private readonly notificationService: INotificationService,117@IMenuService private readonly menuService: IMenuService,118@IContextKeyService private readonly contextKeyService: IContextKeyService,119@IThemeService private readonly themeService: IThemeService,120@ITelemetryService private readonly telemetryService: ITelemetryService,121@IAccessibilityService private readonly accessibilityService: IAccessibilityService122) {123super();124}125126buildHeader(): void {127this._foldingIndicator = DOM.append(this.propertyHeaderContainer, DOM.$('.property-folding-indicator'));128this._foldingIndicator.classList.add(this.accessor.prefix);129const metadataStatus = DOM.append(this.propertyHeaderContainer, DOM.$('div.property-status'));130this._statusSpan = DOM.append(metadataStatus, DOM.$('span'));131this._description = DOM.append(metadataStatus, DOM.$('span.property-description'));132133const cellToolbarContainer = DOM.append(this.propertyHeaderContainer, DOM.$('div.property-toolbar'));134this._toolbar = this._register(new WorkbenchToolBar(cellToolbarContainer, {135actionViewItemProvider: (action, options) => {136if (action instanceof MenuItemAction) {137const item = new CodiconActionViewItem(action, { hoverDelegate: options.hoverDelegate }, this.keybindingService, this.notificationService, this.contextKeyService, this.themeService, this.contextMenuService, this.accessibilityService);138return item;139}140141return undefined;142}143}, this.menuService, this.contextKeyService, this.contextMenuService, this.keybindingService, this.commandService, this.telemetryService));144this._toolbar.context = this.cell;145146const scopedContextKeyService = this.contextKeyService.createScoped(cellToolbarContainer);147this._register(scopedContextKeyService);148this._propertyChanged = NOTEBOOK_DIFF_CELL_PROPERTY.bindTo(scopedContextKeyService);149this._propertyExpanded = NOTEBOOK_DIFF_CELL_PROPERTY_EXPANDED.bindTo(scopedContextKeyService);150151this._menu = this._register(this.menuService.createMenu(this.accessor.menuId, scopedContextKeyService));152this._register(this._menu.onDidChange(() => this.updateMenu()));153154this._register(this.notebookEditor.onMouseUp(e => {155if (!e.event.target || e.target !== this.cell) {156return;157}158159const target = e.event.target as HTMLElement;160161if (162target === this.propertyHeaderContainer ||163target === this._foldingIndicator || this._foldingIndicator.contains(target) ||164target === metadataStatus || metadataStatus.contains(target)165) {166const oldFoldingState = this.accessor.getFoldingState();167this.accessor.updateFoldingState(oldFoldingState === PropertyFoldingState.Expanded ? PropertyFoldingState.Collapsed : PropertyFoldingState.Expanded);168this._updateFoldingIcon();169this.accessor.updateInfoRendering(this.cell.renderOutput);170}171}));172173this.refresh();174this.accessor.updateInfoRendering(this.cell.renderOutput);175}176refresh() {177this.updateMenu();178this._updateFoldingIcon();179180const metadataChanged = this.accessor.checkIfModified();181if (this._propertyChanged) {182this._propertyChanged.set(!!metadataChanged);183}184if (metadataChanged) {185this._statusSpan.textContent = this.accessor.changedLabel;186this._statusSpan.style.fontWeight = 'bold';187if (metadataChanged.reason) {188this._description.textContent = metadataChanged.reason;189}190this.propertyHeaderContainer.classList.add('modified');191} else {192this._statusSpan.textContent = this.accessor.unChangedLabel;193this._statusSpan.style.fontWeight = 'normal';194this._description.textContent = '';195this.propertyHeaderContainer.classList.remove('modified');196}197}198199private updateMenu() {200const metadataChanged = this.accessor.checkIfModified();201if (metadataChanged) {202const actions = getFlatActionBarActions(this._menu.getActions({ shouldForwardArgs: true }));203this._toolbar.setActions(actions);204} else {205this._toolbar.setActions([]);206}207}208209private _updateFoldingIcon() {210if (this.accessor.getFoldingState() === PropertyFoldingState.Collapsed) {211DOM.reset(this._foldingIndicator, renderIcon(collapsedIcon));212this._propertyExpanded?.set(false);213} else {214DOM.reset(this._foldingIndicator, renderIcon(expandedIcon));215this._propertyExpanded?.set(true);216}217218}219}220221interface IDiffElementLayoutState {222outerWidth?: boolean;223editorHeight?: boolean;224metadataEditor?: boolean;225metadataHeight?: boolean;226outputTotalHeight?: boolean;227}228229230export class NotebookDocumentMetadataElement extends Disposable {231private readonly _editor: DiffEditorWidget;232private _editorViewStateChanged: boolean;233private _toolbar!: ToolBar;234private readonly _cellHeaderContainer: HTMLElement;235private readonly _editorContainer: HTMLElement;236private _cellHeader!: PropertyHeader;237private _diffEditorContainer!: HTMLElement;238239constructor(240readonly notebookEditor: INotebookTextDiffEditor,241readonly viewModel: NotebookDocumentMetadataViewModel,242readonly templateData: NotebookDocumentDiffElementRenderTemplate,243@IInstantiationService private readonly instantiationService: IInstantiationService,244@ITextModelService private readonly textModelService: ITextModelService,245@IMenuService private readonly menuService: IMenuService,246@IContextKeyService private readonly contextKeyService: IContextKeyService,247@ITextResourceConfigurationService private readonly textConfigurationService: ITextResourceConfigurationService,248@IConfigurationService private readonly configurationService: IConfigurationService,249) {250super();251this._editor = templateData.sourceEditor;252this._cellHeaderContainer = this.templateData.cellHeaderContainer;253this._editorContainer = this.templateData.editorContainer;254this._diffEditorContainer = this.templateData.diffEditorContainer;255256this._editorViewStateChanged = false;257// init258this._register(viewModel.onDidLayoutChange(e => {259this.layout(e);260this.updateBorders();261}));262this.buildBody();263this.updateBorders();264}265266buildBody(): void {267const body = this.templateData.body;268body.classList.remove('full');269body.classList.add('full');270271this.updateSourceEditor();272273if (this.viewModel instanceof NotebookDocumentMetadataViewModel) {274this._register(this.viewModel.modifiedMetadata.onDidChange(e => {275this._cellHeader.refresh();276}));277}278}279protected layoutNotebookCell() {280this.notebookEditor.layoutNotebookCell(281this.viewModel,282this.viewModel.layoutInfo.totalHeight283);284}285286updateBorders() {287this.templateData.leftBorder.style.height = `${this.viewModel.layoutInfo.totalHeight - 32}px`;288this.templateData.rightBorder.style.height = `${this.viewModel.layoutInfo.totalHeight - 32}px`;289this.templateData.bottomBorder.style.top = `${this.viewModel.layoutInfo.totalHeight - 32}px`;290}291updateSourceEditor(): void {292this._cellHeaderContainer.style.display = 'flex';293this._cellHeaderContainer.innerText = '';294this._editorContainer.classList.add('diff');295296const updateSourceEditor = () => {297if (this.viewModel.cellFoldingState === PropertyFoldingState.Collapsed) {298this._editorContainer.style.display = 'none';299this.viewModel.editorHeight = 0;300return;301}302303const lineHeight = this.notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17;304const editorHeight = this.viewModel.layoutInfo.editorHeight !== 0 ? this.viewModel.layoutInfo.editorHeight : this.viewModel.computeInputEditorHeight(lineHeight);305306this._editorContainer.style.height = `${editorHeight}px`;307this._editorContainer.style.display = 'block';308309const contentHeight = this._editor.getContentHeight();310if (contentHeight >= 0) {311this.viewModel.editorHeight = contentHeight;312}313return editorHeight;314};315const renderSourceEditor = () => {316const editorHeight = updateSourceEditor();317if (!editorHeight) {318return;319}320321// If there is only 1 line, then ensure we have the necessary padding to display the button for whitespaces.322// E.g. assume we have a cell with 1 line and we add some whitespace,323// Then diff editor displays the button `Show Whitespace Differences`, however with 12 paddings on the top, the324// button can get cut off.325const lineCount = this.viewModel.modifiedMetadata.textBuffer.getLineCount();326const options: IDiffEditorOptions = {327padding: getEditorPadding(lineCount)328};329const unchangedRegions = this._register(getUnchangedRegionSettings(this.configurationService));330if (unchangedRegions.options.enabled) {331options.hideUnchangedRegions = unchangedRegions.options;332}333this._editor.updateOptions(options);334this._register(unchangedRegions.onDidChangeEnablement(() => {335options.hideUnchangedRegions = unchangedRegions.options;336this._editor.updateOptions(options);337}));338this._editor.layout({339width: this.notebookEditor.getLayoutInfo().width - 2 * DIFF_CELL_MARGIN,340height: editorHeight341});342this._register(this._editor.onDidContentSizeChange((e) => {343if (this.viewModel.cellFoldingState === PropertyFoldingState.Expanded && e.contentHeightChanged && this.viewModel.layoutInfo.editorHeight !== e.contentHeight) {344this.viewModel.editorHeight = e.contentHeight;345}346}));347this._initializeSourceDiffEditor();348};349350this._cellHeader = this._register(this.instantiationService.createInstance(351PropertyHeader,352this.viewModel,353this._cellHeaderContainer,354this.notebookEditor,355{356updateInfoRendering: () => renderSourceEditor(),357checkIfModified: () => {358return this.viewModel.originalMetadata.getHash() !== this.viewModel.modifiedMetadata.getHash() ? { reason: undefined } : false;359},360getFoldingState: () => this.viewModel.cellFoldingState,361updateFoldingState: (state) => this.viewModel.cellFoldingState = state,362unChangedLabel: 'Notebook Metadata',363changedLabel: 'Notebook Metadata changed',364prefix: 'metadata',365menuId: MenuId.NotebookDiffDocumentMetadata366}367));368this._cellHeader.buildHeader();369renderSourceEditor();370371const scopedContextKeyService = this.contextKeyService.createScoped(this.templateData.inputToolbarContainer);372this._register(scopedContextKeyService);373const inputChanged = NOTEBOOK_DIFF_METADATA.bindTo(scopedContextKeyService);374inputChanged.set(this.viewModel.originalMetadata.getHash() !== this.viewModel.modifiedMetadata.getHash());375376this._toolbar = this.templateData.toolbar;377378this._toolbar.context = this.viewModel;379380const refreshToolbar = () => {381const hasChanges = this.viewModel.originalMetadata.getHash() !== this.viewModel.modifiedMetadata.getHash();382inputChanged.set(hasChanges);383384if (hasChanges) {385const menu = this.menuService.getMenuActions(MenuId.NotebookDiffDocumentMetadata, scopedContextKeyService, { shouldForwardArgs: true });386const actions = getFlatActionBarActions(menu);387this._toolbar.setActions(actions);388} else {389this._toolbar.setActions([]);390}391};392393this._register(this.viewModel.modifiedMetadata.onDidChange(() => {394refreshToolbar();395}));396refreshToolbar();397}398399private async _initializeSourceDiffEditor() {400const [originalRef, modifiedRef] = await Promise.all([401this.textModelService.createModelReference(this.viewModel.originalMetadata.uri),402this.textModelService.createModelReference(this.viewModel.modifiedMetadata.uri)]);403404if (this._store.isDisposed) {405originalRef.dispose();406modifiedRef.dispose();407return;408}409410this._register(originalRef);411this._register(modifiedRef);412413const vm = this._register(this._editor.createViewModel({414original: originalRef.object.textEditorModel,415modified: modifiedRef.object.textEditorModel,416}));417418// Reduces flicker (compute this before setting the model)419// Else when the model is set, the height of the editor will be x, after diff is computed, then height will be y.420// & that results in flicker.421await vm.waitForDiff();422this._editor.setModel(vm);423424const handleViewStateChange = () => {425this._editorViewStateChanged = true;426};427428const handleScrollChange = (e: editorCommon.IScrollEvent) => {429if (e.scrollTopChanged || e.scrollLeftChanged) {430this._editorViewStateChanged = true;431}432};433434this.updateEditorOptionsForWhitespace();435this._register(this._editor.getOriginalEditor().onDidChangeCursorSelection(handleViewStateChange));436this._register(this._editor.getOriginalEditor().onDidScrollChange(handleScrollChange));437this._register(this._editor.getModifiedEditor().onDidChangeCursorSelection(handleViewStateChange));438this._register(this._editor.getModifiedEditor().onDidScrollChange(handleScrollChange));439440const editorViewState = this.viewModel.getSourceEditorViewState() as editorCommon.IDiffEditorViewState | null;441if (editorViewState) {442this._editor.restoreViewState(editorViewState);443}444445const contentHeight = this._editor.getContentHeight();446this.viewModel.editorHeight = contentHeight;447}448private updateEditorOptionsForWhitespace() {449const editor = this._editor;450const uri = editor.getModel()?.modified.uri || editor.getModel()?.original.uri;451if (!uri) {452return;453}454const ignoreTrimWhitespace = this.textConfigurationService.getValue<boolean>(uri, 'diffEditor.ignoreTrimWhitespace');455editor.updateOptions({ ignoreTrimWhitespace });456457this._register(this.textConfigurationService.onDidChangeConfiguration(e => {458if (e.affectsConfiguration(uri, 'diffEditor') &&459e.affectedKeys.has('diffEditor.ignoreTrimWhitespace')) {460const ignoreTrimWhitespace = this.textConfigurationService.getValue<boolean>(uri, 'diffEditor.ignoreTrimWhitespace');461editor.updateOptions({ ignoreTrimWhitespace });462}463}));464}465layout(state: IDiffElementLayoutState) {466DOM.scheduleAtNextAnimationFrame(DOM.getWindow(this._diffEditorContainer), () => {467if (state.editorHeight) {468this._editorContainer.style.height = `${this.viewModel.layoutInfo.editorHeight}px`;469this._editor.layout({470width: this._editor.getViewWidth(),471height: this.viewModel.layoutInfo.editorHeight472});473}474475if (state.outerWidth) {476this._editorContainer.style.height = `${this.viewModel.layoutInfo.editorHeight}px`;477this._editor.layout();478}479480this.layoutNotebookCell();481});482}483484override dispose() {485this._editor.setModel(null);486487if (this._editorViewStateChanged) {488this.viewModel.saveSpirceEditorViewState(this._editor.saveViewState());489}490491super.dispose();492}493}494495496abstract class AbstractElementRenderer extends Disposable {497protected readonly _metadataLocalDisposable = this._register(new DisposableStore());498protected readonly _outputLocalDisposable = this._register(new DisposableStore());499protected _ignoreMetadata: boolean = false;500protected _ignoreOutputs: boolean = false;501protected _cellHeaderContainer!: HTMLElement;502protected _editorContainer!: HTMLElement;503protected _cellHeader!: PropertyHeader;504protected _metadataHeaderContainer!: HTMLElement;505protected _metadataHeader!: PropertyHeader;506protected _metadataInfoContainer!: HTMLElement;507protected _metadataEditorContainer?: HTMLElement;508protected readonly _metadataEditorDisposeStore!: DisposableStore;509protected _metadataEditor?: CodeEditorWidget | DiffEditorWidget;510511protected _outputHeaderContainer!: HTMLElement;512protected _outputHeader!: PropertyHeader;513protected _outputInfoContainer!: HTMLElement;514protected _outputEditorContainer?: HTMLElement;515protected _outputViewContainer?: HTMLElement;516protected _outputLeftContainer?: HTMLElement;517protected _outputRightContainer?: HTMLElement;518protected _outputMetadataContainer?: HTMLElement;519protected _outputEmptyElement?: HTMLElement;520protected _outputLeftView?: OutputContainer;521protected _outputRightView?: OutputContainer;522protected readonly _outputEditorDisposeStore!: DisposableStore;523protected _outputEditor?: CodeEditorWidget | DiffEditorWidget;524protected _outputMetadataEditor?: DiffEditorWidget;525526protected _diffEditorContainer!: HTMLElement;527protected _diagonalFill?: HTMLElement;528protected _isDisposed: boolean;529530constructor(531readonly notebookEditor: INotebookTextDiffEditor,532readonly cell: DiffElementCellViewModelBase,533readonly templateData: CellDiffSingleSideRenderTemplate | CellDiffSideBySideRenderTemplate,534readonly style: 'left' | 'right' | 'full',535protected readonly instantiationService: IInstantiationService,536protected readonly languageService: ILanguageService,537protected readonly modelService: IModelService,538protected readonly textModelService: ITextModelService,539protected readonly contextMenuService: IContextMenuService,540protected readonly keybindingService: IKeybindingService,541protected readonly notificationService: INotificationService,542protected readonly menuService: IMenuService,543protected readonly contextKeyService: IContextKeyService,544protected readonly configurationService: IConfigurationService,545protected readonly textConfigurationService: ITextResourceConfigurationService546) {547super();548// init549this._isDisposed = false;550this._metadataEditorDisposeStore = this._register(new DisposableStore());551this._outputEditorDisposeStore = this._register(new DisposableStore());552this._register(cell.onDidLayoutChange(e => {553this.layout(e);554}));555this._register(cell.onDidLayoutChange(e => this.updateBorders()));556this.init();557this.buildBody();558559this._register(cell.onDidStateChange(() => {560this.updateOutputRendering(this.cell.renderOutput);561}));562}563564abstract init(): void;565abstract styleContainer(container: HTMLElement): void;566abstract _buildOutput(): void;567abstract _disposeOutput(): void;568abstract _buildMetadata(): void;569abstract _disposeMetadata(): void;570571buildBody(): void {572const body = this.templateData.body;573this._diffEditorContainer = this.templateData.diffEditorContainer;574body.classList.remove('left', 'right', 'full');575switch (this.style) {576case 'left':577body.classList.add('left');578break;579case 'right':580body.classList.add('right');581break;582default:583body.classList.add('full');584break;585}586587this.styleContainer(this._diffEditorContainer);588this.updateSourceEditor();589if (this.cell.modified) {590this._register(this.cell.modified.textModel.onDidChangeContent(() => this._cellHeader.refresh()));591}592593this._ignoreMetadata = this.configurationService.getValue('notebook.diff.ignoreMetadata');594if (this._ignoreMetadata) {595this._disposeMetadata();596} else {597this._buildMetadata();598}599600this._ignoreOutputs = this.configurationService.getValue<boolean>('notebook.diff.ignoreOutputs') || !!(this.notebookEditor.textModel?.transientOptions.transientOutputs);601if (this._ignoreOutputs) {602this._disposeOutput();603} else {604this._buildOutput();605}606607this._register(this.configurationService.onDidChangeConfiguration(e => {608let metadataLayoutChange = false;609let outputLayoutChange = false;610if (e.affectsConfiguration('notebook.diff.ignoreMetadata')) {611const newValue = this.configurationService.getValue<boolean>('notebook.diff.ignoreMetadata');612613if (newValue !== undefined && this._ignoreMetadata !== newValue) {614this._ignoreMetadata = newValue;615616this._metadataLocalDisposable.clear();617if (this.configurationService.getValue('notebook.diff.ignoreMetadata')) {618this._disposeMetadata();619} else {620this.cell.metadataStatusHeight = 25;621this._buildMetadata();622this.updateMetadataRendering();623metadataLayoutChange = true;624}625}626}627628if (e.affectsConfiguration('notebook.diff.ignoreOutputs')) {629const newValue = this.configurationService.getValue<boolean>('notebook.diff.ignoreOutputs');630631if (newValue !== undefined && this._ignoreOutputs !== (newValue || this.notebookEditor.textModel?.transientOptions.transientOutputs)) {632this._ignoreOutputs = newValue || !!(this.notebookEditor.textModel?.transientOptions.transientOutputs);633634this._outputLocalDisposable.clear();635if (this._ignoreOutputs) {636this._disposeOutput();637this.cell.layoutChange();638} else {639this.cell.outputStatusHeight = 25;640this._buildOutput();641outputLayoutChange = true;642}643}644}645646if (metadataLayoutChange || outputLayoutChange) {647this.layout({ metadataHeight: metadataLayoutChange, outputTotalHeight: outputLayoutChange });648}649}));650}651652updateMetadataRendering() {653if (this.cell.metadataFoldingState === PropertyFoldingState.Expanded) {654// we should expand the metadata editor655this._metadataInfoContainer.style.display = 'block';656657if (!this._metadataEditorContainer || !this._metadataEditor) {658// create editor659this._metadataEditorContainer = DOM.append(this._metadataInfoContainer, DOM.$('.metadata-editor-container'));660this._buildMetadataEditor();661} else {662this.cell.metadataHeight = this._metadataEditor.getContentHeight();663}664} else {665// we should collapse the metadata editor666this._metadataInfoContainer.style.display = 'none';667// this._metadataEditorDisposeStore.clear();668this.cell.metadataHeight = 0;669}670}671672updateOutputRendering(renderRichOutput: boolean) {673if (this.cell.outputFoldingState === PropertyFoldingState.Expanded) {674this._outputInfoContainer.style.display = 'block';675if (renderRichOutput) {676this._hideOutputsRaw();677this._buildOutputRendererContainer();678this._showOutputsRenderer();679this._showOutputsEmptyView();680} else {681this._hideOutputsRenderer();682this._buildOutputRawContainer();683this._showOutputsRaw();684}685} else {686this._outputInfoContainer.style.display = 'none';687688this._hideOutputsRaw();689this._hideOutputsRenderer();690this._hideOutputsEmptyView();691}692}693694private _buildOutputRawContainer() {695if (!this._outputEditorContainer) {696this._outputEditorContainer = DOM.append(this._outputInfoContainer, DOM.$('.output-editor-container'));697this._buildOutputEditor();698}699}700701private _showOutputsRaw() {702if (this._outputEditorContainer) {703this._outputEditorContainer.style.display = 'block';704this.cell.rawOutputHeight = this._outputEditor!.getContentHeight();705}706}707708private _showOutputsEmptyView() {709this.cell.layoutChange();710}711712protected _hideOutputsRaw() {713if (this._outputEditorContainer) {714this._outputEditorContainer.style.display = 'none';715this.cell.rawOutputHeight = 0;716}717}718719protected _hideOutputsEmptyView() {720this.cell.layoutChange();721}722723abstract _buildOutputRendererContainer(): void;724abstract _hideOutputsRenderer(): void;725abstract _showOutputsRenderer(): void;726727private _applySanitizedMetadataChanges(currentMetadata: NotebookCellMetadata, newMetadata: any) {728const result: { [key: string]: any } = {};729try {730const newMetadataObj = JSON.parse(newMetadata);731const keys = new Set([...Object.keys(newMetadataObj)]);732for (const key of keys) {733switch (key as keyof NotebookCellMetadata) {734case 'inputCollapsed':735case 'outputCollapsed':736// boolean737if (typeof newMetadataObj[key] === 'boolean') {738result[key] = newMetadataObj[key];739} else {740result[key] = currentMetadata[key as keyof NotebookCellMetadata];741}742break;743744default:745result[key] = newMetadataObj[key];746break;747}748}749750const index = this.notebookEditor.textModel!.cells.indexOf(this.cell.modified!.textModel);751752if (index < 0) {753return;754}755756this.notebookEditor.textModel!.applyEdits([757{ editType: CellEditType.Metadata, index, metadata: result }758], true, undefined, () => undefined, undefined, true);759} catch {760}761}762763private async _buildMetadataEditor() {764this._metadataEditorDisposeStore.clear();765766if (this.cell instanceof SideBySideDiffElementViewModel) {767this._metadataEditor = this.instantiationService.createInstance(DiffEditorWidget, this._metadataEditorContainer!, {768...fixedDiffEditorOptions,769overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode(),770readOnly: false,771originalEditable: false,772ignoreTrimWhitespace: false,773automaticLayout: false,774dimension: {775height: this.cell.layoutInfo.metadataHeight,776width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), true, true)777}778}, {779originalEditor: getOptimizedNestedCodeEditorWidgetOptions(),780modifiedEditor: getOptimizedNestedCodeEditorWidgetOptions()781});782783const unchangedRegions = this._register(getUnchangedRegionSettings(this.configurationService));784if (unchangedRegions.options.enabled) {785this._metadataEditor.updateOptions({ hideUnchangedRegions: unchangedRegions.options });786}787this._metadataEditorDisposeStore.add(unchangedRegions.onDidChangeEnablement(() => {788if (this._metadataEditor) {789this._metadataEditor.updateOptions({ hideUnchangedRegions: unchangedRegions.options });790}791}));792793794this.layout({ metadataHeight: true });795this._metadataEditorDisposeStore.add(this._metadataEditor);796797this._metadataEditorContainer?.classList.add('diff');798799const [originalMetadataModel, modifiedMetadataModel] = await Promise.all([800this.textModelService.createModelReference(CellUri.generateCellPropertyUri(this.cell.originalDocument.uri, this.cell.original.handle, Schemas.vscodeNotebookCellMetadata)),801this.textModelService.createModelReference(CellUri.generateCellPropertyUri(this.cell.modifiedDocument.uri, this.cell.modified.handle, Schemas.vscodeNotebookCellMetadata))802]);803804if (this._isDisposed) {805originalMetadataModel.dispose();806modifiedMetadataModel.dispose();807return;808}809810this._metadataEditorDisposeStore.add(originalMetadataModel);811this._metadataEditorDisposeStore.add(modifiedMetadataModel);812const vm = this._metadataEditor.createViewModel({813original: originalMetadataModel.object.textEditorModel,814modified: modifiedMetadataModel.object.textEditorModel815});816this._metadataEditor.setModel(vm);817// Reduces flicker (compute this before setting the model)818// Else when the model is set, the height of the editor will be x, after diff is computed, then height will be y.819// & that results in flicker.820await vm.waitForDiff();821822if (this._isDisposed) {823return;824}825826this.cell.metadataHeight = this._metadataEditor.getContentHeight();827828this._metadataEditorDisposeStore.add(this._metadataEditor.onDidContentSizeChange((e) => {829if (e.contentHeightChanged && this.cell.metadataFoldingState === PropertyFoldingState.Expanded) {830this.cell.metadataHeight = e.contentHeight;831}832}));833834let respondingToContentChange = false;835836this._metadataEditorDisposeStore.add(modifiedMetadataModel.object.textEditorModel.onDidChangeContent(() => {837respondingToContentChange = true;838const value = modifiedMetadataModel.object.textEditorModel.getValue();839this._applySanitizedMetadataChanges(this.cell.modified!.metadata, value);840this._metadataHeader.refresh();841respondingToContentChange = false;842}));843844this._metadataEditorDisposeStore.add(this.cell.modified.textModel.onDidChangeMetadata(() => {845if (respondingToContentChange) {846return;847}848849const modifiedMetadataSource = getFormattedMetadataJSON(this.notebookEditor.textModel?.transientOptions.transientCellMetadata, this.cell.modified?.metadata || {}, this.cell.modified?.language, true);850modifiedMetadataModel.object.textEditorModel.setValue(modifiedMetadataSource);851}));852853return;854} else {855this._metadataEditor = this.instantiationService.createInstance(CodeEditorWidget, this._metadataEditorContainer!, {856...fixedEditorOptions,857dimension: {858width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, true),859height: this.cell.layoutInfo.metadataHeight860},861overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode(),862readOnly: false,863allowVariableLineHeights: false864}, {});865this.layout({ metadataHeight: true });866this._metadataEditorDisposeStore.add(this._metadataEditor);867868const mode = this.languageService.createById('jsonc');869const originalMetadataSource = getFormattedMetadataJSON(this.notebookEditor.textModel?.transientOptions.transientCellMetadata,870this.cell.type === 'insert'871? this.cell.modified!.metadata || {}872: this.cell.original!.metadata || {}, undefined, true);873const uri = this.cell.type === 'insert'874? this.cell.modified!.uri875: this.cell.original!.uri;876const handle = this.cell.type === 'insert'877? this.cell.modified!.handle878: this.cell.original!.handle;879880const modelUri = CellUri.generateCellPropertyUri(uri, handle, Schemas.vscodeNotebookCellMetadata);881const metadataModel = this.modelService.createModel(originalMetadataSource, mode, modelUri, false);882this._metadataEditor.setModel(metadataModel);883this._metadataEditorDisposeStore.add(metadataModel);884885this.cell.metadataHeight = this._metadataEditor.getContentHeight();886887this._metadataEditorDisposeStore.add(this._metadataEditor.onDidContentSizeChange((e) => {888if (e.contentHeightChanged && this.cell.metadataFoldingState === PropertyFoldingState.Expanded) {889this.cell.metadataHeight = e.contentHeight;890}891}));892}893}894895private _buildOutputEditor() {896this._outputEditorDisposeStore.clear();897898if ((this.cell.type === 'modified' || this.cell.type === 'unchanged') && !this.notebookEditor.textModel!.transientOptions.transientOutputs) {899const originalOutputsSource = getFormattedOutputJSON(this.cell.original?.outputs || []);900const modifiedOutputsSource = getFormattedOutputJSON(this.cell.modified?.outputs || []);901if (originalOutputsSource !== modifiedOutputsSource) {902const mode = this.languageService.createById('json');903const originalModel = this.modelService.createModel(originalOutputsSource, mode, undefined, true);904const modifiedModel = this.modelService.createModel(modifiedOutputsSource, mode, undefined, true);905this._outputEditorDisposeStore.add(originalModel);906this._outputEditorDisposeStore.add(modifiedModel);907908const lineHeight = this.notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17;909const lineCount = Math.max(originalModel.getLineCount(), modifiedModel.getLineCount());910this._outputEditor = this.instantiationService.createInstance(DiffEditorWidget, this._outputEditorContainer!, {911...fixedDiffEditorOptions,912overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode(),913readOnly: true,914ignoreTrimWhitespace: false,915automaticLayout: false,916dimension: {917height: Math.min(OUTPUT_EDITOR_HEIGHT_MAGIC, this.cell.layoutInfo.rawOutputHeight || lineHeight * lineCount),918width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, true)919},920accessibilityVerbose: this.configurationService.getValue<boolean>(AccessibilityVerbositySettingId.DiffEditor) ?? false921}, {922originalEditor: getOptimizedNestedCodeEditorWidgetOptions(),923modifiedEditor: getOptimizedNestedCodeEditorWidgetOptions()924});925this._outputEditorDisposeStore.add(this._outputEditor);926927this._outputEditorContainer?.classList.add('diff');928929this._outputEditor.setModel({930original: originalModel,931modified: modifiedModel932});933this._outputEditor.restoreViewState(this.cell.getOutputEditorViewState() as editorCommon.IDiffEditorViewState);934935this.cell.rawOutputHeight = this._outputEditor.getContentHeight();936937this._outputEditorDisposeStore.add(this._outputEditor.onDidContentSizeChange((e) => {938if (e.contentHeightChanged && this.cell.outputFoldingState === PropertyFoldingState.Expanded) {939this.cell.rawOutputHeight = e.contentHeight;940}941}));942943this._outputEditorDisposeStore.add(this.cell.modified!.textModel.onDidChangeOutputs(() => {944const modifiedOutputsSource = getFormattedOutputJSON(this.cell.modified?.outputs || []);945modifiedModel.setValue(modifiedOutputsSource);946this._outputHeader.refresh();947}));948949return;950}951}952953this._outputEditor = this.instantiationService.createInstance(CodeEditorWidget, this._outputEditorContainer!, {954...fixedEditorOptions,955dimension: {956width: Math.min(OUTPUT_EDITOR_HEIGHT_MAGIC, this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, this.cell.type === 'unchanged' || this.cell.type === 'modified') - 32),957height: this.cell.layoutInfo.rawOutputHeight958},959overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode(),960allowVariableLineHeights: false961}, {});962this._outputEditorDisposeStore.add(this._outputEditor);963964const mode = this.languageService.createById('json');965const originaloutputSource = getFormattedOutputJSON(966this.notebookEditor.textModel!.transientOptions.transientOutputs967? []968: this.cell.type === 'insert'969? this.cell.modified?.outputs || []970: this.cell.original?.outputs || []);971const outputModel = this.modelService.createModel(originaloutputSource, mode, undefined, true);972this._outputEditorDisposeStore.add(outputModel);973this._outputEditor.setModel(outputModel);974this._outputEditor.restoreViewState(this.cell.getOutputEditorViewState());975976this.cell.rawOutputHeight = this._outputEditor.getContentHeight();977978this._outputEditorDisposeStore.add(this._outputEditor.onDidContentSizeChange((e) => {979if (e.contentHeightChanged && this.cell.outputFoldingState === PropertyFoldingState.Expanded) {980this.cell.rawOutputHeight = e.contentHeight;981}982}));983}984985protected layoutNotebookCell() {986this.notebookEditor.layoutNotebookCell(987this.cell,988this.cell.layoutInfo.totalHeight989);990}991992updateBorders() {993this.templateData.leftBorder.style.height = `${this.cell.layoutInfo.totalHeight - 32}px`;994this.templateData.rightBorder.style.height = `${this.cell.layoutInfo.totalHeight - 32}px`;995this.templateData.bottomBorder.style.top = `${this.cell.layoutInfo.totalHeight - 32}px`;996}997998override dispose() {999if (this._outputEditor) {1000this.cell.saveOutputEditorViewState(this._outputEditor.saveViewState());1001}10021003if (this._metadataEditor) {1004this.cell.saveMetadataEditorViewState(this._metadataEditor.saveViewState());1005}10061007this._metadataEditorDisposeStore.dispose();1008this._outputEditorDisposeStore.dispose();10091010this._isDisposed = true;1011super.dispose();1012}10131014abstract updateSourceEditor(): void;1015abstract layout(state: IDiffElementLayoutState): void;1016}10171018abstract class SingleSideDiffElement extends AbstractElementRenderer {1019protected _editor!: CodeEditorWidget;1020override readonly cell: SingleSideDiffElementViewModel;1021override readonly templateData: CellDiffSingleSideRenderTemplate;1022abstract get nestedCellViewModel(): DiffNestedCellViewModel;1023abstract get readonly(): boolean;1024constructor(1025notebookEditor: INotebookTextDiffEditor,1026cell: SingleSideDiffElementViewModel,1027templateData: CellDiffSingleSideRenderTemplate,1028style: 'left' | 'right' | 'full',1029instantiationService: IInstantiationService,1030languageService: ILanguageService,1031modelService: IModelService,1032textModelService: ITextModelService,1033contextMenuService: IContextMenuService,1034keybindingService: IKeybindingService,1035notificationService: INotificationService,1036menuService: IMenuService,1037contextKeyService: IContextKeyService,1038configurationService: IConfigurationService,1039textConfigurationService: ITextResourceConfigurationService1040) {1041super(1042notebookEditor,1043cell,1044templateData,1045style,1046instantiationService,1047languageService,1048modelService,1049textModelService,1050contextMenuService,1051keybindingService,1052notificationService,1053menuService,1054contextKeyService,1055configurationService,1056textConfigurationService1057);1058this.cell = cell;1059this.templateData = templateData;10601061this.updateBorders();1062}10631064init() {1065this._diagonalFill = this.templateData.diagonalFill;1066}10671068override buildBody() {1069const body = this.templateData.body;1070this._diffEditorContainer = this.templateData.diffEditorContainer;1071body.classList.remove('left', 'right', 'full');1072switch (this.style) {1073case 'left':1074body.classList.add('left');1075break;1076case 'right':1077body.classList.add('right');1078break;1079default:1080body.classList.add('full');1081break;1082}10831084this.styleContainer(this._diffEditorContainer);1085this.updateSourceEditor();10861087if (this.configurationService.getValue('notebook.diff.ignoreMetadata')) {1088this._disposeMetadata();1089} else {1090this._buildMetadata();1091}10921093if (this.configurationService.getValue('notebook.diff.ignoreOutputs') || this.notebookEditor.textModel?.transientOptions.transientOutputs) {1094this._disposeOutput();1095} else {1096this._buildOutput();1097}10981099this._register(this.configurationService.onDidChangeConfiguration(e => {1100let metadataLayoutChange = false;1101let outputLayoutChange = false;1102if (e.affectsConfiguration('notebook.diff.ignoreMetadata')) {1103this._metadataLocalDisposable.clear();1104if (this.configurationService.getValue('notebook.diff.ignoreMetadata')) {1105this._disposeMetadata();1106} else {1107this.cell.metadataStatusHeight = 25;1108this._buildMetadata();1109this.updateMetadataRendering();1110metadataLayoutChange = true;1111}1112}11131114if (e.affectsConfiguration('notebook.diff.ignoreOutputs')) {1115this._outputLocalDisposable.clear();1116if (this.configurationService.getValue('notebook.diff.ignoreOutputs') || this.notebookEditor.textModel?.transientOptions.transientOutputs) {1117this._disposeOutput();1118} else {1119this.cell.outputStatusHeight = 25;1120this._buildOutput();1121outputLayoutChange = true;1122}1123}11241125if (metadataLayoutChange || outputLayoutChange) {1126this.layout({ metadataHeight: metadataLayoutChange, outputTotalHeight: outputLayoutChange });1127}1128}));1129}11301131override updateSourceEditor(): void {1132this._cellHeaderContainer = this.templateData.cellHeaderContainer;1133this._cellHeaderContainer.style.display = 'flex';1134this._cellHeaderContainer.innerText = '';1135this._editorContainer = this.templateData.editorContainer;1136this._editorContainer.classList.add('diff');11371138const renderSourceEditor = () => {1139if (this.cell.cellFoldingState === PropertyFoldingState.Collapsed) {1140this._editorContainer.style.display = 'none';1141this.cell.editorHeight = 0;1142return;1143}1144const lineHeight = this.notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17;1145const editorHeight = this.cell.computeInputEditorHeight(lineHeight);11461147this._editorContainer.style.height = `${editorHeight}px`;1148this._editorContainer.style.display = 'block';11491150if (this._editor) {1151const contentHeight = this._editor.getContentHeight();1152if (contentHeight >= 0) {1153this.cell.editorHeight = contentHeight;1154}1155return;1156}11571158this._editor = this.templateData.sourceEditor;1159this._editor.layout(1160{1161width: (this.notebookEditor.getLayoutInfo().width - 2 * DIFF_CELL_MARGIN) / 2 - 18,1162height: editorHeight1163}1164);1165this._editor.updateOptions({ readOnly: this.readonly });1166this.cell.editorHeight = editorHeight;11671168this._register(this._editor.onDidContentSizeChange((e) => {1169if (this.cell.cellFoldingState === PropertyFoldingState.Expanded && e.contentHeightChanged && this.cell.layoutInfo.editorHeight !== e.contentHeight) {1170this.cell.editorHeight = e.contentHeight;1171}1172}));1173this._initializeSourceDiffEditor(this.nestedCellViewModel);1174};11751176this._cellHeader = this._register(this.instantiationService.createInstance(1177PropertyHeader,1178this.cell,1179this._cellHeaderContainer,1180this.notebookEditor,1181{1182updateInfoRendering: () => renderSourceEditor(),1183checkIfModified: () => ({ reason: undefined }),1184getFoldingState: () => this.cell.cellFoldingState,1185updateFoldingState: (state) => this.cell.cellFoldingState = state,1186unChangedLabel: 'Input',1187changedLabel: 'Input',1188prefix: 'input',1189menuId: MenuId.NotebookDiffCellInputTitle1190}1191));1192this._cellHeader.buildHeader();1193renderSourceEditor();11941195this._initializeSourceDiffEditor(this.nestedCellViewModel);1196}1197protected calculateDiagonalFillHeight() {1198return this.cell.layoutInfo.cellStatusHeight + this.cell.layoutInfo.editorHeight + this.cell.layoutInfo.editorMargin + this.cell.layoutInfo.metadataStatusHeight + this.cell.layoutInfo.metadataHeight + this.cell.layoutInfo.outputTotalHeight + this.cell.layoutInfo.outputStatusHeight;1199}12001201private async _initializeSourceDiffEditor(modifiedCell: DiffNestedCellViewModel) {1202const modifiedRef = await this.textModelService.createModelReference(modifiedCell.uri);12031204if (this._isDisposed) {1205return;1206}12071208const modifiedTextModel = modifiedRef.object.textEditorModel;1209this._register(modifiedRef);12101211this._editor!.setModel(modifiedTextModel);12121213const editorViewState = this.cell.getSourceEditorViewState() as editorCommon.IDiffEditorViewState | null;1214if (editorViewState) {1215this._editor!.restoreViewState(editorViewState);1216}12171218const contentHeight = this._editor!.getContentHeight();1219this.cell.editorHeight = contentHeight;1220const height = `${this.calculateDiagonalFillHeight()}px`;1221if (this._diagonalFill!.style.height !== height) {1222this._diagonalFill!.style.height = height;1223}1224}12251226_disposeMetadata() {1227this.cell.metadataStatusHeight = 0;1228this.cell.metadataHeight = 0;1229this.templateData.cellHeaderContainer.style.display = 'none';1230this.templateData.metadataHeaderContainer.style.display = 'none';1231this.templateData.metadataInfoContainer.style.display = 'none';1232this._metadataEditor = undefined;1233}12341235_buildMetadata() {1236this._metadataHeaderContainer = this.templateData.metadataHeaderContainer;1237this._metadataInfoContainer = this.templateData.metadataInfoContainer;1238this._metadataHeaderContainer.style.display = 'flex';1239this._metadataInfoContainer.style.display = 'block';1240this._metadataHeaderContainer.innerText = '';1241this._metadataInfoContainer.innerText = '';12421243this._metadataHeader = this.instantiationService.createInstance(1244PropertyHeader,1245this.cell,1246this._metadataHeaderContainer,1247this.notebookEditor,1248{1249updateInfoRendering: this.updateMetadataRendering.bind(this),1250checkIfModified: () => {1251return this.cell.checkMetadataIfModified();1252},1253getFoldingState: () => {1254return this.cell.metadataFoldingState;1255},1256updateFoldingState: (state) => {1257this.cell.metadataFoldingState = state;1258},1259unChangedLabel: 'Metadata',1260changedLabel: 'Metadata changed',1261prefix: 'metadata',1262menuId: MenuId.NotebookDiffCellMetadataTitle1263}1264);1265this._metadataLocalDisposable.add(this._metadataHeader);1266this._metadataHeader.buildHeader();1267}12681269_buildOutput() {1270this.templateData.outputHeaderContainer.style.display = 'flex';1271this.templateData.outputInfoContainer.style.display = 'block';12721273this._outputHeaderContainer = this.templateData.outputHeaderContainer;1274this._outputInfoContainer = this.templateData.outputInfoContainer;12751276this._outputHeaderContainer.innerText = '';1277this._outputInfoContainer.innerText = '';12781279this._outputHeader = this.instantiationService.createInstance(1280PropertyHeader,1281this.cell,1282this._outputHeaderContainer,1283this.notebookEditor,1284{1285updateInfoRendering: this.updateOutputRendering.bind(this),1286checkIfModified: () => {1287return this.cell.checkIfOutputsModified();1288},1289getFoldingState: () => {1290return this.cell.outputFoldingState;1291},1292updateFoldingState: (state) => {1293this.cell.outputFoldingState = state;1294},1295unChangedLabel: 'Outputs',1296changedLabel: 'Outputs changed',1297prefix: 'output',1298menuId: MenuId.NotebookDiffCellOutputsTitle1299}1300);1301this._outputLocalDisposable.add(this._outputHeader);1302this._outputHeader.buildHeader();1303}13041305_disposeOutput() {1306this._hideOutputsRaw();1307this._hideOutputsRenderer();1308this._hideOutputsEmptyView();13091310this.cell.rawOutputHeight = 0;1311this.cell.outputMetadataHeight = 0;1312this.cell.outputStatusHeight = 0;1313this.templateData.outputHeaderContainer.style.display = 'none';1314this.templateData.outputInfoContainer.style.display = 'none';1315this._outputViewContainer = undefined;1316}1317}1318export class DeletedElement extends SingleSideDiffElement {1319constructor(1320notebookEditor: INotebookTextDiffEditor,1321cell: SingleSideDiffElementViewModel,1322templateData: CellDiffSingleSideRenderTemplate,1323@ILanguageService languageService: ILanguageService,1324@IModelService modelService: IModelService,1325@ITextModelService textModelService: ITextModelService,1326@IInstantiationService instantiationService: IInstantiationService,1327@IContextMenuService contextMenuService: IContextMenuService,1328@IKeybindingService keybindingService: IKeybindingService,1329@INotificationService notificationService: INotificationService,1330@IMenuService menuService: IMenuService,1331@IContextKeyService contextKeyService: IContextKeyService,1332@IConfigurationService configurationService: IConfigurationService,1333@ITextResourceConfigurationService textConfigurationService: ITextResourceConfigurationService,1334) {1335super(notebookEditor, cell, templateData, 'left', instantiationService, languageService, modelService, textModelService, contextMenuService, keybindingService, notificationService, menuService, contextKeyService, configurationService, textConfigurationService);1336}13371338get nestedCellViewModel() {1339return this.cell.original!;1340}1341get readonly() {1342return true;1343}13441345styleContainer(container: HTMLElement) {1346container.classList.remove('inserted');1347container.classList.add('removed');1348}13491350layout(state: IDiffElementLayoutState) {1351DOM.scheduleAtNextAnimationFrame(DOM.getWindow(this._diffEditorContainer), () => {1352if ((state.editorHeight || state.outerWidth) && this._editor) {1353this._editorContainer.style.height = `${this.cell.layoutInfo.editorHeight}px`;1354this._editor.layout({1355width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, false),1356height: this.cell.layoutInfo.editorHeight1357});1358}13591360if (state.outerWidth && this._editor) {1361this._editorContainer.style.height = `${this.cell.layoutInfo.editorHeight}px`;1362this._editor.layout();1363}13641365if (state.metadataHeight || state.outerWidth) {1366this._metadataEditor?.layout({1367width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, false),1368height: this.cell.layoutInfo.metadataHeight1369});1370}13711372if (state.outputTotalHeight || state.outerWidth) {1373this._outputEditor?.layout({1374width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, false),1375height: this.cell.layoutInfo.outputTotalHeight1376});1377}13781379if (this._diagonalFill) {1380this._diagonalFill.style.height = `${this.calculateDiagonalFillHeight()}px`;1381}13821383this.layoutNotebookCell();1384});1385}138613871388_buildOutputRendererContainer() {1389if (!this._outputViewContainer) {1390this._outputViewContainer = DOM.append(this._outputInfoContainer, DOM.$('.output-view-container'));1391this._outputEmptyElement = DOM.append(this._outputViewContainer, DOM.$('.output-empty-view'));1392const span = DOM.append(this._outputEmptyElement, DOM.$('span'));1393span.innerText = 'No outputs to render';13941395if (!this.cell.original?.outputs.length) {1396this._outputEmptyElement.style.display = 'block';1397} else {1398this._outputEmptyElement.style.display = 'none';1399}14001401this.cell.layoutChange();14021403this._outputLeftView = this.instantiationService.createInstance(OutputContainer, this.notebookEditor, this.notebookEditor.textModel!, this.cell, this.cell.original!, DiffSide.Original, this._outputViewContainer);1404this._register(this._outputLeftView);1405this._outputLeftView.render();14061407const removedOutputRenderListener = this.notebookEditor.onDidDynamicOutputRendered(e => {1408if (e.cell.uri.toString() === this.cell.original!.uri.toString()) {1409this.notebookEditor.deltaCellOutputContainerClassNames(DiffSide.Original, this.cell.original!.id, ['nb-cellDeleted'], []);1410removedOutputRenderListener.dispose();1411}1412});14131414this._register(removedOutputRenderListener);1415}14161417this._outputViewContainer.style.display = 'block';1418}14191420_decorate() {1421this.notebookEditor.deltaCellOutputContainerClassNames(DiffSide.Original, this.cell.original!.id, ['nb-cellDeleted'], []);1422}14231424_showOutputsRenderer() {1425if (this._outputViewContainer) {1426this._outputViewContainer.style.display = 'block';14271428this._outputLeftView?.showOutputs();1429this._decorate();1430}1431}14321433_hideOutputsRenderer() {1434if (this._outputViewContainer) {1435this._outputViewContainer.style.display = 'none';14361437this._outputLeftView?.hideOutputs();1438}1439}14401441override dispose() {1442if (this._editor) {1443this.cell.saveSpirceEditorViewState(this._editor.saveViewState());1444}14451446super.dispose();1447}1448}14491450export class InsertElement extends SingleSideDiffElement {1451constructor(1452notebookEditor: INotebookTextDiffEditor,1453cell: SingleSideDiffElementViewModel,1454templateData: CellDiffSingleSideRenderTemplate,1455@IInstantiationService instantiationService: IInstantiationService,1456@ILanguageService languageService: ILanguageService,1457@IModelService modelService: IModelService,1458@ITextModelService textModelService: ITextModelService,1459@IContextMenuService contextMenuService: IContextMenuService,1460@IKeybindingService keybindingService: IKeybindingService,1461@INotificationService notificationService: INotificationService,1462@IMenuService menuService: IMenuService,1463@IContextKeyService contextKeyService: IContextKeyService,1464@IConfigurationService configurationService: IConfigurationService,1465@ITextResourceConfigurationService textConfigurationService: ITextResourceConfigurationService,1466) {1467super(notebookEditor, cell, templateData, 'right', instantiationService, languageService, modelService, textModelService, contextMenuService, keybindingService, notificationService, menuService, contextKeyService, configurationService, textConfigurationService);1468}1469get nestedCellViewModel() {1470return this.cell.modified!;1471}1472get readonly() {1473return false;1474}14751476styleContainer(container: HTMLElement): void {1477container.classList.remove('removed');1478container.classList.add('inserted');1479}14801481_buildOutputRendererContainer() {1482if (!this._outputViewContainer) {1483this._outputViewContainer = DOM.append(this._outputInfoContainer, DOM.$('.output-view-container'));1484this._outputEmptyElement = DOM.append(this._outputViewContainer, DOM.$('.output-empty-view'));1485this._outputEmptyElement.innerText = 'No outputs to render';14861487if (!this.cell.modified?.outputs.length) {1488this._outputEmptyElement.style.display = 'block';1489} else {1490this._outputEmptyElement.style.display = 'none';1491}14921493this.cell.layoutChange();14941495this._outputRightView = this.instantiationService.createInstance(OutputContainer, this.notebookEditor, this.notebookEditor.textModel!, this.cell, this.cell.modified!, DiffSide.Modified, this._outputViewContainer);1496this._register(this._outputRightView);1497this._outputRightView.render();14981499const insertOutputRenderListener = this.notebookEditor.onDidDynamicOutputRendered(e => {1500if (e.cell.uri.toString() === this.cell.modified!.uri.toString()) {1501this.notebookEditor.deltaCellOutputContainerClassNames(DiffSide.Modified, this.cell.modified!.id, ['nb-cellAdded'], []);1502insertOutputRenderListener.dispose();1503}1504});1505this._register(insertOutputRenderListener);1506}15071508this._outputViewContainer.style.display = 'block';1509}15101511_decorate() {1512this.notebookEditor.deltaCellOutputContainerClassNames(DiffSide.Modified, this.cell.modified!.id, ['nb-cellAdded'], []);1513}15141515_showOutputsRenderer() {1516if (this._outputViewContainer) {1517this._outputViewContainer.style.display = 'block';1518this._outputRightView?.showOutputs();1519this._decorate();1520}1521}15221523_hideOutputsRenderer() {1524if (this._outputViewContainer) {1525this._outputViewContainer.style.display = 'none';1526this._outputRightView?.hideOutputs();1527}1528}15291530layout(state: IDiffElementLayoutState) {1531DOM.scheduleAtNextAnimationFrame(DOM.getWindow(this._diffEditorContainer), () => {1532if ((state.editorHeight || state.outerWidth) && this._editor) {1533this._editorContainer.style.height = `${this.cell.layoutInfo.editorHeight}px`;1534this._editor.layout({1535width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, false),1536height: this.cell.layoutInfo.editorHeight1537});1538}15391540if (state.outerWidth && this._editor) {1541this._editorContainer.style.height = `${this.cell.layoutInfo.editorHeight}px`;1542this._editor.layout();1543}15441545if (state.metadataHeight || state.outerWidth) {1546this._metadataEditor?.layout({1547width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, true),1548height: this.cell.layoutInfo.metadataHeight1549});1550}15511552if (state.outputTotalHeight || state.outerWidth) {1553this._outputEditor?.layout({1554width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, false),1555height: this.cell.layoutInfo.outputTotalHeight1556});1557}15581559this.layoutNotebookCell();15601561if (this._diagonalFill) {1562this._diagonalFill.style.height = `${this.calculateDiagonalFillHeight()}px`;1563}1564});1565}15661567override dispose() {1568if (this._editor) {1569this.cell.saveSpirceEditorViewState(this._editor.saveViewState());1570}15711572super.dispose();1573}1574}15751576export class ModifiedElement extends AbstractElementRenderer {1577private _editor?: DiffEditorWidget;1578private _editorViewStateChanged: boolean;1579protected _toolbar!: ToolBar;1580protected _menu!: IMenu;15811582override readonly cell: SideBySideDiffElementViewModel;1583override readonly templateData: CellDiffSideBySideRenderTemplate;15841585constructor(1586notebookEditor: INotebookTextDiffEditor,1587cell: SideBySideDiffElementViewModel,1588templateData: CellDiffSideBySideRenderTemplate,1589@IInstantiationService instantiationService: IInstantiationService,1590@ILanguageService languageService: ILanguageService,1591@IModelService modelService: IModelService,1592@ITextModelService textModelService: ITextModelService,1593@IContextMenuService contextMenuService: IContextMenuService,1594@IKeybindingService keybindingService: IKeybindingService,1595@INotificationService notificationService: INotificationService,1596@IMenuService menuService: IMenuService,1597@IContextKeyService contextKeyService: IContextKeyService,1598@IConfigurationService configurationService: IConfigurationService,1599@ITextResourceConfigurationService textConfigurationService: ITextResourceConfigurationService,1600) {1601super(notebookEditor, cell, templateData, 'full', instantiationService, languageService, modelService, textModelService, contextMenuService, keybindingService, notificationService, menuService, contextKeyService, configurationService, textConfigurationService);1602this.cell = cell;1603this.templateData = templateData;1604this._editorViewStateChanged = false;16051606this.updateBorders();1607}16081609init() { }1610styleContainer(container: HTMLElement): void {1611container.classList.remove('inserted', 'removed');1612}16131614override buildBody(): void {1615super.buildBody();1616if (this.cell.displayIconToHideUnmodifiedCells) {1617this._register(this.templateData.marginOverlay.onAction(() => this.cell.hideUnchangedCells()));1618this.templateData.marginOverlay.show();1619} else {1620this.templateData.marginOverlay.hide();1621}1622}1623_disposeMetadata() {1624this.cell.metadataStatusHeight = 0;1625this.cell.metadataHeight = 0;1626this.templateData.metadataHeaderContainer.style.display = 'none';1627this.templateData.metadataInfoContainer.style.display = 'none';1628this._metadataEditor = undefined;1629}16301631_buildMetadata() {1632this._metadataHeaderContainer = this.templateData.metadataHeaderContainer;1633this._metadataInfoContainer = this.templateData.metadataInfoContainer;1634this._metadataHeaderContainer.style.display = 'flex';1635this._metadataInfoContainer.style.display = 'block';16361637this._metadataHeaderContainer.innerText = '';1638this._metadataInfoContainer.innerText = '';16391640this._metadataHeader = this.instantiationService.createInstance(1641PropertyHeader,1642this.cell,1643this._metadataHeaderContainer,1644this.notebookEditor,1645{1646updateInfoRendering: this.updateMetadataRendering.bind(this),1647checkIfModified: () => {1648return this.cell.checkMetadataIfModified();1649},1650getFoldingState: () => {1651return this.cell.metadataFoldingState;1652},1653updateFoldingState: (state) => {1654this.cell.metadataFoldingState = state;1655},1656unChangedLabel: 'Metadata',1657changedLabel: 'Metadata changed',1658prefix: 'metadata',1659menuId: MenuId.NotebookDiffCellMetadataTitle1660}1661);1662this._metadataLocalDisposable.add(this._metadataHeader);1663this._metadataHeader.buildHeader();1664}16651666_disposeOutput() {1667this._hideOutputsRaw();1668this._hideOutputsRenderer();1669this._hideOutputsEmptyView();16701671this.cell.rawOutputHeight = 0;1672this.cell.outputMetadataHeight = 0;1673this.cell.outputStatusHeight = 0;1674this.templateData.outputHeaderContainer.style.display = 'none';1675this.templateData.outputInfoContainer.style.display = 'none';1676this._outputViewContainer = undefined;1677}16781679_buildOutput() {1680this.templateData.outputHeaderContainer.style.display = 'flex';1681this.templateData.outputInfoContainer.style.display = 'block';16821683this._outputHeaderContainer = this.templateData.outputHeaderContainer;1684this._outputInfoContainer = this.templateData.outputInfoContainer;1685this._outputHeaderContainer.innerText = '';1686this._outputInfoContainer.innerText = '';16871688if (this.cell.checkIfOutputsModified()) {1689this._outputInfoContainer.classList.add('modified');1690} else {1691this._outputInfoContainer.classList.remove('modified');1692}16931694this._outputHeader = this.instantiationService.createInstance(1695PropertyHeader,1696this.cell,1697this._outputHeaderContainer,1698this.notebookEditor,1699{1700updateInfoRendering: this.updateOutputRendering.bind(this),1701checkIfModified: () => {1702return this.cell.checkIfOutputsModified();1703},1704getFoldingState: () => {1705return this.cell.outputFoldingState;1706},1707updateFoldingState: (state) => {1708this.cell.outputFoldingState = state;1709},1710unChangedLabel: 'Outputs',1711changedLabel: 'Outputs changed',1712prefix: 'output',1713menuId: MenuId.NotebookDiffCellOutputsTitle1714}1715);1716this._outputLocalDisposable.add(this._outputHeader);1717this._outputHeader.buildHeader();1718}17191720_buildOutputRendererContainer() {1721if (!this._outputViewContainer) {1722this._outputViewContainer = DOM.append(this._outputInfoContainer, DOM.$('.output-view-container'));1723this._outputEmptyElement = DOM.append(this._outputViewContainer, DOM.$('.output-empty-view'));1724this._outputEmptyElement.innerText = 'No outputs to render';17251726if (!this.cell.checkIfOutputsModified() && this.cell.modified.outputs.length === 0) {1727this._outputEmptyElement.style.display = 'block';1728} else {1729this._outputEmptyElement.style.display = 'none';1730}17311732this.cell.layoutChange();17331734this._register(this.cell.modified.textModel.onDidChangeOutputs(() => {1735// currently we only allow outputs change to the modified cell1736if (!this.cell.checkIfOutputsModified() && this.cell.modified.outputs.length === 0) {1737this._outputEmptyElement!.style.display = 'block';1738} else {1739this._outputEmptyElement!.style.display = 'none';1740}1741this._decorate();1742}));17431744this._outputLeftContainer = DOM.append(this._outputViewContainer, DOM.$('.output-view-container-left'));1745this._outputRightContainer = DOM.append(this._outputViewContainer, DOM.$('.output-view-container-right'));1746this._outputMetadataContainer = DOM.append(this._outputViewContainer, DOM.$('.output-view-container-metadata'));17471748const outputModified = this.cell.checkIfOutputsModified();1749const outputMetadataChangeOnly = outputModified1750&& outputModified.kind === OutputComparison.Metadata1751&& this.cell.original.outputs.length === 11752&& this.cell.modified.outputs.length === 11753&& outputEqual(this.cell.original.outputs[0], this.cell.modified.outputs[0]) === OutputComparison.Metadata;17541755if (outputModified && !outputMetadataChangeOnly) {1756const originalOutputRenderListener = this.notebookEditor.onDidDynamicOutputRendered(e => {1757if (e.cell.uri.toString() === this.cell.original.uri.toString() && this.cell.checkIfOutputsModified()) {1758this.notebookEditor.deltaCellOutputContainerClassNames(DiffSide.Original, this.cell.original.id, ['nb-cellDeleted'], []);1759originalOutputRenderListener.dispose();1760}1761});17621763const modifiedOutputRenderListener = this.notebookEditor.onDidDynamicOutputRendered(e => {1764if (e.cell.uri.toString() === this.cell.modified.uri.toString() && this.cell.checkIfOutputsModified()) {1765this.notebookEditor.deltaCellOutputContainerClassNames(DiffSide.Modified, this.cell.modified.id, ['nb-cellAdded'], []);1766modifiedOutputRenderListener.dispose();1767}1768});17691770this._register(originalOutputRenderListener);1771this._register(modifiedOutputRenderListener);1772}17731774// We should use the original text model here1775this._outputLeftView = this.instantiationService.createInstance(OutputContainer, this.notebookEditor, this.notebookEditor.textModel!, this.cell, this.cell.original, DiffSide.Original, this._outputLeftContainer);1776this._outputLeftView.render();1777this._register(this._outputLeftView);1778this._outputRightView = this.instantiationService.createInstance(OutputContainer, this.notebookEditor, this.notebookEditor.textModel!, this.cell, this.cell.modified, DiffSide.Modified, this._outputRightContainer);1779this._outputRightView.render();1780this._register(this._outputRightView);17811782if (outputModified && !outputMetadataChangeOnly) {1783this._decorate();1784}17851786if (outputMetadataChangeOnly) {17871788this._outputMetadataContainer.style.top = `${this.cell.layoutInfo.rawOutputHeight}px`;1789// single output, metadata change, let's render a diff editor for metadata1790this._outputMetadataEditor = this.instantiationService.createInstance(DiffEditorWidget, this._outputMetadataContainer, {1791...fixedDiffEditorOptions,1792overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode(),1793readOnly: true,1794ignoreTrimWhitespace: false,1795automaticLayout: false,1796dimension: {1797height: OUTPUT_EDITOR_HEIGHT_MAGIC,1798width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, true)1799}1800}, {1801originalEditor: getOptimizedNestedCodeEditorWidgetOptions(),1802modifiedEditor: getOptimizedNestedCodeEditorWidgetOptions()1803});18041805this._register(this._outputMetadataEditor);1806const originalOutputMetadataSource = JSON.stringify(this.cell.original.outputs[0].metadata ?? {}, undefined, '\t');1807const modifiedOutputMetadataSource = JSON.stringify(this.cell.modified.outputs[0].metadata ?? {}, undefined, '\t');18081809const mode = this.languageService.createById('json');1810const originalModel = this.modelService.createModel(originalOutputMetadataSource, mode, undefined, true);1811const modifiedModel = this.modelService.createModel(modifiedOutputMetadataSource, mode, undefined, true);18121813this._outputMetadataEditor.setModel({1814original: originalModel,1815modified: modifiedModel1816});18171818this.cell.outputMetadataHeight = this._outputMetadataEditor.getContentHeight();18191820this._register(this._outputMetadataEditor.onDidContentSizeChange((e) => {1821this.cell.outputMetadataHeight = e.contentHeight;1822}));1823}1824}18251826this._outputViewContainer.style.display = 'block';1827}18281829_decorate() {1830if (this.cell.checkIfOutputsModified()) {1831this.notebookEditor.deltaCellOutputContainerClassNames(DiffSide.Original, this.cell.original.id, ['nb-cellDeleted'], []);1832this.notebookEditor.deltaCellOutputContainerClassNames(DiffSide.Modified, this.cell.modified.id, ['nb-cellAdded'], []);1833} else {1834this.notebookEditor.deltaCellOutputContainerClassNames(DiffSide.Original, this.cell.original.id, [], ['nb-cellDeleted']);1835this.notebookEditor.deltaCellOutputContainerClassNames(DiffSide.Modified, this.cell.modified.id, [], ['nb-cellAdded']);1836}1837}18381839_showOutputsRenderer() {1840if (this._outputViewContainer) {1841this._outputViewContainer.style.display = 'block';18421843this._outputLeftView?.showOutputs();1844this._outputRightView?.showOutputs();1845this._outputMetadataEditor?.layout({1846width: this._editor?.getViewWidth() || this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, true),1847height: this.cell.layoutInfo.outputMetadataHeight1848});18491850this._decorate();1851}1852}18531854_hideOutputsRenderer() {1855if (this._outputViewContainer) {1856this._outputViewContainer.style.display = 'none';18571858this._outputLeftView?.hideOutputs();1859this._outputRightView?.hideOutputs();1860}1861}18621863updateSourceEditor(): void {1864this._cellHeaderContainer = this.templateData.cellHeaderContainer;1865this._cellHeaderContainer.style.display = 'flex';1866this._cellHeaderContainer.innerText = '';1867const modifiedCell = this.cell.modified;1868this._editorContainer = this.templateData.editorContainer;1869this._editorContainer.classList.add('diff');18701871const renderSourceEditor = () => {1872if (this.cell.cellFoldingState === PropertyFoldingState.Collapsed) {1873this._editorContainer.style.display = 'none';1874this.cell.editorHeight = 0;1875return;1876}18771878const lineCount = modifiedCell.textModel.textBuffer.getLineCount();1879const lineHeight = this.notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17;1880const editorHeight = this.cell.layoutInfo.editorHeight !== 0 ? this.cell.layoutInfo.editorHeight : this.cell.computeInputEditorHeight(lineHeight);18811882this._editorContainer.style.height = `${editorHeight}px`;1883this._editorContainer.style.display = 'block';18841885if (this._editor) {1886const contentHeight = this._editor.getContentHeight();1887if (contentHeight >= 0) {1888this.cell.editorHeight = contentHeight;1889}1890return;1891}18921893this._editor = this.templateData.sourceEditor;1894// If there is only 1 line, then ensure we have the necessary padding to display the button for whitespaces.1895// E.g. assume we have a cell with 1 line and we add some whitespace,1896// Then diff editor displays the button `Show Whitespace Differences`, however with 12 paddings on the top, the1897// button can get cut off.1898const options: IDiffEditorOptions = {1899padding: getEditorPadding(lineCount)1900};1901const unchangedRegions = this._register(getUnchangedRegionSettings(this.configurationService));1902if (unchangedRegions.options.enabled) {1903options.hideUnchangedRegions = unchangedRegions.options;1904}1905this._editor.updateOptions(options);1906this._register(unchangedRegions.onDidChangeEnablement(() => {1907options.hideUnchangedRegions = unchangedRegions.options;1908this._editor?.updateOptions(options);1909}));1910this._editor.layout({1911width: this.notebookEditor.getLayoutInfo().width - 2 * DIFF_CELL_MARGIN,1912height: editorHeight1913});1914this._register(this._editor.onDidContentSizeChange((e) => {1915if (this.cell.cellFoldingState === PropertyFoldingState.Expanded && e.contentHeightChanged && this.cell.layoutInfo.editorHeight !== e.contentHeight) {1916this.cell.editorHeight = e.contentHeight;1917}1918}));1919this._initializeSourceDiffEditor();1920};19211922this._cellHeader = this._register(this.instantiationService.createInstance(1923PropertyHeader,1924this.cell,1925this._cellHeaderContainer,1926this.notebookEditor,1927{1928updateInfoRendering: () => renderSourceEditor(),1929checkIfModified: () => {1930return this.cell.modified?.textModel.getTextBufferHash() !== this.cell.original?.textModel.getTextBufferHash() ? { reason: undefined } : false;1931},1932getFoldingState: () => this.cell.cellFoldingState,1933updateFoldingState: (state) => this.cell.cellFoldingState = state,1934unChangedLabel: 'Input',1935changedLabel: 'Input changed',1936prefix: 'input',1937menuId: MenuId.NotebookDiffCellInputTitle1938}1939));1940this._cellHeader.buildHeader();1941renderSourceEditor();19421943const scopedContextKeyService = this.contextKeyService.createScoped(this.templateData.inputToolbarContainer);1944this._register(scopedContextKeyService);1945const inputChanged = NOTEBOOK_DIFF_CELL_INPUT.bindTo(scopedContextKeyService);1946inputChanged.set(this.cell.modified.textModel.getTextBufferHash() !== this.cell.original.textModel.getTextBufferHash());19471948const ignoreWhitespace = NOTEBOOK_DIFF_CELL_IGNORE_WHITESPACE.bindTo(scopedContextKeyService);1949const ignore = this.textConfigurationService.getValue<boolean>(this.cell.modified.uri, 'diffEditor.ignoreTrimWhitespace');1950ignoreWhitespace.set(ignore);19511952this._toolbar = this.templateData.toolbar;19531954this._toolbar.context = this.cell;19551956const refreshToolbar = () => {1957const ignore = this.textConfigurationService.getValue<boolean>(this.cell.modified.uri, 'diffEditor.ignoreTrimWhitespace');1958ignoreWhitespace.set(ignore);1959const hasChanges = this.cell.modified.textModel.getTextBufferHash() !== this.cell.original.textModel.getTextBufferHash();1960inputChanged.set(hasChanges);19611962if (hasChanges) {1963const menu = this.menuService.getMenuActions(MenuId.NotebookDiffCellInputTitle, scopedContextKeyService, { shouldForwardArgs: true });1964const actions = getFlatActionBarActions(menu);1965this._toolbar.setActions(actions);1966} else {1967this._toolbar.setActions([]);1968}1969};19701971this._register(this.cell.modified.textModel.onDidChangeContent(() => refreshToolbar()));1972this._register(this.textConfigurationService.onDidChangeConfiguration(e => {1973if (e.affectsConfiguration(this.cell.modified.uri, 'diffEditor') &&1974e.affectedKeys.has('diffEditor.ignoreTrimWhitespace')) {1975refreshToolbar();1976}1977}));1978refreshToolbar();1979}19801981private async _initializeSourceDiffEditor() {1982const [originalRef, modifiedRef] = await Promise.all([1983this.textModelService.createModelReference(this.cell.original.uri),1984this.textModelService.createModelReference(this.cell.modified.uri)]);1985this._register(originalRef);1986this._register(modifiedRef);19871988if (this._isDisposed) {1989originalRef.dispose();1990modifiedRef.dispose();1991return;1992}19931994const vm = this._register(this._editor!.createViewModel({1995original: originalRef.object.textEditorModel,1996modified: modifiedRef.object.textEditorModel,1997}));19981999// Reduces flicker (compute this before setting the model)2000// Else when the model is set, the height of the editor will be x, after diff is computed, then height will be y.2001// & that results in flicker.2002await vm.waitForDiff();2003this._editor!.setModel(vm);20042005const handleViewStateChange = () => {2006this._editorViewStateChanged = true;2007};20082009const handleScrollChange = (e: editorCommon.IScrollEvent) => {2010if (e.scrollTopChanged || e.scrollLeftChanged) {2011this._editorViewStateChanged = true;2012}2013};20142015this.updateEditorOptionsForWhitespace();2016this._register(this._editor!.getOriginalEditor().onDidChangeCursorSelection(handleViewStateChange));2017this._register(this._editor!.getOriginalEditor().onDidScrollChange(handleScrollChange));2018this._register(this._editor!.getModifiedEditor().onDidChangeCursorSelection(handleViewStateChange));2019this._register(this._editor!.getModifiedEditor().onDidScrollChange(handleScrollChange));20202021const editorViewState = this.cell.getSourceEditorViewState() as editorCommon.IDiffEditorViewState | null;2022if (editorViewState) {2023this._editor!.restoreViewState(editorViewState);2024}20252026const contentHeight = this._editor!.getContentHeight();2027this.cell.editorHeight = contentHeight;2028}2029private updateEditorOptionsForWhitespace() {2030const editor = this._editor;2031if (!editor) {2032return;2033}2034const uri = editor.getModel()?.modified.uri || editor.getModel()?.original.uri;2035if (!uri) {2036return;2037}2038const ignoreTrimWhitespace = this.textConfigurationService.getValue<boolean>(uri, 'diffEditor.ignoreTrimWhitespace');2039editor.updateOptions({ ignoreTrimWhitespace });20402041this._register(this.textConfigurationService.onDidChangeConfiguration(e => {2042if (e.affectsConfiguration(uri, 'diffEditor') &&2043e.affectedKeys.has('diffEditor.ignoreTrimWhitespace')) {2044const ignoreTrimWhitespace = this.textConfigurationService.getValue<boolean>(uri, 'diffEditor.ignoreTrimWhitespace');2045editor.updateOptions({ ignoreTrimWhitespace });2046}2047}));2048}2049layout(state: IDiffElementLayoutState) {2050DOM.scheduleAtNextAnimationFrame(DOM.getWindow(this._diffEditorContainer), () => {2051if (state.editorHeight && this._editor) {2052this._editorContainer.style.height = `${this.cell.layoutInfo.editorHeight}px`;2053this._editor.layout({2054width: this._editor!.getViewWidth(),2055height: this.cell.layoutInfo.editorHeight2056});2057}20582059if (state.outerWidth && this._editor) {2060this._editorContainer.style.height = `${this.cell.layoutInfo.editorHeight}px`;2061this._editor.layout();2062}20632064if (state.metadataHeight || state.outerWidth) {2065if (this._metadataEditorContainer) {2066this._metadataEditorContainer.style.height = `${this.cell.layoutInfo.metadataHeight}px`;2067this._metadataEditor?.layout({2068width: this._editor?.getViewWidth() || this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, true),2069height: this.cell.layoutInfo.metadataHeight2070});2071}2072}20732074if (state.outputTotalHeight || state.outerWidth) {2075if (this._outputEditorContainer) {2076this._outputEditorContainer.style.height = `${this.cell.layoutInfo.outputTotalHeight}px`;2077this._outputEditor?.layout({2078width: this._editor?.getViewWidth() || this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, true),2079height: this.cell.layoutInfo.outputTotalHeight2080});2081}20822083if (this._outputMetadataContainer) {2084this._outputMetadataContainer.style.height = `${this.cell.layoutInfo.outputMetadataHeight}px`;2085this._outputMetadataContainer.style.top = `${this.cell.layoutInfo.outputTotalHeight - this.cell.layoutInfo.outputMetadataHeight}px`;2086this._outputMetadataEditor?.layout({2087width: this._editor?.getViewWidth() || this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, true),2088height: this.cell.layoutInfo.outputMetadataHeight2089});2090}2091}20922093this.layoutNotebookCell();2094});2095}20962097override dispose() {2098// The editor isn't disposed yet, it can be re-used.2099// However the model can be disposed before the editor & that causes issues.2100if (this._editor) {2101this._editor.setModel(null);2102}21032104if (this._editor && this._editorViewStateChanged) {2105this.cell.saveSpirceEditorViewState(this._editor.saveViewState());2106}21072108super.dispose();2109}2110}211121122113export class CollapsedCellOverlayWidget extends Disposable implements IDiffCellMarginOverlay {2114private readonly _nodes = DOM.h('div.diff-hidden-cells', [2115DOM.h('div.center@content', { style: { display: 'flex' } }, [2116DOM.$('a', {2117title: localize('showUnchangedCells', 'Show Unchanged Cells'),2118role: 'button',2119onclick: () => { this._action.fire(); }2120},2121...renderLabelWithIcons('$(unfold)'))]2122),2123]);21242125private readonly _action = this._register(new Emitter<void>());2126public readonly onAction = this._action.event;2127constructor(2128private readonly container: HTMLElement2129) {2130super();21312132this._nodes.root.style.display = 'none';2133container.appendChild(this._nodes.root);2134}21352136public show() {2137this._nodes.root.style.display = 'block';2138}21392140public hide() {2141this._nodes.root.style.display = 'none';2142}21432144public override dispose() {2145this.hide();2146this.container.removeChild(this._nodes.root);2147DOM.reset(this._nodes.root);2148super.dispose();2149}2150}21512152export class UnchangedCellOverlayWidget extends Disposable implements IDiffCellMarginOverlay {2153private readonly _nodes = DOM.h('div.diff-hidden-cells', [2154DOM.h('div.center@content', { style: { display: 'flex' } }, [2155DOM.$('a', {2156title: localize('hideUnchangedCells', 'Hide Unchanged Cells'),2157role: 'button',2158onclick: () => { this._action.fire(); }2159},2160...renderLabelWithIcons('$(fold)')2161),2162]2163),2164]);21652166private readonly _action = this._register(new Emitter<void>());2167public readonly onAction = this._action.event;2168constructor(2169private readonly container: HTMLElement2170) {2171super();21722173this._nodes.root.style.display = 'none';2174container.appendChild(this._nodes.root);2175}21762177public show() {2178this._nodes.root.style.display = 'block';2179}21802181public hide() {2182this._nodes.root.style.display = 'none';2183}2184public override dispose() {2185this.hide();2186this.container.removeChild(this._nodes.root);2187DOM.reset(this._nodes.root);2188super.dispose();2189}2190}219121922193