/**
   * Overridden to show the content of a collapsed fold on mouse-overs.
   *
   * @param e The mouse location.
   */
  public String getToolTipText(MouseEvent e) {

    String text = null;

    RSyntaxTextArea rsta = (RSyntaxTextArea) textArea;
    if (rsta.isCodeFoldingEnabled()) {
      FoldManager fm = rsta.getFoldManager();
      int pos = rsta.viewToModel(new Point(0, e.getY()));
      if (pos >= 0) { // Not -1
        int line = 0;
        try {
          line = rsta.getLineOfOffset(pos);
        } catch (BadLocationException ble) {
          ble.printStackTrace(); // Never happens
          return null;
        }
        Fold fold = fm.getFoldForLine(line);
        if (fold != null && fold.isCollapsed()) {

          int endLine = fold.getEndLine();
          if (fold.getLineCount() > 25) { // Not too big
            endLine = fold.getStartLine() + 25;
          }

          StringBuffer sb = new StringBuffer("<html><nobr>");
          while (line <= endLine && line < rsta.getLineCount()) { // Sanity
            Token t = rsta.getTokenListForLine(line);
            while (t != null && t.isPaintable()) {
              t.appendHTMLRepresentation(sb, rsta, true, true);
              t = t.getNextToken();
            }
            sb.append("<br>");
            line++;
          }

          text = sb.toString();
        }
      }
    }

    return text;
  }
  /** {@inheritDoc} */
  @Override
  public List<Fold> getFolds(RSyntaxTextArea textArea) {

    List<Fold> folds = new ArrayList<Fold>();

    Fold currentFold = null;
    int lineCount = textArea.getLineCount();
    boolean inMLC = false;
    int mlcStart = 0;
    int importStartLine = -1;
    int lastSeenImportLine = -1;
    int importGroupStartOffs = -1;
    int importGroupEndOffs = -1;
    int lastRightCurlyLine = -1;
    Fold prevFold = null;

    try {

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

        Token t = textArea.getTokenListForLine(line);
        while (t != null && t.isPaintable()) {

          if (getFoldableMultiLineComments() && t.isComment()) {

            // Java-specific stuff
            if (java) {

              if (importStartLine > -1) {
                if (lastSeenImportLine > importStartLine) {
                  Fold fold = null;
                  // Any imports found *should* be a top-level fold,
                  // but we're extra lenient here and allow groups
                  // of them anywhere to keep our parser better-behaved
                  // if they have random "imports" throughout code.
                  if (currentFold == null) {
                    fold = new Fold(FoldType.IMPORTS, textArea, importGroupStartOffs);
                    folds.add(fold);
                  } else {
                    fold = currentFold.createChild(FoldType.IMPORTS, importGroupStartOffs);
                  }
                  fold.setEndOffset(importGroupEndOffs);
                }
                importStartLine =
                    lastSeenImportLine = importGroupStartOffs = importGroupEndOffs = -1;
              }
            }

            if (inMLC) {
              // If we found the end of an MLC that started
              // on a previous line...
              if (t.endsWith(C_MLC_END)) {
                int mlcEnd = t.getEndOffset() - 1;
                if (currentFold == null) {
                  currentFold = new Fold(FoldType.COMMENT, textArea, mlcStart);
                  currentFold.setEndOffset(mlcEnd);
                  folds.add(currentFold);
                  currentFold = null;
                } else {
                  currentFold = currentFold.createChild(FoldType.COMMENT, mlcStart);
                  currentFold.setEndOffset(mlcEnd);
                  currentFold = currentFold.getParent();
                }
                // System.out.println("Ending MLC at: " + mlcEnd + ", parent==" + currentFold);
                inMLC = false;
                mlcStart = 0;
              }
              // Otherwise, this MLC is continuing on to yet
              // another line.
            } else {
              // If we're an MLC that ends on a later line...
              if (t.getType() != Token.COMMENT_EOL && !t.endsWith(C_MLC_END)) {
                // System.out.println("Starting MLC at: " + t.offset);
                inMLC = true;
                mlcStart = t.getOffset();
              }
            }

          } else if (isLeftCurly(t)) {

            // Java-specific stuff
            if (java) {

              if (importStartLine > -1) {
                if (lastSeenImportLine > importStartLine) {
                  Fold fold = null;
                  // Any imports found *should* be a top-level fold,
                  // but we're extra lenient here and allow groups
                  // of them anywhere to keep our parser better-behaved
                  // if they have random "imports" throughout code.
                  if (currentFold == null) {
                    fold = new Fold(FoldType.IMPORTS, textArea, importGroupStartOffs);
                    folds.add(fold);
                  } else {
                    fold = currentFold.createChild(FoldType.IMPORTS, importGroupStartOffs);
                  }
                  fold.setEndOffset(importGroupEndOffs);
                }
                importStartLine =
                    lastSeenImportLine = importGroupStartOffs = importGroupEndOffs = -1;
              }
            }

            // If a new fold block starts on the same line as the
            // previous one ends, we treat it as one big block
            // (e.g. K&R-style "} else {")
            if (prevFold != null && line == lastRightCurlyLine) {
              currentFold = prevFold;
              // Keep currentFold.endOffset where it was, so that
              // unclosed folds at end of the file work as well
              // as possible
              prevFold = null;
              lastRightCurlyLine = -1;
            } else if (currentFold == null) { // A top-level fold
              currentFold = new Fold(FoldType.CODE, textArea, t.getOffset());
              folds.add(currentFold);
            } else { // A nested fold
              currentFold = currentFold.createChild(FoldType.CODE, t.getOffset());
            }

          } else if (isRightCurly(t)) {

            if (currentFold != null) {
              currentFold.setEndOffset(t.getOffset());
              Fold parentFold = currentFold.getParent();
              // System.out.println("... Adding regular fold at " + t.offset + ", parent==" +
              // parentFold);
              // Don't add fold markers for single-line blocks
              if (currentFold.isOnSingleLine()) {
                if (!currentFold.removeFromParent()) {
                  folds.remove(folds.size() - 1);
                }
              } else {
                // Remember the end of the last completed fold,
                // in case it needs to get merged with the next
                // one (e.g. K&R "} else {" style)
                lastRightCurlyLine = line;
                prevFold = currentFold;
              }
              currentFold = parentFold;
            }

          }

          // Java-specific folding rules
          else if (java) {

            if (t.is(Token.RESERVED_WORD, KEYWORD_IMPORT)) {
              if (importStartLine == -1) {
                importStartLine = line;
                importGroupStartOffs = t.getOffset();
                importGroupEndOffs = t.getOffset();
              }
              lastSeenImportLine = line;
            } else if (importStartLine > -1
                && t.isIdentifier()
                && // SEPARATOR &&
                t.isSingleChar(';')) {
              importGroupEndOffs = t.getOffset();
            }
          }

          t = t.getNextToken();
        }
      }

    } catch (BadLocationException ble) { // Should never happen
      ble.printStackTrace();
    }

    return folds;
  }