Skip to content

Commit 6fb8d2a

Browse files
authored
Merge pull request #225011 from microsoft/don/issue174152HideCells
Hide unchanged cells
2 parents aefef1e + 81e2e6b commit 6fb8d2a

11 files changed

+908
-168
lines changed

src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts

+117-8
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import * as DOM from 'vs/base/browser/dom';
77
import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
88
import { Schemas } from 'vs/base/common/network';
99
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
10-
import { DiffElementViewModelBase, getFormattedMetadataJSON, getFormattedOutputJSON, OutputComparison, outputEqual, OUTPUT_EDITOR_HEIGHT_MAGIC, PropertyFoldingState, SideBySideDiffElementViewModel, SingleSideDiffElementViewModel } from 'vs/workbench/contrib/notebook/browser/diff/diffElementViewModel';
11-
import { CellDiffSideBySideRenderTemplate, CellDiffSingleSideRenderTemplate, DiffSide, DIFF_CELL_MARGIN, INotebookTextDiffEditor, NOTEBOOK_DIFF_CELL_INPUT, NOTEBOOK_DIFF_CELL_PROPERTY, NOTEBOOK_DIFF_CELL_PROPERTY_EXPANDED } from 'vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser';
10+
import { DiffElementCellViewModelBase, getFormattedMetadataJSON, getFormattedOutputJSON, OutputComparison, outputEqual, OUTPUT_EDITOR_HEIGHT_MAGIC, PropertyFoldingState, SideBySideDiffElementViewModel, SingleSideDiffElementViewModel, DiffElementPlaceholderViewModel } from 'vs/workbench/contrib/notebook/browser/diff/diffElementViewModel';
11+
import { CellDiffSideBySideRenderTemplate, CellDiffSingleSideRenderTemplate, DiffSide, DIFF_CELL_MARGIN, INotebookTextDiffEditor, NOTEBOOK_DIFF_CELL_INPUT, NOTEBOOK_DIFF_CELL_PROPERTY, NOTEBOOK_DIFF_CELL_PROPERTY_EXPANDED, CellDiffPlaceholderRenderTemplate, IDiffCellMarginOverlay } from 'vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser';
1212
import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget';
1313
import { IModelService } from 'vs/editor/common/services/model';
1414
import { ILanguageService } from 'vs/editor/common/languages/language';
@@ -31,7 +31,7 @@ import { SuggestController } from 'vs/editor/contrib/suggest/browser/suggestCont
3131
import { MenuPreventer } from 'vs/workbench/contrib/codeEditor/browser/menuPreventer';
3232
import { SelectionClipboardContributionID } from 'vs/workbench/contrib/codeEditor/browser/selectionClipboard';
3333
import { TabCompletionController } from 'vs/workbench/contrib/snippets/browser/tabCompletion';
34-
import { renderIcon } from 'vs/base/browser/ui/iconLabel/iconLabels';
34+
import { renderIcon, renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels';
3535
import * as editorCommon from 'vs/editor/common/editorCommon';
3636
import { ITextModelService } from 'vs/editor/common/services/resolverService';
3737
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
@@ -44,6 +44,8 @@ import { IAccessibilityService } from 'vs/platform/accessibility/common/accessib
4444
import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget';
4545
import { ICommandService } from 'vs/platform/commands/common/commands';
4646
import { DiffNestedCellViewModel } from 'vs/workbench/contrib/notebook/browser/diff/diffNestedCellViewModel';
47+
import { localize } from 'vs/nls';
48+
import { Emitter } from 'vs/base/common/event';
4749

4850
export function getOptimizedNestedCodeEditorWidgetOptions(): ICodeEditorWidgetOptions {
4951
return {
@@ -59,6 +61,29 @@ export function getOptimizedNestedCodeEditorWidgetOptions(): ICodeEditorWidgetOp
5961
};
6062
}
6163

64+
export class CellDiffPlaceholderElement extends Disposable {
65+
constructor(
66+
placeholder: DiffElementPlaceholderViewModel,
67+
templateData: CellDiffPlaceholderRenderTemplate,
68+
) {
69+
super();
70+
templateData.body.classList.remove('left', 'right', 'full');
71+
const text = (placeholder.hiddenCells.length === 1) ?
72+
localize('hiddenCell', '{0} hidden cell', placeholder.hiddenCells.length) :
73+
localize('hiddenCells', '{0} hidden cells', placeholder.hiddenCells.length);
74+
templateData.placeholder.innerText = text;
75+
76+
this._register(DOM.addDisposableListener(templateData.placeholder, 'dblclick', (e: MouseEvent) => {
77+
if (e.button !== 0) {
78+
return;
79+
}
80+
e.preventDefault();
81+
placeholder.showHiddenCells();
82+
}));
83+
this._register(templateData.marginOverlay.onAction(() => placeholder.showHiddenCells()));
84+
templateData.marginOverlay.show();
85+
}
86+
}
6287

6388
class PropertyHeader extends Disposable {
6489
protected _foldingIndicator!: HTMLElement;
@@ -69,14 +94,14 @@ class PropertyHeader extends Disposable {
6994
protected _propertyExpanded?: IContextKey<boolean>;
7095

7196
constructor(
72-
readonly cell: DiffElementViewModelBase,
97+
readonly cell: DiffElementCellViewModelBase,
7398
readonly propertyHeaderContainer: HTMLElement,
7499
readonly notebookEditor: INotebookTextDiffEditor,
75100
readonly accessor: {
76101
updateInfoRendering: (renderOutput: boolean) => void;
77-
checkIfModified: (cell: DiffElementViewModelBase) => false | { reason: string | undefined };
78-
getFoldingState: (cell: DiffElementViewModelBase) => PropertyFoldingState;
79-
updateFoldingState: (cell: DiffElementViewModelBase, newState: PropertyFoldingState) => void;
102+
checkIfModified: (cell: DiffElementCellViewModelBase) => false | { reason: string | undefined };
103+
getFoldingState: (cell: DiffElementCellViewModelBase) => PropertyFoldingState;
104+
updateFoldingState: (cell: DiffElementCellViewModelBase, newState: PropertyFoldingState) => void;
80105
unChangedLabel: string;
81106
changedLabel: string;
82107
prefix: string;
@@ -271,7 +296,7 @@ abstract class AbstractElementRenderer extends Disposable {
271296

272297
constructor(
273298
readonly notebookEditor: INotebookTextDiffEditor,
274-
readonly cell: DiffElementViewModelBase,
299+
readonly cell: DiffElementCellViewModelBase,
275300
readonly templateData: CellDiffSingleSideRenderTemplate | CellDiffSideBySideRenderTemplate,
276301
readonly style: 'left' | 'right' | 'full',
277302
protected readonly instantiationService: IInstantiationService,
@@ -1361,6 +1386,15 @@ export class ModifiedElement extends AbstractElementRenderer {
13611386
container.classList.remove('inserted', 'removed');
13621387
}
13631388

1389+
override buildBody(): void {
1390+
super.buildBody();
1391+
if (this.cell.displayIconToHideUnmodifiedCells) {
1392+
this._register(this.templateData.marginOverlay.onAction(() => this.cell.hideUnchangedCells()));
1393+
this.templateData.marginOverlay.show();
1394+
} else {
1395+
this.templateData.marginOverlay.hide();
1396+
}
1397+
}
13641398
_disposeMetadata() {
13651399
this.cell.metadataStatusHeight = 0;
13661400
this.cell.metadataHeight = 0;
@@ -1790,3 +1824,78 @@ export class ModifiedElement extends AbstractElementRenderer {
17901824
super.dispose();
17911825
}
17921826
}
1827+
1828+
1829+
export class CollapsedCellOverlayWidget extends Disposable implements IDiffCellMarginOverlay {
1830+
private readonly _nodes = DOM.h('div.diff-hidden-cells', [
1831+
DOM.h('div.center@content', { style: { display: 'flex' } }, [
1832+
DOM.$('a', {
1833+
title: localize('showUnchangedCells', 'Show Unchanged Cells'),
1834+
role: 'button',
1835+
onclick: () => { this._action.fire(); }
1836+
},
1837+
...renderLabelWithIcons('$(unfold)'))]
1838+
),
1839+
]);
1840+
1841+
private readonly _action = this._register(new Emitter<void>());
1842+
public readonly onAction = this._action.event;
1843+
constructor(
1844+
private readonly container: HTMLElement
1845+
) {
1846+
super();
1847+
1848+
this._nodes.root.style.display = 'none';
1849+
container.appendChild(this._nodes.root);
1850+
}
1851+
public show() {
1852+
this._nodes.root.style.display = 'block';
1853+
}
1854+
public hide() {
1855+
this._nodes.root.style.display = 'none';
1856+
}
1857+
public override dispose() {
1858+
this.hide();
1859+
this.container.removeChild(this._nodes.root);
1860+
DOM.reset(this._nodes.root);
1861+
super.dispose();
1862+
}
1863+
}
1864+
1865+
export class UnchangedCellOverlayWidget extends Disposable implements IDiffCellMarginOverlay {
1866+
private readonly _nodes = DOM.h('div.diff-hidden-cells', [
1867+
DOM.h('div.center@content', { style: { display: 'flex' } }, [
1868+
DOM.$('a', {
1869+
title: localize('hideUnchangedCells', 'Hide Unchanged Cells'),
1870+
role: 'button',
1871+
onclick: () => { this._action.fire(); }
1872+
},
1873+
...renderLabelWithIcons('$(fold)')
1874+
),
1875+
]
1876+
),
1877+
]);
1878+
1879+
private readonly _action = this._register(new Emitter<void>());
1880+
public readonly onAction = this._action.event;
1881+
constructor(
1882+
private readonly container: HTMLElement
1883+
) {
1884+
super();
1885+
1886+
this._nodes.root.style.display = 'none';
1887+
container.appendChild(this._nodes.root);
1888+
}
1889+
public show() {
1890+
this._nodes.root.style.display = 'block';
1891+
}
1892+
public hide() {
1893+
this._nodes.root.style.display = 'none';
1894+
}
1895+
public override dispose() {
1896+
this.hide();
1897+
this.container.removeChild(this._nodes.root);
1898+
DOM.reset(this._nodes.root);
1899+
super.dispose();
1900+
}
1901+
}

src/vs/workbench/contrib/notebook/browser/diff/diffElementOutputs.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import * as DOM from 'vs/base/browser/dom';
77
import * as nls from 'vs/nls';
88
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
9-
import { DiffElementViewModelBase, SideBySideDiffElementViewModel } from 'vs/workbench/contrib/notebook/browser/diff/diffElementViewModel';
9+
import { DiffElementCellViewModelBase, SideBySideDiffElementViewModel } from 'vs/workbench/contrib/notebook/browser/diff/diffElementViewModel';
1010
import { DiffSide, INotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser';
1111
import { ICellOutputViewModel, IInsetRenderOutput, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
1212
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
@@ -33,7 +33,7 @@ export class OutputElement extends Disposable {
3333
private _notebookTextModel: NotebookTextModel,
3434
private _notebookService: INotebookService,
3535
private _quickInputService: IQuickInputService,
36-
private _diffElementViewModel: DiffElementViewModelBase,
36+
private _diffElementViewModel: DiffElementCellViewModelBase,
3737
private _diffSide: DiffSide,
3838
private _nestedCell: DiffNestedCellViewModel,
3939
private _outputContainer: HTMLElement,
@@ -228,7 +228,7 @@ export class OutputContainer extends Disposable {
228228
constructor(
229229
private _editor: INotebookTextDiffEditor,
230230
private _notebookTextModel: NotebookTextModel,
231-
private _diffElementViewModel: DiffElementViewModelBase,
231+
private _diffElementViewModel: DiffElementCellViewModelBase,
232232
private _nestedCellViewModel: DiffNestedCellViewModel,
233233
private _diffSide: DiffSide,
234234
private _outputContainer: HTMLElement,

src/vs/workbench/contrib/notebook/browser/diff/diffElementViewModel.ts

+74-11
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,77 @@ interface ILayoutInfoDelta extends ILayoutInfoDelta0 {
3333
recomputeOutput?: boolean;
3434
}
3535

36+
export type IDiffElementViewModelBase = DiffElementCellViewModelBase | DiffElementPlaceholderViewModel;
37+
3638
export abstract class DiffElementViewModelBase extends Disposable {
39+
protected _layoutInfoEmitter = this._register(new Emitter<CellDiffViewModelLayoutChangeEvent>());
40+
onDidLayoutChange = this._layoutInfoEmitter.event;
41+
constructor(
42+
public readonly mainDocumentTextModel: INotebookTextModel,
43+
public readonly editorEventDispatcher: NotebookDiffEditorEventDispatcher,
44+
public readonly initData: {
45+
metadataStatusHeight: number;
46+
outputStatusHeight: number;
47+
fontInfo: FontInfo | undefined;
48+
}
49+
) {
50+
super();
51+
52+
this._register(this.editorEventDispatcher.onDidChangeLayout(e => this._layoutInfoEmitter.fire({ outerWidth: true })));
53+
}
54+
55+
abstract layoutChange(): void;
56+
abstract getHeight(lineHeight: number): number;
57+
abstract get totalHeight(): number;
58+
}
59+
60+
export class DiffElementPlaceholderViewModel extends DiffElementViewModelBase {
61+
readonly type: 'placeholder' = 'placeholder';
62+
public hiddenCells: DiffElementCellViewModelBase[] = [];
63+
protected _unfoldHiddenCells = this._register(new Emitter<void>());
64+
onUnfoldHiddenCells = this._unfoldHiddenCells.event;
65+
66+
constructor(
67+
mainDocumentTextModel: INotebookTextModel,
68+
editorEventDispatcher: NotebookDiffEditorEventDispatcher,
69+
initData: {
70+
metadataStatusHeight: number;
71+
outputStatusHeight: number;
72+
fontInfo: FontInfo | undefined;
73+
}
74+
) {
75+
super(mainDocumentTextModel, editorEventDispatcher, initData);
76+
77+
}
78+
get totalHeight() {
79+
return 24 + (2 * DIFF_CELL_MARGIN);
80+
}
81+
getHeight(_: number): number {
82+
return this.totalHeight;
83+
}
84+
override layoutChange(): void {
85+
//
86+
}
87+
showHiddenCells() {
88+
this._unfoldHiddenCells.fire();
89+
}
90+
}
91+
92+
export abstract class DiffElementCellViewModelBase extends DiffElementViewModelBase {
3793
public cellFoldingState: PropertyFoldingState;
3894
public metadataFoldingState: PropertyFoldingState;
3995
public outputFoldingState: PropertyFoldingState;
40-
protected _layoutInfoEmitter = this._register(new Emitter<CellDiffViewModelLayoutChangeEvent>());
41-
onDidLayoutChange = this._layoutInfoEmitter.event;
4296
protected _stateChangeEmitter = this._register(new Emitter<{ renderOutput: boolean }>());
4397
onDidStateChange = this._stateChangeEmitter.event;
4498
protected _layoutInfo!: IDiffElementLayoutInfo;
4599

100+
public displayIconToHideUnmodifiedCells?: boolean;
101+
private _hideUnchangedCells = this._register(new Emitter<void>());
102+
public onHideUnchangedCells = this._hideUnchangedCells.event;
103+
104+
hideUnchangedCells() {
105+
this._hideUnchangedCells.fire();
106+
}
46107
set rawOutputHeight(height: number) {
47108
this._layout({ rawOutputHeight: Math.min(OUTPUT_EDITOR_HEIGHT_MAGIC, height) });
48109
}
@@ -115,23 +176,27 @@ export abstract class DiffElementViewModelBase extends Disposable {
115176
return this._layoutInfo;
116177
}
117178

179+
get totalHeight() {
180+
return this.layoutInfo.totalHeight;
181+
}
182+
118183
private _sourceEditorViewState: editorCommon.ICodeEditorViewState | editorCommon.IDiffEditorViewState | null = null;
119184
private _outputEditorViewState: editorCommon.ICodeEditorViewState | editorCommon.IDiffEditorViewState | null = null;
120185
private _metadataEditorViewState: editorCommon.ICodeEditorViewState | editorCommon.IDiffEditorViewState | null = null;
121186

122187
constructor(
123-
readonly mainDocumentTextModel: INotebookTextModel,
188+
mainDocumentTextModel: INotebookTextModel,
124189
readonly original: DiffNestedCellViewModel | undefined,
125190
readonly modified: DiffNestedCellViewModel | undefined,
126191
readonly type: 'unchanged' | 'insert' | 'delete' | 'modified',
127-
readonly editorEventDispatcher: NotebookDiffEditorEventDispatcher,
128-
readonly initData: {
192+
editorEventDispatcher: NotebookDiffEditorEventDispatcher,
193+
initData: {
129194
metadataStatusHeight: number;
130195
outputStatusHeight: number;
131196
fontInfo: FontInfo | undefined;
132197
}
133198
) {
134-
super();
199+
super(mainDocumentTextModel, editorEventDispatcher, initData);
135200
const editorHeight = this._estimateEditorHeight(initData.fontInfo);
136201
const cellStatusHeight = 25;
137202
this._layoutInfo = {
@@ -154,9 +219,7 @@ export abstract class DiffElementViewModelBase extends Disposable {
154219
this.metadataFoldingState = PropertyFoldingState.Collapsed;
155220
this.outputFoldingState = PropertyFoldingState.Collapsed;
156221

157-
this._register(this.editorEventDispatcher.onDidChangeLayout(e => {
158-
this._layoutInfoEmitter.fire({ outerWidth: true });
159-
}));
222+
this._register(this.editorEventDispatcher.onDidChangeLayout(e => this._layoutInfoEmitter.fire({ outerWidth: true })));
160223
}
161224

162225
layoutChange() {
@@ -385,7 +448,7 @@ export abstract class DiffElementViewModelBase extends Disposable {
385448
}
386449
}
387450

388-
export class SideBySideDiffElementViewModel extends DiffElementViewModelBase {
451+
export class SideBySideDiffElementViewModel extends DiffElementCellViewModelBase {
389452
get originalDocument() {
390453
return this.otherDocumentTextModel;
391454
}
@@ -543,7 +606,7 @@ export class SideBySideDiffElementViewModel extends DiffElementViewModelBase {
543606
}
544607
}
545608

546-
export class SingleSideDiffElementViewModel extends DiffElementViewModelBase {
609+
export class SingleSideDiffElementViewModel extends DiffElementCellViewModelBase {
547610
get cellViewModel() {
548611
return this.type === 'insert' ? this.modified! : this.original!;
549612
}

0 commit comments

Comments
 (0)