// FIXME: assumes C-family multi-line comments.
 private void insertMatchingCloseComment() {
   final int position = textArea.getSelectionStart();
   String whitespace = getIndentationOfLineAtOffset(position);
   String prefix = "\n" + whitespace + " * ";
   String suffix = "\n" + whitespace + " */";
   textArea.replaceSelection(prefix + suffix);
   final int newOffset = position + prefix.length();
   textArea.select(newOffset, newOffset);
 }
 private void removeHighlights() {
   for (int i = 0; i < highlights.length; ++i) {
     PHighlight highlight = highlights[i];
     if (highlight != null) {
       textArea.removeHighlight(highlight);
     }
     highlights[i] = null;
   }
 }
 // PSimpleIndenter has such a method but it's not exposed in the base class.
 private boolean isBlockBegin(CharSequence lineToTheLeft) {
   if (lineToTheLeft.length() == 0) {
     return false;
   }
   char lastChar = lineToTheLeft.charAt(lineToTheLeft.length() - 1);
   if (PBracketUtilities.isOpenBracket(lastChar) == false) {
     return false;
   }
   char closeBracket = PBracketUtilities.getPartnerForBracket(lastChar);
   if (textArea.getIndenter().isElectric(closeBracket) == false) {
     return false;
   }
   return true;
 }
  public void caretMoved(PTextArea textArea, int selectionStart, int selectionEnd) {
    removeHighlights();
    if (selectionStart != selectionEnd) {
      return;
    }

    int offset = selectionStart;

    // Look for a bracket to match with.
    if (PBracketUtilities.beforeCloseBracket(textArea.getTextBuffer(), offset)) {
      highlights[0] = new MatchingBracketHighlight(textArea, offset, offset + 1);
    } else if (PBracketUtilities.afterOpenBracket(textArea.getTextBuffer(), offset)) {
      highlights[0] = new MatchingBracketHighlight(textArea, offset - 1, offset);
    } else {
      // We're not next to a bracket, so we've nothing to match.
      return;
    }

    // Try to find a match.
    int matchingBracketOffset = PBracketUtilities.findMatchingBracketInSameStyle(textArea, offset);
    if (matchingBracketOffset != -1) {
      int start = matchingBracketOffset;
      int end = start + 1;
      highlights[1] = new MatchingBracketHighlight(textArea, start, end);
    } else {
      highlights[0].setColor(FAILED_MATCH_COLOR);
    }

    // Add any highlights now. We may only have one, if we detected a
    // mis-match.
    for (PHighlight highlight : highlights) {
      if (highlight != null) {
        textArea.addHighlight(highlight);
      }
    }
  }
  public void insertNewline(boolean fixIndentation) {
    textArea.getTextBuffer().getUndoBuffer().startCompoundEdit();
    try {
      final int startPosition = textArea.getSelectionStart();
      CharSequence chars = textArea.getTextBuffer();

      int startLineIndex = textArea.getLineOfOffset(startPosition);
      int startLineStartOffset = textArea.getLineStartOffset(startLineIndex);
      CharSequence lineToTheLeft = chars.subSequence(startLineStartOffset, startPosition);

      if (isBlockBegin(lineToTheLeft) && insertMatchingBrackets()) {
        return;
      }

      if (isUnclosedComment(chars, startPosition, lineToTheLeft)) {
        insertMatchingCloseComment();
      } else {
        textArea.replaceSelection(
            "\n" + textArea.getIndenter().getCurrentIndentationOfLine(startLineIndex));
        if (textArea.getIndenter().canOnlyAutoIndent()) {
          // The other indenters all get a chance to fix the current line's indentation before
          // suggesting an auto-indent level for this line.
          // It's possible that not differentiating between these two cases when calling the
          // indenter is a design error.
          // It's also possible that allowing this code to modify lines other than the
          // newly-inserted line is a design error.
          // We certainly make it annoyingly hard for the user to override us even when they want
          // to, though maybe if our indenters were better no-one would care?
          textArea.getIndenter().fixIndentation();
        } else if (fixIndentation) {
          textArea.getIndenter().fixIndentationOnLine(startLineIndex);
          textArea.getIndenter().fixIndentation();
        } else {
          new PCopyingIndenter(textArea).fixIndentation();
        }
      }
    } finally {
      textArea.getTextBuffer().getUndoBuffer().finishCompoundEdit();
    }
  }
 /**
  * Returns a string corresponding to the spaces and tabs found at the start of the line containing
  * the given offset.
  */
 private String getIndentationOfLineAtOffset(int offset) {
   int lineNumber = textArea.getLineOfOffset(offset);
   return textArea.getIndenter().getCurrentIndentationOfLine(lineNumber);
 }
 private String getLineTextAtOffset(int offset) {
   return textArea.getLineText(textArea.getLineOfOffset(offset));
 }
 private boolean insertMatchingBrackets() {
   final int start = textArea.getSelectionStart();
   final int end = textArea.getSelectionEnd();
   int endLineIndex = textArea.getLineOfOffset(end);
   int suffixPosition = textArea.getLineEndOffsetBeforeTerminator(endLineIndex);
   String beforeInsertion = textArea.getTextBuffer().subSequence(0, start).toString();
   String afterInsertion =
       textArea
           .getTextBuffer()
           .subSequence(suffixPosition, textArea.getTextBuffer().length())
           .toString();
   String unmatchedOpenBrackets = getUnmatchedBrackets(beforeInsertion);
   String unmatchedCloseBrackets = getUnmatchedBrackets(afterInsertion);
   String reflectedCloseBrackets = PBracketUtilities.reflectBrackets(unmatchedCloseBrackets);
   if (unmatchedOpenBrackets.startsWith(reflectedCloseBrackets) == false) {
     return false;
   }
   String closingBrackets =
       PBracketUtilities.reflectBrackets(
           unmatchedOpenBrackets.substring(reflectedCloseBrackets.length()));
   if (closingBrackets.length() == 0) {
     return false;
   }
   String startLine = getLineTextAtOffset(start);
   if (closingBrackets.endsWith("}") == false
       || textArea.getIndenter().isInNeedOfClosingSemicolon(startLine)) {
     // TODO: "closingBrackets" is a bad name now it can have a semicolon on the end!
     closingBrackets = closingBrackets + ";";
   }
   String candidateBlockContents =
       textArea.getTextBuffer().subSequence(end, suffixPosition).toString();
   String commonEnding = getCommonEnding(candidateBlockContents, closingBrackets);
   String whitespace = getIndentationOfLineAtOffset(start);
   // TODO: The newline inserter has no business thinking it knows how to increase the indent.
   String prefix = "\n" + whitespace + textArea.getIndentationString();
   String suffix = "\n" + whitespace + closingBrackets;
   final int newCaretPosition = start + prefix.length();
   textArea.replaceSelection(prefix);
   // suffixPosition is invalidated by replaceSelection.
   // But we can't swap the calls because replaceRange clears the selection.
   int selectionSize = end - start;
   suffixPosition -= selectionSize;
   suffixPosition += prefix.length();
   textArea.replaceRange(suffix, suffixPosition - commonEnding.length(), suffixPosition);
   textArea.select(newCaretPosition, newCaretPosition);
   return true;
 }