Skip to content

Commit

Permalink
support rendering of code minings at the very end of the document
Browse files Browse the repository at this point in the history
if the last line at the end of the document is empty

Fixes: #2157
  • Loading branch information
tobias-melcher committed Jan 3, 2025
1 parent d8afbe0 commit df18f8c
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.eclipse.jface.internal.text.codemining.CodeMiningLineHeaderAnnotation;

import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.AnnotationPainter.IDrawingStrategy;

Expand Down Expand Up @@ -154,7 +155,8 @@ public static void draw(AbstractInlinedAnnotation annotation, GC gc, StyledText
private static void draw(LineHeaderAnnotation annotation, GC gc, StyledText textWidget, int offset, int length,
Color color) {
int line= textWidget.getLineAtOffset(offset);
if (isDeleted(annotation)) {
int charCount= textWidget.getCharCount();
if (isDeleted(annotation, charCount)) {
// When annotation is deleted, update metrics to null to remove extra spaces of the line header annotation.
if (textWidget.getLineVerticalIndent(line) > 0)
textWidget.setLineVerticalIndent(line, 0);
Expand All @@ -180,9 +182,16 @@ private static void draw(LineHeaderAnnotation annotation, GC gc, StyledText text
textWidget.setLineVerticalIndent(line, 0);
}
// Compute the location of the annotation
Rectangle bounds= textWidget.getTextBounds(offset, offset);
int x= bounds.x;
int y= bounds.y;
int x, y;
if (offset < charCount) {
Rectangle bounds= textWidget.getTextBounds(offset, offset);
x= bounds.x;
y= bounds.y;
} else {
Point locAtOff= textWidget.getLocationAtOffset(offset);
x= locAtOff.x;
y= locAtOff.y - height;
}
// Draw the line header annotation
gc.setBackground(textWidget.getBackground());
annotation.setLocation(x, y);
Expand All @@ -193,7 +202,11 @@ private static void draw(LineHeaderAnnotation annotation, GC gc, StyledText text
Rectangle client= textWidget.getClientArea();
textWidget.redraw(0, bounds.y, client.width, bounds.height, false);
} else {
textWidget.redrawRange(offset, length, true);
if (offset >= charCount) {
textWidget.redrawRange(charCount - 1, 1, true);
} else {
textWidget.redrawRange(offset, length, true);
}
}
}

Expand All @@ -212,7 +225,11 @@ private static void draw(LineContentAnnotation annotation, GC gc, StyledText tex
Color color) {
if (annotation instanceof CodeMiningLineContentAnnotation a) {
if (a.isAfterPosition()) {
drawAsLeftOf1stCharacter(annotation, gc, textWidget, widgetOffset, length, color);
if (widgetOffset < textWidget.getCharCount()) {
drawAsLeftOf1stCharacter(annotation, gc, textWidget, widgetOffset, length, color);
} else {
drawAtEndOfDocumentInFirstColumn(annotation, gc, textWidget, widgetOffset, length, color);
}
return;
}
}
Expand All @@ -226,7 +243,7 @@ private static void draw(LineContentAnnotation annotation, GC gc, StyledText tex
}

private static void drawAfterLine(LineContentAnnotation annotation, GC gc, StyledText textWidget, int widgetOffset, int length, Color color) {
if (isDeleted(annotation)) {
if (isDeleted(annotation, textWidget.getCharCount())) {
return;
}
if (gc != null) {
Expand All @@ -247,14 +264,38 @@ private static void drawAfterLine(LineContentAnnotation annotation, GC gc, Style
}
}

private static void drawAtEndOfDocumentInFirstColumn(LineContentAnnotation annotation, GC gc, StyledText textWidget, int widgetOffset, int length, Color color) {
if (isDeleted(annotation, textWidget.getCharCount())) {
return;
}
if (gc != null) {
Point locAtOff= textWidget.getLocationAtOffset(widgetOffset);
int x= locAtOff.x;
int y= locAtOff.y;
annotation.setLocation(x, y);
annotation.draw(gc, textWidget, widgetOffset, length, color, x, y);
int width= annotation.getWidth();
if (width != 0) {
if (!gc.getClipping().contains(x, y)) {
Rectangle client= textWidget.getClientArea();
int height= textWidget.getLineHeight();
textWidget.redraw(x, y, client.width, height, false);
}
}
} else {
int charCount= textWidget.getCharCount();
textWidget.redrawRange(charCount - 1, 1, true);
}
}

protected static void drawAsLeftOf1stCharacter(LineContentAnnotation annotation, GC gc, StyledText textWidget, int widgetOffset, int length, Color color) {
StyleRange style= null;
try {
style= textWidget.getStyleRangeAtOffset(widgetOffset);
} catch (Exception e) {
return;
}
if (isDeleted(annotation)) {
if (isDeleted(annotation, textWidget.getCharCount())) {
// When annotation is deleted, update metrics to null to remove extra spaces of the line content annotation.
if (style != null && style.metrics != null) {
style.metrics= null;
Expand Down Expand Up @@ -369,7 +410,7 @@ protected static void drawAsRightOfPreviousCharacter(LineContentAnnotation annot
} catch (Exception e) {
return;
}
if (isDeleted(annotation)) {
if (isDeleted(annotation, textWidget.getCharCount())) {
// When annotation is deleted, update metrics to null to remove extra spaces of the line content annotation.
if (style != null && style.metrics != null) {
style.metrics= null;
Expand Down Expand Up @@ -449,7 +490,17 @@ protected static void drawAsRightOfPreviousCharacter(LineContentAnnotation annot
* @param annotation the inlined annotation to check
* @return <code>true</code> if inlined annotation is deleted and <code>false</code> otherwise.
*/
private static boolean isDeleted(AbstractInlinedAnnotation annotation) {
return annotation.isMarkedDeleted() || annotation.getPosition().isDeleted() || annotation.getPosition().getLength() == 0;
private static boolean isDeleted(AbstractInlinedAnnotation annotation,int maxOffset) {
if (annotation.isMarkedDeleted()) {
return true;
}
Position pos= annotation.getPosition();
if (pos.isDeleted()) {
return true;
}
if (pos.getLength() == 0 && pos.getOffset() < maxOffset) {
return true;
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
Expand Down Expand Up @@ -250,6 +251,71 @@ protected boolean condition() {
}.waitForCondition(fViewer.getTextWidget().getDisplay(), 1000));
}

@Test
public void testLineHeaderCodeMiningAtEndOfDocumentWithEmptyLine() throws Exception {
String source= "first\nsecond\n";
fViewer.getDocument().set(source);
fViewer.setCodeMiningProviders(new ICodeMiningProvider[] { new ICodeMiningProvider() {
@Override
public CompletableFuture<List<? extends ICodeMining>> provideCodeMinings(ITextViewer viewer, IProgressMonitor monitor) {
List<ICodeMining> minings= new ArrayList<>();
try {
minings.add(new LineHeaderCodeMining(new Position(source.length(), 0), this, null) {
@Override
public String getLabel() {
return "multiline first line\nmultiline second line\nmultiline third line\nmultiline fourth line";
}
});
} catch (BadLocationException e) {
e.printStackTrace();
}
return CompletableFuture.completedFuture(minings);
}

@Override
public void dispose() {
}
} });
Assert.assertTrue("Code mining is not visible at end of document", new DisplayHelper() {
@Override
protected boolean condition() {
try {
return hasCodeMiningPrintedAfterTextOnLine(fViewer, 2);
} catch (BadLocationException e) {
e.printStackTrace();
return false;
}
}
}.waitForCondition(fViewer.getTextWidget().getDisplay(), 10_000));
}

@Test
public void testCodeMiningAtEndOfDocumentWithEmptyLine() throws Exception {
String source= "first\nsecond\n";
fViewer.getDocument().set(source);
fViewer.setCodeMiningProviders(new ICodeMiningProvider[] { new ICodeMiningProvider() {
@Override
public CompletableFuture<List<? extends ICodeMining>> provideCodeMinings(ITextViewer viewer, IProgressMonitor monitor) {
return CompletableFuture.completedFuture(Collections.singletonList(new StaticContentLineCodeMining(new Position(source.length(), 0), true, "mining", this)));
}

@Override
public void dispose() {
}
} });
Assert.assertTrue("Code mining is not visible at end of document", new DisplayHelper() {
@Override
protected boolean condition() {
try {
return hasCodeMiningPrintedAfterTextOnLine(fViewer, 2);
} catch (BadLocationException e) {
e.printStackTrace();
return false;
}
}
}.waitForCondition(fViewer.getTextWidget().getDisplay(), 10_000));
}

@Test
public void testCodeMiningEndOfLine() {
fViewer.getDocument().set("a\n");
Expand Down Expand Up @@ -389,7 +455,18 @@ private static boolean hasCodeMiningPrintedAfterTextOnLine(ITextViewer viewer, i
if (lineLength < 0) {
lineLength= 0;
}
Rectangle secondLineBounds= widget.getTextBounds(document.getLineOffset(line), document.getLineOffset(line) + lineLength);
Rectangle secondLineBounds= null;
int lineOffset= document.getLineOffset(line);
if (lineOffset >= document.getLength()) {
int off= document.getLength() - 1;
secondLineBounds= widget.getTextBounds(off, off + lineLength);
Point l= widget.getLocationAtOffset(lineOffset);
int lineVerticalIndent= widget.getLineVerticalIndent(line);
secondLineBounds.x= l.x;
secondLineBounds.y= l.y - lineVerticalIndent;
} else {
secondLineBounds= widget.getTextBounds(lineOffset, lineOffset + lineLength);
}
Image image = new Image(widget.getDisplay(), widget.getSize().x, widget.getSize().y);
GC gc = new GC(widget);
gc.copyArea(image, 0, 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ public StaticContentLineCodeMining(Position position, String message, ICodeMinin
setLabel(message);
}

public StaticContentLineCodeMining(Position position, boolean afterPosition, String message, ICodeMiningProvider provider) {
super(position, afterPosition, provider);
setLabel(message);
}

public StaticContentLineCodeMining(int i, char c, ICodeMiningProvider repeatLettersCodeMiningProvider) {
super(new Position(i, 1), repeatLettersCodeMiningProvider);
setLabel(Character.toString(c));
Expand Down

0 comments on commit df18f8c

Please sign in to comment.