Skip to content

Commit ecd36c0

Browse files
committed
Allow overriding texts and images of unified diff toolbar actions
The unified diff toolbars label their built-in actions with generic wording such as "Apply" or "Revert". That wording assumes the diff is shown against another revision of the same file, and it becomes misleading when a client shows a diff between two unrelated sources, because it does not say what taking over a change actually does. Callers can now override the text and the image of each built-in action through the builder, keyed by the new ToolbarAction enum. Overriding an image with null removes it, so the action shows its text instead. Text and image overrides are independent, and an absent entry keeps the default, so a caller only adapts the actions it cares about.
1 parent d234c3a commit ecd36c0

4 files changed

Lines changed: 341 additions & 18 deletions

File tree

‎team/bundles/org.eclipse.compare/compare/org/eclipse/compare/unifieddiff/UnifiedDiff.java‎

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,20 @@
1313
*******************************************************************************/
1414
package org.eclipse.compare.unifieddiff;
1515

16+
import java.util.EnumMap;
1617
import java.util.List;
18+
import java.util.Map;
1719
import java.util.Objects;
1820
import java.util.Optional;
1921
import java.util.function.Function;
2022

2123
import org.eclipse.compare.contentmergeviewer.IIgnoreWhitespaceContributor;
2224
import org.eclipse.compare.contentmergeviewer.ITokenComparator;
25+
import org.eclipse.compare.unifieddiff.internal.ToolbarActionPresentations;
2326
import org.eclipse.compare.unifieddiff.internal.UnifiedDiffManager;
2427
import org.eclipse.core.runtime.IStatus;
2528
import org.eclipse.jface.action.Action;
29+
import org.eclipse.jface.resource.ImageDescriptor;
2630
import org.eclipse.jface.text.IDocument;
2731
import org.eclipse.ui.texteditor.ITextEditor;
2832

@@ -51,6 +55,41 @@ public static Builder create(ITextEditor editor, String source, UnifiedDiffMode
5155
return new Builder(editor, source, mode);
5256
}
5357

