diff --git a/RSyntaxTextArea/src/main/java/org/fife/ui/rsyntaxtextarea/RSyntaxUtilities.java b/RSyntaxTextArea/src/main/java/org/fife/ui/rsyntaxtextarea/RSyntaxUtilities.java
index aef8441d4..d2b188812 100755
--- a/RSyntaxTextArea/src/main/java/org/fife/ui/rsyntaxtextarea/RSyntaxUtilities.java
+++ b/RSyntaxTextArea/src/main/java/org/fife/ui/rsyntaxtextarea/RSyntaxUtilities.java
@@ -371,7 +371,7 @@ else if (p1>doc.getLength()) {
// token types, etc.), and get the x-location (in pixels) of the
// beginning of this new token list.
TokenSubList subList = TokenUtils.getSubTokenList(t, p0, e, textArea,
- 0, TEMP_TOKEN);
+ 0, TEMP_TOKEN, false, 0);
t = subList.tokenList;
rect = t.listOffsetToView(textArea, e, p1, x0, rect);
diff --git a/RSyntaxTextArea/src/main/java/org/fife/ui/rsyntaxtextarea/Token.java b/RSyntaxTextArea/src/main/java/org/fife/ui/rsyntaxtextarea/Token.java
index b40117668..765c09b0c 100755
--- a/RSyntaxTextArea/src/main/java/org/fife/ui/rsyntaxtextarea/Token.java
+++ b/RSyntaxTextArea/src/main/java/org/fife/ui/rsyntaxtextarea/Token.java
@@ -301,6 +301,19 @@ int getOffsetBeforeX(RSyntaxTextArea textArea, TabExpander e,
*/
float getWidth(RSyntaxTextArea textArea, TabExpander e, float x0);
+ /**
+ * Returns the width of this token given the specified parameters.
+ *
+ * @param textArea The text area in which the token is being painted.
+ * @param e Describes how to expand tabs. This parameter cannot be
+ * null.
+ * @param x0 The pixel-location at which the token begins. This is needed
+ * because of tabs.
+ * @param maxWidth the maximum width we care about measuring
+ * @return The width of the token, in pixels.
+ * @see #getWidthUpTo
+ */
+ float getWidth(RSyntaxTextArea textArea, TabExpander e, float x0, float maxWidth);
/**
* Returns the width of a specified number of characters in this token.
@@ -318,6 +331,23 @@ int getOffsetBeforeX(RSyntaxTextArea textArea, TabExpander e,
float getWidthUpTo(int numChars, RSyntaxTextArea textArea,
TabExpander e, float x0);
+ /**
+ * Returns the width of a specified number of characters in this token.
+ * For example, for the token "while", specifying a value of 3
+ * here returns the width of the "whi" portion of the token.
+ *
+ * @param numChars The number of characters for which to get the width.
+ * @param textArea The text area in which the token is being painted.
+ * @param e How to expand tabs. This value cannot be null.
+ * @param x0 The pixel-location at which this token begins. This is needed
+ * because of tabs.
+ * @param maxWidth the maximum width we care about measuring
+ * @return The width of the specified number of characters in this token.
+ * @see #getWidth
+ */
+ float getWidthUpTo(int numChars, RSyntaxTextArea textArea,
+ TabExpander e, float x0, float maxWidth);
+
/**
* Returns whether this token's lexeme matches a specific character array.
diff --git a/RSyntaxTextArea/src/main/java/org/fife/ui/rsyntaxtextarea/TokenImpl.java b/RSyntaxTextArea/src/main/java/org/fife/ui/rsyntaxtextarea/TokenImpl.java
index 2a3b12533..c7e5c9a39 100755
--- a/RSyntaxTextArea/src/main/java/org/fife/ui/rsyntaxtextarea/TokenImpl.java
+++ b/RSyntaxTextArea/src/main/java/org/fife/ui/rsyntaxtextarea/TokenImpl.java
@@ -570,6 +570,10 @@ public float getWidth(RSyntaxTextArea textArea, TabExpander e, float x0) {
return getWidthUpTo(textCount, textArea, e, x0);
}
+ @Override
+ public float getWidth(RSyntaxTextArea textArea, TabExpander e, float x0, float maxWidth) {
+ return getWidthUpTo(textCount, textArea, e, x0, maxWidth);
+ }
@Override
public float getWidthUpTo(int numChars, RSyntaxTextArea textArea,
@@ -603,6 +607,51 @@ public float getWidthUpTo(int numChars, RSyntaxTextArea textArea,
return width - x0;
}
+ @Override
+ public float getWidthUpTo(int numChars, RSyntaxTextArea textArea,
+ TabExpander e, float x0, float maxWidth) {
+ float width = x0;
+ FontMetrics fm = textArea.getFontMetricsForTokenType(getType());
+ if (fm != null) {
+ int w;
+ int currentStart = textOffset;
+ final int endBefore = textOffset + numChars;
+ for (int i = currentStart; i < endBefore; i++) {
+ if (text[i] == '\t') {
+ // Since TokenMaker implementations usually group all
+ // adjacent whitespace into a single token, there
+ // aren't usually any characters to compute a width
+ // for here, so we check before calling.
+ w = i - currentStart;
+ if (w > 0) {
+ width += fm.charsWidth(text, currentStart, w);
+ }
+ currentStart = i + 1;
+ width = e.nextTabStop(width, 0);
+
+ if (width - x0 >= maxWidth) {
+ return maxWidth;
+ }
+ } else if ((i - currentStart) > 50) {
+ w = i - currentStart + 1;
+ width += fm.charsWidth(text, currentStart, w);
+ currentStart = i + 1;
+
+ if (width - x0 >= maxWidth) {
+ return maxWidth;
+ }
+ }
+ }
+
+ // Most (non-whitespace) tokens will have characters at this
+ // point to get the widths for, so we don't check for w>0 (mini-
+ // optimization).
+ w = endBefore - currentStart;
+ width += fm.charsWidth(text, currentStart, w);
+ }
+ return width - x0;
+ }
+
@Override
public int hashCode() {
diff --git a/RSyntaxTextArea/src/main/java/org/fife/ui/rsyntaxtextarea/TokenUtils.java b/RSyntaxTextArea/src/main/java/org/fife/ui/rsyntaxtextarea/TokenUtils.java
index 9e12062b9..8b2a4904e 100755
--- a/RSyntaxTextArea/src/main/java/org/fife/ui/rsyntaxtextarea/TokenUtils.java
+++ b/RSyntaxTextArea/src/main/java/org/fife/ui/rsyntaxtextarea/TokenUtils.java
@@ -62,7 +62,7 @@ public static TokenSubList getSubTokenList(Token tokenList, int pos,
TabExpander e,
final RSyntaxTextArea textArea,
float x0) {
- return getSubTokenList(tokenList, pos, e, textArea, x0, null);
+ return getSubTokenList(tokenList, pos, e, textArea, x0, null, true, Float.MAX_VALUE);
}
@@ -100,6 +100,8 @@ public static TokenSubList getSubTokenList(Token tokenList, int pos,
* @param tempToken A temporary token to use when creating the token list
* result. This may be null but callers can pass in
* a "buffer" token for performance if desired.
+ * @param careAboutWidth whether we need to know the width or the result or not
+ * @param maxWidth the maximum width we care about measuring, if we care about width
* @return Information about the "sub" token list. This will be
* null if pos was not a valid offset
* into the token list.
@@ -109,7 +111,10 @@ public static TokenSubList getSubTokenList(Token tokenList, int pos,
TabExpander e,
final RSyntaxTextArea textArea,
float x0,
- TokenImpl tempToken) {
+ TokenImpl tempToken,
+ boolean careAboutWidth,
+ float maxWidth) {
+
if (tempToken==null) {
tempToken = new TokenImpl();
@@ -119,7 +124,9 @@ public static TokenSubList getSubTokenList(Token tokenList, int pos,
// Loop through the token list until you find the one that contains
// pos. Remember the cumulative width of all of these tokens.
while (t!=null && t.isPaintable() && !t.containsPosition(pos)) {
- x0 += t.getWidth(textArea, e, x0);
+ if (careAboutWidth) {
+ x0 += t.getWidth(textArea, e, x0, maxWidth);
+ }
t = t.getNextToken();
}
@@ -129,7 +136,9 @@ public static TokenSubList getSubTokenList(Token tokenList, int pos,
if (t.getOffset()!=pos) {
// Number of chars between p0 and token start.
int difference = pos - t.getOffset();
- x0 += t.getWidthUpTo(t.length()-difference+1, textArea, e, x0);
+ if (careAboutWidth) {
+ x0 += t.getWidthUpTo(t.length()-difference+1, textArea, e, x0, maxWidth);
+ }
tempToken.copyFrom(t);
tempToken.makeStartAt(pos);
diff --git a/RSyntaxTextArea/src/main/java/org/fife/ui/rsyntaxtextarea/WrappedSyntaxView.java b/RSyntaxTextArea/src/main/java/org/fife/ui/rsyntaxtextarea/WrappedSyntaxView.java
index ef82a64a0..ca8fb2fd1 100755
--- a/RSyntaxTextArea/src/main/java/org/fife/ui/rsyntaxtextarea/WrappedSyntaxView.java
+++ b/RSyntaxTextArea/src/main/java/org/fife/ui/rsyntaxtextarea/WrappedSyntaxView.java
@@ -18,6 +18,9 @@
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.Shape;
+import java.util.Map.Entry;
+import java.util.NavigableMap;
+import java.util.TreeMap;
import javax.swing.event.DocumentEvent;
import javax.swing.text.BadLocationException;
@@ -124,7 +127,7 @@ protected int calculateBreakPosition(int p0, Token tokenList, float x0) {
// FIXME: Replace the code below with the commented-out line below. This will
// allow long tokens to be broken at embedded spaces (such as MLC's). But it
// currently throws BadLocationExceptions sometimes...
- float tokenWidth = t.getWidth(textArea, this, x0);
+ float tokenWidth = t.getWidth(textArea, this, x0, currentWidth + 1);
if (tokenWidth>currentWidth) {
// If the current token alone is too long for this line,
// break at a character boundary.
@@ -212,7 +215,7 @@ private void childAllocation2(int line, int y, Rectangle alloc) {
* @param fontHeight The height of the font being used.
* @param y The y-coordinate at which to begin painting.
*/
- protected void drawView(TokenPainter painter, Graphics2D g, Rectangle r,
+ protected void drawView(TokenPainter painter, Graphics2D g, Rectangle r, Rectangle clip,
View view, int fontHeight, int y, int line) {
float x = r.x;
@@ -246,11 +249,17 @@ protected void drawView(TokenPainter painter, Graphics2D g, Rectangle r,
int p = calculateBreakPosition(p0, token, x);
x = r.x;
- h.paintLayeredHighlights(g, p0,p, r, host, this);
+ /* NB: for text drawing y is the baseline */
+ final boolean paintThisLine = (y >= clip.getMinY());
+ if (paintThisLine) {
+ h.paintLayeredHighlights(g, p0,p, r, host, this);
+ }
while (token!=null && token.isPaintable() && token.getEndOffset()-1
clip.getMaxY()) { + break; + } + y += fontHeight; } // End of while (token!=null && token.isPaintable()). @@ -301,7 +320,7 @@ protected void drawView(TokenPainter painter, Graphics2D g, Rectangle r, * @param selEnd The end of the selection. */ protected void drawViewWithSelection(TokenPainter painter, Graphics2D g, - Rectangle r, View view, int fontHeight, int y, int selStart, + Rectangle r, Rectangle clip, View view, int fontHeight, int y, int selStart, int selEnd) { float x = r.x; @@ -336,66 +355,71 @@ protected void drawViewWithSelection(TokenPainter painter, Graphics2D g, int p = calculateBreakPosition(p0, token, x); x = r.x; - h.paintLayeredHighlights(g, p0,p, r, host, this); + /* NB: for text drawing y is the baseline */ + final boolean paintThisLine = (y >= clip.getMinY()); - while (token!=null && token.isPaintable() && token.getEndOffset()-1
token.getOffset()) {
+ tempToken.copyFrom(token);
+ tempToken.textCount = selStart - tempToken.getOffset();
+ x = painter.paint(tempToken,g,x,y,host, this);
+ tempToken.textCount = token.length();
+ tempToken.makeStartAt(selStart);
+ // Clone required since token and tempToken must be
+ // different tokens for else statement below
+ token = new TokenImpl(tempToken);
+ }
+ int selCount = Math.min(token.length(), selEnd-token.getOffset());
+ if (selCount==token.length()) {
+ x = painter.paintSelected(token, g, x,y, host, this,
+ useSTC);
+ }
+ else {
+ tempToken.copyFrom(token);
+ tempToken.textCount = selCount;
+ x = painter.paintSelected(tempToken, g, x,y, host, this,
+ useSTC);
+ tempToken.textCount = token.length();
+ tempToken.makeStartAt(token.getOffset() + selCount);
+ token = tempToken;
+ x = painter.paint(token, g, x,y, host, this);
+ }
- if (selStart>token.getOffset()) {
- tempToken.copyFrom(token);
- tempToken.textCount = selStart - tempToken.getOffset();
- x = painter.paint(tempToken,g,x,y,host, this);
- tempToken.textCount = token.length();
- tempToken.makeStartAt(selStart);
- // Clone required since token and tempToken must be
- // different tokens for else statement below
- token = new TokenImpl(tempToken);
}
- int selCount = Math.min(token.length(), selEnd-token.getOffset());
- if (selCount==token.length()) {
- x = painter.paintSelected(token, g, x,y, host, this,
- useSTC);
- }
- else {
+ // Selection ends in this token
+ else if (token.containsPosition(selEnd)) {
tempToken.copyFrom(token);
- tempToken.textCount = selCount;
+ tempToken.textCount = selEnd - tempToken.getOffset();
x = painter.paintSelected(tempToken, g, x,y, host, this,
useSTC);
tempToken.textCount = token.length();
- tempToken.makeStartAt(token.getOffset() + selCount);
+ tempToken.makeStartAt(selEnd);
token = tempToken;
x = painter.paint(token, g, x,y, host, this);
}
- }
-
- // Selection ends in this token
- else if (token.containsPosition(selEnd)) {
- tempToken.copyFrom(token);
- tempToken.textCount = selEnd - tempToken.getOffset();
- x = painter.paintSelected(tempToken, g, x,y, host, this,
- useSTC);
- tempToken.textCount = token.length();
- tempToken.makeStartAt(selEnd);
- token = tempToken;
- x = painter.paint(token, g, x,y, host, this);
- }
-
- // This token is entirely selected
- else if (token.getOffset()>=selStart &&
- token.getEndOffset()<=selEnd) {
- x = painter.paintSelected(token, g, x,y, host, this,useSTC);
- }
+ // This token is entirely selected
+ else if (token.getOffset()>=selStart &&
+ token.getEndOffset()<=selEnd) {
+ x = painter.paintSelected(token, g, x,y, host, this,useSTC);
+ }
- // This token is entirely unselected
- else {
- x = painter.paint(token, g, x,y, host, this);
+ // This token is entirely unselected
+ else {
+ x = painter.paint(token, g, x,y, host, this);
+ }
}
-
token = token.getNextToken();
}
@@ -409,59 +433,61 @@ else if (token.getOffset()>=selStart &&
tokenOffset, token.getType(), token.getLanguageIndex());
token.setLanguageIndex(token.getLanguageIndex());
- // Selection starts in this token
- if (token.containsPosition(selStart)) {
+ if (paintThisLine) {
+ // Selection starts in this token
+ if (token.containsPosition(selStart)) {
+
+ if (selStart>token.getOffset()) {
+ tempToken.copyFrom(token);
+ tempToken.textCount = selStart - tempToken.getOffset();
+ x = painter.paint(tempToken,g,x,y,host, this);
+ tempToken.textCount = token.length();
+ tempToken.makeStartAt(selStart);
+ // Clone required since token and tempToken must be
+ // different tokens for else statement below
+ token = new TokenImpl(tempToken);
+ }
- if (selStart>token.getOffset()) {
- tempToken.copyFrom(token);
- tempToken.textCount = selStart - tempToken.getOffset();
- x = painter.paint(tempToken,g,x,y,host, this);
- tempToken.textCount = token.length();
- tempToken.makeStartAt(selStart);
- // Clone required since token and tempToken must be
- // different tokens for else statement below
- token = new TokenImpl(tempToken);
- }
+ int selCount = Math.min(token.length(), selEnd-token.getOffset());
+ if (selCount==token.length()) {
+ x = painter.paintSelected(token, g, x,y, host, this,
+ useSTC);
+ }
+ else {
+ tempToken.copyFrom(token);
+ tempToken.textCount = selCount;
+ x = painter.paintSelected(tempToken, g, x,y, host,
+ this, useSTC);
+ tempToken.textCount = token.length();
+ tempToken.makeStartAt(token.getOffset() + selCount);
+ token = tempToken;
+ x = painter.paint(token, g, x,y, host, this);
+ }
- int selCount = Math.min(token.length(), selEnd-token.getOffset());
- if (selCount==token.length()) {
- x = painter.paintSelected(token, g, x,y, host, this,
- useSTC);
}
- else {
+
+ // Selection ends in this token
+ else if (token.containsPosition(selEnd)) {
tempToken.copyFrom(token);
- tempToken.textCount = selCount;
- x = painter.paintSelected(tempToken, g, x,y, host,
- this, useSTC);
+ tempToken.textCount = selEnd - tempToken.getOffset();
+ x = painter.paintSelected(tempToken, g, x,y, host, this,
+ useSTC);
tempToken.textCount = token.length();
- tempToken.makeStartAt(token.getOffset() + selCount);
+ tempToken.makeStartAt(selEnd);
token = tempToken;
x = painter.paint(token, g, x,y, host, this);
}
- }
-
- // Selection ends in this token
- else if (token.containsPosition(selEnd)) {
- tempToken.copyFrom(token);
- tempToken.textCount = selEnd - tempToken.getOffset();
- x = painter.paintSelected(tempToken, g, x,y, host, this,
- useSTC);
- tempToken.textCount = token.length();
- tempToken.makeStartAt(selEnd);
- token = tempToken;
- x = painter.paint(token, g, x,y, host, this);
- }
-
- // This token is entirely selected
- else if (token.getOffset()>=selStart &&
- token.getEndOffset()<=selEnd) {
- x = painter.paintSelected(token, g, x,y, host, this,useSTC);
- }
+ // This token is entirely selected
+ else if (token.getOffset()>=selStart &&
+ token.getEndOffset()<=selEnd) {
+ x = painter.paintSelected(token, g, x,y, host, this,useSTC);
+ }
- // This token is entirely unselected
- else {
- x = painter.paint(token, g, x,y, host, this);
+ // This token is entirely unselected
+ else {
+ x = painter.paint(token, g, x,y, host, this);
+ }
}
token = new TokenImpl(orig);
@@ -469,11 +495,19 @@ else if (token.getOffset()>=selStart &&
}
- // Paint parser (e.g. squiggle underline) highlights after
- // text and selection
- h.paintParserHighlights(g, p0,p, r, host, this);
+ if (paintThisLine) {
+ // Paint parser (e.g. squiggle underline) highlights after
+ // text and selection
+ h.paintParserHighlights(g, p0,p, r, host, this);
+ }
p0 = (p==p0) ? p1 : p;
+
+ /* NB: y is the baseline, so we check it before we add fontHeight */
+ if (y > clip.getMaxY()) {
+ break;
+ }
+
y += fontHeight;
} // End of while (token!=null && token.isPaintable()).
@@ -923,12 +957,12 @@ public void paint(Graphics g, Shape a) {
View view = getView(i);
if (selStart==selEnd || startOffset>=selEnd ||
endOffset