Skip to content

Commit 1436dd7

Browse files
authored
Implements #160292: diffing against base (#163553)
1 parent bf85140 commit 1436dd7

File tree

7 files changed

+105
-13
lines changed

7 files changed

+105
-13
lines changed

src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfigurat
5050
localize('diffAlgorithm.experimental', "Uses an experimental diffing algorithm."),
5151
]
5252
},
53+
'mergeEditor.showDeletionMarkers': {
54+
type: 'boolean',
55+
default: true,
56+
description: 'Controls if deletions in base or one of the inputs should be indicated by a vertical bar.',
57+
},
5358
}
5459
});
5560

src/vs/workbench/contrib/mergeEditor/browser/view/colors.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,18 @@ export const diffWord = registerColor(
1818
localize('mergeEditor.change.word.background', 'The background color for word changes.')
1919
);
2020

21+
export const diffBase = registerColor(
22+
'mergeEditor.changeBase.background',
23+
{ dark: '#4B1818FF', light: '#FFCCCCFF', hcDark: '#4B1818FF', hcLight: '#FFCCCCFF', },
24+
localize('mergeEditor.changeBase.background', 'The background color for changes in base.')
25+
);
26+
27+
export const diffWordBase = registerColor(
28+
'mergeEditor.changeBase.word.background',
29+
{ dark: '#6F1313FF', light: '#FFA3A3FF', hcDark: '#6F1313FF', hcLight: '#FFA3A3FF', },
30+
localize('mergeEditor.changeBase.word.background', 'The background color for word changes in base.')
31+
);
32+
2133
export const conflictBorderUnhandledUnfocused = registerColor(
2234
'mergeEditor.conflict.unhandledUnfocused.border',
2335
{ dark: '#ffa6007a', light: '#ffa6007a', hcDark: '#ffa6007a', hcLight: '#ffa6007a', },

src/vs/workbench/contrib/mergeEditor/browser/view/editors/baseCodeEditorView.ts

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { reset } from 'vs/base/browser/dom';
6+
import { h, reset } from 'vs/base/browser/dom';
77
import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels';
88
import { BugIndicatingError } from 'vs/base/common/errors';
99
import { Iterable } from 'vs/base/common/iterator';
@@ -56,6 +56,16 @@ export class BaseCodeEditorView extends CodeEditorView {
5656
}
5757
this.editor.setModel(vm.model.base);
5858
reset(this.htmlElements.title, ...renderLabelWithIcons(localize('base', 'Base')));
59+
60+
const baseShowDiffAgainst = vm.baseShowDiffAgainst.read(reader);
61+
62+
let node: Node | undefined = undefined;
63+
if (baseShowDiffAgainst) {
64+
const label = localize('compareWith', 'Comparing with {0}', baseShowDiffAgainst === 1 ? vm.model.input1.title : vm.model.input2.title);
65+
const tooltip = localize('compareWithTooltip', 'Differences are highlighted with a background color.');
66+
node = h('span', { title: tooltip }, [label]).root;
67+
}
68+
reset(this.htmlElements.description, ...(node ? [node] : []));
5969
})
6070
);
6171

