Skip to content

Commit df18f8c

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 df18f8c

File tree

3 files changed

+145
-12
lines changed

3 files changed

+145
-12
lines changed

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

Lines changed: 62 additions & 11 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

@@ -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,11 @@ 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+
textWidget.redrawRange(charCount - 1, 1, true);
207+
} else {
208+
textWidget.redrawRange(offset, length, true);
209+
}
197210
}
198211
}
199212

@@ -212,7 +225,11 @@ private static void draw(LineContentAnnotation annotation, GC gc, StyledText tex
212225
Color color) {
213226
if (annotation instanceof CodeMiningLineContentAnnotation a) {
214227
if (a.isAfterPosition()) {
215-
drawAsLeftOf1stCharacter(annotation, gc, textWidget, widgetOffset, length, color);
228+
if (widgetOffset < textWidget.getCharCount()) {
229+
drawAsLeftOf1stCharacter(annotation, gc, textWidget, widgetOffset, length, color);
230+
} else {
231+
drawAtEndOfDocumentInFirstColumn(annotation, gc, textWidget, widgetOffset, length, color);
232+
}
216233
return;
217234
}
218235
}
@@ -226,7 +243,7 @@ private static void draw(LineContentAnnotation annotation, GC gc, StyledText tex
226243
}
227244

228245
private static void drawAfterLine(LineContentAnnotation annotation, GC gc, StyledText textWidget, int widgetOffset, int length, Color color) {
229-
if (isDeleted(annotation)) {
246+
if (isDeleted(annotation, textWidget.getCharCount())) {
230247
return;
231248
}
232249
if (gc != null) {
@@ -247,14 +264,38 @@ private static void drawAfterLine(LineContentAnnotation annotation, GC gc, Style
247264
}
248265
}
249266

267+
private static void drawAtEndOfDocumentInFirstColumn(LineContentAnnotation annotation, GC gc, StyledText textWidget, int widgetOffset, int length, Color color) {
268+
if (isDeleted(annotation, textWidget.getCharCount())) {
269+
return;
270+
}
271+
if (gc != null) {
272+
Point locAtOff= textWidget.getLocationAtOffset(widgetOffset);
273+
int x= locAtOff.x;
274+
int y= locAtOff.y;
275+
annotation.setLocation(x, y);
276+
annotation.draw(gc, textWidget, widgetOffset, length, color, x, y);
277+
int width= annotation.getWidth();
278+
if (width != 0) {
279+
if (!gc.getClipping().contains(x, y)) {
280+
Rectangle client= textWidget.getClientArea();
281+
int height= textWidget.getLineHeight();
282+
textWidget.redraw(x, y, client.width, height, false);
283+
}
284+
}
285+
} else {
286+
int charCount= textWidget.getCharCount();
287+
textWidget.redrawRange(charCount - 1, 1, true);
288+
}
289+
}
290+
250291
protected static void drawAsLeftOf1stCharacter(LineContentAnnotation annotation, GC gc, StyledText textWidget, int widgetOffset, int length, Color color) {
251292
StyleRange style= null;
252293
try {
253294
style= textWidget.getStyleRangeAtOffset(widgetOffset);
254295
} catch (Exception e) {
255296
return;
256297
}
257-
if (isDeleted(annotation)) {
298+
if (isDeleted(annotation, textWidget.getCharCount())) {
258299
// When annotation is deleted, update metrics to null to remove extra spaces of the line content annotation.
259300
if (style != null && style.metrics != null) {
260301
style.metrics= null;
@@ -369,7 +410,7 @@ protected static void drawAsRightOfPreviousCharacter(LineContentAnnotation annot
369410
} catch (Exception e) {
370411
return;
371412
}
372-
if (isDeleted(annotation)) {
413+
if (isDeleted(annotation, textWidget.getCharCount())) {
373414
// When annotation is deleted, update metrics to null to remove extra spaces of the line content annotation.
374415
if (style != null && style.metrics != null) {
375416
style.metrics= null;
@@ -449,7 +490,17 @@ protected static void drawAsRightOfPreviousCharacter(LineContentAnnotation annot
449490
* @param annotation the inlined annotation to check
450491
* @return <code>true</code> if inlined annotation is deleted and <code>false</code> otherwise.
451492
*/
452-
private static boolean isDeleted(AbstractInlinedAnnotation annotation) {
453-
return annotation.isMarkedDeleted() || annotation.getPosition().isDeleted() || annotation.getPosition().getLength() == 0;
493+
private static boolean isDeleted(AbstractInlinedAnnotation annotation,int maxOffset) {
494+
if (annotation.isMarkedDeleted()) {
495+
return true;
496+
}
497+
Position pos= annotation.getPosition();
498+
if (pos.isDeleted()) {
499+
return true;
500+
}
501+
if (pos.getLength() == 0 && pos.getOffset() < maxOffset) {
502+
return true;
503+
}
504+
return false;
454505
}
455506
}

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)