Skip to content

Commit

Permalink
code minings drawn more consistently always via drawAsLeftOf1stCharacter
Browse files Browse the repository at this point in the history
this improves the selection behavior for the scenario where the
selection end offset contains a LineContentAnnotation. Selection is no
longer extended with the code mining.
  • Loading branch information
tobias-melcher committed Sep 6, 2024
1 parent a04b617 commit 5f67aff
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 168 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
*******************************************************************************/
package org.eclipse.jface.text;

import java.util.HashSet;
import java.util.Set;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyleRange;
Expand Down Expand Up @@ -405,6 +405,10 @@ private void drawCharRange(GC gc, int startOffset, int endOffset, int lineOffset
break;
case '\r':
if (fShowCarriageReturn) {
if (visibleChar.length() > 0 && cache.contains(fTextWidget, lineOffset + textOffset)) {
textOffset--;
break;
}
visibleChar.append(CARRIAGE_RETURN_SIGN);
}
if (textOffset >= endOffsetInLine - 1 || lineText.charAt(textOffset + 1) != '\n') {
Expand All @@ -414,6 +418,10 @@ private void drawCharRange(GC gc, int startOffset, int endOffset, int lineOffset
continue;
case '\n':
if (fShowLineFeed) {
if (visibleChar.length() > 0 && cache.contains(fTextWidget, lineOffset + textOffset)) {
textOffset--;
break;
}
visibleChar.append(LINE_FEED_SIGN);
}
eol= true;
Expand All @@ -439,7 +447,7 @@ private void drawCharRange(GC gc, int startOffset, int endOffset, int lineOffset
fg= styleRange.foreground;
}
}
draw(gc, widgetOffset, visibleChar.toString(), fg);
draw(gc, widgetOffset, visibleChar.toString(), fg, cache);
}
visibleChar.delete(0, visibleChar.length());
}
Expand Down Expand Up @@ -492,40 +500,57 @@ private void redrawAll() {
* @param s the string to be drawn
* @param fg the foreground color
*/
private void draw(GC gc, int offset, String s, Color fg) {
private void draw(GC gc, int offset, String s, Color fg,StyleRangeWithMetricsOffsets cache) {
// Compute baseline delta (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=165640)
int baseline= fTextWidget.getBaseline(offset);
FontMetrics fontMetrics= gc.getFontMetrics();
int fontBaseline= fontMetrics.getAscent() + fontMetrics.getLeading();
int baslineDelta= baseline - fontBaseline;

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

private static class StyleRangeWithMetricsOffsets {
private Set<Integer> offsets= null;
private Map<Integer, StyleRange> offsets= null;

public boolean contains(StyledText st, int offset) {
if (offsets == null) {
fillSet(st);
fillMap(st);
}
if (offsets.contains(offset)) {
if (offsets.containsKey(offset)) {
return true;
}
return false;
}

private void fillSet(StyledText st) {
offsets= new HashSet<>();
public StyleRange get(StyledText st, int offset) {
if (offsets == null) {
fillMap(st);
}
StyleRange styleRange= offsets.get(offset);
return styleRange;
}

private void fillMap(StyledText st) {
offsets= new HashMap<>();
StyleRange[] ranges= st.getStyleRanges();
if (ranges == null) {
return;
}
for (StyleRange range : ranges) {
if (range != null && range.metrics != null) {
offsets.add(range.start);
offsets.put(range.start, range);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,35 +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.isEmptyLine(widgetOffset, textWidget)) {
drawAfterLine(annotation, gc, textWidget, widgetOffset, length, color);
} else if (LineContentAnnotation.drawRightToPreviousChar(widgetOffset, textWidget)) {
drawAsRightOfPreviousCharacter(annotation, gc, textWidget, widgetOffset, length, color);
} else {
drawAsLeftOf1stCharacter(annotation, gc, textWidget, widgetOffset, length, color);
}
}

private static void drawAfterLine(LineContentAnnotation annotation, GC gc, StyledText textWidget, int widgetOffset, int length, Color color) {
if (isDeleted(annotation)) {
return;
}
if (gc != null) {
if (textWidget.getCharCount() == 0) {
annotation.draw(gc, textWidget, widgetOffset, length, color, 0, 0);
} else {
int line= textWidget.getLineAtOffset(widgetOffset);
int lineEndOffset= (line == textWidget.getLineCount() - 1) ? //
textWidget.getCharCount() - 1 : //
textWidget.getOffsetAtLine(line + 1) - 1;
Rectangle bounds= textWidget.getTextBounds(lineEndOffset, lineEndOffset);
int lineEndX= bounds.x + bounds.width + gc.stringExtent(" ").x; //$NON-NLS-1$
annotation.setLocation(lineEndX, textWidget.getLinePixel(line) + textWidget.getLineVerticalIndent(line));
annotation.draw(gc, textWidget, widgetOffset, length, color, lineEndX, textWidget.getLinePixel(line) + textWidget.getLineVerticalIndent(line));
}
} else {
textWidget.redrawRange(widgetOffset, length, true);
}
drawAsLeftOf1stCharacter(annotation, gc, textWidget, widgetOffset, length, color);
}

protected static void drawAsLeftOf1stCharacter(LineContentAnnotation annotation, GC gc, StyledText textWidget, int widgetOffset, int length, Color color) {
Expand All @@ -254,9 +226,16 @@ protected static void drawAsLeftOf1stCharacter(LineContentAnnotation annotation,

// Compute the location of the annotation
Rectangle bounds= textWidget.getTextBounds(widgetOffset, widgetOffset);
int x= bounds.x + (isEndOfLine ? bounds.width * 2 : 0);
int x;
if (isEndOfLine) {
// getTextBounds at offset with char '\r' or '\n' returns incorrect x position, use getLocationAtOffset instead
x= textWidget.getLocationAtOffset(widgetOffset).x;
} else {
x= bounds.x;
}
int y= bounds.y;

isEndOfLine= false;
// When line text has line header annotation, there is a space on the top, adjust the y by using char height
y+= bounds.height - textWidget.getLineHeight();

Expand All @@ -265,107 +244,20 @@ protected static void drawAsLeftOf1stCharacter(LineContentAnnotation annotation,
annotation.draw(gc, textWidget, widgetOffset, length, color, x, y);
int width= annotation.getWidth();
if (width != 0) {
if (isEndOfLine) {
if (!gc.getClipping().contains(x, y)) {
// The draw of mining is not inside the gc clipping, redraw the area which contains the mining to draw.
Rectangle client= textWidget.getClientArea();
textWidget.redraw(x, y, client.width, bounds.height, false);
}
} else {
// Get size of the character where GlyphMetrics width is added
Point charBounds= gc.stringExtent(hostCharacter);
int charWidth= charBounds.x;

// FIXME: remove this code when we need not redraw the character (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=531769)
// START TO REMOVE
annotation.setRedrawnCharacterWidth(charWidth);
// END TO REMOVE

// Annotation takes place, add GlyphMetrics width to the style
StyleRange newStyle= annotation.updateStyle(style, gc.getFontMetrics(), textWidget.getData() instanceof ITextViewer viewer ? viewer : annotation.getViewer());
if (newStyle != null) {
textWidget.setStyleRange(newStyle);
return;
}

// FIXME: remove this code when we need not redraw the character (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=531769)
// START TO REMOVE
// The inline annotation replaces one character by taking a place width
// GlyphMetrics
// Here we need to redraw this first character because GlyphMetrics clip this
// character.
int redrawnHostCharX= x + bounds.width - charWidth;
int redrawnHostCharY= y;
gc.setForeground(textWidget.getForeground());
gc.setBackground(textWidget.getBackground());
gc.setFont(textWidget.getFont());
if (style != null) {
if (style.background != null) {
gc.setBackground(style.background);
gc.fillRectangle(redrawnHostCharX, redrawnHostCharY, charWidth + 1, bounds.height);
}
if (style.foreground != null) {
gc.setForeground(style.foreground);
}
if (style.font != null) {
gc.setFont(style.font);
}
}
if (textWidget.getSelection().x <= widgetOffset && textWidget.getSelection().y > widgetOffset) {
gc.setForeground(textWidget.getSelectionForeground());
gc.setBackground(textWidget.getSelectionBackground());
}
gc.drawString(hostCharacter, redrawnHostCharX, redrawnHostCharY, true);
// Get size of the character where GlyphMetrics width is added
Point charBounds= gc.stringExtent(hostCharacter);
int charWidth= charBounds.x;
if (charWidth == 0 && ("\r".equals(hostCharacter) || "\n".equals(hostCharacter))) { //$NON-NLS-1$ //$NON-NLS-2$
// charWidth is 0 for '\r' on font Consolas, but not on other fonts, why?
charWidth= gc.stringExtent(" ").x; //$NON-NLS-1$
}
// END TO REMOVE
} else if (style != null && style.metrics != null && style.metrics.width != 0) {
// line content annotation had an , reset it
style.metrics= null;
textWidget.setStyleRange(style);
}
} else {
textWidget.redrawRange(widgetOffset, length, true);
}
}

protected static void drawAsRightOfPreviousCharacter(LineContentAnnotation annotation, GC gc, StyledText textWidget, int widgetOffset, int length, Color color) {
StyleRange style= null;
try {
style= textWidget.getStyleRangeAtOffset(widgetOffset - 1);
} catch (Exception e) {
return;
}
if (isDeleted(annotation)) {
// 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;
textWidget.setStyleRange(style);
}
return;
}
if (gc != null) {
char hostCharacter= textWidget.getText(widgetOffset - 1, widgetOffset - 1).charAt(0);
// use gc.stringExtent instead of gc.geCharWidth because of bug 548866
int redrawnCharacterWidth= hostCharacter != '\t' ? gc.stringExtent(Character.toString(hostCharacter)).x : textWidget.getTabs() * gc.stringExtent(" ").x; //$NON-NLS-1$
Rectangle charBounds= textWidget.getTextBounds(widgetOffset - 1, widgetOffset - 1);
Rectangle annotationBounds= new Rectangle(charBounds.x + redrawnCharacterWidth, charBounds.y, annotation.getWidth(), charBounds.height);

// When line text has line header annotation, there is a space on the top, adjust the y by using char height
int verticalDrawingOffset= charBounds.height - textWidget.getLineHeight();
annotationBounds.y += verticalDrawingOffset;

// Draw the line content annotation
annotation.setLocation(annotationBounds.x, annotationBounds.y);
annotation.draw(gc, textWidget, widgetOffset, length, color, annotationBounds.x, annotationBounds.y);
int width= annotation.getWidth();
if (width != 0) {
// FIXME: remove this code when we need not redraw the character (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=531769)
// START TO REMOVE
annotation.setRedrawnCharacterWidth(redrawnCharacterWidth);
annotation.setRedrawnCharacterWidth(charWidth);
// END TO REMOVE

// Annotation takes place, add GlyphMetrics width to the style
StyleRange newStyle= annotation.updateStyle(style, gc.getFontMetrics(), InlinedAnnotationSupport.getSupport(textWidget).getViewer());
StyleRange newStyle= annotation.updateStyle(style, gc.getFontMetrics(), textWidget.getData() instanceof ITextViewer viewer ? viewer : annotation.getViewer());
if (newStyle != null) {
textWidget.setStyleRange(newStyle);
return;
Expand All @@ -377,13 +269,15 @@ protected static void drawAsRightOfPreviousCharacter(LineContentAnnotation annot
// GlyphMetrics
// Here we need to redraw this first character because GlyphMetrics clip this
// character.
int redrawnHostCharX= x + bounds.width - charWidth;
int redrawnHostCharY= y;
gc.setForeground(textWidget.getForeground());
gc.setBackground(textWidget.getBackground());
gc.setFont(textWidget.getFont());
if (style != null) {
if (style.background != null) {
gc.setBackground(style.background);
gc.fillRectangle(charBounds.x, annotationBounds.y, redrawnCharacterWidth, charBounds.height);
gc.fillRectangle(redrawnHostCharX, redrawnHostCharY, charWidth + 1, bounds.height);
}
if (style.foreground != null) {
gc.setForeground(style.foreground);
Expand All @@ -392,12 +286,11 @@ protected static void drawAsRightOfPreviousCharacter(LineContentAnnotation annot
gc.setFont(style.font);
}
}
int toRedrawCharOffset= widgetOffset - 1;
if (textWidget.getSelection().x <= toRedrawCharOffset && textWidget.getSelection().y > toRedrawCharOffset) {
if (textWidget.getSelection().x <= widgetOffset && textWidget.getSelection().y > widgetOffset) {
gc.setForeground(textWidget.getSelectionForeground());
gc.setBackground(textWidget.getSelectionBackground());
}
gc.drawString(Character.toString(hostCharacter), charBounds.x, charBounds.y + verticalDrawingOffset, true);
gc.drawString(hostCharacter, redrawnHostCharX, redrawnHostCharY, true);
// END TO REMOVE
} else if (style != null && style.metrics != null && style.metrics.width != 0) {
// line content annotation had an , reset it
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,18 +122,13 @@ StyleRange updateStyle(StyleRange style, FontMetrics fontMetrics, ITextViewer vi
if (widgetPosition == null) {
return null;
}
StyledText textWidget = viewer.getTextWidget();
boolean usePreviousChar= drawRightToPreviousChar(widgetPosition.getOffset(), textWidget);
if (width == 0 || getRedrawnCharacterWidth() == 0) {
return null;
}
int fullWidth= width + getRedrawnCharacterWidth();
if (style == null) {
style= new StyleRange();
style.start= widgetPosition.getOffset();
if (usePreviousChar) {
style.start--;
}
style.length= 1;
}
GlyphMetrics metrics= style.metrics;
Expand All @@ -157,18 +152,4 @@ StyleRange updateStyle(StyleRange style, FontMetrics fontMetrics, ITextViewer vi
style.metrics= metrics;
return style;
}

static boolean drawRightToPreviousChar(int widgetOffset, StyledText textWidget) {
return widgetOffset > 0 && widgetOffset < textWidget.getCharCount() &&
textWidget.getLineAtOffset(widgetOffset) == textWidget.getLineAtOffset(widgetOffset - 1);
}

boolean isEmptyLine(int widgetOffset, StyledText text) {
if (text.getCharCount() <= widgetOffset) { // Assuming widgetOffset >= 0
return true;
}
int line= text.getLineAtOffset(widgetOffset);
String lineStr= text.getLine(line);
return lineStr.length() == 0;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@
import java.util.concurrent.atomic.AtomicReference;

import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextViewerExtension2;
import org.eclipse.jface.text.WhitespaceCharacterPainter;
import org.eclipse.jface.text.codemining.ICodeMiningProvider;
import org.eclipse.jface.text.reconciler.DirtyRegion;
import org.eclipse.jface.text.reconciler.IReconcilingStrategy;
Expand All @@ -42,6 +44,8 @@
*/
public class CodeMiningDemo {

private static boolean showWhitespaces = false;

public static void main(String[] args) throws Exception {

Display display = new Display();
Expand All @@ -54,7 +58,13 @@ public static void main(String[] args) throws Exception {
endOfLineText.setText(endOfLineString.get());
GridDataFactory.fillDefaults().grab(true, false).applyTo(endOfLineText);

ISourceViewer sourceViewer = new SourceViewer(shell, null, SWT.V_SCROLL | SWT.BORDER);
SourceViewer sourceViewer = new SourceViewer(shell, null, SWT.V_SCROLL | SWT.BORDER);
sourceViewer.getTextWidget().setFont(JFaceResources.getTextFont());
if (showWhitespaces) {
WhitespaceCharacterPainter whitespaceCharPainter = new WhitespaceCharacterPainter(sourceViewer, true, true,
true, true, true, true, true, true, true, true, true, 100);
sourceViewer.addPainter(whitespaceCharPainter);
}
sourceViewer.setDocument(
new Document("// Type class & new keyword and see references CodeMining\n"
+ "// Name class with a number N to emulate Nms before resolving the references CodeMining\n"
Expand Down
Loading

0 comments on commit 5f67aff

Please sign in to comment.