Skip to content

Commit e0529df

Browse files
authored
Improves unchanged regions (microsoft#186159)
1 parent 3013efd commit e0529df

File tree

5 files changed

+77
-11
lines changed

5 files changed

+77
-11
lines changed

src/vs/editor/browser/widget/diffEditorWidget2/style.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,7 @@
9797
.char-insert.diff-range-empty {
9898
border-left: solid var(--vscode-diffEditor-insertedTextBackground) 3px;
9999
}
100+
101+
.fold-unchanged {
102+
cursor: pointer;
103+
}

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

Lines changed: 67 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,26 @@
55

66
import { $, addDisposableListener, h, reset } from 'vs/base/browser/dom';
77
import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels';
8+
import { Codicon } from 'vs/base/common/codicons';
9+
import { MarkdownString } from 'vs/base/common/htmlContent';
810
import { Disposable } from 'vs/base/common/lifecycle';
911
import { IObservable, observableFromEvent, transaction } from 'vs/base/common/observable';
1012
import { autorun, autorunWithStore2 } from 'vs/base/common/observableImpl/autorun';
1113
import { derived, derivedWithStore } from 'vs/base/common/observableImpl/derived';
14+
import { ThemeIcon } from 'vs/base/common/themables';
1215
import { isDefined } from 'vs/base/common/types';
1316
import { ICodeEditor, IViewZone } from 'vs/editor/browser/editorBrowser';
1417
import { DiffEditorEditors } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors';
1518
import { DiffEditorOptions } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorOptions';
1619
import { DiffEditorViewModel, UnchangedRegion } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel';
1720
import { PlaceholderViewZone, ViewZoneOverlayWidget, applyObservableDecorations, applyStyle, applyViewZones } from 'vs/editor/browser/widget/diffEditorWidget2/utils';
1821
import { EditorOption } from 'vs/editor/common/config/editorOptions';
22+
import { LineRange } from 'vs/editor/common/core/lineRange';
23+
import { Position } from 'vs/editor/common/core/position';
24+
import { Range } from 'vs/editor/common/core/range';
1925
import { CursorChangeReason } from 'vs/editor/common/cursorEvents';
2026
import { IModelDecorationOptions, IModelDeltaDecoration } from 'vs/editor/common/model';
27+
import { localize } from 'vs/nls';
2128

2229
export class UnchangedRangesFeature extends Disposable {
2330
private _isUpdatingViewZones = false;
@@ -90,21 +97,45 @@ export class UnchangedRangesFeature extends Disposable {
9097
className: 'diff-unchanged-lines',
9198
isWholeLine: true,
9299
};
100+
const unchangedLinesDecorationShow: IModelDecorationOptions = {
101+
description: 'Fold Unchanged',
102+
glyphMarginHoverMessage: new MarkdownString(undefined, { isTrusted: true, supportThemeIcons: true }).appendMarkdown(localize('foldUnchanged', 'Fold Unchanged Region')),
103+
glyphMarginClassName: 'fold-unchanged ' + ThemeIcon.asClassName(Codicon.fold),
104+
zIndex: 10001,
105+
};
93106

94107
this._register(applyObservableDecorations(this._editors.original, derived('decorations', (reader) => {
95108
const curUnchangedRegions = unchangedRegions.read(reader);
96-
return curUnchangedRegions.map<IModelDeltaDecoration>(r => ({
109+
const result = curUnchangedRegions.map<IModelDeltaDecoration>(r => ({
97110
range: r.originalRange.toInclusiveRange()!,
98111
options: unchangedLinesDecoration,
99112
}));
113+
for (const r of curUnchangedRegions) {
114+
if (r.shouldHideControls(reader)) {
115+
result.push({
116+
range: Range.fromPositions(new Position(r.originalLineNumber, 1)),
117+
options: unchangedLinesDecorationShow
118+
});
119+
}
120+
}
121+
return result;
100122
})));
101123

102124
this._register(applyObservableDecorations(this._editors.modified, derived('decorations', (reader) => {
103125
const curUnchangedRegions = unchangedRegions.read(reader);
104-
return curUnchangedRegions.map<IModelDeltaDecoration>(r => ({
126+
const result = curUnchangedRegions.map<IModelDeltaDecoration>(r => ({
105127
range: r.modifiedRange.toInclusiveRange()!,
106128
options: unchangedLinesDecoration,
107129
}));
130+
for (const r of curUnchangedRegions) {
131+
if (r.shouldHideControls(reader)) {
132+
result.push({
133+
range: LineRange.ofLength(r.modifiedLineNumber, 1).toInclusiveRange()!,
134+
options: unchangedLinesDecorationShow
135+
});
136+
}
137+
}
138+
return result;
108139
})));
109140

110141
this._register(applyViewZones(this._editors.original, viewZones.map(v => v.origViewZones), v => this._isUpdatingViewZones = v));
@@ -115,6 +146,32 @@ export class UnchangedRangesFeature extends Disposable {
115146
this._editors.original.setHiddenAreas(curUnchangedRegions.map(r => r.getHiddenOriginalRange(reader).toInclusiveRange()).filter(isDefined));
116147
this._editors.modified.setHiddenAreas(curUnchangedRegions.map(r => r.getHiddenModifiedRange(reader).toInclusiveRange()).filter(isDefined));
117148
}));
149+
150+
this._register(this._editors.modified.onMouseUp(event => {
151+
if (!event.event.rightButton && event.target.position && event.target.element?.className.includes('fold-unchanged')) {
152+
const lineNumber = event.target.position.lineNumber;
153+
const model = this._diffModel.get();
154+
if (!model) { return; }
155+
const region = model.unchangedRegions.get().find(r => r.modifiedRange.includes(lineNumber));
156+
if (!region) { return; }
157+
region.setState(0, 0, undefined);
158+
event.event.stopPropagation();
159+
event.event.preventDefault();
160+
}
161+
}));
162+
163+
this._register(this._editors.original.onMouseUp(event => {
164+
if (!event.event.rightButton && event.target.position && event.target.element?.className.includes('fold-unchanged')) {
165+
const lineNumber = event.target.position.lineNumber;
166+
const model = this._diffModel.get();
167+
if (!model) { return; }
168+
const region = model.unchangedRegions.get().find(r => r.originalRange.includes(lineNumber));
169+
if (!region) { return; }
170+
region.setState(0, 0, undefined);
171+
event.event.stopPropagation();
172+
event.event.preventDefault();
173+
}
174+
}));
118175
}
119176
}
120177

@@ -165,7 +222,7 @@ class CollapsedCodeOverlayWidget extends ViewZoneOverlayWidget {
165222
this._unchangedRegion.isDragged.set(true, undefined);
166223

167224

168-
const mouseMoveListener = addDisposableListener(document.body, 'mousemove', e => {
225+
const mouseMoveListener = addDisposableListener(window, 'mousemove', e => {
169226
const currentTop = e.clientY;
170227
const delta = currentTop - startTop;
171228
didMove = didMove || Math.abs(delta) > 2;
@@ -174,7 +231,7 @@ class CollapsedCodeOverlayWidget extends ViewZoneOverlayWidget {
174231
this._unchangedRegion.visibleLineCountTop.set(newVal, undefined);
175232
});
176233

177-
const mouseUpListener = addDisposableListener(document.body, 'mouseup', e => {
234+
const mouseUpListener = addDisposableListener(window, 'mouseup', e => {
178235
if (!didMove) {
179236
this._unchangedRegion.showMoreAbove(20, undefined);
180237
}
@@ -198,26 +255,26 @@ class CollapsedCodeOverlayWidget extends ViewZoneOverlayWidget {
198255
const cur = this._unchangedRegion.visibleLineCountBottom.get();
199256
this._unchangedRegion.isDragged.set(true, undefined);
200257

201-
const mouseMoveListener = addDisposableListener(document.body, 'mousemove', e => {
258+
const mouseMoveListener = addDisposableListener(window, 'mousemove', e => {
202259
const currentTop = e.clientY;
203260
const delta = currentTop - startTop;
204261
didMove = didMove || Math.abs(delta) > 2;
205262
const lineDelta = Math.round(delta / editor.getOption(EditorOption.lineHeight));
206263
const newVal = Math.max(0, Math.min(cur - lineDelta, this._unchangedRegion.getMaxVisibleLineCountBottom()));
207-
const top = editor.getTopForLineNumber(this._unchangedRegion.modifiedRange.endLineNumberExclusive);
264+
const top = editor.getTopForLineNumber(this._unchangedRegion.originalRange.endLineNumberExclusive);
208265
this._unchangedRegion.visibleLineCountBottom.set(newVal, undefined);
209-
const top2 = editor.getTopForLineNumber(this._unchangedRegion.modifiedRange.endLineNumberExclusive);
266+
const top2 = editor.getTopForLineNumber(this._unchangedRegion.originalRange.endLineNumberExclusive);
210267
editor.setScrollTop(editor.getScrollTop() + (top2 - top));
211268
});
212269

213-
const mouseUpListener = addDisposableListener(document.body, 'mouseup', e => {
270+
const mouseUpListener = addDisposableListener(window, 'mouseup', e => {
214271
this._unchangedRegion.isDragged.set(false, undefined);
215272

216273
if (!didMove) {
217-
const top = editor.getTopForLineNumber(this._unchangedRegion.modifiedRange.endLineNumberExclusive);
274+
const top = editor.getTopForLineNumber(this._unchangedRegion.originalRange.endLineNumberExclusive);
218275

219276
this._unchangedRegion.showMoreBelow(20, undefined);
220-
const top2 = editor.getTopForLineNumber(this._unchangedRegion.modifiedRange.endLineNumberExclusive);
277+
const top2 = editor.getTopForLineNumber(this._unchangedRegion.originalRange.endLineNumberExclusive);
221278
editor.setScrollTop(editor.getScrollTop() + (top2 - top));
222279
}
223280
this._nodes.bottom.classList.toggle('dragging', false);

src/vs/editor/common/core/lineRange.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,10 @@ export class LineRange {
225225
public serialize(): ISerializedLineRange {
226226
return [this.startLineNumber, this.endLineNumberExclusive];
227227
}
228+
229+
public includes(lineNumber: number): boolean {
230+
return this.startLineNumber <= lineNumber && lineNumber < this.endLineNumberExclusive;
231+
}
228232
}
229233

230234
export type ISerializedLineRange = [startLineNumber: number, endLineNumberExclusive: number];

src/vs/monaco.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2480,6 +2480,7 @@ declare namespace monaco.editor {
24802480
toInclusiveRange(): Range | null;
24812481
toExclusiveRange(): Range;
24822482
mapToLineArray<T>(f: (lineNumber: number) => T): T[];
2483+
includes(lineNumber: number): boolean;
24832484
}
24842485

24852486
/**

src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,7 @@ export class BreakpointEditorContribution implements IBreakpointEditorContributi
475475
if (decorations) {
476476
for (const { options } of decorations) {
477477
const clz = options.glyphMarginClassName;
478-
if (clz && (!clz.includes('codicon-') || clz.includes('codicon-testing-') || clz.includes('codicon-merge-') || clz.includes('codicon-arrow-') || clz.includes('codicon-loading'))) {
478+
if (clz && (!clz.includes('codicon-') || clz.includes('codicon-testing-') || clz.includes('codicon-merge-') || clz.includes('codicon-arrow-') || clz.includes('codicon-loading') || clz.includes('codicon-fold'))) {
479479
return false;
480480
}
481481
}

0 commit comments

Comments
 (0)