Skip to content

Commit 9b21618

Browse files
tobias-melchermickaelistria
authored andcommitted
additional constructor param afterPosition in LineContentCodeMining
which allows to render a code mining where the cursor selection does not include the code mining at the given source position.
1 parent e97cfd0 commit 9b21618

File tree

12 files changed

+257
-30
lines changed

12 files changed

+257
-30
lines changed

bundles/org.eclipse.jface.text/META-INF/MANIFEST.MF

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
22
Bundle-ManifestVersion: 2
33
Bundle-Name: %pluginName
44
Bundle-SymbolicName: org.eclipse.jface.text
5-
Bundle-Version: 3.25.300.qualifier
5+
Bundle-Version: 3.26.0.qualifier
66
Bundle-Vendor: %providerName
77
Bundle-Localization: plugin
88
Export-Package:

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ public class CodeMiningLineContentAnnotation extends LineContentAnnotation imple
6161
*/
6262
private IProgressMonitor fMonitor;
6363

64+
private final boolean afterPosition;
65+
6466
/**
6567
* Code mining annotation constructor.
6668
*
@@ -72,6 +74,21 @@ public CodeMiningLineContentAnnotation(Position position, ISourceViewer viewer)
7274
fResolvedMinings= null;
7375
fMinings= new ArrayList<>();
7476
fBounds= new ArrayList<>();
77+
afterPosition= false;
78+
}
79+
80+
/**
81+
* Code mining annotation constructor.
82+
*
83+
* @param position the position
84+
* @param viewer the viewer
85+
*/
86+
public CodeMiningLineContentAnnotation(Position position, ISourceViewer viewer, boolean afterPosition) {
87+
super(position, viewer);
88+
fResolvedMinings= null;
89+
fMinings= new ArrayList<>();
90+
fBounds= new ArrayList<>();
91+
this.afterPosition= afterPosition;
7592
}
7693

