Skip to content

Commit eb071a9

Browse files
tobias-melcherBeckerWdf
authored andcommitted
introduce DocumentFooterCodeMining to draw minings at the document end
1 parent 4e127ef commit eb071a9

File tree

9 files changed

+575
-272
lines changed

9 files changed

+575
-272
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 SAP SE
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+
package org.eclipse.jface.internal.text.codemining;
12+
13+
import java.util.function.Consumer;
14+
15+
import org.eclipse.swt.events.MouseEvent;
16+
17+
import org.eclipse.jface.text.Position;
18+
import org.eclipse.jface.text.source.ISourceViewer;
19+
20+
public class CodeMiningDocumentFooterAnnotation extends CodeMiningMultilineAnnotation {
21+
22+
/**
23+
* Code mining annotation constructor.
24+
*
25+
* @param position the position
26+
* @param viewer the viewer
27+
* @param onMouseHover the consumer to be called on mouse hover. If set, the implementor needs
28+
* to take care of setting the cursor if wanted.
29+
* @param onMouseOut the consumer to be called on mouse out. If set, the implementor needs to
30+
* take care of resetting the cursor.
31+
* @param onMouseMove the consumer to be called on mouse move
32+
*/
33+
public CodeMiningDocumentFooterAnnotation(Position position, ISourceViewer viewer, Consumer<MouseEvent> onMouseHover, Consumer<MouseEvent> onMouseOut, Consumer<MouseEvent> onMouseMove) {
34+
super(position, viewer, onMouseHover, onMouseOut, onMouseMove);
35+
}
36+
}

bundles/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/codemining/CodeMiningLineHeaderAnnotation.java

Lines changed: 1 addition & 235 deletions
Original file line numberDiff line numberDiff line change
@@ -13,54 +13,19 @@
1313
*/
1414
package org.eclipse.jface.internal.text.codemining;
1515

16-
import java.util.ArrayList;
17-
import java.util.List;
1816
import java.util.function.Consumer;
19-
import java.util.stream.Stream;
2017

21-
import org.eclipse.swt.custom.StyledText;
2218
import org.eclipse.swt.events.MouseEvent;
23-
import org.eclipse.swt.graphics.Color;
24-
import org.eclipse.swt.graphics.GC;
25-
import org.eclipse.swt.graphics.Point;
26-
import org.eclipse.swt.graphics.Rectangle;
27-
28-
import org.eclipse.core.runtime.IProgressMonitor;
2919

3020
import org.eclipse.jface.text.Position;
31-
import org.eclipse.jface.text.codemining.ICodeMining;
3221
import org.eclipse.jface.text.source.ISourceViewer;
33-
import org.eclipse.jface.text.source.inlined.LineHeaderAnnotation;
3422

