|
|
|
@ -9,6 +9,24 @@
|
|
|
|
|
*/ |
|
|
|
|
package com.fr.design.gui.syntax.ui.rsyntaxtextarea; |
|
|
|
|
|
|
|
|
|
import com.fr.design.gui.syntax.ui.rsyntaxtextarea.TokenUtils.TokenSubList; |
|
|
|
|
import com.fr.design.gui.syntax.ui.rsyntaxtextarea.folding.FoldManager; |
|
|
|
|
import com.fr.design.gui.syntax.ui.rtextarea.Gutter; |
|
|
|
|
import com.fr.design.gui.syntax.ui.rtextarea.RTextArea; |
|
|
|
|
import com.fr.design.gui.syntax.ui.rtextarea.RTextScrollPane; |
|
|
|
|
|
|
|
|
|
import javax.swing.JLabel; |
|
|
|
|
import javax.swing.JViewport; |
|
|
|
|
import javax.swing.SwingConstants; |
|
|
|
|
import javax.swing.UIManager; |
|
|
|
|
import javax.swing.text.BadLocationException; |
|
|
|
|
import javax.swing.text.Caret; |
|
|
|
|
import javax.swing.text.Document; |
|
|
|
|
import javax.swing.text.Element; |
|
|
|
|
import javax.swing.text.Position; |
|
|
|
|
import javax.swing.text.Segment; |
|
|
|
|
import javax.swing.text.TabExpander; |
|
|
|
|
import javax.swing.text.View; |
|
|
|
|
import java.awt.Color; |
|
|
|
|
import java.awt.Container; |
|
|
|
|
import java.awt.Point; |
|
|
|
@ -16,23 +34,9 @@ import java.awt.Rectangle;
|
|
|
|
|
import java.awt.Shape; |
|
|
|
|
import java.awt.Toolkit; |
|
|
|
|
import java.util.Map; |
|
|
|
|
import java.util.Objects; |
|
|
|
|
import java.util.regex.Pattern; |
|
|
|
|
import java.util.regex.PatternSyntaxException; |
|
|
|
|
import javax.swing.*; |
|
|
|
|
import javax.swing.text.BadLocationException; |
|
|
|
|
import javax.swing.text.Caret; |
|
|
|
|
import javax.swing.text.Document; |
|
|
|
|
import javax.swing.text.Element; |
|
|
|
|
import javax.swing.text.Position; |
|
|
|
|
import javax.swing.text.Segment; |
|
|
|
|
import javax.swing.text.TabExpander; |
|
|
|
|
import javax.swing.text.View; |
|
|
|
|
|
|
|
|
|
import com.fr.design.gui.syntax.ui.rsyntaxtextarea.TokenUtils.TokenSubList; |
|
|
|
|
import com.fr.design.gui.syntax.ui.rsyntaxtextarea.folding.FoldManager; |
|
|
|
|
import com.fr.design.gui.syntax.ui.rtextarea.Gutter; |
|
|
|
|
import com.fr.design.gui.syntax.ui.rtextarea.RTextArea; |
|
|
|
|
import com.fr.design.gui.syntax.ui.rtextarea.RTextScrollPane; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
@ -163,8 +167,7 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
case ' ': |
|
|
|
|
if (inPreBlock || !lastWasSpace) { |
|
|
|
|
sb.append(' '); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
} else { |
|
|
|
|
sb.append(" "); |
|
|
|
|
} |
|
|
|
|
lastWasSpace = true; |
|
|
|
@ -361,10 +364,11 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
RSyntaxDocument doc = (RSyntaxDocument) textArea.getDocument(); |
|
|
|
|
|
|
|
|
|
// Ensure p0 and p1 are valid document positions.
|
|
|
|
|
if (p0<0) |
|
|
|
|
if (p0 < 0) { |
|
|
|
|
throw new BadLocationException("Invalid document position", p0); |
|
|
|
|
else if (p1>doc.getLength()) |
|
|
|
|
} else if (p1 > doc.getLength()) { |
|
|
|
|
throw new BadLocationException("Invalid document position", p1); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Ensure p0 and p1 are in the same line, and get the start/end
|
|
|
|
|
// offsets for that line.
|
|
|
|
@ -373,9 +377,10 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
// We do ">1" because p1 might be the first position on the next line
|
|
|
|
|
// or the last position on the previous one.
|
|
|
|
|
// if (lineNum!=map.getElementIndex(p1))
|
|
|
|
|
if (Math.abs(lineNum-map.getElementIndex(p1))>1) |
|
|
|
|
if (Math.abs(lineNum - map.getElementIndex(p1)) > 1) { |
|
|
|
|
throw new IllegalArgumentException("p0 and p1 are not on the " + |
|
|
|
|
"same line (" + p0 + ", " + p1 + ")."); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Get the token list.
|
|
|
|
|
Token t = doc.getTokenListForLine(lineNum); |
|
|
|
@ -455,14 +460,13 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
Token token = doc.getTokenListForLine(curLine); |
|
|
|
|
token = RSyntaxUtilities.getTokenAtOffset(token, caretPosition); |
|
|
|
|
// All brackets are always returned as "separators."
|
|
|
|
|
if (token.getType()!=Token.SEPARATOR) { |
|
|
|
|
if (Objects.requireNonNull(token).getType() != Token.SEPARATOR) { |
|
|
|
|
return input; |
|
|
|
|
} |
|
|
|
|
if (index < 3) { // One of "{[("
|
|
|
|
|
goForward = true; |
|
|
|
|
bracketMatch = BRACKETS.charAt(index + 3); |
|
|
|
|
} |
|
|
|
|
else { // One of ")]}"
|
|
|
|
|
} else { // One of ")]}"
|
|
|
|
|
goForward = false; |
|
|
|
|
bracketMatch = BRACKETS.charAt(index - 3); |
|
|
|
|
} |
|
|
|
@ -487,24 +491,23 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
char ch = charSegment.array[i]; |
|
|
|
|
|
|
|
|
|
if (ch == bracket) { |
|
|
|
|
if (haveTokenList==false) { |
|
|
|
|
if (!haveTokenList) { |
|
|
|
|
token = doc.getTokenListForLine(curLine); |
|
|
|
|
haveTokenList = true; |
|
|
|
|
} |
|
|
|
|
int offset = start + (i - segOffset); |
|
|
|
|
token = RSyntaxUtilities.getTokenAtOffset(token, offset); |
|
|
|
|
if (token.getType()==Token.SEPARATOR) |
|
|
|
|
if (Objects.requireNonNull(token).getType() == Token.SEPARATOR) { |
|
|
|
|
numEmbedded++; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
else if (ch==bracketMatch) { |
|
|
|
|
if (haveTokenList==false) { |
|
|
|
|
} else if (ch == bracketMatch) { |
|
|
|
|
if (!haveTokenList) { |
|
|
|
|
token = doc.getTokenListForLine(curLine); |
|
|
|
|
haveTokenList = true; |
|
|
|
|
} |
|
|
|
|
int offset = start + (i - segOffset); |
|
|
|
|
token = RSyntaxUtilities.getTokenAtOffset(token, offset); |
|
|
|
|
if (token.getType()==Token.SEPARATOR) { |
|
|
|
|
if (Objects.requireNonNull(token).getType() == Token.SEPARATOR) { |
|
|
|
|
if (numEmbedded == 0) { |
|
|
|
|
if (textArea.isCodeFoldingEnabled() && |
|
|
|
|
textArea.getFoldManager().isLineHidden(curLine)) { |
|
|
|
@ -521,8 +524,9 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
|
|
|
|
|
// Bail out if we've gone through all lines and
|
|
|
|
|
// haven't found the match.
|
|
|
|
|
if (++curLine==lastLine) |
|
|
|
|
if (++curLine == lastLine) { |
|
|
|
|
return input; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Otherwise, go through the next line.
|
|
|
|
|
haveTokenList = false; |
|
|
|
@ -557,24 +561,23 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
char ch = charSegment.array[i]; |
|
|
|
|
|
|
|
|
|
if (ch == bracket) { |
|
|
|
|
if (haveTokenList==false) { |
|
|
|
|
if (!haveTokenList) { |
|
|
|
|
token = doc.getTokenListForLine(curLine); |
|
|
|
|
haveTokenList = true; |
|
|
|
|
} |
|
|
|
|
int offset = start + (i - segOffset); |
|
|
|
|
t2 = RSyntaxUtilities.getTokenAtOffset(token, offset); |
|
|
|
|
if (t2.getType()==Token.SEPARATOR) |
|
|
|
|
if (Objects.requireNonNull(t2).getType() == Token.SEPARATOR) { |
|
|
|
|
numEmbedded++; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
else if (ch==bracketMatch) { |
|
|
|
|
if (haveTokenList==false) { |
|
|
|
|
} else if (ch == bracketMatch) { |
|
|
|
|
if (!haveTokenList) { |
|
|
|
|
token = doc.getTokenListForLine(curLine); |
|
|
|
|
haveTokenList = true; |
|
|
|
|
} |
|
|
|
|
int offset = start + (i - segOffset); |
|
|
|
|
t2 = RSyntaxUtilities.getTokenAtOffset(token, offset); |
|
|
|
|
if (t2.getType()==Token.SEPARATOR) { |
|
|
|
|
if (Objects.requireNonNull(t2).getType() == Token.SEPARATOR) { |
|
|
|
|
if (numEmbedded == 0) { |
|
|
|
|
input.setLocation(caretPosition, offset); |
|
|
|
|
return input; |
|
|
|
@ -642,7 +645,7 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
* Some views may not be visible, |
|
|
|
|
* they might not be in the same order found in the model, or they just |
|
|
|
|
* might not allow access to some of the locations in the model.<p> |
|
|
|
|
* |
|
|
|
|
* <p> |
|
|
|
|
* NOTE: You should only call this method if the passed-in |
|
|
|
|
* <code>javax.swing.text.View</code> is an instance of |
|
|
|
|
* {@link TokenOrientedView} and <code>javax.swing.text.TabExpander</code>; |
|
|
|
@ -661,8 +664,8 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
* </ul> |
|
|
|
|
* @return the location within the model that best represents the next |
|
|
|
|
* location visual position |
|
|
|
|
* @exception BadLocationException |
|
|
|
|
* @exception IllegalArgumentException if <code>direction</code> |
|
|
|
|
* @throws BadLocationException |
|
|
|
|
* @throws IllegalArgumentException if <code>direction</code> |
|
|
|
|
* doesn't have one of the legal values above |
|
|
|
|
*/ |
|
|
|
|
public static int getNextVisualPositionFrom(int pos, Position.Bias b, |
|
|
|
@ -688,29 +691,29 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
// YECK! Ideally, the x location from the magic caret
|
|
|
|
|
// position would be passed in.
|
|
|
|
|
Point mcp; |
|
|
|
|
if (c != null) |
|
|
|
|
if (c != null) { |
|
|
|
|
mcp = c.getMagicCaretPosition(); |
|
|
|
|
else |
|
|
|
|
} else { |
|
|
|
|
mcp = null; |
|
|
|
|
} |
|
|
|
|
int x; |
|
|
|
|
if (mcp == null) { |
|
|
|
|
Rectangle loc = target.modelToView(pos); |
|
|
|
|
Rectangle loc = Objects.requireNonNull(target).modelToView(pos); |
|
|
|
|
x = (loc == null) ? 0 : loc.x; |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
} else { |
|
|
|
|
x = mcp.x; |
|
|
|
|
} |
|
|
|
|
if (direction == NORTH) |
|
|
|
|
if (direction == NORTH) { |
|
|
|
|
pos = getPositionAbove(target, pos, x, (TabExpander) view); |
|
|
|
|
else |
|
|
|
|
} else { |
|
|
|
|
pos = getPositionBelow(target, pos, x, (TabExpander) view); |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case WEST: |
|
|
|
|
if (pos == -1) { |
|
|
|
|
pos = Math.max(0, view.getEndOffset() - 1); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
} else { |
|
|
|
|
pos = Math.max(0, pos - 1); |
|
|
|
|
if (target.isCodeFoldingEnabled()) { |
|
|
|
|
int last = target.getLineOfOffset(pos + 1); |
|
|
|
@ -718,7 +721,9 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
if (last != current) { // If moving up a line...
|
|
|
|
|
FoldManager fm = target.getFoldManager(); |
|
|
|
|
if (fm.isLineHidden(current)) { |
|
|
|
|
while (--current>0 && fm.isLineHidden(current)); |
|
|
|
|
while (--current > 0 && fm.isLineHidden(current)) { |
|
|
|
|
; |
|
|
|
|
} |
|
|
|
|
pos = target.getLineEndOffset(current) - 1; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -729,8 +734,7 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
case EAST: |
|
|
|
|
if (pos == -1) { |
|
|
|
|
pos = view.getStartOffset(); |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
} else { |
|
|
|
|
pos = Math.min(pos + 1, view.getDocument().getLength()); |
|
|
|
|
if (target.isCodeFoldingEnabled()) { |
|
|
|
|
int last = target.getLineOfOffset(pos - 1); |
|
|
|
@ -739,7 +743,9 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
FoldManager fm = target.getFoldManager(); |
|
|
|
|
if (fm.isLineHidden(current)) { |
|
|
|
|
int lineCount = target.getLineCount(); |
|
|
|
|
while (++current<lineCount && fm.isLineHidden(current)); |
|
|
|
|
while (++current < lineCount && fm.isLineHidden(current)) { |
|
|
|
|
; |
|
|
|
|
} |
|
|
|
|
pos = current == lineCount ? |
|
|
|
|
target.getLineEndOffset(last) - 1 : // Was the last visible line
|
|
|
|
|
target.getLineStartOffset(current); |
|
|
|
@ -770,23 +776,22 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
* @param x the X coordinate >= 0 |
|
|
|
|
* @return the position >= 0 if the request can be computed, otherwise |
|
|
|
|
* a value of -1 will be returned. |
|
|
|
|
* @exception BadLocationException if the offset is out of range |
|
|
|
|
* @throws BadLocationException if the offset is out of range |
|
|
|
|
*/ |
|
|
|
|
public static final int getPositionAbove(RSyntaxTextArea c, int offs, |
|
|
|
|
float x, TabExpander e) throws BadLocationException { |
|
|
|
|
|
|
|
|
|
TokenOrientedView tov = (TokenOrientedView) e; |
|
|
|
|
Token token = tov.getTokenListForPhysicalLineAbove(offs); |
|
|
|
|
if (token==null) |
|
|
|
|
if (token == null) { |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// A line containing only Token.NULL is an empty line.
|
|
|
|
|
else if (token.getType() == Token.NULL) { |
|
|
|
|
int line = c.getLineOfOffset(offs); // Sure to be >0 ??
|
|
|
|
|
return c.getLineStartOffset(line - 1); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
else { |
|
|
|
|
} else { |
|
|
|
|
return token.getListOffset(c, e, 0, x); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -804,15 +809,16 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
* @param x the X coordinate >= 0 |
|
|
|
|
* @return the position >= 0 if the request can be computed, otherwise |
|
|
|
|
* a value of -1 will be returned. |
|
|
|
|
* @exception BadLocationException if the offset is out of range |
|
|
|
|
* @throws BadLocationException if the offset is out of range |
|
|
|
|
*/ |
|
|
|
|
public static final int getPositionBelow(RSyntaxTextArea c, int offs, |
|
|
|
|
float x, TabExpander e) throws BadLocationException { |
|
|
|
|
|
|
|
|
|
TokenOrientedView tov = (TokenOrientedView) e; |
|
|
|
|
Token token = tov.getTokenListForPhysicalLineBelow(offs); |
|
|
|
|
if (token==null) |
|
|
|
|
if (token == null) { |
|
|
|
|
return -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// A line containing only Token.NULL is an empty line.
|
|
|
|
|
else if (token.getType() == Token.NULL) { |
|
|
|
@ -821,9 +827,7 @@ public class RSyntaxUtilities implements SwingConstants {
|
|
|
|
|
FoldManager fm = c.getFoldManager(); |
|
|
|
|
line = fm.getVisibleLineBelow(line); |
|
|
|
|
return c.getLineStartOffset(line); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
else { |
|
|
|
|
} else { |
|
|
|
|
return token.getListOffset(c, e, 0, x); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -869,9 +873,10 @@ return c.getLineStartOffset(line);
|
|
|
|
|
*/ |
|
|
|
|
public static final Token getTokenAtOffset(Token tokenList, int offset) { |
|
|
|
|
for (Token t = tokenList; t != null && t.isPaintable(); t = t.getNextToken()) { |
|
|
|
|
if (t.containsPosition(offset)) |
|
|
|
|
if (t.containsPosition(offset)) { |
|
|
|
|
return t; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -901,12 +906,14 @@ return c.getLineStartOffset(line);
|
|
|
|
|
int count = s.length(); |
|
|
|
|
char ch = s.charAt(i); |
|
|
|
|
if (Character.isWhitespace(ch)) { |
|
|
|
|
while (i<count && Character.isWhitespace(s.charAt(i++))); |
|
|
|
|
while (i < count && Character.isWhitespace(s.charAt(i++))) { |
|
|
|
|
; |
|
|
|
|
} |
|
|
|
|
else if (Character.isLetterOrDigit(ch)) { |
|
|
|
|
while (i<count && Character.isLetterOrDigit(s.charAt(i++))); |
|
|
|
|
} else if (Character.isLetterOrDigit(ch)) { |
|
|
|
|
while (i < count && Character.isLetterOrDigit(s.charAt(i++))) { |
|
|
|
|
; |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
} else { |
|
|
|
|
i = 2; |
|
|
|
|
} |
|
|
|
|
offs += i - 1; |
|
|
|
@ -949,8 +956,7 @@ return c.getLineStartOffset(line);
|
|
|
|
|
i--; |
|
|
|
|
} |
|
|
|
|
offs = lineStart + i; |
|
|
|
|
} |
|
|
|
|
else if (Character.isLetterOrDigit(ch)) { |
|
|
|
|
} else if (Character.isLetterOrDigit(ch)) { |
|
|
|
|
while (i > 0 && Character.isLetterOrDigit(s.charAt(i - 1))) { |
|
|
|
|
i--; |
|
|
|
|
} |
|
|
|
@ -968,7 +974,7 @@ return c.getLineStartOffset(line);
|
|
|
|
|
* Determines the width of the given token list taking tabs |
|
|
|
|
* into consideration. This is implemented in a 1.1 style coordinate |
|
|
|
|
* system where ints are used and 72dpi is assumed.<p> |
|
|
|
|
* |
|
|
|
|
* <p> |
|
|
|
|
* This method also assumes that the passed-in token list begins at |
|
|
|
|
* x-pixel <code>0</code> in the view (for tab purposes). |
|
|
|
|
* |
|
|
|
@ -1203,8 +1209,9 @@ return c.getLineStartOffset(line);
|
|
|
|
|
// We do it this way as we'd need to do two conditions anyway (first
|
|
|
|
|
// to check that ch<255 so it can index into our table, then whether
|
|
|
|
|
// that table position has the upper-case mask).
|
|
|
|
|
if (ch>='A' && ch<='Z') |
|
|
|
|
if (ch >= 'A' && ch <= 'Z') { |
|
|
|
|
return (char) (ch | 0x20); |
|
|
|
|
} |
|
|
|
|
return ch; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -1233,15 +1240,16 @@ return c.getLineStartOffset(line);
|
|
|
|
|
String osName = System.getProperty("os.name"); |
|
|
|
|
if (osName != null) { // Should always be true.
|
|
|
|
|
osName = osName.toLowerCase(); |
|
|
|
|
if (osName.indexOf("windows") > -1) |
|
|
|
|
if (osName.contains("windows")) { |
|
|
|
|
os = OS_WINDOWS; |
|
|
|
|
else if (osName.indexOf("mac os x") > -1) |
|
|
|
|
} else if (osName.contains("mac os x")) { |
|
|
|
|
os = OS_MAC_OSX; |
|
|
|
|
else if (osName.indexOf("linux") > -1) |
|
|
|
|
} else if (osName.contains("linux")) { |
|
|
|
|
os = OS_LINUX; |
|
|
|
|
else |
|
|
|
|
} else { |
|
|
|
|
os = OS_OTHER; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return os; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -1280,12 +1288,17 @@ return c.getLineStartOffset(line);
|
|
|
|
|
sb.append('^'); |
|
|
|
|
break; |
|
|
|
|
case '\\': |
|
|
|
|
case '.': case '|': |
|
|
|
|
case '+': case '-': |
|
|
|
|
case '.': |
|
|
|
|
case '|': |
|
|
|
|
case '+': |
|
|
|
|
case '-': |
|
|
|
|
case '$': |
|
|
|
|
case '[': case ']': |
|
|
|
|
case '{': case '}': |
|
|
|
|
case '(': case ')': |
|
|
|
|
case '[': |
|
|
|
|
case ']': |
|
|
|
|
case '{': |
|
|
|
|
case '}': |
|
|
|
|
case '(': |
|
|
|
|
case ')': |
|
|
|
|
sb.append('\\').append(ch); |
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|