Skip to content

Commit 794db81

Browse files
support rendering of code minings at the very end of the document
if the last line at the end of the document is empty Fixes: #2157
1 parent d8afbe0 commit 794db81

File tree

3 files changed

+156
-13
lines changed

3 files changed

+156
-13
lines changed

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

Lines changed: 73 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.eclipse.jface.internal.text.codemining.CodeMiningLineHeaderAnnotation;
2929

3030
import org.eclipse.jface.text.ITextViewer;
31+
import org.eclipse.jface.text.Position;
3132
import org.eclipse.jface.text.source.Annotation;
3233
import org.eclipse.jface.text.source.AnnotationPainter.IDrawingStrategy;
3334

@@ -99,7 +100,7 @@ private Font getAnnotationFont(StyledText textWidget) {
99100
if (annotationFont == null) {
100101
annotationFont = createInlineAnnotationFont(textWidget);
101102
textWidget.setData(INLINE_ANNOTATION_FONT, annotationFont);
102-
textWidget.addDisposeListener(e -> ((Font)textWidget.getData(INLINE_ANNOTATION_FONT)).dispose());
103+
textWidget.addDisposeListener(e -> ((Font) textWidget.getData(INLINE_ANNOTATION_FONT)).dispose());
103104
}
104105
return annotationFont;
105106
}
@@ -154,7 +155,8 @@ public static void draw(AbstractInlinedAnnotation annotation, GC gc, StyledText
154155
private static void draw(LineHeaderAnnotation annotation, GC gc, StyledText textWidget, int offset, int length,
155156
Color color) {
156157
int line= textWidget.getLineAtOffset(offset);
157-
if (isDeleted(annotation)) {
158+
int charCount= textWidget.getCharCount();
159+
if (isDeleted(annotation, charCount)) {
158160
// When annotation is deleted, update metrics to null to remove extra spaces of the line header annotation.
159161
if (textWidget.getLineVerticalIndent(line) > 0)
160162
textWidget.setLineVerticalIndent(line, 0);
@@ -180,9 +182,16 @@ private static void draw(LineHeaderAnnotation annotation, GC gc, StyledText text
180182
textWidget.setLineVerticalIndent(line, 0);
181183
}
182184
// Compute the location of the annotation
183-
Rectangle bounds= textWidget.getTextBounds(offset, offset);
184-
int x= bounds.x;
185-
int y= bounds.y;
185+
int x, y;
186+
if (offset < charCount) {
187+
Rectangle bounds= textWidget.getTextBounds(offset, offset);
188+
x= bounds.x;
189+
y= bounds.y;
190+
} else {
191+
Point locAtOff= textWidget.getLocationAtOffset(offset);
192+
x= locAtOff.x;
193+
y= locAtOff.y - height;
194+
}
186195
// Draw the line header annotation
187196
gc.setBackground(textWidget.getBackground());
188197
annotation.setLocation(x, y);
@@ -193,7 +202,16 @@ private static void draw(LineHeaderAnnotation annotation, GC gc, StyledText text
193202
Rectangle client= textWidget.getClientArea();
194203
textWidget.redraw(0, bounds.y, client.width, bounds.height, false);
195204
} else {
196-
textWidget.redrawRange(offset, length, true);
205+
if (offset >= charCount) {
206+
if (charCount > 0) {
207+
textWidget.redrawRange(charCount - 1, 1, true);
208+
} else {
209+
Rectangle client= textWidget.getClientArea();
210+
textWidget.redraw(0, 0, client.width, client.height, false);
211+
}
212+
} else {
213+
textWidget.redrawRange(offset, length, true);
214+
}
197215
}
198216
}
199217

@@ -212,7 +230,11 @@ private static void draw(LineContentAnnotation annotation, GC gc, StyledText tex
212230
Color color) {
213231
if (annotation instanceof CodeMiningLineContentAnnotation a) {
214232
if (a.isAfterPosition()) {
215-
drawAsLeftOf1stCharacter(annotation, gc, textWidget, widgetOffset, length, color);
233+
if (widgetOffset < textWidget.getCharCount()) {
234+
drawAsLeftOf1stCharacter(annotation, gc, textWidget, widgetOffset, length, color);
235+
} else {
236+
drawAtEndOfDocumentInFirstColumn(annotation, gc, textWidget, widgetOffset, length, color);
237+
}
216238
return;
217239
}
218240
}
@@ -226,7 +248,7 @@ private static void draw(LineContentAnnotation annotation, GC gc, StyledText tex
226248
}
227249

228250
private static void drawAfterLine(LineContentAnnotation annotation, GC gc, StyledText textWidget, int widgetOffset, int length, Color color) {
229-
if (isDeleted(annotation)) {
251+
if (isDeleted(annotation, textWidget.getCharCount())) {
230252
return;
231253
}
232254
if (gc != null) {
@@ -247,14 +269,43 @@ private static void drawAfterLine(LineContentAnnotation annotation, GC gc, Style
247269
}
248270
}
249271

272+
private static void drawAtEndOfDocumentInFirstColumn(LineContentAnnotation annotation, GC gc, StyledText textWidget, int widgetOffset, int length, Color color) {
273+
if (isDeleted(annotation, textWidget.getCharCount())) {
274+
return;
275+
}
276+
if (gc != null) {
277+
Point locAtOff= textWidget.getLocationAtOffset(widgetOffset);
278+
int x= locAtOff.x;
279+
int y= locAtOff.y;
280+
annotation.setLocation(x, y);
281+
annotation.draw(gc, textWidget, widgetOffset, length, color, x, y);
282+
int width= annotation.getWidth();
283+
if (width != 0) {
284+
if (!gc.getClipping().contains(x, y)) {
285+
Rectangle client= textWidget.getClientArea();
286+
int height= textWidget.getLineHeight();
287+
textWidget.redraw(x, y, client.width, height, false);
288+
}
289+
}
290+
} else {
291+
int charCount= textWidget.getCharCount();
292+
if (charCount > 0) {
293+
textWidget.redrawRange(charCount - 1, 1, true);
294+
} else {
295+
Rectangle client= textWidget.getClientArea();
296+
textWidget.redraw(0, 0, client.width, client.height, false);
297+
}
298+
}
299+
}
300+
250301
protected static void drawAsLeftOf1stCharacter(LineContentAnnotation annotation, GC gc, StyledText textWidget, int widgetOffset, int length, Color color) {
251302
StyleRange style= null;
252303
try {
253304
style= textWidget.getStyleRangeAtOffset(widgetOffset);
254305
} catch (Exception e) {
255306
return;
256307
}
257-
if (isDeleted(annotation)) {
308+
if (isDeleted(annotation, textWidget.getCharCount())) {
258309
// When annotation is deleted, update metrics to null to remove extra spaces of the line content annotation.
259310
if (style != null && style.metrics != null) {
260311
style.metrics= null;
@@ -369,7 +420,7 @@ protected static void drawAsRightOfPreviousCharacter(LineContentAnnotation annot
369420
} catch (Exception e) {
370421
return;
371422
}
372-
if (isDeleted(annotation)) {
423+
if (isDeleted(annotation, textWidget.getCharCount())) {
373424
// When annotation is deleted, update metrics to null to remove extra spaces of the line content annotation.
374425
if (style != null && style.metrics != null) {
375426
style.metrics= null;
@@ -449,7 +500,17 @@ protected static void drawAsRightOfPreviousCharacter(LineContentAnnotation annot
449500
* @param annotation the inlined annotation to check
450501
* @return <code>true</code> if inlined annotation is deleted and <code>false</code> otherwise.
451502
*/
452-
private static boolean isDeleted(AbstractInlinedAnnotation annotation) {
453-
return annotation.isMarkedDeleted() || annotation.getPosition().isDeleted() || annotation.getPosition().getLength() == 0;
503+
private static boolean isDeleted(AbstractInlinedAnnotation annotation,int maxOffset) {
504+
if (annotation.isMarkedDeleted()) {
505+
return true;
506+
}
507+
Position pos= annotation.getPosition();
508+
if (pos.isDeleted()) {
509+
return true;
510+
}
511+
if (pos.getLength() == 0 && pos.getOffset() < maxOffset) {
512+
return true;
513+
}
514+
return false;
454515
}
455516
}

tests/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/codemining/CodeMiningTest.java

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.eclipse.swt.graphics.GC;
3636
import org.eclipse.swt.graphics.Image;
3737
import org.eclipse.swt.graphics.ImageData;
38+
import org.eclipse.swt.graphics.Point;
3839
import org.eclipse.swt.graphics.Rectangle;
3940
import org.eclipse.swt.layout.FillLayout;
4041
import org.eclipse.swt.widgets.Display;
@@ -250,6 +251,71 @@ protected boolean condition() {
250251
}.waitForCondition(fViewer.getTextWidget().getDisplay(), 1000));
251252
}
252253

254+
@Test
255+
public void testLineHeaderCodeMiningAtEndOfDocumentWithEmptyLine() throws Exception {
256+
String source= "first\nsecond\n";
257+
fViewer.getDocument().set(source);
258+
fViewer.setCodeMiningProviders(new ICodeMiningProvider[] { new ICodeMiningProvider() {
259+
@Override
260+
public CompletableFuture<List<? extends ICodeMining>> provideCodeMinings(ITextViewer viewer, IProgressMonitor monitor) {
261+
List<ICodeMining> minings= new ArrayList<>();
262+
try {
263+
minings.add(new LineHeaderCodeMining(new Position(source.length(), 0), this, null) {
264+
@Override
265+
public String getLabel() {
266+
return "multiline first line\nmultiline second line\nmultiline third line\nmultiline fourth line";
267+
}
268+
});
269+
} catch (BadLocationException e) {
270+
e.printStackTrace();
271+
}
272+
return CompletableFuture.completedFuture(minings);
273+
}
274+
275+
@Override
276+
public void dispose() {
277+
}
278+
} });
279+
Assert.assertTrue("Code mining is not visible at end of document", new DisplayHelper() {
280+
@Override
281+
protected boolean condition() {
282+
try {
283+
return hasCodeMiningPrintedAfterTextOnLine(fViewer, 2);
284+
} catch (BadLocationException e) {
285+
e.printStackTrace();
286+
return false;
287+
}
288+
}
289+
}.waitForCondition(fViewer.getTextWidget().getDisplay(), 10_000));
290+
}
291+
292+
@Test
293+
public void testCodeMiningAtEndOfDocumentWithEmptyLine() throws Exception {
294+
String source= "first\nsecond\n";
295+
fViewer.getDocument().set(source);
296+
fViewer.setCodeMiningProviders(new ICodeMiningProvider[] { new ICodeMiningProvider() {
297+
@Override
298+
public CompletableFuture<List<? extends ICodeMining>> provideCodeMinings(ITextViewer viewer, IProgressMonitor monitor) {
299+
return CompletableFuture.completedFuture(Collections.singletonList(new StaticContentLineCodeMining(new Position(source.length(), 0), true, "mining", this)));
300+
}
301+
302+
@Override
303+
public void dispose() {
304+
}
305+
} });
306+
Assert.assertTrue("Code mining is not visible at end of document", new DisplayHelper() {
307+
@Override
308+
protected boolean condition() {
309+
try {
310+
return hasCodeMiningPrintedAfterTextOnLine(fViewer, 2);
311+
} catch (BadLocationException e) {
312+
e.printStackTrace();
313+
return false;
314+
}
315+
}
316+
}.waitForCondition(fViewer.getTextWidget().getDisplay(), 10_000));
317+
}
318+
253319
@Test
254320
public void testCodeMiningEndOfLine() {
255321
fViewer.getDocument().set("a\n");
@@ -389,7 +455,18 @@ private static boolean hasCodeMiningPrintedAfterTextOnLine(ITextViewer viewer, i
389455
if (lineLength < 0) {
390456
lineLength= 0;
391457
}
392-
Rectangle secondLineBounds= widget.getTextBounds(document.getLineOffset(line), document.getLineOffset(line) + lineLength);
458+
Rectangle secondLineBounds= null;
459+
int lineOffset= document.getLineOffset(line);
460+
if (lineOffset >= document.getLength()) {
461+
int off= document.getLength() - 1;
462+
secondLineBounds= widget.getTextBounds(off, off + lineLength);
463+
Point l= widget.getLocationAtOffset(lineOffset);
464+
int lineVerticalIndent= widget.getLineVerticalIndent(line);
465+
secondLineBounds.x= l.x;
466+
secondLineBounds.y= l.y - lineVerticalIndent;
467+
} else {
468+
secondLineBounds= widget.getTextBounds(lineOffset, lineOffset + lineLength);
469+
}
393470
Image image = new Image(widget.getDisplay(), widget.getSize().x, widget.getSize().y);
394471
GC gc = new GC(widget);
395472
gc.copyArea(image, 0, 0);

tests/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/codemining/StaticContentLineCodeMining.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ public StaticContentLineCodeMining(Position position, String message, ICodeMinin
2424
setLabel(message);
2525
}
2626

27+
public StaticContentLineCodeMining(Position position, boolean afterPosition, String message, ICodeMiningProvider provider) {
28+
super(position, afterPosition, provider);
29+
setLabel(message);
30+
}
31+
2732
public StaticContentLineCodeMining(int i, char c, ICodeMiningProvider repeatLettersCodeMiningProvider) {
2833
super(new Position(i, 1), repeatLettersCodeMiningProvider);
2934
setLabel(Character.toString(c));

0 commit comments

Comments
 (0)