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; }
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)); }