diff --git a/bundles/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/InlinedAnnotationDrawingStrategy.java b/bundles/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/InlinedAnnotationDrawingStrategy.java index 60f4214018f..09464aef653 100644 --- a/bundles/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/InlinedAnnotationDrawingStrategy.java +++ b/bundles/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/InlinedAnnotationDrawingStrategy.java @@ -13,9 +13,6 @@ */ package org.eclipse.jface.text.source.inlined; -import org.eclipse.jface.text.ITextViewer; -import org.eclipse.jface.text.source.Annotation; -import org.eclipse.jface.text.source.AnnotationPainter.IDrawingStrategy; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.custom.StyledText; @@ -27,6 +24,10 @@ import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.source.Annotation; +import org.eclipse.jface.text.source.AnnotationPainter.IDrawingStrategy; + /** * {@link IDrawingStrategy} implementation to render {@link AbstractInlinedAnnotation}. * @@ -115,7 +116,7 @@ private boolean match(Font annotationFont, StyledText widget) { } int widgetFontHeight = widget.getFont().getFontData()[0].getHeight(); int annotationFontHeight = annotationFont.getFontData()[0].getHeight(); - return annotationFontHeight == widgetFontHeight; + return annotationFontHeight == widgetFontHeight; } /** @@ -201,7 +202,7 @@ private static void draw(LineHeaderAnnotation annotation, GC gc, StyledText text */ private static void draw(LineContentAnnotation annotation, GC gc, StyledText textWidget, int widgetOffset, int length, Color color) { - if (annotation.isEndOfLine(widgetOffset, textWidget)) { + if (annotation.isEmptyLine(widgetOffset, textWidget)) { drawAfterLine(annotation, gc, textWidget, widgetOffset, length, color); } else if (LineContentAnnotation.drawRightToPreviousChar(widgetOffset, textWidget)) { drawAsRightOfPreviousCharacter(annotation, gc, textWidget, widgetOffset, length, color); diff --git a/bundles/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/LineContentAnnotation.java b/bundles/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/LineContentAnnotation.java index e0fbe501e5e..f451206f0d5 100644 --- a/bundles/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/LineContentAnnotation.java +++ b/bundles/org.eclipse.jface.text/src/org/eclipse/jface/text/source/inlined/LineContentAnnotation.java @@ -13,11 +13,6 @@ */ package org.eclipse.jface.text.source.inlined; -import org.eclipse.jface.text.ITextViewer; -import org.eclipse.jface.text.ITextViewerExtension5; -import org.eclipse.jface.text.Position; -import org.eclipse.jface.text.TextPresentation; -import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.graphics.Color; @@ -25,6 +20,12 @@ import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.GlyphMetrics; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.ITextViewerExtension5; +import org.eclipse.jface.text.Position; +import org.eclipse.jface.text.TextPresentation; +import org.eclipse.jface.text.source.ISourceViewer; + /** * Inlined annotation which is drawn in the line content and which takes some place with a given * width. @@ -162,14 +163,12 @@ static boolean drawRightToPreviousChar(int widgetOffset, StyledText textWidget) textWidget.getLineAtOffset(widgetOffset) == textWidget.getLineAtOffset(widgetOffset - 1); } - boolean isEndOfLine(int widgetOffset, StyledText text) { + boolean isEmptyLine(int widgetOffset, StyledText text) { if (text.getCharCount() <= widgetOffset) { // Assuming widgetOffset >= 0 return true; } int line= text.getLineAtOffset(widgetOffset); - int startOfLine= text.getOffsetAtLine(line); - int offsetInLine= widgetOffset - startOfLine; - return offsetInLine >= text.getLine(line).length(); + String lineStr= text.getLine(line); + return lineStr.length() == 0; } - } diff --git a/tests/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/codemining/CodeMiningTest.java b/tests/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/codemining/CodeMiningTest.java index 815ff06a6a3..e7bf25f6f06 100644 --- a/tests/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/codemining/CodeMiningTest.java +++ b/tests/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/codemining/CodeMiningTest.java @@ -26,6 +26,7 @@ import org.junit.Test; import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; @@ -194,6 +195,62 @@ protected boolean condition() { }.waitForCondition(fViewer.getControl().getDisplay(), 3000)); } + @Test + public void testCodeMiningEmptyLine() { + fViewer.getDocument().set("\n"); + fViewer.setCodeMiningProviders(new ICodeMiningProvider[] { new ICodeMiningProvider() { + @Override + public CompletableFuture> provideCodeMinings(ITextViewer viewer, IProgressMonitor monitor) { + return CompletableFuture.completedFuture(Collections.singletonList(new StaticContentLineCodeMining(new Position(0, 1), "mining", this))); + } + + @Override + public void dispose() { + } + } }); + StyledText widget= fViewer.getTextWidget(); + Assert.assertTrue("Code mining is not visible in 1st empty line after line break character", new DisplayHelper() { + @Override + protected boolean condition() { + try { + StyleRange range= widget.getStyleRangeAtOffset(0); + return range == null && hasCodeMiningPrintedAfterTextOnLine(fViewer, 0); + } catch (BadLocationException e) { + e.printStackTrace(); + return false; + } + } + }.waitForCondition(fViewer.getTextWidget().getDisplay(), 1000)); + } + + @Test + public void testCodeMiningEndOfLine() { + fViewer.getDocument().set("a\n"); + fViewer.setCodeMiningProviders(new ICodeMiningProvider[] { new ICodeMiningProvider() { + @Override + public CompletableFuture> provideCodeMinings(ITextViewer viewer, IProgressMonitor monitor) { + return CompletableFuture.completedFuture(Collections.singletonList(new StaticContentLineCodeMining(new Position(1, 1), "mining", this))); + } + + @Override + public void dispose() { + } + } }); + StyledText widget= fViewer.getTextWidget(); + Assert.assertTrue("Code mining is not visible in 1st line after character a before line break character", new DisplayHelper() { + @Override + protected boolean condition() { + try { + StyleRange range= widget.getStyleRangeAtOffset(0); + return range != null && range.metrics != null && hasCodeMiningPrintedAfterTextOnLine(fViewer, 0) == false; + } catch (BadLocationException e) { + e.printStackTrace(); + return false; + } + } + }.waitForCondition(fViewer.getTextWidget().getDisplay(), 1000)); + } + @Test public void testCodeMiningMultiLine() { fViewer.getDocument().set("a\nbc"); @@ -225,7 +282,11 @@ protected boolean condition() { private static boolean hasCodeMiningPrintedAfterTextOnLine(ITextViewer viewer, int line) throws BadLocationException { StyledText widget = viewer.getTextWidget(); IDocument document= viewer.getDocument(); - Rectangle secondLineBounds = widget.getTextBounds(document.getLineOffset(1), document.getLineOffset(line) + document.getLineLength(line) - 1); + int lineLength= document.getLineLength(line) - 1; + if (lineLength < 0) { + lineLength= 0; + } + Rectangle secondLineBounds= widget.getTextBounds(document.getLineOffset(line), document.getLineOffset(line) + lineLength); Image image = new Image(widget.getDisplay(), widget.getSize().x, widget.getSize().y); GC gc = new GC(widget); gc.copyArea(image, 0, 0);