/**
   * 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;
  }
  /**
   * Provides a mapping from the view coordinate space to the logical coordinate space of the model.
   *
   * @param fx the X coordinate &gt;= 0
   * @param fy the Y coordinate &gt;= 0
   * @param a the allocated region to render into
   * @return the location within the model that best represents the given point in the view &gt;= 0
   */
  @Override
  public int viewToModel(float fx, float fy, Shape a, Position.Bias[] bias) {

    bias[0] = Position.Bias.Forward;

    Rectangle alloc = a.getBounds();
    RSyntaxDocument doc = (RSyntaxDocument) getDocument();
    int x = (int) fx;
    int y = (int) fy;

    // If they're asking about a view position above the area covered by
    // this view, then the position is assumed to be the starting position
    // of this view.
    if (y < alloc.y) {
      return getStartOffset();
    }

    // If they're asking about a position below this view, the position
    // is assumed to be the ending position of this view.
    else if (y > alloc.y + alloc.height) {
      return host.getLastVisibleOffset();
    }

    // They're asking about a position within the coverage of this view
    // vertically.  So, we figure out which line the point corresponds to.
    // If the line is greater than the number of lines contained, then
    // simply use the last line as it represents the last possible place
    // we can position to.
    else {

      Element map = doc.getDefaultRootElement();
      int lineIndex = Math.abs((y - alloc.y) / lineHeight); // metrics.getHeight() );
      FoldManager fm = host.getFoldManager();
      // System.out.print("--- " + lineIndex);
      lineIndex += fm.getHiddenLineCountAbove(lineIndex, true);
      // System.out.println(" => " + lineIndex);
      if (lineIndex >= map.getElementCount()) {
        return host.getLastVisibleOffset();
      }

      Element line = map.getElement(lineIndex);

      // If the point is to the left of the line...
      if (x < alloc.x) {
        return line.getStartOffset();
      } else if (x > alloc.x + alloc.width) {
        return line.getEndOffset() - 1;
      } else {
        // Determine the offset into the text
        int p0 = line.getStartOffset();
        Token tokenList = doc.getTokenListForLine(lineIndex);
        tabBase = alloc.x;
        int offs = tokenList.getListOffset((RSyntaxTextArea) getContainer(), this, tabBase, x);
        return offs != -1 ? offs : p0;
      }
    } // End of else.
  }
    /**
     * Provides a mapping from the document model coordinate space to the coordinate space of the
     * view mapped to it.
     *
     * @param pos the position to convert
     * @param a the allocated region to render into
     * @return the bounding box of the given position is returned
     * @exception BadLocationException if the given position does not represent a valid location in
     *     the associated document.
     */
    @Override
    public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException {

      // System.err.println("--- begin modelToView ---");
      Rectangle alloc = a.getBounds();
      RSyntaxTextArea textArea = (RSyntaxTextArea) getContainer();
      alloc.height = textArea.getLineHeight(); // metrics.getHeight();
      alloc.width = 1;
      int p0 = getStartOffset();
      int p1 = getEndOffset();
      int testP = (b == Position.Bias.Forward) ? pos : Math.max(p0, pos - 1);

      // Get the token list for this line so we don't have to keep
      // recomputing it if this logical line spans multiple physical
      // lines.
      RSyntaxDocument doc = (RSyntaxDocument) getDocument();
      Element map = doc.getDefaultRootElement();
      int line = map.getElementIndex(p0);
      Token tokenList = doc.getTokenListForLine(line);
      float x0 = alloc.x; // 0;

      while (p0 < p1) {
        TokenSubList subList =
            TokenUtils.getSubTokenList(
                tokenList, p0, WrappedSyntaxView.this, textArea, x0, lineCountTempToken);
        x0 = subList != null ? subList.x : x0;
        tokenList = subList != null ? subList.tokenList : null;
        int p = calculateBreakPosition(p0, tokenList, x0);
        if ((pos >= p0) && (testP < p)) { // pos < p)) {
          // it's in this line
          alloc =
              RSyntaxUtilities.getLineWidthUpTo(
                  textArea, s, p0, pos, WrappedSyntaxView.this, alloc, alloc.x);
          // System.err.println("--- end modelToView ---");
          return alloc;
        }
        // if (p == p1 && pos == p1) {
        if (p == p1 - 1 && pos == p1 - 1) {
          // Wants end.
          if (pos > p0) {
            alloc =
                RSyntaxUtilities.getLineWidthUpTo(
                    textArea, s, p0, pos, WrappedSyntaxView.this, alloc, alloc.x);
          }
          // System.err.println("--- end modelToView ---");
          return alloc;
        }

        p0 = (p == p0) ? p1 : p;
        // System.err.println("... ... Incrementing y");
        alloc.y += alloc.height;
      }

      throw new BadLocationException(null, pos);
    }
 /**
  * Returns a token list for the <i>physical</i> line above the physical line containing the
  * specified offset into the document. Note that for this plain (non-wrapped) view, this is simply
  * the token list for the logical line above the line containing <code>offset</code>, since lines
  * are not wrapped.
  *
  * @param offset The offset in question.
  * @return A token list for the physical (and in this view, logical) line before this one. If
  *     <code>offset</code> is in the first line in the document, <code>null</code> is returned.
  */
 public Token getTokenListForPhysicalLineAbove(int offset) {
   RSyntaxDocument document = (RSyntaxDocument) getDocument();
   Element map = document.getDefaultRootElement();
   int line = map.getElementIndex(offset);
   FoldManager fm = host.getFoldManager();
   if (fm == null) {
     line--;
     if (line >= 0) {
       return document.getTokenListForLine(line);
     }
   } else {
     line = fm.getVisibleLineAbove(line);
     if (line >= 0) {
       return document.getTokenListForLine(line);
     }
   }
   //		int line = map.getElementIndex(offset) - 1;
   //		if (line>=0)
   //			return document.getTokenListForLine(line);
   return null;
 }
