Skip to content

Commit 9a9264c

Browse files
authored
Merge pull request microsoft#186148 from microsoft/hediet/b/zoophagous-raven
Preserves collapsed range states
2 parents 23051f0 + 0194790 commit 9a9264c

File tree

11 files changed

+183
-160
lines changed

11 files changed

+183
-160
lines changed

src/vs/editor/browser/widget/diffEditorWidget.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1031,7 +1031,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE
10311031
const modifiedViewState = this._modifiedEditor.saveViewState();
10321032
return {
10331033
original: originalViewState,
1034-
modified: modifiedViewState
1034+
modified: modifiedViewState,
10351035
};
10361036
}
10371037

src/vs/editor/browser/widget/diffEditorWidget2/diffEditorDecorations.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
import { Disposable } from 'vs/base/common/lifecycle';
77
import { IObservable, derived } from 'vs/base/common/observable';
88
import { isDefined } from 'vs/base/common/types';
9-
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
109
import { arrowRevertChange, diffAddDecoration, diffAddDecorationEmpty, diffDeleteDecoration, diffDeleteDecorationEmpty, diffLineAddDecorationBackground, diffLineAddDecorationBackgroundWithIndicator, diffLineDeleteDecorationBackground, diffLineDeleteDecorationBackgroundWithIndicator } from 'vs/editor/browser/widget/diffEditorWidget2/decorations';
10+
import { DiffEditorEditors } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors';
1111
import { DiffEditorOptions } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorOptions';
1212
import { DiffEditorViewModel } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel';
1313
import { MovedBlocksLinesPart } from 'vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines';
@@ -19,15 +19,14 @@ import { IModelDeltaDecoration } from 'vs/editor/common/model';
1919

