Skip to content

Commit bf85140

Browse files
authored
Fixes #162173 (#163548)
1 parent 73e5ad2 commit bf85140

File tree

4 files changed

+420
-282
lines changed

4 files changed

+420
-282
lines changed

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

Lines changed: 132 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -74,118 +74,32 @@ export class ConflictActionsFactory extends Disposable {
7474
};
7575
}
7676

77-
addResultWidget(viewZoneChangeAccessor: IViewZoneChangeAccessor, lineNumber: number, viewModel: MergeEditorViewModel, modifiedBaseRange: ModifiedBaseRange): IDisposable {
78-
function command(title: string, action: () => Promise<void>, tooltip?: string): IContentWidgetAction {
79-
return {
80-
text: title,
81-
action,
82-
tooltip
83-
};
84-
}
85-
86-
const items = derived('items', reader => {
87-
const state = viewModel.model.getState(modifiedBaseRange).read(reader);
88-
const model = viewModel.model;
89-
90-
const result: IContentWidgetAction[] = [];
91-
92-
93-
if (state.conflicting) {
94-
result.push({
95-
text: localize('manualResolution', "Manual Resolution"),
96-
tooltip: localize('manualResolutionTooltip', "This conflict has been resolved manually."),
97-
});
98-
} else if (state.isEmpty) {
99-
result.push({
100-
text: localize('noChangesAccepted', 'No Changes Accepted'),
101-
tooltip: localize(
102-
'noChangesAcceptedTooltip',
103-
'The current resolution of this conflict equals the common ancestor of both the right and left changes.'
104-
),
105-
});
106-
107-
} else {
108-
const labels = [];
109-
if (state.input1) {
110-
labels.push(model.input1.title);
111-
}
112-
if (state.input2) {
113-
labels.push(model.input2.title);
114-
}
115-
if (state.input2First) {
116-
labels.reverse();
117-
}
118-
result.push({
119-
text: `${labels.join(' + ')}`
120-
});
121-
}
122-
123-
const stateToggles: IContentWidgetAction[] = [];
124-
if (state.input1) {
125-
result.push(command(localize('remove', "Remove {0}", model.input1.title), async () => {
126-
transaction((tx) => {
127-
model.setState(
128-
modifiedBaseRange,
129-
state.withInputValue(1, false),
130-
true,
131-
tx
132-
);
133-
});
134-
}, localize('removeTooltip', "Remove {0} from the result document.", model.input1.title)),
135-
);
136-
}
137-
if (state.input2) {
138-
result.push(command(localize('remove', "Remove {0}", model.input2.title), async () => {
139-
transaction((tx) => {
140-
model.setState(
141-
modifiedBaseRange,
142-
state.withInputValue(2, false),
143-
true,
144-
tx
145-
);
146-
});
147-
}, localize('removeTooltip', "Remove {0} from the result document.", model.input2.title)),
148-
);
149-
}
150-
if (state.input2First) {
151-
stateToggles.reverse();
152-
}
153-
result.push(...stateToggles);
154-
155-
156-
157-
if (state.conflicting) {
158-
result.push(
159-
command(localize('resetToBase', "Reset to base"), async () => {
160-
transaction((tx) => {
161-
model.setState(
162-
modifiedBaseRange,
163-
ModifiedBaseRangeState.default,
164-
true,
165-
tx
166-
);
167-
});
168-
}, localize('resetToBaseTooltip', "Reset this conflict to the common ancestor of both the right and left changes.")),
169-
);
170-
}
171-
return result;
172-
});
173-
77+
public createWidget(viewZoneChangeAccessor: IViewZoneChangeAccessor, lineNumber: number, items: IObservable<IContentWidgetAction[]>, viewZoneIdsToCleanUp: string[]): IDisposable {
17478
const layoutInfo = this._getLayoutInfo();
79+
return new ActionsContentWidget(
80+
this._editor,
81+
viewZoneChangeAccessor,
82+
lineNumber,
83+
layoutInfo.codeLensHeight + 2,
84+
this._styleClassName,
85+
items,
86+
viewZoneIdsToCleanUp,
87+
);
88+
}
89+
}
17590