Exemple #5
0
  @Override
  public ParseResult parse(RSyntaxDocument doc, String style) {

    Element root = doc.getDefaultRootElement();
    int lineCount = root.getElementCount();

    if (taskPattern == null || style == null || SyntaxConstants.SYNTAX_STYLE_NONE.equals(style)) {
      result.clearNotices();
      result.setParsedLines(0, lineCount - 1);
      return result;
    }

    // TODO: Pass in parsed line range and just do that
    result.clearNotices();
    result.setParsedLines(0, lineCount - 1);

    for (int line = 0; line < lineCount; line++) {

      Token t = doc.getTokenListForLine(line);
      int offs = -1;
      int start = -1;
      String text = null;

      while (t != null && t.isPaintable()) {
        if (t.isComment()) {

          offs = t.offset;
          text = t.getLexeme();

          Matcher m = taskPattern.matcher(text);
          if (m.find()) {
            start = m.start();
            offs += start;
            break;
          }
        }
        t = t.getNextToken();
      }

      if (start > -1) {
        text = text.substring(start);
        // TODO: Strip off end of MLC's if they're there.
        int len = text.length();
        TaskNotice pn = new TaskNotice(this, text, line, offs, len);
        pn.setLevel(ParserNotice.INFO);
        pn.setShowInEditor(false);
        pn.setColor(COLOR);
        result.addNotice(pn);
      }
    }

    return result;
  }
 /**
  * Returns a token list for the <i>physical</i> line below the physical line containing the
  * specified offset into the document. Note that for this plain (non-wrapped) view, this is simply
  * the token list for the logical line below the line containing <code>offset</code>, since lines
  * are not wrapped.
  *
  * @param offset The offset in question.
  * @return A token list for the physical (and in this view, logical) line after this one. If
  *     <code>offset</code> is in the last physical line in the document, <code>null</code> is
  *     returned.
  */
 public Token getTokenListForPhysicalLineBelow(int offset) {
   RSyntaxDocument document = (RSyntaxDocument) getDocument();
   Element map = document.getDefaultRootElement();
   int lineCount = map.getElementCount();
   int line = map.getElementIndex(offset);
   if (!host.isCodeFoldingEnabled()) {
     if (line < lineCount - 1) {
       return document.getTokenListForLine(line + 1);
     }
   } else {
     FoldManager fm = host.getFoldManager();
     line = fm.getVisibleLineBelow(line);
     if (line >= 0 && line < lineCount) {
       return document.getTokenListForLine(line);
     }
   }
   //		int line = map.getElementIndex(offset);
   //		int lineCount = map.getElementCount();
   //		if (line<lineCount-1)
   //			return document.getTokenListForLine(line+1);
   return null;
 }
    /** Calculate the number of lines that will be rendered by logical line when it is wrapped. */
    final int calculateLineCount() {

      int nlines = 0;
      int startOffset = getStartOffset();
      int p1 = getEndOffset();

      // Get the token list for this line so we don't have to keep
      // recomputing it if this logical line spans multiple physical
      // lines.
      RSyntaxTextArea textArea = (RSyntaxTextArea) getContainer();
      RSyntaxDocument doc = (RSyntaxDocument) getDocument();
      Element map = doc.getDefaultRootElement();
      int line = map.getElementIndex(startOffset);
      Token tokenList = doc.getTokenListForLine(line);
      float x0 = 0; // FIXME:  should be alloc.x!! alloc.x;//0;

      // System.err.println(">>> calculateLineCount: " + startOffset + "-" + p1);
      for (int p0 = startOffset; p0 < p1; ) {
        // System.err.println("... ... " + p0 + ", " + p1);
        nlines += 1;
        TokenSubList subList =
            TokenUtils.getSubTokenList(
                tokenList, p0, WrappedSyntaxView.this, textArea, x0, lineCountTempToken);
        x0 = subList != null ? subList.x : x0;
        tokenList = subList != null ? subList.tokenList : null;
        int p = calculateBreakPosition(p0, tokenList, x0);

        // System.err.println("... ... ... break position p==" + p);
        p0 = (p == p0) ? ++p : p; // this is the fix of #4410243
        // we check on situation when
        // width is too small and
        // break position is calculated
        // incorrectly.
        // System.err.println("... ... ... new p0==" + p0);
      }
      /*
      int numLines = 0;
      try {
      	numLines = textArea.getLineCount();
      } catch (BadLocationException ble) {
      	ble.printStackTrace();
      }
      System.err.println(">>> >>> calculated number of lines for this view (line " + line + "/" + numLines + ": " + nlines);
      */
      return nlines;
    }
  /**
   * Provides a mapping from the document model coordinate space to the coordinate space of the view
   * mapped to it.
   *
   * @param pos the position to convert &gt;= 0
   * @param a the allocated region to render into
   * @return the bounding box of the given position
   * @exception BadLocationException if the given position does not represent a valid location in
   *     the associated document
   * @see View#modelToView
   */
  @Override
  public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException {

    // line coordinates
    Element map = getElement();
    RSyntaxDocument doc = (RSyntaxDocument) getDocument();
    int lineIndex = map.getElementIndex(pos);
    Token tokenList = doc.getTokenListForLine(lineIndex);
    Rectangle lineArea = lineToRect(a, lineIndex);
    tabBase = lineArea.x; // Used by listOffsetToView().

    // int x = (int)RSyntaxUtilities.getTokenListWidthUpTo(tokenList,
    //							(RSyntaxTextArea)getContainer(),
    //							this, 0, pos);
    // We use this method instead as it returns the actual bounding box,
    // not just the x-coordinate.
    lineArea =
        tokenList.listOffsetToView((RSyntaxTextArea) getContainer(), this, pos, tabBase, lineArea);

    return lineArea;
  }
  /**
   * Draws a single view (i.e., a line of text for a wrapped view), wrapping the text onto multiple
   * lines if necessary. Any selected text is rendered with the editor's "selected text" color.
   *
   * @param painter The painter to use to render tokens.
   * @param g The graphics context in which to paint.
   * @param r The rectangle in which to paint.
   * @param view The <code>View</code> to paint.
   * @param fontHeight The height of the font being used.
   * @param y The y-coordinate at which to begin painting.
   * @param selStart The start of the selection.
   * @param selEnd The end of the selection.
   */
  protected void drawViewWithSelection(
      TokenPainter painter,
      Graphics2D g,
      Rectangle r,
      View view,
      int fontHeight,
      int y,
      int selStart,
      int selEnd) {

    float x = r.x;

    LayeredHighlighter h = (LayeredHighlighter) host.getHighlighter();

    RSyntaxDocument document = (RSyntaxDocument) getDocument();
    Element map = getElement();

    int p0 = view.getStartOffset();
    int lineNumber = map.getElementIndex(p0);
    int p1 = view.getEndOffset(); // - 1;

    setSegment(p0, p1 - 1, document, drawSeg);
    // System.err.println("drawSeg=='" + drawSeg + "' (p0/p1==" + p0 + "/" + p1 + ")");
    int start = p0 - drawSeg.offset;
    Token token = document.getTokenListForLine(lineNumber);

    // If this line is an empty line, then the token list is simply a
    // null token.  In this case, the line highlight will be skipped in
    // the loop below, so unfortunately we must manually do it here.
    if (token != null && token.getType() == Token.NULL) {
      h.paintLayeredHighlights(g, p0, p1, r, host, this);
      return;
    }

    // Loop through all tokens in this view and paint them!
    while (token != null && token.isPaintable()) {

      int p = calculateBreakPosition(p0, token, x);
      x = r.x;

      h.paintLayeredHighlights(g, p0, p, r, host, this);

      while (token != null && token.isPaintable() && token.getEndOffset() - 1 < p) { // <=p) {

        // Selection starts in this token
        if (token.containsPosition(selStart)) {

          if (selStart > token.getOffset()) {
            tempToken.copyFrom(token);
            tempToken.textCount = selStart - tempToken.getOffset();
            x = painter.paint(tempToken, g, x, y, host, this);
            tempToken.textCount = token.length();
            tempToken.makeStartAt(selStart);
            // Clone required since token and tempToken must be
            // different tokens for else statement below
            token = new TokenImpl(tempToken);
          }

          int selCount = Math.min(token.length(), selEnd - token.getOffset());
          if (selCount == token.length()) {
            x = painter.paintSelected(token, g, x, y, host, this);
          } else {
            tempToken.copyFrom(token);
            tempToken.textCount = selCount;
            x = painter.paintSelected(tempToken, g, x, y, host, this);
            tempToken.textCount = token.length();
            tempToken.makeStartAt(token.getOffset() + selCount);
            token = tempToken;
            x = painter.paint(token, g, x, y, host, this);
          }

        }

        // Selection ends in this token
        else if (token.containsPosition(selEnd)) {
          tempToken.copyFrom(token);
          tempToken.textCount = selEnd - tempToken.getOffset();
          x = painter.paintSelected(tempToken, g, x, y, host, this);
          tempToken.textCount = token.length();
          tempToken.makeStartAt(selEnd);
          token = tempToken;
          x = painter.paint(token, g, x, y, host, this);
        }

        // This token is entirely selected
        else if (token.getOffset() >= selStart && token.getEndOffset() <= selEnd) {
          x = painter.paintSelected(token, g, x, y, host, this);
        }

        // This token is entirely unselected
        else {
          x = painter.paint(token, g, x, y, host, this);
        }

        token = token.getNextToken();
      }

      // If there's a token that's going to be split onto the next line
      if (token != null && token.isPaintable() && token.getOffset() < p) {

        int tokenOffset = token.getOffset();
        Token orig = token;
        token =
            new TokenImpl(
                drawSeg, tokenOffset - start, p - 1 - start, tokenOffset, token.getType());

        // Selection starts in this token
        if (token.containsPosition(selStart)) {

          if (selStart > token.getOffset()) {
            tempToken.copyFrom(token);
            tempToken.textCount = selStart - tempToken.getOffset();
            x = painter.paint(tempToken, g, x, y, host, this);
            tempToken.textCount = token.length();
            tempToken.makeStartAt(selStart);
            // Clone required since token and tempToken must be
            // different tokens for else statement below
            token = new TokenImpl(tempToken);
          }

          int selCount = Math.min(token.length(), selEnd - token.getOffset());
          if (selCount == token.length()) {
            x = painter.paintSelected(token, g, x, y, host, this);
          } else {
            tempToken.copyFrom(token);
            tempToken.textCount = selCount;
            x = painter.paintSelected(tempToken, g, x, y, host, this);
            tempToken.textCount = token.length();
            tempToken.makeStartAt(token.getOffset() + selCount);
            token = tempToken;
            x = painter.paint(token, g, x, y, host, this);
          }

        }

        // Selection ends in this token
        else if (token.containsPosition(selEnd)) {
          tempToken.copyFrom(token);
          tempToken.textCount = selEnd - tempToken.getOffset();
          x = painter.paintSelected(tempToken, g, x, y, host, this);
          tempToken.textCount = token.length();
          tempToken.makeStartAt(selEnd);
          token = tempToken;
          x = painter.paint(token, g, x, y, host, this);
        }

        // This token is entirely selected
        else if (token.getOffset() >= selStart && token.getEndOffset() <= selEnd) {
          x = painter.paintSelected(token, g, x, y, host, this);
        }

        // This token is entirely unselected
        else {
          x = painter.paint(token, g, x, y, host, this);
        }

        token = new TokenImpl(orig);
        ((TokenImpl) token).makeStartAt(p);
      }

      p0 = (p == p0) ? p1 : p;
      y += fontHeight;
    } // End of while (token!=null && token.isPaintable()).

    // NOTE: We should re-use code from Token (paintBackground()) here,
    // but don't because I'm just too lazy.
    if (host.getEOLMarkersVisible()) {
      g.setColor(host.getForegroundForTokenType(Token.WHITESPACE));
      g.setFont(host.getFontForTokenType(Token.WHITESPACE));
      g.drawString("\u00B6", x, y - fontHeight);
    }
  }