2020
export class DiffEditorDecorations extends Disposable {
2121
constructor(
22-
private readonly _originalEditor: CodeEditorWidget,
23-
private readonly _modifiedEditor: CodeEditorWidget,
22+
private readonly _editors: DiffEditorEditors,
2423
private readonly _diffModel: IObservable<DiffEditorViewModel | undefined>,
2524
private readonly _options: DiffEditorOptions,
2625
) {
2726
super();
2827

29-
this._register(applyObservableDecorations(this._originalEditor, this._decorations.map(d => d?.originalDecorations || [])));
30-
this._register(applyObservableDecorations(this._modifiedEditor, this._decorations.map(d => d?.modifiedDecorations || [])));
28+
this._register(applyObservableDecorations(this._editors.original, this._decorations.map(d => d?.originalDecorations || [])));
29+
this._register(applyObservableDecorations(this._editors.modified, this._decorations.map(d => d?.modifiedDecorations || [])));
3130
}
3231

3332
private readonly _decorations = derived('decorations', (reader) => {

src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts

Lines changed: 45 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { Disposable } from 'vs/base/common/lifecycle';
88
import { IObservable, IReader, ISettableObservable, ITransaction, derived, observableSignal, observableSignalFromEvent, observableValue, transaction, waitForState } from 'vs/base/common/observable';
99
import { autorunWithStore2 } from 'vs/base/common/observableImpl/autorun';
1010
import { isDefined } from 'vs/base/common/types';
11-
import { LineRange } from 'vs/editor/common/core/lineRange';
11+
import { ISerializedLineRange, LineRange } from 'vs/editor/common/core/lineRange';
1212
import { Range } from 'vs/editor/common/core/range';
1313
import { IDocumentDiff, IDocumentDiffProvider } from 'vs/editor/common/diff/documentDiffProvider';
1414
import { LineRangeMapping, MovedText, RangeMapping, SimpleLineRangeMapping } from 'vs/editor/common/diff/linesDiffComputer';
@@ -137,20 +137,6 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo
137137
.filter(r => !!r)
138138
.map(r => LineRange.fromRange(r!));
139139

140-
for (const r of newUnchangedRegions) {
141-
for (let i = 0; i < lastUnchangedRegions.regions.length; i++) {
142-
if (r.originalRange.intersectsStrict(lastUnchangedRegionsOrigRanges[i])
143-
&& r.modifiedRange.intersectsStrict(lastUnchangedRegionsModRanges[i])) {
144-
r.setState(
145-
lastUnchangedRegions.regions[i].visibleLineCountTop.get(),
146-
lastUnchangedRegions.regions[i].visibleLineCountBottom.get(),
147-
undefined,
148-
);
149-
break;
150-
}
151-
}
152-
}
153-
154140
const originalDecorationIds = model.original.deltaDecorations(
155141
lastUnchangedRegions.originalDecorationIds,
156142
newUnchangedRegions.map(r => ({ range: r.originalRange.toInclusiveRange()!, options: { description: 'unchanged' } }))
@@ -161,6 +147,16 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo
161147
);
162148

163149
transaction(tx => {
150+
for (const r of newUnchangedRegions) {
151+
for (let i = 0; i < lastUnchangedRegions.regions.length; i++) {
152+
if (r.originalRange.intersectsStrict(lastUnchangedRegionsOrigRanges[i])
153+
&& r.modifiedRange.intersectsStrict(lastUnchangedRegionsModRanges[i])) {
154+
r.setHiddenModifiedRange(lastUnchangedRegions.regions[i].getHiddenModifiedRange(undefined), tx);
155+
break;
156+
}
157+
}
158+
}
159+
164160
this._lastDiff = result;
165161
this._diff.set(DiffState.fromDiffResult(result), tx);
166162
this._isDiffUpToDate.set(true, tx);
@@ -208,6 +204,32 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo
208204
public async waitForDiff(): Promise<void> {
209205
await waitForState(this.isDiffUpToDate, s => s);
210206
}
207+
208+
public serializeState(): SerializedState {
209+
const regions = this._unchangedRegions.get();
210+
return {
211+
collapsedRegions: regions.regions.map(r => ({ range: r.getHiddenModifiedRange(undefined).serialize() }))
212+
};
213+
}
214+
215+
public restoreSerializedState(state: SerializedState): void {
216+
const ranges = state.collapsedRegions.map(r => LineRange.deserialize(r.range));
217+
const regions = this._unchangedRegions.get();
218+
transaction(tx => {
219+
for (const r of regions.regions) {
220+
for (const range of ranges) {
221+
if (r.modifiedRange.intersect(range)) {
222+
r.setHiddenModifiedRange(range, tx);
223+
break;
224+
}
225+
}
226+
}
227+
});
228+
}
229+
}
230+
231+
interface SerializedState {
232+
collapsedRegions: { range: ISerializedLineRange }[];
211233
}
212234

213235
export class DiffState {
@@ -334,6 +356,12 @@ export class UnchangedRegion {
334356
);
335357
}
336358

359+
public setHiddenModifiedRange(range: LineRange, tx: ITransaction) {
360+
const visibleLineCountTop = range.startLineNumber - this.modifiedLineNumber;
361+
const visibleLineCountBottom = (this.modifiedLineNumber + this.lineCount) - range.endLineNumberExclusive;
362+
this.setState(visibleLineCountTop, visibleLineCountBottom, tx);
363+
}
364+
337365
public getMaxVisibleLineCountTop() {
338366
return this.lineCount - this._visibleLineCountBottom.get();
339367
}
@@ -357,8 +385,8 @@ export class UnchangedRegion {
357385
}
358386

359387
public setState(visibleLineCountTop: number, visibleLineCountBottom: number, tx: ITransaction | undefined): void {
360-
visibleLineCountTop = Math.min(visibleLineCountTop, this.lineCount);
361-
visibleLineCountBottom = Math.min(visibleLineCountBottom, this.lineCount - visibleLineCountTop);
388+
visibleLineCountTop = Math.max(Math.min(visibleLineCountTop, this.lineCount), 0);
389+
visibleLineCountBottom = Math.max(Math.min(visibleLineCountBottom, this.lineCount - visibleLineCountTop), 0);
362390

363391
this._visibleLineCountTop.set(visibleLineCountTop, tx);
364392
this._visibleLineCountBottom.set(visibleLineCountBottom, tx);

src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts

Lines changed: 47 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -104,56 +104,10 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor {
104104
(i, c, o, o2) => this._createInnerEditor(i, c, o, o2),
105105
));
106106

107-
108-
this._register(this._editors.original.onDidChangeCursorPosition(e => {
109-
const m = this._diffModel.get();
110-
if (!m) { return; }
111-
112-
const movedText = m.diff.get()!.movedTexts.find(m => m.lineRangeMapping.originalRange.contains(e.position.lineNumber));
113-
m.syncedMovedTexts.set(movedText, undefined);
114-
}));
115-
this._register(this._editors.modified.onDidChangeCursorPosition(e => {
116-
const m = this._diffModel.get();
117-
if (!m) { return; }
118-
119-
const movedText = m.diff.get()!.movedTexts.find(m => m.lineRangeMapping.modifiedRange.contains(e.position.lineNumber));
120-
m.syncedMovedTexts.set(movedText, undefined);
121-
}));
122-
// Revert change when an arrow is clicked.
123-
this._register(this._editors.modified.onMouseDown(event => {
124-
if (!event.event.rightButton && event.target.position && event.target.element?.className.includes('arrow-revert-change')) {
125-
const lineNumber = event.target.position.lineNumber;
126-
const viewZone = event.target as IMouseTargetViewZone | undefined;
127-
128-
const model = this._diffModel.get();
129-
if (!model) {
130-
return;
131-
}
132-
const diffs = model.diff.get()?.mappings;
133-
if (!diffs) {
134-
return;
135-
}
136-
const diff = diffs.find(d =>
137-
viewZone?.detail.afterLineNumber === d.lineRangeMapping.modifiedRange.startLineNumber - 1 ||
138-
d.lineRangeMapping.modifiedRange.startLineNumber === lineNumber
139-
);
140-
if (!diff) {
141-
return;
142-
}
143-
this.revert(diff.lineRangeMapping);
144-
145-
event.event.stopPropagation();
146-
return;
147-
}
148-
}));
149-
150-
151107
this._sash = derivedWithStore('sash', (reader, store) => {
152108
const showSash = this._options.renderSideBySide.read(reader);
153109
this.elements.root.classList.toggle('side-by-side', showSash);
154-
if (!showSash) {
155-
return undefined;
156-
}
110+
if (!showSash) { return undefined; }
157111
const result = store.add(new DiffEditorSash(
158112
this._options,
159113
this.elements.root,
@@ -173,36 +127,25 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor {
173127
this._register(keepAlive(this._sash, true));
174128

175129
this._register(autorunWithStore2('unchangedRangesFeature', (reader, store) => {
176-
this.unchangedRangesFeature = store.add(new (readHotReloadableExport(UnchangedRangesFeature, reader))(
177-
this._editors.original,
178-
this._editors.modified,
179-
this._diffModel,
180-
this._options.renderSideBySide,
181-
));
130+
this.unchangedRangesFeature = store.add(new (readHotReloadableExport(UnchangedRangesFeature, reader))(this._editors, this._diffModel, this._options));
182131
}));
183132

184133
this._register(autorunWithStore2('decorations', (reader, store) => {
185-
store.add(new (readHotReloadableExport(DiffEditorDecorations, reader))(
186-
this._editors.original,
187-
this._editors.modified,
188-
this._diffModel,
189-
this._options,
190-
));
134+
store.add(new (readHotReloadableExport(DiffEditorDecorations, reader))(this._editors, this._diffModel, this._options));
191135
}));
192136

193137
this._register(this._instantiationService.createInstance(
194138
ViewZoneManager,
195-
this._editors.original,
196-
this._editors.modified,
139+
this._editors,
197140
this._diffModel,
198141
this._options,
199142
this,
200143
() => this.unchangedRangesFeature.isUpdatingViewZones,
201144
));
202145

203-
this._register(this._instantiationService.createInstance(OverviewRulerPart,
204-
this._editors.original,
205-
this._editors.modified,
146+
this._register(this._instantiationService.createInstance(
147+
OverviewRulerPart,
148+
this._editors,
206149
this.elements.root,
207150
this._diffModel,
208151
this._rootSizeObserver.width,
@@ -227,8 +170,7 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor {
227170
this._diffModel,
228171
this._layoutInfo.map(i => i.originalEditor),
229172
this._layoutInfo.map(i => i.modifiedEditor),
230-
this._editors.original,
231-
this._editors.modified,
173+
this._editors,
232174
));
233175

234176
this._register(applyStyle(this.elements.overlay, {
@@ -238,6 +180,40 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor {
238180
? 'visible' : 'hidden'
239181
),
240182
}));
183+
184+
this._register(this._editors.original.onDidChangeCursorPosition(e => {
185+
const m = this._diffModel.get();
186+
if (!m) { return; }
187+
const movedText = m.diff.get()!.movedTexts.find(m => m.lineRangeMapping.originalRange.contains(e.position.lineNumber));
188+
m.syncedMovedTexts.set(movedText, undefined);
189+
}));
190+
this._register(this._editors.modified.onDidChangeCursorPosition(e => {
191+
const m = this._diffModel.get();
192+
if (!m) { return; }
193+
const movedText = m.diff.get()!.movedTexts.find(m => m.lineRangeMapping.modifiedRange.contains(e.position.lineNumber));
194+
m.syncedMovedTexts.set(movedText, undefined);
195+
}));
196+
197+
// Revert change when an arrow is clicked.
198+
this._register(this._editors.modified.onMouseDown(event => {
199+
if (!event.event.rightButton && event.target.position && event.target.element?.className.includes('arrow-revert-change')) {
200+
const lineNumber = event.target.position.lineNumber;
201+
const viewZone = event.target as IMouseTargetViewZone | undefined;
202+
203+
const model = this._diffModel.get();
204+
if (!model) { return; }
205+
const diffs = model.diff.get()?.mappings;
206+
if (!diffs) { return; }
207+
const diff = diffs.find(d =>
208+
viewZone?.detail.afterLineNumber === d.lineRangeMapping.modifiedRange.startLineNumber - 1 ||
209+
d.lineRangeMapping.modifiedRange.startLineNumber === lineNumber
210+
);
211+
if (!diff) { return; }
212+
this.revert(diff.lineRangeMapping);
213+
214+
event.event.stopPropagation();
215+
}
216+
}));
241217
}
242218

243219
protected _createInnerEditor(instantiationService: IInstantiationService, container: HTMLElement, options: Readonly<IEditorConstructionOptions>, editorWidgetOptions: ICodeEditorWidgetOptions): CodeEditorWidget {
@@ -307,7 +283,8 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor {
307283
const modifiedViewState = this._editors.modified.saveViewState();
308284
return {
309285
original: originalViewState,
310-
modified: modifiedViewState
286+
modified: modifiedViewState,
287+
modelState: this._diffModel.get()?.serializeState(),
311288
};
312289
}
313290

@@ -316,6 +293,9 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor {
316293
const diffEditorState = s as IDiffEditorViewState;
317294
this._editors.original.restoreViewState(diffEditorState.original);
318295
this._editors.modified.restoreViewState(diffEditorState.modified);
296+
if (diffEditorState.modelState) {
297+
this._diffModel.get()?.restoreSerializedState(diffEditorState.modelState as any);
298+
}
319299
}
320300
}
321301

0 commit comments

Comments
 (0)