3523
/**
3624
* Code Mining annotation in line header.
3725
*
3826
* @since 3.13
3927
*/
40-
public class CodeMiningLineHeaderAnnotation extends LineHeaderAnnotation implements ICodeMiningAnnotation {
41-
42-
private static final String SEPARATOR= " | "; //$NON-NLS-1$
43-
44-
/**
45-
* List of resolved minings which contains current resolved minings and last resolved minings
46-
* and null if mining is not resolved.
47-
*/
48-
private ICodeMining[] fResolvedMinings;
49-
50-
/**
51-
* List of current resolved/unresolved minings
52-
*/
53-
private final List<ICodeMining> fMinings;
54-
55-
/**
56-
* List of bounds minings
57-
*/
58-
private final List<Rectangle> fBounds;
59-
60-
/**
61-
* The current progress monitor
62-
*/
63-
private IProgressMonitor fMonitor;
28+
public class CodeMiningLineHeaderAnnotation extends CodeMiningMultilineAnnotation {
6429

6530
/**
6631
* Code mining annotation constructor.
@@ -85,204 +50,5 @@ public CodeMiningLineHeaderAnnotation(Position position, ISourceViewer viewer) {
8550
*/
8651
public CodeMiningLineHeaderAnnotation(Position position, ISourceViewer viewer, Consumer<MouseEvent> onMouseHover, Consumer<MouseEvent> onMouseOut, Consumer<MouseEvent> onMouseMove) {
8752
super(position, viewer, onMouseHover, onMouseOut, onMouseMove);
88-
fResolvedMinings= null;
89-
fMinings= new ArrayList<>();
90-
fBounds= new ArrayList<>();
91-
}
92-
93-
@Override
94-
public int getHeight() {
95-
return hasAtLeastOneResolvedMiningNotEmpty() ? getMultilineHeight(null) : 0;
96-
}
97-
98-
public int getHeight(GC gc) {
99-
return hasAtLeastOneResolvedMiningNotEmpty() ? getMultilineHeight(gc) : 0;
100-
}
101-
102-
private int getMultilineHeight(GC gc) {
103-
int numLinesOfAllMinings= 0;
104-
StyledText styledText= super.getTextWidget();
105-
boolean ignoreFirstLine= false;
106-
int sumLineHeight= 0;
107-
for (int i= 0; i < fMinings.size(); i++) {
108-
ICodeMining mining= fMinings.get(i);
109-
String label= mining.getLabel();
110-
if (label == null) {
111-
continue;
112-
}
113-
String[] splitted= label.split("\\r?\\n|\\r"); //$NON-NLS-1$
114-
int numLines= splitted.length;
115-
if (ignoreFirstLine) {
116-
numLines--;
117-
}
118-
numLinesOfAllMinings+= numLines;
119-
if (gc != null) {
120-
sumLineHeight= calculateLineHeight(gc, styledText, ignoreFirstLine, sumLineHeight, i, splitted);
121-
}
122-
ignoreFirstLine= true;
123-
}
124-
if (sumLineHeight == 0) {
125-
return super.getHeight();
126-
}
127-
if (gc != null) {
128-
return sumLineHeight;
129-
} else {
130-
int lineHeight= styledText.getLineHeight();
131-
int result= numLinesOfAllMinings * (lineHeight + styledText.getLineSpacing());
132-
return result;
133-
}
134-
}
135-
136-
private int calculateLineHeight(GC gc, StyledText styledText, boolean ignoreFirstLine, int sumLineHeight, int miningIndex, String[] splitted) {
137-
for (int j= 0; j < splitted.length; j++) {
138-
String line= splitted[j];
139-
if (j == 0 && ignoreFirstLine) {
140-
continue;
141-
}
142-
if (j == splitted.length - 1 && miningIndex + 1 < fMinings.size()) { // last line, take first line from next mining
143-
String nextLabel= fMinings.get(miningIndex + 1).getLabel();
144-
if (nextLabel != null) {
145-
String firstFromNext= nextLabel.split("\\r?\\n|\\r")[0]; //$NON-NLS-1$
146-
line+= firstFromNext;
147-
}
148-
}
149-
Point ext= gc.textExtent(line);
150-
sumLineHeight+= ext.y + styledText.getLineSpacing();
151-
}
152-
return sumLineHeight;
153-
}
154-
155-
/**
156-
* Returns <code>true</code> if the annotation has at least one resolved mining which have a
157-
* label and <code>false</code> otherwise.
158-
*
159-
* @return <code>true</code> if the annotation has at least one resolved mining which have a
160-
* label and <code>false</code> otherwise.
161-
*/
162-
private boolean hasAtLeastOneResolvedMiningNotEmpty() {
163-
if (fMinings.stream().anyMatch(m -> m.getLabel() != null && !m.getLabel().isEmpty())) {
164-
return true; // will have a resolved mining.
165-
}
166-
167-
if (fResolvedMinings == null || fResolvedMinings.length == 0) {
168-
return false;
169-
}
170-
return Stream.of(fResolvedMinings).anyMatch(CodeMiningManager::isValidMining);
171-
}
172-
173-
@Override
174-
public void update(List<ICodeMining> minings, IProgressMonitor monitor) {
175-
if (fResolvedMinings == null || (fResolvedMinings.length != minings.size())) {
176-
// size of resolved minings are different from size of minings to update, initialize it with size of minings to update
177-
fResolvedMinings= new ICodeMining[minings.size()];
178-
}
179-
// fill valid resolved minings with old minings.
180-
int length= Math.min(fMinings.size(), minings.size());
181-
for (int i= 0; i < length; i++) {
182-
ICodeMining mining= fMinings.get(i);
183-
if (mining.getLabel() != null) {
184-
fResolvedMinings[i]= mining;
185-
}
186-
}
187-
disposeMinings();
188-
fMonitor= monitor;
189-
fMinings.addAll(minings);
190-
}
191-
192-
@Override
193-
public void markDeleted(boolean deleted) {
194-
super.markDeleted(deleted);
195-
if (deleted) {
196-
disposeMinings();
197-
fResolvedMinings= null;
198-
}
199-
}
200-
201-
private void disposeMinings() {
202-
fMinings.stream().forEach(ICodeMining::dispose);
203-
fMinings.clear();
204-
}
205-
206-
@Override
207-
public void draw(GC gc, StyledText textWidget, int offset, int length, Color color, int x, int y) {
208-
List<ICodeMining> minings= new ArrayList<>(fMinings);
209-
int nbDraw= 0;
210-
int separatorWidth= -1;
211-
boolean redrawn= false;
212-
fBounds.clear();
213-
int singleLineHeight= super.getHeight();
214-
int lineSpacing= textWidget.getLineSpacing();
215-
for (int i= 0; i < minings.size(); i++) {
216-
ICodeMining mining= minings.get(i);
217-
// try to get the last resolved mining.
218-
ICodeMining lastResolvedMining= (fResolvedMinings != null && fResolvedMinings.length > i) ? fResolvedMinings[i] : null;
219-
if (mining.getLabel() != null) {
220-
// mining is resolved without error, update the resolved mining list
221-
fResolvedMinings[i]= mining;
222-
} else if (!mining.isResolved()) {
223-
// the mining is not resolved, draw the last resolved mining
224-
mining= lastResolvedMining;
225-
if (!redrawn) {
226-
// redraw the annotation when mining is resolved.
227-
redraw();
228-
redrawn= true;
229-
}
230-
} else {
231-
// the mining is resolved with error, draw the last resolved mining
232-
mining= lastResolvedMining;
233-
}
234-
if (!CodeMiningManager.isValidMining(mining)) {
235-
// ignore the draw of mining
236-
continue;
237-
}
238-
// draw the mining
239-
if (nbDraw > 0) {
240-
gc.drawText(SEPARATOR, x, y);
241-
if (separatorWidth == -1) {
242-
separatorWidth= gc.stringExtent(SEPARATOR).x;
243-
}
244-
x+= separatorWidth;
245-
}
246-
Point loc= null;
247-
if (mining != null) {
248-
loc= mining.draw(gc, textWidget, color, x, y);
249-
}
250-
if (loc != null) {
251-
fBounds.add(new Rectangle(x, y, loc.x, loc.y));
252-
x+= loc.x;
253-
if (loc.y > singleLineHeight) {
254-
y+= loc.y + lineSpacing - singleLineHeight;
255-
}
256-
}
257-
nbDraw++;
258-
}
259-
}
260-
261-
@Override
262-
public void redraw() {
263-
// redraw codemining annotation is done only if all current minings are resolved.
264-
List<ICodeMining> minings= new ArrayList<>(fMinings);
265-
for (ICodeMining mining : minings) {
266-
if (!mining.isResolved()) {
267-
// one of mining is not resolved, resolve it and then redraw the annotation.
268-
mining.resolve(getViewer(), fMonitor).thenRunAsync(() -> {
269-
this.redraw();
270-
});
271-
return;
272-
}
273-
}
274-
// all minings are resolved, redraw the annotation
275-
super.redraw();
276-
}
277-
278-
@Override
279-
public Consumer<MouseEvent> getAction(MouseEvent e) {
280-
ICodeMining mining= CodeMiningManager.getValidCodeMiningAtLocation(fResolvedMinings, fBounds, e.x, e.y);
281-
return mining != null ? mining.getAction() : null;
282-
}
283-
284-
@Override
285-
public boolean isInVisibleLines() {
286-
return super.isInVisibleLines();
28753
}
28854
}

bundles/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/codemining/CodeMiningManager.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import org.eclipse.jface.text.IDocument;
4343
import org.eclipse.jface.text.ITextViewer;
4444
import org.eclipse.jface.text.Position;
45+
import org.eclipse.jface.text.codemining.DocumentFooterCodeMining;
4546
import org.eclipse.jface.text.codemining.ICodeMining;
4647
import org.eclipse.jface.text.codemining.ICodeMiningProvider;
4748
import org.eclipse.jface.text.codemining.LineContentCodeMining;
@@ -256,6 +257,7 @@ private void renderCodeMinings(Map<Position, List<ICodeMining>> groups, ISourceV
256257
List<ICodeMining> minings= g.getValue();
257258
ICodeMining first= minings.get(0);
258259
boolean inLineHeader= !minings.isEmpty() ? (first instanceof LineHeaderCodeMining) : true;
260+
boolean inFooter= !minings.isEmpty() ? (first instanceof DocumentFooterCodeMining) : false;
259261
// Try to find existing annotation
260262
AbstractInlinedAnnotation ann= fInlinedAnnotationSupport.findExistingAnnotation(pos);
261263
if (ann == null) {
@@ -272,9 +274,13 @@ private void renderCodeMinings(Map<Position, List<ICodeMining>> groups, ISourceV
272274
mouseOut= first.getMouseOut();
273275
mouseMove= first.getMouseMove();
274276
}
275-
ann= inLineHeader
276-
? new CodeMiningLineHeaderAnnotation(pos, viewer, mouseHover, mouseOut, mouseMove)
277-
: new CodeMiningLineContentAnnotation(pos, viewer, afterPosition, mouseHover, mouseOut, mouseMove);
277+
if (inFooter) {
278+
ann= new CodeMiningDocumentFooterAnnotation(pos, viewer, mouseHover, mouseOut, mouseMove);
279+
} else {
280+
ann= inLineHeader
281+
? new CodeMiningLineHeaderAnnotation(pos, viewer, mouseHover, mouseOut, mouseMove)
282+
: new CodeMiningLineContentAnnotation(pos, viewer, afterPosition, mouseHover, mouseOut, mouseMove);
283+
}
278284
} else if (ann instanceof ICodeMiningAnnotation && ((ICodeMiningAnnotation) ann).isInVisibleLines()) {
279285
// annotation is in visible lines
280286
annotationsToRedraw.add((ICodeMiningAnnotation) ann);

0 commit comments

Comments
 (0)