176-
return new ActionsContentWidget(this._editor, viewZoneChangeAccessor, lineNumber, layoutInfo.codeLensHeight + 2, this._styleClassName, items);
91+
export class ActionsSource {
92+
constructor(
93+
private readonly viewModel: MergeEditorViewModel,
94+
private readonly modifiedBaseRange: ModifiedBaseRange,
95+
) {
17796
}
17897

179-
addContentWidget(viewZoneChangeAccessor: IViewZoneChangeAccessor, lineNumber: number, viewModel: MergeEditorViewModel, modifiedBaseRange: ModifiedBaseRange, inputNumber: 1 | 2): IDisposable {
180-
function command(title: string, action: () => Promise<void>, tooltip?: string): IContentWidgetAction {
181-
return {
182-
text: title,
183-
action,
184-
tooltip,
185-
};
186-
}
98+
private getItemsInput(inputNumber: 1 | 2): IObservable<IContentWidgetAction[]> {
99+
return derived('items', reader => {
100+
const viewModel = this.viewModel;
101+
const modifiedBaseRange = this.modifiedBaseRange;
187102

188-
const items = derived('items', reader => {
189103
if (!viewModel.model.hasBaseRange(modifiedBaseRange)) {
190104
return [];
191105
}
@@ -260,15 +174,120 @@ export class ConflictActionsFactory extends Disposable {
260174
}
261175
return result;
262176
});
177+
}
263178

264-
const layoutInfo = this._getLayoutInfo();
179+
public readonly itemsInput1 = this.getItemsInput(1);
180+
public readonly itemsInput2 = this.getItemsInput(2);
265181

266-
return new ActionsContentWidget(this._editor, viewZoneChangeAccessor, lineNumber, layoutInfo.codeLensHeight + 2, this._styleClassName, items);
267-
}
182+
public readonly resultItems = derived('items', reader => {
183+
const viewModel = this.viewModel;
184+
const modifiedBaseRange = this.modifiedBaseRange;
185+
186+
const state = viewModel.model.getState(modifiedBaseRange).read(reader);
187+
const model = viewModel.model;
188+
189+
const result: IContentWidgetAction[] = [];
190+
191+
192+
if (state.conflicting) {
193+
result.push({
194+
text: localize('manualResolution', "Manual Resolution"),
195+
tooltip: localize('manualResolutionTooltip', "This conflict has been resolved manually."),
196+
});
197+
} else if (state.isEmpty) {
198+
result.push({
199+
text: localize('noChangesAccepted', 'No Changes Accepted'),
200+
tooltip: localize(
201+
'noChangesAcceptedTooltip',
202+
'The current resolution of this conflict equals the common ancestor of both the right and left changes.'
203+
),
204+
});
205+
206+
} else {
207+
const labels = [];
208+
if (state.input1) {
209+
labels.push(model.input1.title);
210+
}
211+
if (state.input2) {
212+
labels.push(model.input2.title);
213+
}
214+
if (state.input2First) {
215+
labels.reverse();
216+
}
217+
result.push({
218+
text: `${labels.join(' + ')}`
219+
});
220+
}
221+
222+
const stateToggles: IContentWidgetAction[] = [];
223+
if (state.input1) {
224+
result.push(command(localize('remove', "Remove {0}", model.input1.title), async () => {
225+
transaction((tx) => {
226+
model.setState(
227+
modifiedBaseRange,
228+
state.withInputValue(1, false),
229+
true,
230+
tx
231+
);
232+
});
233+
}, localize('removeTooltip', "Remove {0} from the result document.", model.input1.title)),
234+
);
235+
}
236+
if (state.input2) {
237+
result.push(command(localize('remove', "Remove {0}", model.input2.title), async () => {
238+
transaction((tx) => {
239+
model.setState(
240+
modifiedBaseRange,
241+
state.withInputValue(2, false),
242+
true,
243+
tx
244+
);
245+
});
246+
}, localize('removeTooltip', "Remove {0} from the result document.", model.input2.title)),
247+
);
248+
}
249+
if (state.input2First) {
250+
stateToggles.reverse();
251+
}
252+
result.push(...stateToggles);
253+
254+
255+
256+
if (state.conflicting) {
257+
result.push(
258+
command(localize('resetToBase', "Reset to base"), async () => {
259+
transaction((tx) => {
260+
model.setState(
261+
modifiedBaseRange,
262+
ModifiedBaseRangeState.default,
263+
true,
264+
tx
265+
);
266+
});
267+
}, localize('resetToBaseTooltip', "Reset this conflict to the common ancestor of both the right and left changes.")),
268+
);
269+
}
270+
return result;
271+
});
272+
273+
public readonly isEmpty = derived('isEmpty', reader => {
274+
return this.itemsInput1.read(reader).length + this.itemsInput2.read(reader).length + this.resultItems.read(reader).length === 0;
275+
});
276+
277+
public readonly inputIsEmpty = derived('inputIsEmpty', reader => {
278+
return this.itemsInput1.read(reader).length + this.itemsInput2.read(reader).length === 0;
279+
});
268280
}
269281

282+
function command(title: string, action: () => Promise<void>, tooltip?: string): IContentWidgetAction {
283+
return {
284+
text: title,
285+
action,
286+
tooltip,
287+
};
288+
}
270289

271-
interface IContentWidgetAction {
290+
export interface IContentWidgetAction {
272291
text: string;
273292
tooltip?: string;
274293
action?: () => Promise<void>;
@@ -285,8 +304,9 @@ class ActionsContentWidget extends FixedZoneWidget {
285304

286305
className: string,
287306
items: IObservable<IContentWidgetAction[]>,
307+
viewZoneIdsToCleanUp: string[],
288308
) {
289-
super(editor, viewZoneAccessor, afterLineNumber, height);
309+
super(editor, viewZoneAccessor, afterLineNumber, height, viewZoneIdsToCleanUp);
290310

291311
this.widgetDomNode.appendChild(this._domNode);
292312

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export abstract class FixedZoneWidget extends Disposable {
2424
viewZoneAccessor: IViewZoneChangeAccessor,
2525
afterLineNumber: number,
2626
height: number,
27+
viewZoneIdsToCleanUp: string[],
2728
) {
2829
super();
2930

@@ -38,6 +39,7 @@ export abstract class FixedZoneWidget extends Disposable {
3839
this.widgetDomNode.style.top = `${top}px`;
3940
}
4041
});
42+
viewZoneIdsToCleanUp.push(this.viewZoneId);
4143

4244
this.widgetDomNode.style.left = this.editor.getLayoutInfo().contentLeft + 'px';
4345

@@ -46,9 +48,6 @@ export abstract class FixedZoneWidget extends Disposable {
4648
this._register({
4749
dispose: () => {
4850
this.editor.removeOverlayWidget(this.overlayWidget);
49-
this.editor.changeViewZones(accessor => {
50-
accessor.removeZone(this.viewZoneId);
51-
});
5251
},
5352
});
5453
}

0 commit comments

Comments
 (0)