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