Exemple #10
0
  /**
   * Draws a single view (i.e., a line of text for a wrapped view), wrapping the text onto multiple
   * lines if necessary.
   *
   * @param painter The painter to use to render tokens.
   * @param g The graphics context in which to paint.
   * @param r The rectangle in which to paint.
   * @param view The <code>View</code> to paint.
   * @param fontHeight The height of the font being used.
   * @param y The y-coordinate at which to begin painting.
   */
  protected void drawView(
      TokenPainter painter, Graphics2D g, Rectangle r, View view, int fontHeight, int y) {

    float x = r.x;

    LayeredHighlighter h = (LayeredHighlighter) host.getHighlighter();

    RSyntaxDocument document = (RSyntaxDocument) getDocument();
    Element map = getElement();

    int p0 = view.getStartOffset();
    int lineNumber = map.getElementIndex(p0);
    int p1 = view.getEndOffset(); // - 1;

    setSegment(p0, p1 - 1, document, drawSeg);
    // System.err.println("drawSeg=='" + drawSeg + "' (p0/p1==" + p0 + "/" + p1 + ")");
    int start = p0 - drawSeg.offset;
    Token token = document.getTokenListForLine(lineNumber);

    // If this line is an empty line, then the token list is simply a
    // null token.  In this case, the line highlight will be skipped in
    // the loop below, so unfortunately we must manually do it here.
    if (token != null && token.getType() == Token.NULL) {
      h.paintLayeredHighlights(g, p0, p1, r, host, this);
      return;
    }

    // Loop through all tokens in this view and paint them!
    while (token != null && token.isPaintable()) {

      int p = calculateBreakPosition(p0, token, x);
      x = r.x;

      h.paintLayeredHighlights(g, p0, p, r, host, this);

      while (token != null && token.isPaintable() && token.getEndOffset() - 1 < p) { // <=p) {
        x = painter.paint(token, g, x, y, host, this);
        token = token.getNextToken();
      }

      if (token != null && token.isPaintable() && token.getOffset() < p) {
        int tokenOffset = token.getOffset();
        tempToken.set(
            drawSeg.array, tokenOffset - start, p - 1 - start, tokenOffset, token.getType());
        painter.paint(tempToken, g, x, y, host, this);
        tempToken.copyFrom(token);
        tempToken.makeStartAt(p);
        token = new TokenImpl(tempToken);
      }

      p0 = (p == p0) ? p1 : p;
      y += fontHeight;
    } // End of while (token!=null && token.isPaintable()).

    // NOTE: We should re-use code from Token (paintBackground()) here,
    // but don't because I'm just too lazy.
    if (host.getEOLMarkersVisible()) {
      g.setColor(host.getForegroundForTokenType(Token.WHITESPACE));
      g.setFont(host.getFontForTokenType(Token.WHITESPACE));
      g.drawString("\u00B6", x, y - fontHeight);
    }
  }