7794
@Override
@@ -183,4 +200,8 @@ public Consumer<MouseEvent> getAction(MouseEvent e) {
183200
public boolean isInVisibleLines() {
184201
return super.isInVisibleLines();
185202
}
203+
204+
public final boolean isAfterPosition() {
205+
return afterPosition;
206+
}
186207
}

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import org.eclipse.jface.text.Position;
4343
import org.eclipse.jface.text.codemining.ICodeMining;
4444
import org.eclipse.jface.text.codemining.ICodeMiningProvider;
45+
import org.eclipse.jface.text.codemining.LineContentCodeMining;
4546
import org.eclipse.jface.text.codemining.LineHeaderCodeMining;
4647
import org.eclipse.jface.text.source.ISourceViewer;
4748
import org.eclipse.jface.text.source.inlined.AbstractInlinedAnnotation;
@@ -251,12 +252,17 @@ private void renderCodeMinings(Map<Position, List<ICodeMining>> groups, ISourceV
251252

252253
Position pos= new Position(g.getKey().offset, g.getKey().length);
253254
List<ICodeMining> minings= g.getValue();
254-
boolean inLineHeader= !minings.isEmpty() ? (minings.get(0) instanceof LineHeaderCodeMining) : true;
255+
ICodeMining first= minings.get(0);
256+
boolean inLineHeader= !minings.isEmpty() ? (first instanceof LineHeaderCodeMining) : true;
255257
// Try to find existing annotation
256258
AbstractInlinedAnnotation ann= fInlinedAnnotationSupport.findExistingAnnotation(pos);
257259
if (ann == null) {
258260
// The annotation doesn't exists, create it.
259-
ann= inLineHeader ? new CodeMiningLineHeaderAnnotation(pos, viewer) : new CodeMiningLineContentAnnotation(pos, viewer);
261+
boolean afterPosition= false;
262+
if (first instanceof LineContentCodeMining m) {
263+
afterPosition= m.isAfterPosition();
264+
}
265+
ann= inLineHeader ? new CodeMiningLineHeaderAnnotation(pos, viewer) : new CodeMiningLineContentAnnotation(pos, viewer, afterPosition);
260266
} else if (ann instanceof ICodeMiningAnnotation && ((ICodeMiningAnnotation) ann).isInVisibleLines()) {
261267
// annotation is in visible lines
262268
annotationsToRedraw.add((ICodeMiningAnnotation) ann);

bundles/org.eclipse.jface.text/src/org/eclipse/jface/text/WhitespaceCharacterPainter.java

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
*******************************************************************************/
1818
package org.eclipse.jface.text;
1919

20-
import java.util.HashSet;
21-
import java.util.Set;
20+
import java.util.HashMap;
21+
import java.util.Map;
2222

2323
import org.eclipse.swt.SWT;
2424
import org.eclipse.swt.custom.StyleRange;
@@ -405,6 +405,10 @@ private void drawCharRange(GC gc, int startOffset, int endOffset, int lineOffset
405405
break;
406406
case '\r':
407407
if (fShowCarriageReturn) {
408+
if (visibleChar.length() > 0 && cache.contains(fTextWidget, lineOffset + textOffset)) {
409+
textOffset--;
410+
break;
411+
}
408412
visibleChar.append(CARRIAGE_RETURN_SIGN);
409413
}
410414
if (textOffset >= endOffsetInLine - 1 || lineText.charAt(textOffset + 1) != '\n') {
@@ -414,6 +418,10 @@ private void drawCharRange(GC gc, int startOffset, int endOffset, int lineOffset
414418
continue;
415419
case '\n':
416420
if (fShowLineFeed) {
421+
if (visibleChar.length() > 0 && cache.contains(fTextWidget, lineOffset + textOffset)) {
422+
textOffset--;
423+
break;
424+
}
417425
visibleChar.append(LINE_FEED_SIGN);
418426
}
419427
eol= true;
@@ -439,7 +447,7 @@ private void drawCharRange(GC gc, int startOffset, int endOffset, int lineOffset
439447
fg= styleRange.foreground;
440448
}
441449
}
442-
draw(gc, widgetOffset, visibleChar.toString(), fg);
450+
draw(gc, widgetOffset, visibleChar.toString(), fg, cache);
443451
}
444452
visibleChar.delete(0, visibleChar.length());
445453
}
@@ -492,40 +500,57 @@ private void redrawAll() {
492500
* @param s the string to be drawn
493501
* @param fg the foreground color
494502
*/
495-
private void draw(GC gc, int offset, String s, Color fg) {
503+
private void draw(GC gc, int offset, String s, Color fg,StyleRangeWithMetricsOffsets cache) {
496504
// Compute baseline delta (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=165640)
497505
int baseline= fTextWidget.getBaseline(offset);
498506
FontMetrics fontMetrics= gc.getFontMetrics();
499507
int fontBaseline= fontMetrics.getAscent() + fontMetrics.getLeading();
500508
int baslineDelta= baseline - fontBaseline;
501509

502510
Point pos= fTextWidget.getLocationAtOffset(offset);
511+
StyleRange styleRange= cache.get(fTextWidget, offset);
512+
if (styleRange != null && styleRange.metrics != null) { // code mining at \r or \n character - line break character should be drawn at end of code mining
513+
String charBeforeOffset= " "; //$NON-NLS-1$
514+
if (offset > 0) {
515+
charBeforeOffset= fTextWidget.getText(offset - 1, offset - 1);
516+
}
517+
Point extCharBeforeOffset= gc.textExtent(charBeforeOffset);
518+
pos.x= pos.x + styleRange.metrics.width - extCharBeforeOffset.x;
519+
}
503520
gc.setForeground(fg);
504521
gc.drawString(s, pos.x, pos.y + baslineDelta, true);
505522
}
506523

507524
private static class StyleRangeWithMetricsOffsets {
508-
private Set<Integer> offsets= null;
525+
private Map<Integer, StyleRange> offsets= null;
509526

510527
public boolean contains(StyledText st, int offset) {
511528
if (offsets == null) {
512-
fillSet(st);
529+
fillMap(st);
513530
}
514-
if (offsets.contains(offset)) {
531+
if (offsets.containsKey(offset)) {
515532
return true;
516533
}
517534
return false;
518535
}
519536

520-
private void fillSet(StyledText st) {
521-
offsets= new HashSet<>();
537+
public StyleRange get(StyledText st, int offset) {
538+
if (offsets == null) {
539+
fillMap(st);
540+
}
541+
StyleRange styleRange= offsets.get(offset);
542+
return styleRange;
543+
}
544+
545+
private void fillMap(StyledText st) {
546+
offsets= new HashMap<>();
522547
StyleRange[] ranges= st.getStyleRanges();
523548
if (ranges == null) {
524549
return;
525550
}
526551
for (StyleRange range : ranges) {
527552
if (range != null && range.metrics != null) {
528-
offsets.add(range.start);
553+
offsets.put(range.start, range);
529554
}
530555
}
531556
}

bundles/org.eclipse.jface.text/src/org/eclipse/jface/text/codemining/LineContentCodeMining.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
*/
2727
public abstract class LineContentCodeMining extends AbstractCodeMining {
2828

29+
private final boolean afterPosition;
30+
2931
/**
3032
* CodeMining constructor to locate the code mining in a given position.
3133
*
@@ -36,6 +38,20 @@ public LineContentCodeMining(Position position, ICodeMiningProvider provider) {
3638
this(position, provider, null);
3739
}
3840

41+
/**
42+
* CodeMining constructor to locate the code mining in a given position.
43+
*
44+
* @param position the position where the mining must be drawn.
45+
* @param afterPosition if true code mining is treated as suffix code mining where cursor and
46+
* selection is not including the mining
47+
* @param provider the owner codemining provider which creates this mining.
48+
*
49+
* @since 3.26
50+
*/
51+
public LineContentCodeMining(Position position, boolean afterPosition, ICodeMiningProvider provider) {
52+
this(position, afterPosition, provider, null);
53+
}
54+
3955
/**
4056
* CodeMining constructor to locate the code mining in a given position.
4157
*
@@ -44,7 +60,33 @@ public LineContentCodeMining(Position position, ICodeMiningProvider provider) {
4460
* @param action the action to execute when mining is clicked and null otherwise.
4561
*/
4662
public LineContentCodeMining(Position position, ICodeMiningProvider provider, Consumer<MouseEvent> action) {
63+
this(position, false, provider, action);
64+
}
65+
66+
/**
67+
* CodeMining constructor to locate the code mining in a given position.
68+
*
69+
* @param position the position where the mining must be drawn.
70+
* @param provider the owner codemining provider which creates this mining.
71+
* @param action the action to execute when mining is clicked and null otherwise.
72+
* @param afterPosition if true code mining is treated as suffix code mining where cursor and
73+
* selection is not including the mining
74+
*
75+
* @since 3.26
76+
*/
77+
public LineContentCodeMining(Position position, boolean afterPosition, ICodeMiningProvider provider, Consumer<MouseEvent> action) {
4778
super(position, provider, action);
79+
this.afterPosition= afterPosition;
80+
}
81+
82+
/**
83+
* indicates if code mining should be rendered after given position; cursor and selection does
84+
* not include the code mining if set to true.
85+
*
86+
* @since 3.26
87+
*/
88+
public boolean isAfterPosition() {
89+
return afterPosition;
4890
}
4991

5092
}

bundles/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/InlinedAnnotationDrawingStrategy.java

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
import org.eclipse.swt.graphics.Point;
2525
import org.eclipse.swt.graphics.Rectangle;
2626

27+
import org.eclipse.jface.internal.text.codemining.CodeMiningLineContentAnnotation;
28+
2729
import org.eclipse.jface.text.ITextViewer;
2830
import org.eclipse.jface.text.source.Annotation;
2931
import org.eclipse.jface.text.source.AnnotationPainter.IDrawingStrategy;
@@ -202,6 +204,12 @@ private static void draw(LineHeaderAnnotation annotation, GC gc, StyledText text
202204
*/
203205
private static void draw(LineContentAnnotation annotation, GC gc, StyledText textWidget, int widgetOffset, int length,
204206
Color color) {
207+
if (annotation instanceof CodeMiningLineContentAnnotation a) {
208+
if (a.isAfterPosition()) {
209+
drawAsLeftOf1stCharacter(annotation, gc, textWidget, widgetOffset, length, color);
210+
return;
211+
}
212+
}
205213
if (annotation.isEmptyLine(widgetOffset, textWidget)) {
206214
drawAfterLine(annotation, gc, textWidget, widgetOffset, length, color);
207215
} else if (LineContentAnnotation.drawRightToPreviousChar(widgetOffset, textWidget)) {
@@ -254,9 +262,18 @@ protected static void drawAsLeftOf1stCharacter(LineContentAnnotation annotation,
254262

255263
// Compute the location of the annotation
256264
Rectangle bounds= textWidget.getTextBounds(widgetOffset, widgetOffset);
257-
int x= bounds.x + (isEndOfLine ? bounds.width * 2 : 0);
258-
int y= bounds.y;
259265

266+
int x;
267+
if (isEndOfLine) {
268+
// getTextBounds at offset with char '\r' or '\n' returns incorrect x position, use getLocationAtOffset instead
269+
x= textWidget.getLocationAtOffset(widgetOffset).x;
270+
} else {
271+
x= bounds.x;
272+
}
273+
int y= bounds.y;
274+
if (isAfterPosition(annotation)) {
275+
isEndOfLine= false;
276+
}
260277
// When line text has line header annotation, there is a space on the top, adjust the y by using char height
261278
y+= bounds.height - textWidget.getLineHeight();
262279

@@ -275,14 +292,18 @@ protected static void drawAsLeftOf1stCharacter(LineContentAnnotation annotation,
275292
// Get size of the character where GlyphMetrics width is added
276293
Point charBounds= gc.stringExtent(hostCharacter);
277294
int charWidth= charBounds.x;
278-
295+
if (charWidth == 0 && ("\r".equals(hostCharacter) || "\n".equals(hostCharacter))) { //$NON-NLS-1$ //$NON-NLS-2$
296+
// charWidth is 0 for '\r' on font Consolas, but not on other fonts, why?
297+
charWidth= gc.stringExtent(" ").x; //$NON-NLS-1$
298+
}
279299
// FIXME: remove this code when we need not redraw the character (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=531769)
280300
// START TO REMOVE
281301
annotation.setRedrawnCharacterWidth(charWidth);
282302
// END TO REMOVE
283303

284304
// Annotation takes place, add GlyphMetrics width to the style
285-
StyleRange newStyle= annotation.updateStyle(style, gc.getFontMetrics(), textWidget.getData() instanceof ITextViewer viewer ? viewer : annotation.getViewer());
305+
StyleRange newStyle= annotation.updateStyle(style, gc.getFontMetrics(), textWidget.getData() instanceof ITextViewer viewer ? viewer : annotation.getViewer(),
306+
isAfterPosition(annotation));
286307
if (newStyle != null) {
287308
textWidget.setStyleRange(newStyle);
288309
return;
@@ -328,6 +349,13 @@ protected static void drawAsLeftOf1stCharacter(LineContentAnnotation annotation,
328349
}
329350
}
330351

352+
private static boolean isAfterPosition(LineContentAnnotation annotation) {
353+
if (annotation instanceof CodeMiningLineContentAnnotation a) {
354+
return a.isAfterPosition();
355+
}
356+
return false;
357+
}
358+
331359
protected static void drawAsRightOfPreviousCharacter(LineContentAnnotation annotation, GC gc, StyledText textWidget, int widgetOffset, int length, Color color) {
332360
StyleRange style= null;
333361
try {
@@ -365,7 +393,7 @@ protected static void drawAsRightOfPreviousCharacter(LineContentAnnotation annot
365393
// END TO REMOVE
366394

367395
// Annotation takes place, add GlyphMetrics width to the style
368-
StyleRange newStyle= annotation.updateStyle(style, gc.getFontMetrics(), InlinedAnnotationSupport.getSupport(textWidget).getViewer());
396+
StyleRange newStyle= annotation.updateStyle(style, gc.getFontMetrics(), InlinedAnnotationSupport.getSupport(textWidget).getViewer(), isAfterPosition(annotation));
369397
if (newStyle != null) {
370398
textWidget.setStyleRange(newStyle);
371399
return;

bundles/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/InlinedAnnotationSupport.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@
4242

4343
import org.eclipse.core.runtime.Assert;
4444

45+
import org.eclipse.jface.internal.text.codemining.CodeMiningLineContentAnnotation;
46+
4547
import org.eclipse.jface.text.BadLocationException;
4648
import org.eclipse.jface.text.DocumentEvent;
4749
import org.eclipse.jface.text.IDocument;
@@ -107,7 +109,7 @@ public void applyTextPresentation(TextPresentation textPresentation) {
107109
.forEachRemaining(annotation -> {
108110
if (annotation instanceof LineContentAnnotation) {
109111
LineContentAnnotation ann= (LineContentAnnotation) annotation;
110-
StyleRange style= ann.updateStyle(null, fFontMetrics, fViewer);
112+
StyleRange style= ann.updateStyle(null, fFontMetrics, fViewer, isAfterPosition(ann));
111113
if (style != null) {
112114
if (fViewer instanceof ITextViewerExtension5 projectionViewer) {
113115
IRegion annotationRegion= projectionViewer.widgetRange2ModelRange(new Region(style.start, style.length));
@@ -119,6 +121,13 @@ public void applyTextPresentation(TextPresentation textPresentation) {
119121
}
120122
});
121123
}
124+
125+
private static boolean isAfterPosition(LineContentAnnotation annotation) {
126+
if (annotation instanceof CodeMiningLineContentAnnotation a) {
127+
return a.isAfterPosition();
128+
}
129+
return false;
130+
}
122131
}
123132

124133
/**

bundles/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/LineContentAnnotation.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,13 +117,16 @@ boolean contains(int x, int y) {
117117
* @return the style to apply with GlyphMetrics width only if needed. It uses widget position,
118118
* not model position.
119119
*/
120-
StyleRange updateStyle(StyleRange style, FontMetrics fontMetrics, ITextViewer viewer) {
120+
StyleRange updateStyle(StyleRange style, FontMetrics fontMetrics, ITextViewer viewer, boolean afterPosition) {
121121
Position widgetPosition= computeWidgetPosition(viewer);
122122
if (widgetPosition == null) {
123123
return null;
124124
}
125125
StyledText textWidget = viewer.getTextWidget();
126-
boolean usePreviousChar= drawRightToPreviousChar(widgetPosition.getOffset(), textWidget);
126+
boolean usePreviousChar= false;
127+
if (!afterPosition) {
128+
usePreviousChar= drawRightToPreviousChar(widgetPosition.getOffset(), textWidget);
129+
}
127130
if (width == 0 || getRedrawnCharacterWidth() == 0) {
128131
return null;
129132
}

0 commit comments

Comments
 (0)