diff --git a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/enter/EnterAfterUnmatchedBraceHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/enter/EnterAfterUnmatchedBraceHandler.java index dd2899548bc3..17abf8bf0c45 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/enter/EnterAfterUnmatchedBraceHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/enter/EnterAfterUnmatchedBraceHandler.java @@ -31,6 +31,7 @@ import com.intellij.openapi.editor.highlighter.EditorHighlighter; import com.intellij.openapi.editor.highlighter.HighlighterIterator; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; @@ -57,15 +58,32 @@ public class EnterAfterUnmatchedBraceHandler extends EnterHandlerDelegateAdapter final EditorActionHandler originalHandler) { int caretOffset = caretOffsetRef.get(); + if (!isApplicable(file, caretOffset)) { + return Result.Continue; + } + int maxRBraceCount = getMaxRBraceCount(file, editor, caretOffset); - if (maxRBraceCount > 0 && insertRBraces(file, editor, caretOffset, - getRBraceOffset(file, editor, caretOffset), - adjustRBraceCountForPosition(editor, caretOffset, maxRBraceCount))) { + if (maxRBraceCount > 0) { + insertRBraces(file, editor, + caretOffset, + getRBraceOffset(file, editor, caretOffset), + adjustRBraceCountForPosition(editor, caretOffset, maxRBraceCount)); return Result.DefaultForceIndent; } return Result.Continue; } + /** + * Checks that the text context is in responsibility of the handler. + * + * @param file target PSI file + * @param caretOffset target caret offset + * @return true, if handler is in charge + */ + public boolean isApplicable(@NotNull PsiFile file, int caretOffset) { + return true; + } + /** * Calculates the maximum number of '}' that can be inserted by handler. * Can return 0 or less in custom implementation to skip '}' insertion in the preprocessEnter call @@ -84,10 +102,10 @@ public class EnterAfterUnmatchedBraceHandler extends EnterHandlerDelegateAdapter } /** - * Calculates the precise number of '}' that have be inserted by handler. + * Calculates the precise number of '}' that have to be inserted by handler. * - * @param editor target editor - * @param caretOffset target caret offset + * @param editor target editor + * @param caretOffset target caret offset * @param maxRBraceCount the maximum number of '}' for insert at position, it always positive * @return number of '}' that has to be inserted by handler, it has to positive */ @@ -96,23 +114,29 @@ public class EnterAfterUnmatchedBraceHandler extends EnterHandlerDelegateAdapter CharSequence text = editor.getDocument().getCharsSequence(); int bracesToInsert = 0; - outer: for (int i = caretOffset - 1; i >= 0 && bracesToInsert < maxRBraceCount; --i) { - switch (text.charAt(i)) { - case ' ': - case '\n': - case '\t': - continue; - case '{': - bracesToInsert++; - break; - default: - break outer; + final char c = text.charAt(i); + if (c == '{') { + ++bracesToInsert; + } + else if (isStopChar(c)) { + break; } } return Math.max(bracesToInsert, 1); } + /** + * Checks the character before the inserted '}' to reduce the count of inserted '}'. + * The number of inserted '}' will increase for each found '{'. + * + * @param c character to check + * @return true, to stop back iteration + */ + protected boolean isStopChar(char c) { + return " \n\t".indexOf(c) < 0; + } + /** * Calculates the position for insertion of one or more '}'. * @@ -126,7 +150,7 @@ public class EnterAfterUnmatchedBraceHandler extends EnterHandlerDelegateAdapter int offset = CharArrayUtil.shiftForward(text, caretOffset, " \t"); final int fileLength = text.length(); if (offset < fileLength && ")];,%rBracesCount of '}' at the rBracesInsertOffset position. + * + * @param document target document + * @param caretOffset target caret offset + * @param rBracesInsertOffset target position to insert + * @param rBracesCount count of '}' to insert + */ + protected void insertRBracesAtPosition(Document document, int caretOffset, int rBracesInsertOffset, int rBracesCount) { document.insertString(rBracesInsertOffset, "\n" + StringUtil.repeatSymbol('}', rBracesCount)); // We need to adjust indents of the text that will be moved, hence, need to insert preliminary line feed. // Example: @@ -165,7 +201,22 @@ public class EnterAfterUnmatchedBraceHandler extends EnterHandlerDelegateAdapter // to 'if' code block('{}')). So, we insert temporary line feed between 'if' and 'else', correct indent and remove that temporary // line feed. document.insertString(caretOffset, "\n"); + } + /** + * Formats the code block between caret and inserted braces. + * + * @param file target PSI file + * @param document target document + * @param caretOffset target caret offset + * @param rBracesInsertOffset target position to insert + * @param rBracesCount count of '}' to insert + */ + protected void formatCodeFragmentBetweenBraces(@NotNull PsiFile file, + @NotNull Document document, + int caretOffset, + int rBracesInsertOffset, + int rBracesCount) { Project project = file.getProject(); long stamp = document.getModificationStamp(); boolean closingBraceIndentAdjusted; @@ -178,6 +229,7 @@ public class EnterAfterUnmatchedBraceHandler extends EnterHandlerDelegateAdapter } finally { closingBraceIndentAdjusted = stamp != document.getModificationStamp(); + // do you remember that we insert the '\n'? here we take it back! document.deleteString(caretOffset, caretOffset + 1); } @@ -202,7 +254,6 @@ public class EnterAfterUnmatchedBraceHandler extends EnterHandlerDelegateAdapter document.insertString(rBracesInsertOffset + 1, buffer); } } - return true; } /** @@ -235,13 +286,13 @@ public class EnterAfterUnmatchedBraceHandler extends EnterHandlerDelegateAdapter * @param file target PSI file * @param text text from the given file * @param offset target offset where line feed will be inserted - * @return offset to use for inserting closing brace + * @return pair of (element, offset). The element is the '}' owner, if applicable; the offset is the position for inserting closing brace */ - protected int calculateOffsetToInsertClosingBrace(PsiFile file, CharSequence text, final int offset) { + protected Pair calculateOffsetToInsertClosingBrace(@NotNull PsiFile file, @NotNull CharSequence text, final int offset) { PsiElement element = PsiUtilCore.getElementAtOffset(file, offset); ASTNode node = element.getNode(); if (node != null && node.getElementType() == TokenType.WHITE_SPACE) { - return CharArrayUtil.shiftForwardUntil(text, offset, "\n"); + return Pair.create(null, CharArrayUtil.shiftForwardUntil(text, offset, "\n")); } for (PsiElement parent = element.getParent(); parent != null; parent = parent.getParent()) { ASTNode parentNode = parent.getNode(); @@ -251,9 +302,9 @@ public class EnterAfterUnmatchedBraceHandler extends EnterHandlerDelegateAdapter element = parent; } if (element.getTextOffset() != offset) { - return CharArrayUtil.shiftForwardUntil(text, offset, "\n"); + return Pair.create(null, CharArrayUtil.shiftForwardUntil(text, offset, "\n")); } - return element.getTextRange().getEndOffset(); + return Pair.create(element, element.getTextRange().getEndOffset()); } public static boolean isAfterUnmatchedLBrace(Editor editor, int offset, FileType fileType) {