boolean (@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file, int caretOffset) { if (!Registry.is("editor.move.semicolon.after.paren")) { return false; } ApplicationManager.getApplication().assertWriteAccessAllowed(); // Note, this feature may be rewritten using only lexer if needed. // In that case accuracy will not be 100%, but good enough. HighlighterIterator it = editor.getHighlighter().createIterator(caretOffset); int afterLastParenOffset = -1; while (!it.atEnd()) { if (isAtLineEnd(it)) { break; } else if (it.getTokenType() == JavaTokenType.RBRACE) { break; } else if (it.getTokenType() == JavaTokenType.RPARENTH) { afterLastParenOffset = it.getEnd(); } else if (it.getTokenType() != TokenType.WHITE_SPACE) { // Other tokens are not permitted return false; } it.advance(); } if (!it.atEnd() && afterLastParenOffset >= 0 && afterLastParenOffset >= caretOffset) { PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument()); PsiElement curElement = file.findElementAt(caretOffset); ASTNode curStmt = BasicJavaAstTreeUtil.getParentOfType(BasicJavaAstTreeUtil.toNode(curElement), STATEMENT_SET); if (curStmt != null) { if (BasicJavaAstTreeUtil.is(curStmt, BASIC_TRY_STATEMENT)) { // try-with-resources can contain semicolons inside return false; } if (BasicJavaAstTreeUtil.is(curStmt, BASIC_FOR_STATEMENT)) { // for loop can have semicolons inside return false; } // It may worth to check if the error element is about expecting semicolon PsiElement curPsiElement = BasicJavaAstTreeUtil.toPsi(curStmt); if (curPsiElement != null && PsiTreeUtil.getDeepestLast(curPsiElement) instanceof PsiErrorElement) { int stmtEndOffset = curStmt.getTextRange().getEndOffset(); if (stmtEndOffset == afterLastParenOffset || stmtEndOffset == it.getStart()) { editor.getDocument().insertString(stmtEndOffset, ";"); editor.getCaretModel().moveToOffset(stmtEndOffset + 1); return true; } } } } return false; }