/**
   * Indents line <code>line</code> in <code>document</code> with <code>indent</code>. Leaves
   * leading comment signs alone.
   *
   * @param document the document
   * @param line the line
   * @param indent the indentation to insert
   * @param tabLength the length of a tab
   * @throws BadLocationException on concurrent document modification
   */
  private void addIndent(Document document, int line, CharSequence indent, int tabLength)
      throws BadLocationException {
    IRegion region = document.getLineInformation(line);
    int insert = region.getOffset();
    int endOffset = region.getOffset() + region.getLength();

    // Compute insert after all leading line comment markers
    int newInsert = insert;
    while (newInsert < endOffset - 2 && document.get(newInsert, 2).equals(LINE_COMMENT))
      newInsert += 2;

    // Heuristic to check whether it is commented code or just a comment
    if (newInsert > insert) {
      int whitespaceCount = 0;
      int i = newInsert;
      while (i < endOffset - 1) {
        char ch = document.get(i, 1).charAt(0);
        if (!Character.isWhitespace(ch)) break;
        whitespaceCount = whitespaceCount + computeVisualLength(ch, tabLength);
        i++;
      }

      if (whitespaceCount != 0 && whitespaceCount >= CodeFormatterUtil.getIndentWidth(fProject))
        insert = newInsert;
    }

    // Insert indent
    document.replace(insert, 0, indent.toString());
  }
  /**
   * Cuts the visual equivalent of <code>toDelete</code> characters out of the indentation of line
   * <code>line</code> in <code>document</code>. Leaves leading comment signs alone.
   *
   * @param document the document
   * @param line the line
   * @param toDelete the number of space equivalents to delete
   * @param tabLength the length of a tab
   * @throws BadLocationException on concurrent document modification
   */
  private void cutIndent(Document document, int line, int toDelete, int tabLength)
      throws BadLocationException {
    IRegion region = document.getLineInformation(line);
    int from = region.getOffset();
    int endOffset = region.getOffset() + region.getLength();

    // go behind line comments
    while (from < endOffset - 2 && document.get(from, 2).equals(LINE_COMMENT)) from += 2;

    int to = from;
    while (toDelete > 0 && to < endOffset) {
      char ch = document.getChar(to);
      if (!Character.isWhitespace(ch)) break;
      toDelete -= computeVisualLength(ch, tabLength);
      if (toDelete >= 0) to++;
      else break;
    }

    document.replace(from, to - from, ""); // $NON-NLS-1$
  }
 private final synchronized void eclipseReplace(int pos, int length, String text)
     throws BadLocationException {
   // changes.addChange(new Change(pos, length, text));
   changed = true;
   super.replace(pos, length, text);
 }
 /**
  * This is a new method, not known to the eclipse platform. It is used to handle document changes
  * that were not directly triggered by the user. These changes come from the according
  * DocumentText. We say this are changes "from below", in contradiction to changes "from above"
  * {@link #replace(int, int, String)}.
  */
 public final void doReplace(int pos, int length, String text) throws BadLocationException {
   super.replace(pos, length, text);
 }
  private int getPeerPosition(IDocument document, DocumentCommand command) {
    if (document.getLength() == 0) return 0;
    /*
     * Search for scope closers in the pasted text and find their opening peers
     * in the document.
     */
    Document pasted = new Document(command.text);
    installJavaStuff(pasted);
    int firstPeer = command.offset;

    JavaHeuristicScanner pScanner = new JavaHeuristicScanner(pasted);
    JavaHeuristicScanner dScanner = new JavaHeuristicScanner(document);

    // add scope relevant after context to peer search
    int afterToken =
        dScanner.nextToken(command.offset + command.length, JavaHeuristicScanner.UNBOUND);
    try {
      switch (afterToken) {
        case Symbols.TokenRBRACE:
          pasted.replace(pasted.getLength(), 0, "}"); // $NON-NLS-1$
          break;
        case Symbols.TokenRPAREN:
          pasted.replace(pasted.getLength(), 0, ")"); // $NON-NLS-1$
          break;
        case Symbols.TokenRBRACKET:
          pasted.replace(pasted.getLength(), 0, "]"); // $NON-NLS-1$
          break;
      }
    } catch (BadLocationException e) {
      // cannot happen
      Assert.isTrue(false);
    }

    int pPos = 0; // paste text position (increasing from 0)
    int dPos = Math.max(0, command.offset - 1); // document position (decreasing from paste offset)
    while (true) {
      int token = pScanner.nextToken(pPos, JavaHeuristicScanner.UNBOUND);
      pPos = pScanner.getPosition();
      switch (token) {
        case Symbols.TokenLBRACE:
        case Symbols.TokenLBRACKET:
        case Symbols.TokenLPAREN:
          pPos = skipScope(pScanner, pPos, token);
          if (pPos == JavaHeuristicScanner.NOT_FOUND) return firstPeer;
          break; // closed scope -> keep searching
        case Symbols.TokenRBRACE:
          int peer = dScanner.findOpeningPeer(dPos, '{', '}');
          dPos = peer - 1;
          if (peer == JavaHeuristicScanner.NOT_FOUND) return firstPeer;
          firstPeer = peer;
          break; // keep searching
        case Symbols.TokenRBRACKET:
          peer = dScanner.findOpeningPeer(dPos, '[', ']');
          dPos = peer - 1;
          if (peer == JavaHeuristicScanner.NOT_FOUND) return firstPeer;
          firstPeer = peer;
          break; // keep searching
        case Symbols.TokenRPAREN:
          peer = dScanner.findOpeningPeer(dPos, '(', ')');
          dPos = peer - 1;
          if (peer == JavaHeuristicScanner.NOT_FOUND) return firstPeer;
          firstPeer = peer;
          break; // keep searching
        case Symbols.TokenCASE:
        case Symbols.TokenDEFAULT:
          JavaIndenter indenter = new JavaIndenter(document, dScanner, fProject);
          peer = indenter.findReferencePosition(dPos, false, false, false, true);
          if (peer == JavaHeuristicScanner.NOT_FOUND) return firstPeer;
          firstPeer = peer;
          break; // keep searching

        case Symbols.TokenEOF:
          return firstPeer;
        default:
          // keep searching
      }
    }
  }