rsyntaxutilities.java
来自「具有不同语法高亮的编辑器实例」· Java 代码 · 共 895 行 · 第 1/3 页
JAVA
895 行
/*
* 08/06/2004
*
* RSyntaxUtilities.java - Utility methods used by RSyntaxTextArea and its
* views.
* Copyright (C) 2004 Robert Futrell
* email@address.com
* www.website.com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.fife.ui.rsyntaxtextarea;
import java.awt.FontMetrics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import javax.swing.*;
import javax.swing.text.BadLocationException;
import javax.swing.text.Caret;
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;
/**
* Utility methods used by <code>RSyntaxTextArea</code> and its associated
* classes.
*
* @author Robert Futrell
* @version 0.2
*/
public class RSyntaxUtilities implements SwingConstants {
private static final int DIGIT_MASK = 1;
private static final int LETTER_MASK = 2;
private static final int WHITESPACE_MASK = 4;
private static final int UPPER_CASE_MASK = 8;
private static final int HEX_CHARACTER_MASK = 16;
private static final int LETTER_OR_DIGIT_MASK = 32;
private static final int BRACKET_MASK = 64;
private static final int JAVA_OPERATOR_MASK = 128;
/**
* A lookup table used to quickly decide if a 16-bit Java char is a
* US-ASCII letter (A-Z or a-z), a digit, a whitespace char (either space
* (0x0020) or tab (0x0009)), etc. This method should be faster
* than <code>Character.isLetter</code>, <code>Character.isDigit</code>,
* and <code>Character.isWhitespace</code> because we know we are dealing
* with ASCII chars and so don't have to worry about code planes, etc.
*/
private static final int[] dataTable = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, // 0-15
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16-31
4, 128, 0, 0, 0, 128, 128, 0, 64, 64, 128, 128, 0, 128, 0, 128, // 32-47
49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 128, 0, 128, 128, 128, 128, // 48-63
0, 58, 58, 58, 58, 58, 58, 42, 42, 42, 42, 42, 42, 42, 42, 42, // 64-79
42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 64, 0, 64, 128, 0, // 80-95
0, 50, 50, 50, 50, 50, 50, 34, 34, 34, 34, 34, 34, 34, 34, 34, // 96-111
34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 64, 128, 64, 128, 0, // 112-127
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 128-143
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 144-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 160-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 176-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 192-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 208-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 224-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 240-255.
};
/*****************************************************************************/
/**
* Returns the bounding box (in the current view) of a specified position
* in the model. This method is designed for line-wrapped views to use,
* as it allows you to specify a "starting position" in the line, from
* which the x-value is assumed to be zero. The idea is that you specify
* the first character in a physical line as <code>p0</code>, as this is
* the character where the x-pixel value is 0.
*
* @param textArea The text area containing the text.
* @param s A segment in which to load the line. This is passed in so we
* don't have to reallocate a new <code>Segment</code> for each
* call.
* @param p0 The starting position in the physical line in the document.
* @param p1 The position for which to get the bounding box in the view.
* @param e How to expand tabs.
* @param rect The rectangle whose x- and width-values are changed to
* represent the bounding box of <code>p1</code>. This is
* reused to keep from needlessly reallocating Rectangles.
* @param x0 The x-coordinate (pixel) marking the left-hand border of the
* text. This is useful if the text area has a border, for example.
* @return The bounding box in the view of the character <code>p1</code>.
* @throws BadLocationException If <code>p0</code> or <code>p1</code> is
* not a valid location in the specified text
* area's document.
* @throws IllegalArgumentException If <code>p0</code> and <code>p1</code>
* are not on the same line.
*/
public static Rectangle getLineWidthUpTo(RSyntaxTextArea textArea,
Segment s, int p0, int p1,
TabExpander e, Rectangle rect,
int x0)
throws BadLocationException {
RSyntaxDocument doc = (RSyntaxDocument)textArea.getDocument();
// Ensure p0 and p1 are valid document positions.
if (p0<0)
throw new BadLocationException("Invalid document position", p0);
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.
Element map = doc.getDefaultRootElement();
int lineNum = map.getElementIndex(p0);
// 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)
throw new IllegalArgumentException("p0 and p1 are not on the " +
"same line (" + p0 + ", " + p1 + ").");
// Get the token list.
Token t = doc.getTokenListForLine(lineNum);
// Modify the token list 't' to begin at p0 (but still have correct
// token types, etc.), and get the x-location (in pixels) of the
// beginning of this new token list.
makeTokenListStartAt(t, p0, e, textArea, 0);
rect = t.listOffsetToView(textArea, e, p1, x0, rect);
return rect;
}
/*****************************************************************************/
/**
* Returns the location of the bracket paired with the one at the current
* caret position.
*
* @param textArea The text area.
* @return The location of the matching bracket in the document, or
* <code>-1</code> if there isn't a matching bracket (or the caret
* isn't on a bracket).
*/
private static Segment charSegment = new Segment();
public static int getMatchingBracketPosition(RSyntaxTextArea textArea) {
try {
// Actually position just BEFORE caret.
int caretPosition = textArea.getCaretPosition() - 1;
if (caretPosition>-1) {
// Some variables that will be used later.
Token token;
Element map;
int curLine;
Element line;
int start, end;
RSyntaxDocument document = (RSyntaxDocument)textArea.getDocument();
document.getText(caretPosition,1, charSegment);
// First, see if the previous char was a bracket
// ('{', '}', '(', ')', '[', ']').
// If it was, then make sure this bracket isn't sitting in
// the middle of a comment or string. If it isn't, then
// initialize some stuff so we can continue on.
char bracket = charSegment.array[charSegment.offset];
char bracketMatch;
boolean goForward;
switch (bracket) {
case '{':
case '(':
case '[':
// Ensure this bracket isn't in a comment.
map = document.getDefaultRootElement();
curLine = map.getElementIndex(caretPosition);
line = map.getElement(curLine);
start = line.getStartOffset();
end = line.getEndOffset();
token = document.getTokenListForLine(curLine);
token = RSyntaxUtilities.getTokenAtOffset(token, caretPosition);
if (token.type!=Token.SEPARATOR) // All brackets are always returned as "sepatarors."
return -1;
bracketMatch = bracket=='{' ? '}' : (bracket=='(' ? ')' : ']');
goForward = true;
break;
case '}':
case ')':
case ']':
// Ensure this bracket isn't in a comment.
map = document.getDefaultRootElement();
curLine = map.getElementIndex(caretPosition);
line = map.getElement(curLine);
start = line.getStartOffset();
end = line.getEndOffset();
token = document.getTokenListForLine(curLine);
token = RSyntaxUtilities.getTokenAtOffset(token, caretPosition);
if (token.type!=Token.SEPARATOR) // All brackets are always returned as "sepatarors."
return -1;
bracketMatch = bracket=='}' ? '{' : (bracket==')' ? '(' : '[');
goForward = false;
break;
default:
return -1;
}
if (goForward) {
int lastLine = map.getElementCount();
// Start just after the found bracket since we're sure
// we're not in a comment.
start = caretPosition + 1;
int numEmbedded = 0;
boolean haveTokenList = false;
while (true) {
document.getText(start,end-start, charSegment);
int segOffset = charSegment.offset;
for (int i=segOffset; i<segOffset+charSegment.count; i++) {
char ch = charSegment.array[i];
if (ch==bracket) {
if (haveTokenList==false) {
token = document.getTokenListForLine(curLine);
haveTokenList = true;
}
token = RSyntaxUtilities.getTokenAtOffset(token, start+(i-segOffset));
if (token.type==Token.SEPARATOR)
numEmbedded++;
}
else if (ch==bracketMatch) {
if (haveTokenList==false) {
token = document.getTokenListForLine(curLine);
haveTokenList = true;
}
int offset = start + (i-segOffset);
token = RSyntaxUtilities.getTokenAtOffset(token, offset);
if (token.type==Token.SEPARATOR) {
if (numEmbedded==0)
return offset;
numEmbedded--;
}
}
} // End of for (int i=segOffset; i<segOffset+charSegment.count; i++).
// Bail out if we've gone through all lines and
// haven't found the match.
if (++curLine==lastLine)
return -1;
// Otherwise, go through the next line.
haveTokenList = false;
line = map.getElement(curLine);
start = line.getStartOffset();
end = line.getEndOffset();
} // End of while (true).
} // End of if (goForward).
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?