All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.fife.ui.rsyntaxtextarea.RSyntaxUtilities Maven / Gradle / Ivy

/*
 * 08/06/2004
 *
 * RSyntaxUtilities.java - Utility methods used by RSyntaxTextArea and its
 * views.
 * 
 * This library is distributed under a modified BSD license.  See the included
 * RSyntaxTextArea.License.txt file for details.
 */
package org.fife.ui.rsyntaxtextarea;

import java.awt.Color;
import java.awt.Container;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Toolkit;
import java.util.Map;
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 org.fife.ui.rsyntaxtextarea.folding.FoldManager;
import org.fife.ui.rtextarea.Gutter;
import org.fife.ui.rtextarea.RTextArea;
import org.fife.ui.rtextarea.RTextScrollPane;


/**
 * Utility methods used by RSyntaxTextArea 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 Character.isLetter, Character.isDigit,
	 * and Character.isWhitespace 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.
	};

	/**
	 * Used in bracket matching methods.
	 */
	private static Segment charSegment = new Segment();

	/**
	 * Used internally.
	 */
	private static final char[] JS_KEYWORD_RETURN = { 'r', 'e', 't', 'u', 'r', 'n' };


	/**
	 * Returns the rendering hints for text that will most accurately reflect
	 * those of the native windowing system.
	 *
	 * @return The rendering hints, or null if they cannot be
	 *         determined.
	 */
	public static Map getDesktopAntiAliasHints() {
		return (Map)Toolkit.getDefaultToolkit().getDesktopProperty("awt.font.desktophints");
	}


	/**
	 * Returns the color to use for the line underneath a folded region line.
	 *
	 * @param textArea The text area.
	 * @return The color to use.
	 */
	public static Color getFoldedLineBottomColor(RSyntaxTextArea textArea) {
		Color color = Color.gray;
		Gutter gutter = RSyntaxUtilities.getGutter(textArea);
		if (gutter!=null) {
			color = gutter.getFoldIndicatorForeground();
		}
		return color;
	}


	/**
	 * Returns the gutter component of the scroll pane containing a text
	 * area, if any.
	 *
	 * @param textArea The text area.
	 * @return The gutter, or null if the text area is not in
	 *         an {@link RTextScrollPane}.
	 * @see RTextScrollPane#getGutter()
	 */
	public static Gutter getGutter(RTextArea textArea) {
		Gutter gutter = null;
		Container parent = textArea.getParent();
		if (parent instanceof JViewport) {
			parent = parent.getParent();
			if (parent instanceof RTextScrollPane) {
				RTextScrollPane sp = (RTextScrollPane)parent;
				gutter = sp.getGutter(); // Should always be non-null
			}
		}
		return gutter;
	}


	/**
	 * Returns the leading whitespace of a string.
	 *
	 * @param text The String to check.
	 * @return The leading whitespace.
	 */
	public static String getLeadingWhitespace(String text) {
		int count = 0;
		int len = text.length();
		while (count=elem.getStartOffset()) && (offsp0, 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 Segment 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 p1.  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 p1.
	 * @throws BadLocationException If p0 or p1 is
	 *         not a valid location in the specified text area's document.
	 * @throws IllegalArgumentException If p0 and p1
	 *         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
	 *         -1 if there isn't a matching bracket (or the caret
	 *         isn't on a bracket).
	 */
	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 doc = (RSyntaxDocument)textArea.getDocument();
				char bracket  = doc.charAt(caretPosition);

				// 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 bracketMatch;
				boolean goForward;
				switch (bracket) {

					case '{':
					case '(':
					case '[':

						// Ensure this bracket isn't in a comment.
						map = doc.getDefaultRootElement();
						curLine = map.getElementIndex(caretPosition);
						line = map.getElement(curLine);
						start = line.getStartOffset();
						end = line.getEndOffset();
						token = doc.getTokenListForLine(curLine);
						token = RSyntaxUtilities.getTokenAtOffset(token, caretPosition);
						// All brackets are always returned as "separators."
						if (token.type!=Token.SEPARATOR) {
							return -1;
						}
						bracketMatch = bracket=='{' ? '}' : (bracket=='(' ? ')' : ']');
						goForward = true;
						break;

					case '}':
					case ')':
					case ']':

						// Ensure this bracket isn't in a comment.
						map = doc.getDefaultRootElement();
						curLine = map.getElementIndex(caretPosition);
						line = map.getElement(curLine);
						start = line.getStartOffset();
						end = line.getEndOffset();
						token = doc.getTokenListForLine(curLine);
						token = RSyntaxUtilities.getTokenAtOffset(token, caretPosition);
						// All brackets are always returned as "separators."
						if (token.type!=Token.SEPARATOR) {
							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) {

						doc.getText(start,end-start, charSegment);
						int segOffset = charSegment.offset;

						for (int i=segOffset; i




© 2015 - 2024 Weber Informatics LLC | Privacy Policy