/**
   * Returns the start of the word at the given offset.
   *
   * @param textArea The text area.
   * @param offs The offset into the text area's content.
   * @return The start offset of the word.
   * @throws BadLocationException If <code>offs</code> is invalid.
   * @see #getWordEnd(RSyntaxTextArea, int)
   */
  public static int getWordStart(RSyntaxTextArea textArea, int offs) throws BadLocationException {

    Document doc = textArea.getDocument();
    Element line = getLineElem(doc, offs);
    if (line == null) {
      throw new BadLocationException("No word at " + offs, offs);
    }

    int lineStart = line.getStartOffset();
    if (offs == lineStart) { // Start of the line.
      return offs;
    }

    int endOffs = Math.min(offs + 1, doc.getLength());
    String s = doc.getText(lineStart, endOffs - lineStart);
    if (s != null && s.length() > 0) {
      int i = s.length() - 1;
      char ch = s.charAt(i);
      if (Character.isWhitespace(ch)) {
        while (i > 0 && Character.isWhitespace(s.charAt(i - 1))) {
          i--;
        }
        offs = lineStart + i;
      } else if (Character.isLetterOrDigit(ch)) {
        while (i > 0 && Character.isLetterOrDigit(s.charAt(i - 1))) {
          i--;
        }
        offs = lineStart + i;
      }
    }

    return offs;
  }
  /**
   * Returns the end of the word at the given offset.
   *
   * @param textArea The text area.
   * @param offs The offset into the text area's content.
   * @return The end offset of the word.
   * @throws BadLocationException If <code>offs</code> is invalid.
   * @see #getWordStart(RSyntaxTextArea, int)
   */
  public static int getWordEnd(RSyntaxTextArea textArea, int offs) throws BadLocationException {

    Document doc = textArea.getDocument();
    int endOffs = textArea.getLineEndOffsetOfCurrentLine();
    int lineEnd = Math.min(endOffs, doc.getLength());
    if (offs == lineEnd) { // End of the line.
      return offs;
    }

    String s = doc.getText(offs, lineEnd - offs - 1);
    if (s != null && s.length() > 0) { // Should always be true
      int i = 0;
      int count = s.length();
      char ch = s.charAt(i);
      if (Character.isWhitespace(ch)) {
        while (i < count && Character.isWhitespace(s.charAt(i++))) ;
      } else if (Character.isLetterOrDigit(ch)) {
        while (i < count && Character.isLetterOrDigit(s.charAt(i++))) ;
      } else {
        i = 2;
      }
      offs += i - 1;
    }

    return offs;
  }
  /**
   * 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 template that should be inserted at the current caret position, assuming the
  * trigger character was pressed.
  *
  * @param textArea The text area that's getting text inserted into it.
  * @return A template that should be inserted, if appropriate, or <code>null</code> if no template
  *     should be inserted.
  */
 public CodeTemplate getTemplate(RSyntaxTextArea textArea) {
   int caretPos = textArea.getCaretPosition();
   int charsToGet = Math.min(caretPos, maxTemplateIDLength);
   try {
     Document doc = textArea.getDocument();
     doc.getText(caretPos - charsToGet, charsToGet, s);
     int index = Collections.binarySearch(templates, s, comparator);
     return index >= 0 ? (CodeTemplate) templates.get(index) : null;
   } catch (BadLocationException ble) {
     ble.printStackTrace();
     throw new InternalError("Error in CodeTemplateManager");
   }
 }
    @Override
    public void actionPerformedImpl(ActionEvent e, RTextArea textArea) {

      if (!textArea.isEditable() || !textArea.isEnabled()) {
        UIManager.getLookAndFeel().provideErrorFeedback(textArea);
        return;
      }

      RSyntaxTextArea rsta = (RSyntaxTextArea) getTextComponent(e);
      RSyntaxDocument doc = (RSyntaxDocument) rsta.getDocument();

      int line = textArea.getCaretLineNumber();
      int type = doc.getLastTokenTypeOnLine(line);
      if (type < 0) {
        type = doc.getClosestStandardTokenTypeForInternalType(type);
      }

      // Only in MLC's should we try this
      if (type == Token.COMMENT_DOCUMENTATION || type == Token.COMMENT_MULTILINE) {
        insertBreakInMLC(e, rsta, line);
      } else {
        handleInsertBreak(rsta, true);
      }
    }
  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 < segOffset + charSegment.count; i++) {

              char ch = charSegment.array[i];

              if (ch == bracket) {
                if (haveTokenList == false) {
                  token = doc.getTokenListForLine(curLine);
                  haveTokenList = true;
                }
                int offset = start + (i - segOffset);
                token = RSyntaxUtilities.getTokenAtOffset(token, offset);
                if (token.type == Token.SEPARATOR) numEmbedded++;
              } else if (ch == bracketMatch) {
                if (haveTokenList == false) {
                  token = doc.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).

        // Otherwise, we're going backward through the file
        // (since we found '}', ')' or ']').
        else { // goForward==false

          // End just before the found bracket since we're sure
          // we're not in a comment.
          end = caretPosition; // - 1;
          int numEmbedded = 0;
          boolean haveTokenList = false;
          Token t2;

          while (true) {

            doc.getText(start, end - start, charSegment);
            int segOffset = charSegment.offset;
            int iStart = segOffset + charSegment.count - 1;

            for (int i = iStart; i >= segOffset; i--) {

              char ch = charSegment.array[i];

              if (ch == bracket) {
                if (haveTokenList == false) {
                  token = doc.getTokenListForLine(curLine);
                  haveTokenList = true;
                }
                int offset = start + (i - segOffset);
                t2 = RSyntaxUtilities.getTokenAtOffset(token, offset);
                if (t2.type == Token.SEPARATOR) numEmbedded++;
              } else if (ch == bracketMatch) {
                if (haveTokenList == false) {
                  token = doc.getTokenListForLine(curLine);
                  haveTokenList = true;
                }
                int offset = start + (i - segOffset);
                t2 = RSyntaxUtilities.getTokenAtOffset(token, offset);
                if (t2.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 == -1) return -1;

            // Otherwise, get ready for going through the
            // next line.
            haveTokenList = false;
            line = map.getElement(curLine);
            start = line.getStartOffset();
            end = line.getEndOffset();
          } // End of while (true).
        } // End of else.
      } // End of if (caretPosition>-1).

    } catch (BadLocationException ble) {
      // Shouldn't ever happen.
      ble.printStackTrace();
    }

    // Something went wrong...
    return -1;
  }