Exemple #11
0
    /**
     * Provides a mapping from the view coordinate space to the logical coordinate space of the
     * model.
     *
     * @param fx the X coordinate
     * @param fy the Y coordinate
     * @param a the allocated region to render into
     * @return the location within the model that best represents the given point in the view
     * @see View#viewToModel
     */
    @Override
    public int viewToModel(float fx, float fy, Shape a, Position.Bias[] bias) {

      // PENDING(prinz) implement bias properly
      bias[0] = Position.Bias.Forward;

      Rectangle alloc = (Rectangle) a;
      RSyntaxDocument doc = (RSyntaxDocument) getDocument();
      int x = (int) fx;
      int y = (int) fy;
      if (y < alloc.y) {
        // above the area covered by this icon, so the the position
        // is assumed to be the start of the coverage for this view.
        return getStartOffset();
      } else if (y > alloc.y + alloc.height) {
        // below the area covered by this icon, so the the position
        // is assumed to be the end of the coverage for this view.
        return getEndOffset() - 1;
      } else {

        // positioned within the coverage of this view vertically,
        // so we figure out which line the point corresponds to.
        // if the line is greater than the number of lines
        // contained, then simply use the last line as it represents
        // the last possible place we can position to.

        RSyntaxTextArea textArea = (RSyntaxTextArea) getContainer();
        alloc.height = textArea.getLineHeight();
        int p1 = getEndOffset();

        // Get the token list for this line so we don't have to keep
        // recomputing it if this logical line spans multiple
        // physical lines.
        Element map = doc.getDefaultRootElement();
        int p0 = getStartOffset();
        int line = map.getElementIndex(p0);
        Token tlist = doc.getTokenListForLine(line);

        // Look at each physical line-chunk of this logical line.
        while (p0 < p1) {

          // We can always use alloc.x since we always break
          // lines so they start at the beginning of a physical
          // line.
          TokenSubList subList =
              TokenUtils.getSubTokenList(
                  tlist, p0, WrappedSyntaxView.this, textArea, alloc.x, lineCountTempToken);
          tlist = subList != null ? subList.tokenList : null;
          int p = calculateBreakPosition(p0, tlist, alloc.x);

          // If desired view position is in this physical chunk.
          if ((y >= alloc.y) && (y < (alloc.y + alloc.height))) {

            // Point is to the left of the line
            if (x < alloc.x) {
              return p0;
            }

            // Point is to the right of the line
            else if (x > alloc.x + alloc.width) {
              return p - 1;
            }

            // Point is in this physical line!
            else {

              // Start at alloc.x since this chunk starts
              // at the beginning of a physical line.
              int n = tlist.getListOffset(textArea, WrappedSyntaxView.this, alloc.x, x);

              // NOTE:  We needed to add the max() with
              // p0 as getTokenListForLine returns -1
              // for empty lines (just a null token).
              // How did this work before?
              // FIXME:  Have null tokens have their
              // offset but a -1 length.
              return Math.max(Math.min(n, p1 - 1), p0);
            } // End of else.
          } // End of if ((y>=alloc.y) && ...

          p0 = (p == p0) ? p1 : p;
          alloc.y += alloc.height;
        } // End of while (p0<p1).

        return getEndOffset() - 1;
      } // End of else.
    }
  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;
  }
  /**
   * Actually paints the text area. Only lines that have been damaged are repainted.
   *
   * @param g The graphics context with which to paint.
   * @param a The allocated region in which to render.
   */
  @Override
  public void paint(Graphics g, Shape a) {

    RSyntaxDocument document = (RSyntaxDocument) getDocument();

    Rectangle alloc = a.getBounds();

    tabBase = alloc.x;
    host = (RSyntaxTextArea) getContainer();

    Rectangle clip = g.getClipBounds();
    // An attempt to speed things up for files with long lines.  Note that
    // this will actually slow things down a bit for the common case of
    // regular-length lines, but it doesn't make a perceivable difference.
    clipStart = clip.x;
    clipEnd = clipStart + clip.width;

    lineHeight = host.getLineHeight();
    ascent = host.getMaxAscent(); // metrics.getAscent();
    int heightAbove = clip.y - alloc.y;
    int linesAbove = Math.max(0, heightAbove / lineHeight);

    FoldManager fm = host.getFoldManager();
    linesAbove += fm.getHiddenLineCountAbove(linesAbove, true);
    Rectangle lineArea = lineToRect(a, linesAbove);
    int y = lineArea.y + ascent;
    int x = lineArea.x;
    Element map = getElement();
    int lineCount = map.getElementCount();

    // Whether token styles should always be painted, even in selections
    int selStart = host.getSelectionStart();
    int selEnd = host.getSelectionEnd();

    RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter) host.getHighlighter();

    Graphics2D g2d = (Graphics2D) g;
    Token token;
    // System.err.println("Painting lines: " + linesAbove + " to " + (endLine-1));

    TokenPainter painter = host.getTokenPainter();
    int line = linesAbove;
    // int count = 0;
    while (y < clip.y + clip.height + ascent && line < lineCount) {

      Fold fold = fm.getFoldForLine(line);
      Element lineElement = map.getElement(line);
      int startOffset = lineElement.getStartOffset();
      // int endOffset = (line==lineCount ? lineElement.getEndOffset()-1 :
      //							lineElement.getEndOffset()-1);
      int endOffset = lineElement.getEndOffset() - 1; // Why always "-1"?
      h.paintLayeredHighlights(g2d, startOffset, endOffset, a, host, this);

      // Paint a line of text.
      token = document.getTokenListForLine(line);
      if (selStart == selEnd || startOffset >= selEnd || endOffset < selStart) {
        drawLine(painter, token, g2d, x, y, line);
      } else {
        // System.out.println("Drawing line with selection: " + line);
        drawLineWithSelection(painter, token, g2d, x, y, selStart, selEnd);
      }

      if (fold != null && fold.isCollapsed()) {

        // Visible indicator of collapsed lines
        Color c = RSyntaxUtilities.getFoldedLineBottomColor(host);
        if (c != null) {
          g.setColor(c);
          g.drawLine(x, y + lineHeight - ascent - 1, host.getWidth(), y + lineHeight - ascent - 1);
        }

        // Skip to next line to paint, taking extra care for lines with
        // block ends and begins together, e.g. "} else {"
        do {
          int hiddenLineCount = fold.getLineCount();
          if (hiddenLineCount == 0) {
            // Fold parser identified a zero-line fold region.
            // This is really a bug, but we'll be graceful here
            // and avoid an infinite loop.
            break;
          }
          line += hiddenLineCount;
          fold = fm.getFoldForLine(line);
        } while (fold != null && fold.isCollapsed());
      }

      y += lineHeight;
      line++;
      // count++;

    }

    // System.out.println("SyntaxView: lines painted=" + count);

  }
  /** {@inheritDoc} */
  public void markOccurrences(
      RSyntaxDocument doc,
      Token t,
      RSyntaxTextAreaHighlighter h,
      MarkOccurrencesHighlightPainter p) {

    char[] lexeme = t.getLexeme().toCharArray();
    int tokenOffs = t.getOffset();
    Element root = doc.getDefaultRootElement();
    int lineCount = root.getElementCount();
    int curLine = root.getElementIndex(t.getOffset());

    // For now, we only check for tags on the current line, for
    // simplicity.  Tags spanning multiple lines aren't common anyway.
    boolean found = false;
    boolean forward = true;
    t = doc.getTokenListForLine(curLine);
    while (t != null && t.isPaintable()) {
      if (t.getType() == Token.MARKUP_TAG_DELIMITER) {
        if (t.isSingleChar('<') && t.getOffset() + 1 == tokenOffs) {
          found = true;
          break;
        } else if (t.is(CLOSE_TAG_START) && t.getOffset() + 2 == tokenOffs) {
          found = true;
          forward = false;
          break;
        }
      }
      t = t.getNextToken();
    }

    if (!found) {
      return;
    }

    if (forward) {

      int depth = 0;
      t = t.getNextToken().getNextToken();

      do {

        while (t != null && t.isPaintable()) {
          if (t.getType() == Token.MARKUP_TAG_DELIMITER) {
            if (t.isSingleChar('<')) {
              depth++;
            } else if (t.is(TAG_SELF_CLOSE)) {
              if (depth > 0) {
                depth--;
              } else {
                return; // No match for this tag
              }
            } else if (t.is(CLOSE_TAG_START)) {
              if (depth > 0) {
                depth--;
              } else {
                Token match = t.getNextToken();
                if (match != null && match.is(lexeme)) {
                  try {
                    int end = match.getOffset() + match.length();
                    h.addMarkedOccurrenceHighlight(match.getOffset(), end, p);
                    end = tokenOffs + match.length();
                    h.addMarkedOccurrenceHighlight(tokenOffs, end, p);
                  } catch (BadLocationException ble) {
                    ble.printStackTrace(); // Never happens
                  }
                }
                return; // We're done!
              }
            }
          }
          t = t.getNextToken();
        }

        if (++curLine < lineCount) {
          t = doc.getTokenListForLine(curLine);
        }

      } while (curLine < lineCount);

    } else { // !forward

      Stack<Token> matches = new Stack<Token>();
      boolean inPossibleMatch = false;
      t = doc.getTokenListForLine(curLine);
      final int endBefore = tokenOffs - 2; // Stop before "</".

      do {

        while (t != null && t.getOffset() < endBefore && t.isPaintable()) {
          if (t.getType() == Token.MARKUP_TAG_DELIMITER) {
            if (t.isSingleChar('<')) {
              Token next = t.getNextToken();
              if (next != null) {
                if (next.is(lexeme)) {
                  matches.push(next);
                  inPossibleMatch = true;
                } else {
                  inPossibleMatch = false;
                }
                t = next;
              }
            } else if (t.isSingleChar('>')) {
              inPossibleMatch = false;
            } else if (inPossibleMatch && t.is(TAG_SELF_CLOSE)) {
              matches.pop();
            } else if (t.is(CLOSE_TAG_START)) {
              Token next = t.getNextToken();
              if (next != null) {
                // Invalid XML might not have a match
                if (next.is(lexeme) && !matches.isEmpty()) {
                  matches.pop();
                }
                t = next;
              }
            }
          }
          t = t.getNextToken();
        }

        if (!matches.isEmpty()) {
          try {
            Token match = matches.pop();
            int end = match.getOffset() + match.length();
            h.addMarkedOccurrenceHighlight(match.getOffset(), end, p);
            end = tokenOffs + match.length();
            h.addMarkedOccurrenceHighlight(tokenOffs, end, p);
          } catch (BadLocationException ble) {
            ble.printStackTrace(); // Never happens
          }
          return;
        }

        if (--curLine >= 0) {
          t = doc.getTokenListForLine(curLine);
        }

      } while (curLine >= 0);
    }
  }