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 + -
显示快捷键?