@@ -72,6 +82,7 @@ export class BaseCodeEditorView extends CodeEditorView {
7282

7383
const activeModifiedBaseRange = viewModel.activeModifiedBaseRange.read(reader);
7484
const showNonConflictingChanges = viewModel.showNonConflictingChanges.read(reader);
85+
const showDeletionMarkers = this.showDeletionMarkers.read(reader);
7586

7687
const result: IModelDeltaDecoration[] = [];
7788
for (const modifiedBaseRange of model.modifiedBaseRanges.read(reader)) {
@@ -95,6 +106,37 @@ export class BaseCodeEditorView extends CodeEditorView {
95106
}
96107
blockClassNames.push('base');
97108

109+
const inputToDiffAgainst = viewModel.baseShowDiffAgainst.read(reader);
110+
111+
if (inputToDiffAgainst) {
112+
for (const diff of modifiedBaseRange.getInputDiffs(inputToDiffAgainst)) {
113+
const range = diff.inputRange.toInclusiveRange();
114+
if (range) {
115+
result.push({
116+
range,
117+
options: {
118+
className: `merge-editor-diff base`,
119+
description: 'Merge Editor',
120+
isWholeLine: true,
121+
}
122+
});
123+
}
124+
125+
for (const diff2 of diff.rangeMappings) {
126+
if (showDeletionMarkers || !diff2.inputRange.isEmpty()) {
127+
result.push({
128+
range: diff2.inputRange,
129+
options: {
130+
className: diff2.inputRange.isEmpty() ? `merge-editor-diff-empty-word base` : `merge-editor-diff-word base`,
131+
description: 'Merge Editor',
132+
showIfCollapsed: true,
133+
},
134+
});
135+
}
136+
}
137+
}
138+
}
139+
98140
result.push({
99141
range: range.toInclusiveRangeOrEmpty(),
100142
options: {

src/vs/workbench/contrib/mergeEditor/browser/view/editors/codeEditorView.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ export abstract class CodeEditorView extends Disposable {
6464
() => /** @description checkboxesVisible */ this.configurationService.getValue('mergeEditor.showCheckboxes') ?? false
6565
);
6666

67+
protected readonly showDeletionMarkers = observableFromEvent<boolean>(
68+
this.configurationService.onDidChangeConfiguration,
69+
() => /** @description showDeletionMarkers */ this.configurationService.getValue('mergeEditor.showDeletionMarkers')
70+
);
71+
6772
public readonly editor = this.instantiationService.createInstance(
6873
CodeEditorWidget,
6974
this.htmlElements.editor,

src/vs/workbench/contrib/mergeEditor/browser/view/editors/inputCodeEditorView.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ export class InputCodeEditorView extends CodeEditorView {
130130
const result = new Array<IModelDeltaDecoration>();
131131

132132
const showNonConflictingChanges = viewModel.showNonConflictingChanges.read(reader);
133+
const showDeletionMarkers = this.showDeletionMarkers.read(reader);
133134

134135
for (const modifiedBaseRange of model.modifiedBaseRanges.read(reader)) {
135136
const range = modifiedBaseRange.getInputRange(this.inputNumber);
@@ -148,7 +149,7 @@ export class InputCodeEditorView extends CodeEditorView {
148149
if (modifiedBaseRange.isConflicting) {
149150
blockClassNames.push('conflicting');
150151
}
151-
const inputClassName = this.inputNumber === 1 ? 'input1' : 'input2';
152+
const inputClassName = this.inputNumber === 1 ? 'input 1' : 'input 2';
152153
blockClassNames.push(inputClassName);
153154

154155
if (!modifiedBaseRange.isConflicting && !showNonConflictingChanges && isHandled) {
@@ -190,13 +191,16 @@ export class InputCodeEditorView extends CodeEditorView {
190191

191192
if (diff.rangeMappings) {
192193
for (const d of diff.rangeMappings) {
193-
result.push({
194-
range: d.outputRange,
195-
options: {
196-
className: `merge-editor-diff-word ${inputClassName}`,
197-
description: 'Merge Editor'
198-
}
199-
});
194+
if (showDeletionMarkers || !d.outputRange.isEmpty()) {
195+
result.push({
196+
range: d.outputRange,
197+
options: {
198+
className: d.outputRange.isEmpty() ? `merge-editor-diff-empty-word ${inputClassName}` : `merge-editor-diff-word ${inputClassName}`,
199+
description: 'Merge Editor',
200+
showIfCollapsed: true,
201+
}
202+
});
203+
}
200204
}
201205
}
202206
}

src/vs/workbench/contrib/mergeEditor/browser/view/media/mergeEditor.css

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -226,10 +226,6 @@
226226
border: 1px solid var(--vscode-mergeEditor-conflict-unhandledFocused-border);
227227
}
228228

229-
.merge-conflict-input-selection {
230-
background-color: red;
231-
}
232-
233229
.merge-editor-conflict-actions {
234230
margin: 0px 3px;
235231
overflow: hidden;
@@ -272,3 +268,21 @@
272268
.fixed-zone-widget {
273269
width: 100%;
274270
}
271+
272+
.merge-editor-diff-empty-word.base {
273+
margin-left: 3px;
274+
border-left: solid var(--vscode-mergeEditor-changeBase-word-background) 3px;
275+
}
276+
277+
.merge-editor-diff-empty-word.input {
278+
margin-left: 3px;
279+
border-left: solid var(--vscode-mergeEditor-change-word-background) 3px;
280+
}
281+
282+
.merge-editor-diff-word.base {
283+
background-color: var(--vscode-mergeEditor-changeBase-word-background);
284+
}
285+
286+
.merge-editor-diff.base {
287+
background-color: var(--vscode-mergeEditor-changeBase-background);
288+
}

src/vs/workbench/contrib/mergeEditor/browser/view/viewModel.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,16 @@ export class MergeEditorViewModel extends Disposable {
5959
return view ? { view, counter: this.counter++ } : lastValue || { view: undefined, counter: this.counter++ };
6060
});
6161

62+
public readonly baseShowDiffAgainst = derived<1 | 2 | undefined>('baseShowDiffAgainst', reader => {
63+
const lastFocusedEditor = this.lastFocusedEditor.read(reader);
64+
if (lastFocusedEditor.view === this.inputCodeEditorView1) {
65+
return 1;
66+
} else if (lastFocusedEditor.view === this.inputCodeEditorView2) {
67+
return 2;
68+
}
69+
return undefined;
70+
});
71+
6272
public readonly selectionInBase = derived('selectionInBase', (reader) => {
6373
const sourceEditor = this.lastFocusedEditor.read(reader).view;
6474
if (!sourceEditor) {

0 commit comments

Comments
 (0)