58+
/**
59+
* Identifies a built-in action of a unified diff toolbar, so that its text or
60+
* image can be replaced. Which actions are shown depends on the
61+
* {@link UnifiedDiffMode}; unused constants are simply ignored.
62+
*
63+
* @see Builder#toolbarActionText(ToolbarAction, String)
64+
* @see Builder#toolbarActionImage(ToolbarAction, ImageDescriptor)
65+
*/
66+
public enum ToolbarAction {
67+
/** Applies every diff, in the toolbar shown for all diffs. */
68+
ACCEPT_ALL,
69+
/** Discards every diff, in the toolbar shown for all diffs. */
70+
HIDE_ALL,
71+
/** Reverts every diff, in the toolbar shown for all diffs. */
72+
REVERT_ALL,
73+
/** Keeps every diff, in the toolbar shown for all diffs. */
74+
KEEP_ALL,
75+
/** Undoes every diff, in the toolbar shown for all diffs. */
76+
UNDO_ALL,
77+
/** Reveals the previous diff, in the toolbar shown for all diffs. */
78+
PREVIOUS,
79+
/** Reveals the next diff, in the toolbar shown for all diffs. */
80+
NEXT,
81+
/** Applies the hovered diff, in the toolbar shown for a single diff. */
82+
ACCEPT,
83+
/** Discards the hovered diff, in the toolbar shown for a single diff. */
84+
HIDE,
85+
/** Reverts the hovered diff, in the toolbar shown for a single diff. */
86+
REVERT,
87+
/** Keeps the hovered diff, in the toolbar shown for a single diff. */
88+
KEEP,
89+
/** Undoes the hovered diff, in the toolbar shown for a single diff. */
90+
UNDO
91+
}
92+
5493
public static final class Builder {
5594
// Required parameters
5695
private final ITextEditor editor;
@@ -63,6 +102,8 @@ public static final class Builder {
63102
private TokenComparatorFactory tokenComparatorFactory;
64103
private IgnoreWhitespaceContributorFactory ignoreWhitespaceContributorFactory;
65104
private int foldContextLines = -1;
105+
private final Map<ToolbarAction, String> toolbarActionTexts = new EnumMap<>(ToolbarAction.class);
106+
private final Map<ToolbarAction, ImageDescriptor> toolbarActionImages = new EnumMap<>(ToolbarAction.class);
66107

67108
private Builder(ITextEditor editor, String source, UnifiedDiffMode mode) {
68109
this.editor = Objects.requireNonNull(editor, "Editor cannot be null"); //$NON-NLS-1$
@@ -90,6 +131,34 @@ public Builder ignoreWhiteSpace(boolean value) {
90131
return this;
91132
}
92133

134+
/**
135+
* Overrides the text, including the tooltip text, for an action in a unified
136+
* diff toolbar.
137+
*
138+
* @param action the action whose text is overridden
139+
* @param text the replacement text
140+
* @return this builder
141+
*/
142+
public Builder toolbarActionText(ToolbarAction action, String text) {
143+
toolbarActionTexts.put(Objects.requireNonNull(action, "Action cannot be null"), //$NON-NLS-1$
144+
Objects.requireNonNull(text, "Action text cannot be null")); //$NON-NLS-1$
145+
return this;
146+
}
147+
148+
/**
149+
* Overrides the image for an action in a unified diff toolbar. Passing
150+
* <code>null</code> removes the action's default image and displays its text
151+
* instead.
152+
*
153+
* @param action the action whose image is overridden
154+
* @param image the replacement image, or <code>null</code> to show text
155+
* @return this builder
156+
*/
157+
public Builder toolbarActionImage(ToolbarAction action, ImageDescriptor image) {
158+
toolbarActionImages.put(Objects.requireNonNull(action, "Action cannot be null"), image); //$NON-NLS-1$
159+
return this;
160+
}
161+
93162
/**
94163
* Collapses unchanged regions between diffs, keeping the given number of
95164
* context lines (at least one) around each change. A negative value disables
@@ -106,7 +175,8 @@ public Builder foldUnchanged(int contextLines) {
106175

107176
public IStatus open() {
108177
return UnifiedDiffManager.open(editor, source, mode, additionalActions, tokenComparatorFactory,
109-
ignoreWhitespaceContributorFactory, ignoreWhiteSpace, foldContextLines);
178+
ignoreWhitespaceContributorFactory, ignoreWhiteSpace, foldContextLines,
179+
new ToolbarActionPresentations(toolbarActionTexts, toolbarActionImages));
110180
}
111181
}
112182
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2026 SAP
3+
*
4+
* This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License 2.0
6+
* which accompanies this distribution, and is available at
7+
* https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
*
11+
* Contributors:
12+
* SAP - initial implementation
13+
*******************************************************************************/
14+
package org.eclipse.compare.unifieddiff.internal;
15+
16+
import java.util.EnumMap;
17+
import java.util.Map;
18+
19+
import org.eclipse.compare.unifieddiff.UnifiedDiff.ToolbarAction;
20+
import org.eclipse.jface.resource.ImageDescriptor;
21+
22+
/**
23+
* The texts and images a caller supplied for the built-in toolbar actions.
24+
* Absent entries fall back to the default of the action; an entry mapped to
25+
* <code>null</code> is an override to "no image".
26+
*/
27+
public final class ToolbarActionPresentations {
28+
29+
/** No overrides, so every action keeps its default text and image. */
30+
public static final ToolbarActionPresentations NONE = new ToolbarActionPresentations(Map.of(), Map.of());
31+
32+
private final Map<ToolbarAction, String> texts = new EnumMap<>(ToolbarAction.class);
33+
private final Map<ToolbarAction, ImageDescriptor> images = new EnumMap<>(ToolbarAction.class);
34+
35+
public ToolbarActionPresentations(Map<ToolbarAction, String> texts, Map<ToolbarAction, ImageDescriptor> images) {
36+
this.texts.putAll(texts);
37+
this.images.putAll(images);
38+
}
39+
40+
String text(ToolbarAction action, String defaultText) {
41+
return texts.getOrDefault(action, defaultText);
42+
}
43+
44+
ImageDescriptor image(ToolbarAction action, ImageDescriptor defaultImage) {
45+
return images.containsKey(action) ? images.get(action) : defaultImage;
46+
}
47+
}

‎team/bundles/org.eclipse.compare/compare/org/eclipse/compare/unifieddiff/internal/UnifiedDiffManager.java‎

Lines changed: 66 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.eclipse.compare.rangedifferencer.RangeDifferencer;
3838
import org.eclipse.compare.unifieddiff.UnifiedDiff.IgnoreWhitespaceContributorFactory;
3939
import org.eclipse.compare.unifieddiff.UnifiedDiff.TokenComparatorFactory;
40+
import org.eclipse.compare.unifieddiff.UnifiedDiff.ToolbarAction;
4041
import org.eclipse.compare.unifieddiff.UnifiedDiffMode;
4142
import org.eclipse.compare.unifieddiff.internal.UnifiedDiffCodeMiningProvider.UnifiedDiffLineHeaderCodeMining;
4243
import org.eclipse.core.resources.IFile;
@@ -123,9 +124,11 @@ public class UnifiedDiffManager {
123124
private static final String DETAILED_DELETION_ANNO_TYPE = "org.eclipse.compare.unifieddiff.internal.detailedDeletion"; //$NON-NLS-1$
124125
private static final String TOOLBAR_COMPOSITE_FOR_ONE_DIFF_KEY = "TOOLBAR_COMPOSITE_FOR_ONE_DIFF_KEY"; //$NON-NLS-1$
125126
private static final String TOOLBAR_COMPOSITE_FOR_ALL_DIFFS_KEY = "TOOLBAR_COMPOSITE_FOR_ALL_DIFFS_KEY"; //$NON-NLS-1$
127+
private static final String TOOLBAR_ACTION_PRESENTATIONS_KEY = "TOOLBAR_ACTION_PRESENTATIONS_KEY"; //$NON-NLS-1$
126128
private static final Map<ITextViewer, List<UnifiedDiff>> diffsByViewer = new HashMap<>();
127129

128130
/**
131+
129132
* The user canceled the computation. The text editor is already open, so the
130133
* caller must not fall back to the classic compare editor. Compared by identity,
131134
* because a status code would clash with {@link Status#CANCEL_STATUS}, which
@@ -142,6 +145,16 @@ public static List<UnifiedDiff> get(ITextViewer viewer) {
142145
return diffsByViewer.get(viewer);
143146
}
144147

148+
private static void setToolbarActionPresentations(ITextViewer viewer, ToolbarActionPresentations presentations) {
149+
viewer.getTextWidget().setData(TOOLBAR_ACTION_PRESENTATIONS_KEY, presentations);
150+
}
151+
152+
private static ToolbarActionPresentations toolbarActionPresentations(ITextViewer viewer) {
153+
Object presentations = viewer.getTextWidget().getData(TOOLBAR_ACTION_PRESENTATIONS_KEY);
154+
return presentations instanceof ToolbarActionPresentations result ? result : ToolbarActionPresentations.NONE;
155+
}
156+
157+
145158
/**
146159
* Returns the annotation model the diffs of the given editor live in, or
147160
* <code>null</code> when it has none. The document provider of an editor on
@@ -174,6 +187,14 @@ public static IStatus open(ITextEditor editor, String source, UnifiedDiffMode mo
174187
TokenComparatorFactory tokenComparatorFactory,
175188
IgnoreWhitespaceContributorFactory ignoreWhitespaceContributorFactory, boolean ignoreWhiteSpace,
176189
int foldContextLines) {
190+
return open(editor, source, mode, additionalActions, tokenComparatorFactory, ignoreWhitespaceContributorFactory,
191+
ignoreWhiteSpace, foldContextLines, ToolbarActionPresentations.NONE);
192+
}
193+
194+
public static IStatus open(ITextEditor editor, String source, UnifiedDiffMode mode, List<Action> additionalActions,
195+
TokenComparatorFactory tokenComparatorFactory,
196+
IgnoreWhitespaceContributorFactory ignoreWhitespaceContributorFactory, boolean ignoreWhiteSpace,
197+
int foldContextLines, ToolbarActionPresentations toolbarActionPresentations) {
177198
ITextViewer viewer = editor.getAdapter(ITextViewer.class);
178199
IDocument leftDocument = editor.getDocumentProvider().getDocument(editor.getEditorInput());
179200
IAnnotationModel editorModel = annotationModelOf(editor);
@@ -183,7 +204,7 @@ public static IStatus open(ITextEditor editor, String source, UnifiedDiffMode mo
183204
}
184205
IFile file = editor.getEditorInput().getAdapter(IFile.class);
185206
return open(viewer, leftDocument, model, file, source, mode, additionalActions, tokenComparatorFactory,
186-
ignoreWhitespaceContributorFactory, ignoreWhiteSpace, foldContextLines);
207+
ignoreWhitespaceContributorFactory, ignoreWhiteSpace, foldContextLines, toolbarActionPresentations);
187208
}
188209

189210
/**
@@ -196,6 +217,17 @@ public static IStatus open(ITextViewer viewer, IDocument leftDocument, IAnnotati
196217
TokenComparatorFactory tokenComparatorFactory,
197218
IgnoreWhitespaceContributorFactory ignoreWhitespaceContributorFactory, boolean ignoreWhiteSpace,
198219
int foldContextLines) {
220+
return open(viewer, leftDocument, model, file, source, mode, additionalActions, tokenComparatorFactory,
221+
ignoreWhitespaceContributorFactory, ignoreWhiteSpace, foldContextLines,
222+
ToolbarActionPresentations.NONE);
223+
}
224+
225+
public static IStatus open(ITextViewer viewer, IDocument leftDocument, IAnnotationModel model, IFile file,
226+
String source, UnifiedDiffMode mode, List<Action> additionalActions,
227+
TokenComparatorFactory tokenComparatorFactory,
228+
IgnoreWhitespaceContributorFactory ignoreWhitespaceContributorFactory, boolean ignoreWhiteSpace,
229+
int foldContextLines, ToolbarActionPresentations toolbarActionPresentations) {
230+
199231
if (viewer instanceof ProjectionViewer pv) {
200232
pv.doOperation(ProjectionViewer.EXPAND_ALL);
201233
removeFoldAnnotations(pv);
@@ -279,12 +311,14 @@ public static IStatus open(ITextViewer viewer, IDocument leftDocument, IAnnotati
279311
}
280312

281313
UnifiedDiffManager.put(viewer, unifiedDiffs);
314+
setToolbarActionPresentations(viewer, toolbarActionPresentations);
282315
addPaintListener(viewer, model, mode);
283316
addMouseMoveListener(viewer, model);
284317
if (viewer instanceof ISourceViewerExtension5 ext) {
285318
ext.updateCodeMinings();
286319
}
287-
drawToolBarForAllDiffs(viewer, model, additionalActions, mode);
320+
drawToolBarForAllDiffs(viewer, model, additionalActions, mode, toolbarActionPresentations);
321+
288322
addUndoListener(viewer, leftDocument, model);
289323
addAnnoModelChangeListener(viewer, model);
290324

@@ -969,33 +1003,41 @@ public UnifiedDiff getUnifiedDiff() {
9691003
}
9701004

9711005
private static void drawToolBarForAllDiffs(ITextViewer tv, IAnnotationModel model, List<Action> additionalActions,
972-
UnifiedDiffMode mode) {
1006+
UnifiedDiffMode mode, ToolbarActionPresentations presentations) {
9731007
StyledText tw = tv.getTextWidget();
9741008
var tm = new ToolBarManager(SWT.FLAT | SWT.HORIZONTAL | SWT.RIGHT);
9751009
if (UnifiedDiffMode.OVERLAY_MODE.equals(mode) || UnifiedDiffMode.OVERLAY_READ_ONLY_MODE.equals(mode)) {
9761010
List<UnifiedDiff> diffs = get(tv);
9771011
if (!isReadOnly(diffs)) {
9781012
var acceptAll = new AcceptAllRunnable(tv, model);
979-
addToolbarAction(tm, acceptAll.getLabel(), AcceptAllRunnable.getImageDescriptor(), acceptAll);
1013+
addToolbarAction(tm, presentations.text(ToolbarAction.ACCEPT_ALL, acceptAll.getLabel()),
1014+
presentations.image(ToolbarAction.ACCEPT_ALL, AcceptAllRunnable.getImageDescriptor()), acceptAll);
9801015
}
9811016
var hideAll = new HideAllDiffsRunnable(tv, model);
982-
addToolbarAction(tm, hideAll.getLabel(), hideAll.getImageDescriptor(), hideAll);
1017+
addToolbarAction(tm, presentations.text(ToolbarAction.HIDE_ALL, hideAll.getLabel()),
1018+
presentations.image(ToolbarAction.HIDE_ALL, hideAll.getImageDescriptor()), hideAll);
9831019
} else if (UnifiedDiffMode.REVERT_MODE.equals(mode)) {
9841020
var revertAll = new AcceptAllRunnable(tv, model);
985-
addToolbarAction(tm, CompareMessages.UnifiedDiff_revert, AcceptAllRunnable.getUndoImageDescriptor(), revertAll);
1021+
addToolbarAction(tm, presentations.text(ToolbarAction.REVERT_ALL, CompareMessages.UnifiedDiff_revert),
1022+
presentations.image(ToolbarAction.REVERT_ALL, AcceptAllRunnable.getUndoImageDescriptor()), revertAll);
9861023
var hideAll = new HideAllDiffsRunnable(tv, model);
987-
addToolbarAction(tm, hideAll.getLabel(), hideAll.getImageDescriptor(), hideAll);
1024+
addToolbarAction(tm, presentations.text(ToolbarAction.HIDE_ALL, hideAll.getLabel()),
1025+
presentations.image(ToolbarAction.HIDE_ALL, hideAll.getImageDescriptor()), hideAll);
9881026
} else {
9891027
var keepAll = new KeepAllRunnable(tv, model);
990-
addToolbarAction(tm, keepAll.getLabel(), null, keepAll);
1028+
addToolbarAction(tm, presentations.text(ToolbarAction.KEEP_ALL, keepAll.getLabel()),
1029+
presentations.image(ToolbarAction.KEEP_ALL, null), keepAll);
9911030
var undoAll = new UndoAllRunnable(tv, model);
992-
addToolbarAction(tm, undoAll.getLabel(), null, undoAll);
1031+
addToolbarAction(tm, presentations.text(ToolbarAction.UNDO_ALL, undoAll.getLabel()),
1032+
presentations.image(ToolbarAction.UNDO_ALL, null), undoAll);
9931033
}
9941034

9951035
var previous = new PreviousRunnable(tv, model, tm);
996-
addToolbarAction(tm, previous.getLabel(), previous.getImageDescriptor(), previous);
1036+
addToolbarAction(tm, presentations.text(ToolbarAction.PREVIOUS, previous.getLabel()),
1037+
presentations.image(ToolbarAction.PREVIOUS, previous.getImageDescriptor()), previous);
9971038
var next = new NextRunnable(tv, model, tm);
998-
addToolbarAction(tm, next.getLabel(), next.getImageDescriptor(), next);
1039+
addToolbarAction(tm, presentations.text(ToolbarAction.NEXT, next.getLabel()),
1040+
presentations.image(ToolbarAction.NEXT, next.getImageDescriptor()), next);
9991041
if (additionalActions != null) {
10001042
for (var additionalAction : additionalActions) {
10011043
addToolbarAction(tm, additionalAction);
@@ -1206,22 +1248,29 @@ private static void dismissCurrentDiff(ITextViewer tv, IAnnotationModel model) {
12061248
private static void drawToolbarForOneDiff(ITextViewer tv, IAnnotationModel model) {
12071249
List<UnifiedDiff> diffs = get(tv);
12081250
StyledText tw = tv.getTextWidget();
1251+
ToolbarActionPresentations presentations = toolbarActionPresentations(tv);
12091252
var tm = new ToolBarManager(SWT.FLAT | SWT.HORIZONTAL | SWT.RIGHT);
12101253
if (isOverlay(diffs)) {
12111254
if (!isReadOnly(diffs)) {
1212-
addToolbarAction(tm, CompareMessages.UnifiedDiff_accept, AcceptAllRunnable.getImageDescriptor(),
1255+
addToolbarAction(tm, presentations.text(ToolbarAction.ACCEPT, CompareMessages.UnifiedDiff_accept),
1256+
presentations.image(ToolbarAction.ACCEPT, AcceptAllRunnable.getImageDescriptor()),
12131257
() -> applyDiffRightStr(tv, model));
12141258
}
1215-
addToolbarAction(tm, CompareMessages.UnifiedDiff_hideDiff, HideAllDiffsRunnable.getHideDiffImageDescriptor(),
1259+
addToolbarAction(tm, presentations.text(ToolbarAction.HIDE, CompareMessages.UnifiedDiff_hideDiff),
1260+
presentations.image(ToolbarAction.HIDE, HideAllDiffsRunnable.getHideDiffImageDescriptor()),
12161261
() -> dismissCurrentDiff(tv, model));
12171262
} else if (isRevert(diffs)) {
1218-
addToolbarAction(tm, CompareMessages.UnifiedDiff_revert, AcceptAllRunnable.getUndoImageDescriptor(),
1263+
addToolbarAction(tm, presentations.text(ToolbarAction.REVERT, CompareMessages.UnifiedDiff_revert),
1264+
presentations.image(ToolbarAction.REVERT, AcceptAllRunnable.getUndoImageDescriptor()),
12191265
() -> applyDiffRightStr(tv, model));
1220-
addToolbarAction(tm, CompareMessages.UnifiedDiff_hideDiff, HideAllDiffsRunnable.getHideDiffImageDescriptor(),
1266+
addToolbarAction(tm, presentations.text(ToolbarAction.HIDE, CompareMessages.UnifiedDiff_hideDiff),
1267+
presentations.image(ToolbarAction.HIDE, HideAllDiffsRunnable.getHideDiffImageDescriptor()),
12211268
() -> dismissCurrentDiff(tv, model));
12221269
} else {
1223-
addToolbarAction(tm, CompareMessages.UnifiedDiff_keep, null, () -> dismissCurrentDiff(tv, model));
1224-
addToolbarAction(tm, CompareMessages.UnifiedDiff_undo, null, () -> {
1270+
addToolbarAction(tm, presentations.text(ToolbarAction.KEEP, CompareMessages.UnifiedDiff_keep),
1271+
presentations.image(ToolbarAction.KEEP, null), () -> dismissCurrentDiff(tv, model));
1272+
addToolbarAction(tm, presentations.text(ToolbarAction.UNDO, CompareMessages.UnifiedDiff_undo),
1273+
presentations.image(ToolbarAction.UNDO, null), () -> {
12251274
var fToolbarShellForOneDiff = getToolbarCompositeForOneDiff(tv.getTextWidget());
12261275
if (fToolbarShellForOneDiff == null) {
12271276
return;

0 commit comments

Comments
 (0)