[java-rd] IDEA-322563 Improve editing experience in Remote Dev for Java

- editor actions

GitOrigin-RevId: e774f42e72be12b613a9c300dd988589048bd483
This commit is contained in:
Mikhail Pyltsin
2023-09-13 09:09:01 +00:00
committed by intellij-monorepo-bot
parent e23e60a0eb
commit d2af2f9736
124 changed files with 4490 additions and 3699 deletions
@@ -0,0 +1,348 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions;
import com.intellij.codeInsight.CodeInsightSettings;
import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.EditorModificationUtil;
import com.intellij.openapi.editor.EditorModificationUtilEx;
import com.intellij.openapi.editor.highlighter.HighlighterIterator;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.psi.impl.source.BasicJavaTokenSet;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.text.CharArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
public abstract class AbstractBasicJavaTypedHandler extends TypedHandlerDelegate {
private boolean myJavaLTTyped;
protected AbstractBasicJavaTypedHandler() {
}
protected abstract boolean isJavaFile(@NotNull PsiFile file);
protected abstract boolean isJspFile(@NotNull PsiFile file);
protected abstract void autoPopupMemberLookup(@NotNull Project project, @NotNull Editor editor);
protected abstract void autoPopupJavadocLookup(@NotNull final Project project, @NotNull final Editor editor);
protected abstract boolean isLanguageLevel5OrHigher(@NotNull PsiFile file);
@NotNull
protected abstract Result processWhileAndIfStatementBody(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file);
/**
* Automatically inserts parentheses if != or == was typed after a&b, a|b or a^b where a and b are numbers.
*
* @return true if the '=' char was processed
*/
public abstract boolean handleEquality(Project project, Editor editor, PsiFile file, int offsetBefore);
/**
* Automatically insert parentheses around the ?: when necessary.
*
* @return true if question mark was handled
*/
protected abstract boolean handleQuestionMark(Project project, Editor editor, PsiFile file, int offsetBefore);
protected abstract boolean handleAnnotationParameter(Project project, @NotNull Editor editor, @NotNull PsiFile file);
@NotNull
@Override
public Result beforeCharTyped(final char c,
@NotNull final Project project,
@NotNull final Editor editor,
@NotNull final PsiFile file,
@NotNull final FileType fileType) {
if (!isJavaFile(file)) return Result.CONTINUE;
if (c == '@') {
autoPopupJavadocLookup(project, editor);
}
else if (c == '#' || c == '.') {
autoPopupMemberLookup(project, editor);
}
int offsetBefore = editor.getCaretModel().getOffset();
//important to calculate before inserting charTyped
myJavaLTTyped = '<' == c &&
!isJspFile(file) &&
CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET &&
isLanguageLevel5OrHigher(file) &&
TypedHandlerUtil.isAfterClassLikeIdentifierOrDot(offsetBefore, editor, JavaTokenType.DOT, JavaTokenType.IDENTIFIER,
true);
if ('>' == c) {
if (!isJspFile(file) && CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET && isLanguageLevel5OrHigher(file)) {
if (TypedHandlerUtil.handleGenericGT(editor, JavaTokenType.LT, JavaTokenType.GT, JavaTypingTokenSets.INVALID_INSIDE_REFERENCE)) {
return Result.STOP;
}
}
}
if (c == '?') {
if (handleQuestionMark(project, editor, file, offsetBefore)) {
return Result.STOP;
}
}
if (c == '=') {
if (handleEquality(project, editor, file, offsetBefore)) {
return Result.STOP;
}
}
if (c == ';') {
if (handleSemicolon(project, editor, file, fileType)) return Result.STOP;
}
if (fileType instanceof JavaFileType && c == '{') {
int offset = editor.getCaretModel().getOffset();
if (offset == 0) {
return Result.CONTINUE;
}
HighlighterIterator iterator = editor.getHighlighter().createIterator(offset - 1);
while (!iterator.atEnd() && iterator.getTokenType() == TokenType.WHITE_SPACE) {
iterator.retreat();
}
if (iterator.atEnd() || iterator.getTokenType() == JavaTokenType.RBRACKET || iterator.getTokenType() == JavaTokenType.EQ) {
return Result.CONTINUE;
}
Document doc = editor.getDocument();
PsiDocumentManager.getInstance(project).commitDocument(doc);
final PsiElement leaf = file.findElementAt(offset);
if (BasicJavaAstTreeUtil.getParentOfType(leaf, BASIC_ARRAY_INITIALIZER_EXPRESSION, false,
BasicJavaTokenSet.orSet(BasicJavaTokenSet.create(BASIC_CODE_BLOCK), MEMBER_SET)) != null) {
return Result.CONTINUE;
}
PsiElement st = leaf != null ? leaf.getParent() : null;
PsiElement prev = offset > 1 ? file.findElementAt(offset - 1) : null;
if (CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET && isRparenth(leaf) &&
st != null &&
(BasicJavaAstTreeUtil.is(st.getNode(), BASIC_WHILE_STATEMENT) ||
BasicJavaAstTreeUtil.is(st.getNode(), BASIC_IF_STATEMENT)) &&
shouldInsertStatementBody(st, doc, prev)) {
return processWhileAndIfStatementBody(project, editor, file);
}
if (BasicJavaAstTreeUtil.getParentOfType(leaf, BASIC_CODE_BLOCK, false, MEMBER_SET) != null &&
!shouldInsertPairedBrace(leaf)) {
EditorModificationUtilEx.insertStringAtCaret(editor, "{");
TypedHandler.indentOpenedBrace(project, editor);
return Result.STOP; // use case: manually wrapping part of method's code in 'if', 'while', etc
}
}
return Result.CONTINUE;
}
private static boolean shouldInsertPairedBrace(@NotNull PsiElement leaf) {
PsiElement prevLeaf = PsiTreeUtil.prevVisibleLeaf(leaf);
// lambda
if (prevLeaf != null && prevLeaf.getNode().getElementType() == JavaTokenType.ARROW) return true;
// anonymous class
BasicJavaTokenSet stopAt = BasicJavaTokenSet.orSet(MEMBER_SET, BasicJavaTokenSet.create(BASIC_CODE_BLOCK));
if (BasicJavaAstTreeUtil.getParentOfType(prevLeaf, BASIC_NEW_EXPRESSION, true, stopAt) != null) return true;
// local class
if (prevLeaf != null && prevLeaf.getParent() != null && BasicJavaAstTreeUtil.is(prevLeaf.getNode(), JavaTokenType.IDENTIFIER) &&
BasicJavaAstTreeUtil.is(prevLeaf.getParent().getNode(), CLASS_SET)) {
return true;
}
// local record
if (prevLeaf != null && prevLeaf.getParent() != null && prevLeaf.getNode().getElementType() == JavaTokenType.RPARENTH &&
BasicJavaAstTreeUtil.is(prevLeaf.getParent().getNode(), BASIC_RECORD_HEADER)) {
return true;
}
return false;
}
private static boolean shouldInsertStatementBody(@NotNull PsiElement statement, @NotNull Document doc, @Nullable PsiElement prev) {
ASTNode block;
ASTNode astNodeStatement = statement.getNode();
if (BasicJavaAstTreeUtil.is(astNodeStatement, BASIC_WHILE_STATEMENT)) {
block = BasicJavaAstTreeUtil.getBlock(astNodeStatement);
}
else {
block = BasicJavaAstTreeUtil.getThenBranch(astNodeStatement);
}
ASTNode condition = BasicJavaAstTreeUtil.findChildByType(astNodeStatement, EXPRESSION_SET);
ASTNode latestExpression = BasicJavaAstTreeUtil.getParentOfType(BasicJavaAstTreeUtil.toNode(prev), EXPRESSION_SET);
if (BasicJavaAstTreeUtil.is(latestExpression, BASIC_NEW_EXPRESSION) &&
BasicJavaAstTreeUtil.getAnonymousClass(latestExpression) == null) {
return false;
}
return !(BasicJavaAstTreeUtil.is(block, BASIC_BLOCK_STATEMENT)) &&
(block == null || startLine(doc, block) != startLine(doc, astNodeStatement) || condition == null);
}
private static boolean isRparenth(@Nullable PsiElement leaf) {
if (leaf == null) return false;
if (leaf.getNode().getElementType() == JavaTokenType.RPARENTH) return true;
PsiElement next = PsiTreeUtil.nextVisibleLeaf(leaf);
if (next == null) return false;
return next.getNode().getElementType() == JavaTokenType.RPARENTH;
}
private static int startLine(@NotNull Document doc, @NotNull ASTNode astNode) {
return doc.getLineNumber(astNode.getTextRange().getStartOffset());
}
@NotNull
@Override
public Result charTyped(final char c, @NotNull final Project project, @NotNull final Editor editor, @NotNull final PsiFile file) {
if (!(isJavaFile(file))) return Result.CONTINUE;
if (myJavaLTTyped) {
myJavaLTTyped = false;
TypedHandlerUtil.handleAfterGenericLT(editor, JavaTokenType.LT, JavaTokenType.GT, JavaTypingTokenSets.INVALID_INSIDE_REFERENCE);
return Result.STOP;
}
else if (c == ':') {
if (autoIndentCase(editor, project, file)) {
return Result.STOP;
}
}
else if (c == ',' && handleAnnotationParameter(project, editor, file)) {
return Result.STOP;
}
else if (c == '.') {
if (handleDotTyped(project, editor, file)) {
return Result.STOP;
}
}
return Result.CONTINUE;
}
private static boolean handleDotTyped(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
int offset = editor.getCaretModel().getOffset() - 1;
if (offset >= 0) {
Document document = editor.getDocument();
int line = document.getLineNumber(offset);
int lineStart = document.getLineStartOffset(line);
if (StringUtil.isEmptyOrSpaces(document.getCharsSequence().subSequence(lineStart, offset))) {
PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument());
CodeStyleManager.getInstance(project).adjustLineIndent(file, offset);
return true;
}
}
return false;
}
private static boolean handleSemicolon(@NotNull Project project,
@NotNull Editor editor,
@NotNull PsiFile file,
@NotNull FileType fileType) {
if (!(fileType instanceof JavaFileType)) return false;
int offset = editor.getCaretModel().getOffset();
if (offset == editor.getDocument().getTextLength()) return false;
if (moveSemicolonAtRParen(project, editor, file, offset)) return true;
char charAt = editor.getDocument().getCharsSequence().charAt(offset);
if (charAt != ';') return false;
HighlighterIterator hi = editor.getHighlighter().createIterator(offset);
if (hi.atEnd() || hi.getTokenType() != JavaTokenType.SEMICOLON) return false;
EditorModificationUtil.moveCaretRelatively(editor, 1);
return true;
}
private static boolean moveSemicolonAtRParen(@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;
}
private static boolean isAtLineEnd(HighlighterIterator it) {
if (it.getTokenType() == TokenType.WHITE_SPACE) {
CharSequence tokenText = it.getDocument().getImmutableCharSequence().subSequence(it.getStart(), it.getEnd());
return CharArrayUtil.containLineBreaks(tokenText);
}
return false;
}
private static boolean autoIndentCase(Editor editor, Project project, PsiFile file) {
int offset = editor.getCaretModel().getOffset();
PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument());
PsiElement currElement = file.findElementAt(offset - 1);
if (currElement != null) {
PsiElement parent = currElement.getParent();
if (BasicJavaAstTreeUtil.is(BasicJavaAstTreeUtil.toNode(parent), BASIC_SWITCH_LABEL_STATEMENT)) {
CodeStyleManager.getInstance(project).adjustLineIndent(file, parent.getTextOffset());
return true;
}
}
return false;
}
}
@@ -0,0 +1,207 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions;
import com.intellij.codeInsight.CodeInsightSettings;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.psi.impl.source.BasicJavaTokenSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static com.intellij.psi.impl.source.BasicJavaDocElementType.*;
import static com.intellij.util.text.CharArrayUtil.containsOnlyWhiteSpaces;
/**
* Advises typing in javadoc if necessary.
*/
public abstract class AbstractBasicJavadocTypedHandler extends TypedHandlerDelegate {
private static final char START_TAG_SYMBOL = '<';
private static final char CLOSE_TAG_SYMBOL = '>';
private static final char SLASH = '/';
private static final String COMMENT_PREFIX = "!--";
protected AbstractBasicJavadocTypedHandler() {
}
@NotNull
@Override
public Result charTyped(char c, @NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
if (isJavaFile(file)) {
if (!insertClosingTagIfNecessary(c, project, editor, file)) {
adjustStartTagIndent(c, editor, file);
}
}
return Result.CONTINUE;
}
public abstract boolean isJavaFile(@Nullable PsiFile file);
private static void adjustStartTagIndent(char c, @NotNull Editor editor, @NotNull PsiFile file) {
if (c == '@') {
final int offset = editor.getCaretModel().getOffset();
PsiElement currElement = file.findElementAt(offset);
if (currElement instanceof PsiWhiteSpace) {
PsiElement prev = currElement.getPrevSibling();
if (prev != null && prev.getNode().getElementType() == JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS) {
editor.getDocument().replaceString(currElement.getTextRange().getStartOffset(), offset - 1, " ");
}
}
}
}
/**
* Checks if it's necessary to insert closing tag on typed character.
*
* @param c typed symbol
* @param project current project
* @param editor current editor
* @param file current file
* @return {@code true} if closing tag is inserted; {@code false} otherwise
*/
private boolean insertClosingTagIfNecessary(char c, @NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
if (c != CLOSE_TAG_SYMBOL || !CodeInsightSettings.getInstance().JAVADOC_GENERATE_CLOSING_TAG) {
return false;
}
PsiDocumentManager.getInstance(project).commitAllDocuments();
if (!isAppropriatePlace(editor, file)) {
return false;
}
// Inspect symbols to the left of the current caret position, insert closing tag only if valid tag is just typed
// (e.g. don't insert anything on single '>' symbol typing).
int offset = editor.getCaretModel().getOffset();
Document document = editor.getDocument();
String tagName = getTagName(document.getText(), offset);
if (tagName == null || isSingleHtmlTag(tagName) || tagName.startsWith(COMMENT_PREFIX)) {
return false;
}
document.insertString(offset, String.valueOf(START_TAG_SYMBOL) + SLASH + tagName + CLOSE_TAG_SYMBOL);
return true;
}
public abstract boolean isSingleHtmlTag(@NotNull String tagName);
/**
* Tries to derive start tag name assuming that given offset points to position just after {@code '>'} symbol.
* <p/>
* Is expected to return {@code null} when offset is not located just after start tag, e.g. the following situations:
* <pre>
* <ul>
* <li>standalone {@code '>'} symbol (surrounded by white spaces);</li>
* <li>after end tag {@code <mytag><mytag>[caret]};</li>
* <li>after empty element tag {@code <p/>[caret]};</li>
* </ul>
* </pre>
*
* @param text target text
* @param afterTagOffset offset that points after
* @return tag name if the one is parsed; {@code null} otherwise
*/
@Nullable
public static String getTagName(@NotNull CharSequence text, int afterTagOffset) {
if (afterTagOffset > text.length()) {
return null;
}
int endOffset = afterTagOffset - 1;
// Check empty element like <p/>
if (endOffset > 0 && text.charAt(endOffset - 1) == SLASH) {
return null;
}
for (int i = endOffset - 1; i >= 0; i--) {
char c = text.charAt(i);
switch (c) {
case '\n' -> {
return null;
}
case CLOSE_TAG_SYMBOL -> {
return null;
}
case START_TAG_SYMBOL -> {
if (text.charAt(i + 1) == SLASH) {
// Handle situation like <tag></tag>[offset].
return null;
}
return text.subSequence(i + 1, endOffset).toString();
}
// There is a possible case that opening tag has attributes, e.g. <a href='bla-bla-bla'>[offset]. We want to extract
// only tag name then.
case ' ', '\t' -> endOffset = i;
}
}
return null;
}
private static boolean isAppropriatePlace(Editor editor, PsiFile file) {
FileViewProvider provider = file.getViewProvider();
int offset = editor.getCaretModel().getOffset();
final PsiElement elementAtCaret;
if (offset < editor.getDocument().getTextLength()) {
elementAtCaret = provider.findElementAt(offset);
}
else {
elementAtCaret = provider.findElementAt(editor.getDocument().getTextLength() - 1);
}
PsiElement element = elementAtCaret;
while (element instanceof PsiWhiteSpace || element != null && containsOnlyWhiteSpaces(element.getText())) {
element = element.getPrevSibling();
}
if (element == null) {
return false;
}
ASTNode astNode = BasicJavaAstTreeUtil.toNode(element);
if (BasicJavaAstTreeUtil.is(astNode, DOC_PARAMETER_REF)) {
astNode = astNode.getTreeParent();
}
if (BasicJavaAstTreeUtil.is(astNode, DOC_TAG, DOC_SNIPPET_TAG, DOC_INLINE_TAG) &&
"param".equals(BasicJavaAstTreeUtil.getTagName(astNode)) &&
isTypeParamBracketClosedAfterParamTag(astNode, offset)) {
return false;
}
// The contents of inline tags is not HTML, so the paired tag completion isn't appropriate there.
if (BasicJavaAstTreeUtil.is(astNode, DOC_INLINE_TAG, DOC_SNIPPET_TAG) ||
BasicJavaAstTreeUtil.getParentOfType(astNode, BasicJavaTokenSet.create(DOC_INLINE_TAG, DOC_SNIPPET_TAG)) != null) {
return false;
}
ASTNode node = element.getNode();
return node != null
&& (JavaDocTokenType.ALL_JAVADOC_TOKENS.contains(node.getElementType())
|| ALL_JAVADOC_ELEMENTS.contains(node.getElementType()));
}
private static boolean isTypeParamBracketClosedAfterParamTag(ASTNode tag, int bracketOffset) {
ASTNode paramToDocument = getDocumentingParameter(tag);
if (paramToDocument == null) return false;
TextRange paramRange = paramToDocument.getTextRange();
return paramRange.getEndOffset() == bracketOffset;
}
@Nullable
private static ASTNode getDocumentingParameter(@NotNull ASTNode tag) {
for (ASTNode element = tag.getFirstChildNode(); element != null; element = element.getTreeNext()) {
if (BasicJavaAstTreeUtil.is(element, DOC_PARAMETER_REF)) {
return element;
}
}
return null;
}
}
@@ -1,18 +1,4 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions;
import com.intellij.application.options.CodeStyle;
@@ -20,7 +6,7 @@ import com.intellij.lang.Language;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.EditorModificationUtil;
import com.intellij.openapi.editor.EditorModificationUtilEx;
import com.intellij.openapi.editor.highlighter.HighlighterIterator;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.project.Project;
@@ -86,7 +72,7 @@ public class AutoFormatTypedHandler extends TypedHandlerDelegate {
return Result.CONTINUE;
}
if (isInsertSpaceAtCaret(editor, c, project)) {
EditorModificationUtil.insertStringAtCaret(editor, " ");
EditorModificationUtilEx.insertStringAtCaret(editor, " ");
}
return Result.CONTINUE;
@@ -1,26 +1,14 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2000-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInsight.editorActions;
import com.intellij.codeInsight.definition.AbstractBasicJavaDefinitionService;
import com.intellij.openapi.editor.Editor;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.AbstractBasicJavaFile;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiJavaFile;
import com.intellij.psi.util.PsiUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class JavaBackspaceHandler extends BackspaceHandlerDelegate {
private boolean myToDeleteGt;
@@ -28,12 +16,16 @@ public class JavaBackspaceHandler extends BackspaceHandlerDelegate {
@Override
public void beforeCharDeleted(char c, @NotNull PsiFile file, @NotNull Editor editor) {
myToDeleteGt = c == '<' &&
file instanceof PsiJavaFile &&
PsiUtil.isLanguageLevel5OrHigher(file) &&
isHigherThan50r(file) &&
TypedHandlerUtil.isAfterClassLikeIdentifierOrDot(editor.getCaretModel().getOffset() - 1,
editor, JavaTokenType.DOT, JavaTokenType.IDENTIFIER, true);
}
private boolean isHigherThan50r(@Nullable PsiFile file){
return file instanceof AbstractBasicJavaFile &&
AbstractBasicJavaDefinitionService.getJavaDefinitionService().getLanguageLevel(file).isAtLeast(LanguageLevel.JDK_1_5);
}
@Override
public boolean charDeleted(final char c, @NotNull final PsiFile file, @NotNull final Editor editor) {
if (c == '<' && myToDeleteGt) {
@@ -0,0 +1,45 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInsight.editorActions;
import com.intellij.codeInsight.editorActions.enter.EnterAfterUnmatchedBraceHandler;
import com.intellij.core.JavaPsiBundle;
import com.intellij.lang.ASTNode;
import com.intellij.psi.AbstractBasicJavaFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiErrorElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import org.jetbrains.annotations.NotNull;
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_EXPRESSION_LIST_STATEMENT;
import static com.intellij.psi.impl.source.BasicJavaElementType.EXPRESSION_SET;
public class JavaEnterAfterUnmatchedBraceHandler extends EnterAfterUnmatchedBraceHandler {
protected JavaEnterAfterUnmatchedBraceHandler() {
}
@Override
public boolean isApplicable(@NotNull PsiFile file, int caretOffset) {
return file instanceof AbstractBasicJavaFile;
}
@Override
protected int calculateOffsetToInsertClosingBraceInsideElement(PsiElement element) {
if (element instanceof PsiErrorElement &&
((PsiErrorElement)element).getErrorDescription().equals(JavaPsiBundle.message("else.without.if"))) {
return element.getTextRange().getStartOffset();
}
ASTNode node = BasicJavaAstTreeUtil.toNode(element);
if (BasicJavaAstTreeUtil.is(node, BASIC_EXPRESSION_LIST_STATEMENT)) {
final ASTNode list = BasicJavaAstTreeUtil.getExpressionList(node);
if (list != null) {
final ASTNode firstExpression = BasicJavaAstTreeUtil.findChildByType(list, EXPRESSION_SET);
if (firstExpression != null) {
return firstExpression.getTextRange().getEndOffset();
}
}
}
return super.calculateOffsetToInsertClosingBraceInsideElement(element);
}
}
@@ -1,7 +1,8 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInsight.editorActions;
import com.intellij.codeInsight.editorActions.enter.EnterInStringLiteralHandler;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
@@ -9,11 +10,17 @@ import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.util.ObjectUtils;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_LITERAL_EXPRESSION;
public class JavaEnterInTextBlockHandler extends EnterInStringLiteralHandler {
@@ -25,7 +32,7 @@ public class JavaEnterInTextBlockHandler extends EnterInStringLiteralHandler {
@NotNull DataContext dataContext,
EditorActionHandler originalHandler) {
int offset = editor.getCaretModel().getOffset();
PsiLiteralExpression textBlock = getTextBlockAt(file, offset);
PsiElement textBlock = getTextBlockAt(file, offset);
if (textBlock == null) return Result.Continue;
int textBlockOffset = textBlock.getTextOffset();
String text = textBlock.getText();
@@ -56,13 +63,20 @@ public class JavaEnterInTextBlockHandler extends EnterInStringLiteralHandler {
}
@Contract("null, _ -> null")
private static PsiLiteralExpression getTextBlockAt(PsiFile file, int offset) {
if (!(file instanceof PsiJavaFile)) return null;
PsiJavaToken token = ObjectUtils.tryCast(file.findElementAt(offset), PsiJavaToken.class);
if (token == null || token.getTokenType() != JavaTokenType.TEXT_BLOCK_LITERAL) return null;
return ObjectUtils.tryCast(token.getParent(), PsiLiteralExpression.class);
private static PsiElement getTextBlockAt(PsiFile file, int offset) {
if (!isJavaFile(file)) return null;
PsiElement token = file.findElementAt(offset);
if (token == null || token.getNode() == null || !BasicJavaAstTreeUtil.is(token.getNode(), JavaTokenType.TEXT_BLOCK_LITERAL)) return null;
PsiElement parent = token.getParent();
if (!BasicJavaAstTreeUtil.is(BasicJavaAstTreeUtil.toNode(parent), BASIC_LITERAL_EXPRESSION)) {
return null;
}
return parent;
}
private static boolean isJavaFile(@Nullable PsiFile file) {
return file != null && file.getLanguage() == JavaLanguage.INSTANCE;
}
private static int getIndent(@NotNull String text, int start) {
int indent = 0;
for (int i = start; i < text.length(); i++) {
@@ -1,30 +1,37 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInsight.editorActions;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightingFeature;
import com.intellij.codeInsight.definition.AbstractBasicJavaDefinitionService;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.highlighter.HighlighterIterator;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.impl.source.tree.ElementType;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.psi.impl.source.BasicJavaTokenSet;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import com.intellij.util.ObjectUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static com.intellij.psi.impl.source.BasicElementTypes.JAVA_COMMENT_OR_WHITESPACE_BIT_SET;
import static com.intellij.psi.impl.source.BasicElementTypes.TEXT_LITERALS;
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_LITERAL_EXPRESSION;
import static com.intellij.psi.impl.source.BasicJavaElementType.REFERENCE_EXPRESSION_SET;
public class JavaQuoteHandler extends SimpleTokenSetQuoteHandler implements JavaLikeQuoteHandler, MultiCharQuoteHandler {
private final TokenSet myConcatenableStrings = TokenSet.create(JavaTokenType.STRING_LITERAL);
private final TokenSet myAppropriateElementTypeForLiteral = TokenSet.orSet(
JavaDocTokenType.ALL_JAVADOC_TOKENS,
ElementType.JAVA_COMMENT_OR_WHITESPACE_BIT_SET, ElementType.TEXT_LITERALS,
TokenSet.create(JavaTokenType.SEMICOLON, JavaTokenType.COMMA, JavaTokenType.RPARENTH, JavaTokenType.RBRACKET, JavaTokenType.RBRACE));
private final BasicJavaTokenSet myConcatenableStrings = BasicJavaTokenSet.create(JavaTokenType.STRING_LITERAL);
private final BasicJavaTokenSet myAppropriateElementTypeForLiteral = BasicJavaTokenSet.orSet(
BasicJavaTokenSet.create(JavaDocTokenType.ALL_JAVADOC_TOKENS),
JAVA_COMMENT_OR_WHITESPACE_BIT_SET, TEXT_LITERALS,
BasicJavaTokenSet.create(JavaTokenType.SEMICOLON, JavaTokenType.COMMA, JavaTokenType.RPARENTH, JavaTokenType.RBRACKET,
JavaTokenType.RBRACE));
public JavaQuoteHandler() {
super(TokenSet.orSet(ElementType.TEXT_LITERALS, TokenSet.create(JavaDocTokenType.DOC_TAG_VALUE_QUOTE)));
super(BasicJavaTokenSet.orSet(TEXT_LITERALS, BasicJavaTokenSet.create(JavaDocTokenType.DOC_TAG_VALUE_QUOTE)).toTokenSet());
}
@Override
@@ -65,9 +72,8 @@ public class JavaQuoteHandler extends SimpleTokenSetQuoteHandler implements Java
@NotNull
@Override
@SuppressWarnings("SpellCheckingInspection")
public TokenSet getConcatenatableStringTokenTypes() {
return myConcatenableStrings;
return myConcatenableStrings.toTokenSet();
}
@Override
@@ -88,7 +94,9 @@ public class JavaQuoteHandler extends SimpleTokenSetQuoteHandler implements Java
@Override
public boolean needParenthesesAroundConcatenation(PsiElement element) {
// example code: "some string".length() must become ("some" + " string").length()
return element.getParent() instanceof PsiLiteralExpression && element.getParent().getParent() instanceof PsiReferenceExpression;
return element != null && element.getParent() != null && element.getParent().getParent() != null &&
BasicJavaAstTreeUtil.is(element.getParent().getNode(), BASIC_LITERAL_EXPRESSION) &&
BasicJavaAstTreeUtil.is(element.getParent().getParent().getNode(), REFERENCE_EXPRESSION_SET);
}
@Nullable
@@ -103,7 +111,7 @@ public class JavaQuoteHandler extends SimpleTokenSetQuoteHandler implements Java
Document document = editor.getDocument();
Project project = editor.getProject();
PsiFile file = project == null ? null : PsiDocumentManager.getInstance(project).getPsiFile(document);
if (file == null || !HighlightingFeature.TEXT_BLOCKS.isAvailable(file)) return false;
if (file == null || !testBlocksIsAvailable(file)) return false;
String text = document.getText();
boolean hasOpenQuotes = StringUtil.equals(text.substring(iterator.getStart(), offset + 1), "\"\"\"");
if (hasOpenQuotes) {
@@ -117,16 +125,22 @@ public class JavaQuoteHandler extends SimpleTokenSetQuoteHandler implements Java
return super.hasNonClosedLiteral(editor, iterator, offset);
}
private static boolean testBlocksIsAvailable(@NotNull PsiFile file){
return AbstractBasicJavaDefinitionService.getJavaDefinitionService()
.getLanguageLevel(file).isAtLeast(LanguageLevel.JDK_15);
}
@Override
public void insertClosingQuote(@NotNull Editor editor, int offset, @NotNull PsiFile file, @NotNull CharSequence closingQuote) {
editor.getDocument().insertString(offset, "\n\"\"\"");
Project project = file.getProject();
PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument());
PsiJavaToken token = ObjectUtils.tryCast(file.findElementAt(offset), PsiJavaToken.class);
PsiElement token = file.findElementAt(offset);
if (token == null) return;
PsiLiteralExpression textBlock = ObjectUtils.tryCast(token.getParent(), PsiLiteralExpression.class);
if (textBlock == null) return;
CodeStyleManager.getInstance(project).reformat(textBlock);
editor.getCaretModel().moveToOffset(textBlock.getTextRange().getEndOffset() - 3);
PsiElement parent = token.getParent();
if (parent != null && BasicJavaAstTreeUtil.is(parent.getNode(), BASIC_LITERAL_EXPRESSION)) {
CodeStyleManager.getInstance(project).reformat(parent);
editor.getCaretModel().moveToOffset(parent.getTextRange().getEndOffset() - 3);
}
}
}
@@ -1,26 +1,26 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions;
import com.intellij.lang.java.parser.ExpressionParser;
import com.intellij.lang.java.parser.BasicExpressionParser;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.tree.TokenSet;
final class JavaTypingTokenSets {
public final class JavaTypingTokenSets {
private JavaTypingTokenSets() {
}
static final TokenSet INVALID_INSIDE_REFERENCE = TokenSet.create(JavaTokenType.SEMICOLON, JavaTokenType.LBRACE, JavaTokenType.RBRACE);
static final TokenSet UNWANTED_TOKEN_AT_QUESTION =
public static final TokenSet UNWANTED_TOKEN_AT_QUESTION =
TokenSet.create(JavaTokenType.C_STYLE_COMMENT, JavaTokenType.END_OF_LINE_COMMENT, JavaTokenType.CHARACTER_LITERAL,
JavaTokenType.STRING_LITERAL, JavaTokenType.TEXT_BLOCK_LITERAL);
static final TokenSet UNWANTED_TOKEN_BEFORE_QUESTION =
TokenSet.orSet(ExpressionParser.ASSIGNMENT_OPS, TokenSet.create(JavaTokenType.QUEST, JavaTokenType.COLON));
public static final TokenSet UNWANTED_TOKEN_BEFORE_QUESTION =
TokenSet.orSet(BasicExpressionParser.ASSIGNMENT_OPS, TokenSet.create(JavaTokenType.QUEST, JavaTokenType.COLON));
static final TokenSet WANTED_TOKEN_BEFORE_QUESTION =
public static final TokenSet WANTED_TOKEN_BEFORE_QUESTION =
// Tokens that may appear before ?: in polyadic expression that may have non-boolean result
TokenSet.orSet(
TokenSet.create(JavaTokenType.OR, JavaTokenType.XOR, JavaTokenType.AND),
ExpressionParser.SHIFT_OPS, ExpressionParser.ADDITIVE_OPS, ExpressionParser.MULTIPLICATIVE_OPS);
BasicExpressionParser.SHIFT_OPS, BasicExpressionParser.ADDITIVE_OPS, BasicExpressionParser.MULTIPLICATIVE_OPS);
}
@@ -1,4 +1,4 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions;
import com.intellij.openapi.editor.actions.WordBoundaryFilter;
@@ -1,4 +1,4 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInsight.editorActions;
import com.intellij.openapi.actionSystem.DataContext;
@@ -10,22 +10,23 @@ import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
import com.intellij.openapi.editor.ex.EditorSettingsExternalizable;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.javadoc.PsiDocToken;
import com.intellij.psi.tree.IElementType;
import com.intellij.util.text.CharArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class JavadocLineStartHandler extends EditorActionHandler.ForEachCaret {
private static final String WHITESPACE = " \t";
private final EditorActionHandler myOriginalHandler;
private final boolean myWithSelection;
public JavadocLineStartHandler(EditorActionHandler originalHandler) {
public JavadocLineStartHandler(@NotNull EditorActionHandler originalHandler) {
this(originalHandler, false);
}
JavadocLineStartHandler(EditorActionHandler originalHandler, boolean withSelection) {
public JavadocLineStartHandler(@NotNull EditorActionHandler originalHandler,
boolean withSelection) {
myOriginalHandler = originalHandler;
myWithSelection = withSelection;
}
@@ -41,29 +42,34 @@ public class JavadocLineStartHandler extends EditorActionHandler.ForEachCaret {
if (CharArrayUtil.regionMatches(text, nonWsStartOffset, "/**") || CharArrayUtil.regionMatches(text, nonWsStartOffset, "*")) {
PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(project);
PsiFile file = psiDocumentManager.getPsiFile(document);
if (file instanceof PsiJavaFile) {
if (file != null && isJavaFile(file)) {
psiDocumentManager.commitDocument(document);
PsiElement startElement = file.findElementAt(nonWsStartOffset);
if (startElement instanceof PsiDocToken) {
IElementType type = ((PsiDocToken)startElement).getTokenType();
if (type == JavaDocTokenType.DOC_COMMENT_START || type == JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS) {
int targetOffset = CharArrayUtil.shiftForward(text, startElement.getTextRange().getEndOffset(), WHITESPACE);
if (caret.getOffset() == targetOffset) targetOffset = lineStartOffset;
int selectionStartOffset = caret.getLeadSelectionOffset();
caret.moveToOffset(targetOffset);
if (myWithSelection) {
caret.setSelection(selectionStartOffset, caret.getVisualPosition(), caret.getOffset());
}
else {
caret.removeSelection();
}
EditorModificationUtil.scrollToCaret(editor);
return;
if (startElement == null || startElement.getNode() == null) {
return;
}
IElementType type = startElement.getNode().getElementType();
if (type == JavaDocTokenType.DOC_COMMENT_START || type == JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS) {
int targetOffset = CharArrayUtil.shiftForward(text, startElement.getTextRange().getEndOffset(), WHITESPACE);
if (caret.getOffset() == targetOffset) targetOffset = lineStartOffset;
int selectionStartOffset = caret.getLeadSelectionOffset();
caret.moveToOffset(targetOffset);
if (myWithSelection) {
caret.setSelection(selectionStartOffset, caret.getVisualPosition(), caret.getOffset());
}
else {
caret.removeSelection();
}
EditorModificationUtil.scrollToCaret(editor);
return;
}
}
}
}
myOriginalHandler.execute(editor, caret, dataContext);
myOriginalHandler.execute(editor, caret, dataContext);
}
private boolean isJavaFile(@Nullable PsiFile file){
return file instanceof AbstractBasicJavaFile;
}
}
@@ -1,4 +1,4 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions;
import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
@@ -0,0 +1,22 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiElement;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import org.jetbrains.annotations.NotNull;
public interface ASTNodeEnterProcessor extends EnterProcessor {
boolean doEnter(@NotNull Editor editor, @NotNull ASTNode psiElement, boolean isModified);
@Override
default boolean doEnter(Editor editor, PsiElement psiElement, boolean isModified) {
ASTNode node = BasicJavaAstTreeUtil.toNode(psiElement);
if (node == null) {
return false;
}
return doEnter(editor, node, isModified);
}
}
@@ -0,0 +1,398 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.application.options.CodeStyle;
import com.intellij.codeInsight.lookup.LookupManager;
import com.intellij.lang.ASTNode;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.actionSystem.IdeActions;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
import com.intellij.openapi.editor.actionSystem.EditorActionManager;
import com.intellij.openapi.editor.ex.util.EditorUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.SyntaxTraverser;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.text.CharArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import static com.intellij.psi.impl.source.BasicElementTypes.JAVA_COMMENT_BIT_SET;
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
public abstract class AbstractBasicJavaSmartEnterProcessor extends SmartEnterProcessor {
private static final Logger LOG = Logger.getInstance(AbstractBasicJavaSmartEnterProcessor.class);
private final List<Fixer> ourFixers;
private final EnterProcessor[] ourEnterProcessors;
private final EnterProcessor[] ourAfterCompletionEnterProcessors;
protected int myFirstErrorOffset = Integer.MAX_VALUE;
protected boolean mySkipEnter;
private static final int MAX_ATTEMPTS = 20;
private static final Key<Long> SMART_ENTER_TIMESTAMP = Key.create("smartEnterOriginalTimestamp");
private final EnterProcessor myBreakerEnterProcessor;
protected void insertBraces(@NotNull Editor editor, int offset) {
Document document = editor.getDocument();
document.insertString(offset, "{");
insertCloseBrace(editor, offset + 1);
}
protected void insertCloseBrace(@NotNull Editor editor, int offset) {
Document document = editor.getDocument();
document.insertString(offset, "}");
}
protected void insertBracesWithNewLine(Editor editor, int offset) {
Document document = editor.getDocument();
document.insertString(offset, "{\n");
insertCloseBrace(editor, offset + 2);
}
private static class TooManyAttemptsException extends Exception {
}
private final AbstractBasicJavadocFixer myJavadocFixer;
protected AbstractBasicJavaSmartEnterProcessor(@NotNull List<Fixer> fixers,
EnterProcessor @NotNull [] enterProcessors,
EnterProcessor @NotNull [] afterCompletionEnterProcessors,
@NotNull AbstractBasicJavadocFixer thinJavadocFixer,
@NotNull EnterProcessor breakerEnterProcessor) {
myBreakerEnterProcessor = breakerEnterProcessor;
ourFixers = fixers;
ourEnterProcessors = enterProcessors;
ourAfterCompletionEnterProcessors = afterCompletionEnterProcessors;
myJavadocFixer = thinJavadocFixer;
}
@Override
public boolean process(@NotNull final Project project, @NotNull final Editor editor, @NotNull final PsiFile psiFile) {
return invokeProcessor(editor, psiFile, false);
}
@Override
public boolean processAfterCompletion(@NotNull Editor editor, @NotNull PsiFile psiFile) {
return invokeProcessor(editor, psiFile, true);
}
private boolean invokeProcessor(Editor editor, PsiFile psiFile, boolean afterCompletion) {
final Document document = editor.getDocument();
final CharSequence textForRollback = document.getImmutableCharSequence();
try {
editor.putUserData(SMART_ENTER_TIMESTAMP, editor.getDocument().getModificationStamp());
myFirstErrorOffset = Integer.MAX_VALUE;
mySkipEnter = false;
process(editor, psiFile, 0, afterCompletion);
}
catch (TooManyAttemptsException e) {
document.replaceString(0, document.getTextLength(), textForRollback);
}
catch (Exception e) {
e.printStackTrace();
}
finally {
editor.putUserData(SMART_ENTER_TIMESTAMP, null);
}
return true;
}
private void process(@NotNull final Editor editor, @NotNull final PsiFile file, final int attempt, boolean afterCompletion)
throws TooManyAttemptsException {
if (attempt > MAX_ATTEMPTS) throw new TooManyAttemptsException();
try {
commit(editor);
if (myFirstErrorOffset != Integer.MAX_VALUE) {
editor.getCaretModel().moveToOffset(myFirstErrorOffset);
}
myFirstErrorOffset = Integer.MAX_VALUE;
PsiElement atCaret = getStatementAtCaret(editor, file);
if (atCaret == null) {
if (myJavadocFixer.process(editor, file)) {
return;
}
if (!(myBreakerEnterProcessor).doEnter(editor, file, false)) {
plainEnter(editor);
}
return;
}
List<ASTNode> queue = new ArrayList<>();
ASTNode caretNode = atCaret.getNode();
collectAllElements(caretNode, queue, true);
queue.add(caretNode);
for (ASTNode astNode : queue) {
for (Fixer fixer : ourFixers) {
Document document = editor.getDocument();
int offset = myFirstErrorOffset;
long stamp = document.getModificationStamp();
fixer.apply(editor, this, astNode);
Project project = file.getProject();
if (document.getModificationStamp() != stamp || offset != myFirstErrorOffset) {
log(fixer, project);
}
if (LookupManager.getInstance(project).getActiveLookup() != null) {
return;
}
PsiElement psi = BasicJavaAstTreeUtil.toPsi(astNode);
if (isUncommited(project) || !(psi != null && psi.isValid())) {
moveCaretInsideBracesIfAny(editor, file);
process(editor, file, attempt + 1, afterCompletion);
return;
}
}
}
doEnter(atCaret, editor, afterCompletion);
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
}
protected abstract void log(@NotNull Fixer fixer, @NotNull Project project);
@Override
public void reformat(PsiElement atCaretElement) throws IncorrectOperationException {
if (atCaretElement == null) {
return;
}
ASTNode atCaret = atCaretElement.getNode();
ASTNode parent = atCaret.getTreeParent();
if (BasicJavaAstTreeUtil.is(parent, BASIC_FOR_STATEMENT)) {
atCaret = parent;
}
if (BasicJavaAstTreeUtil.is(parent, BASIC_IF_STATEMENT) &&
atCaret == BasicJavaAstTreeUtil.getElseBranch(parent)) {
PsiFile file = atCaretElement.getContainingFile();
Document document = file.getViewProvider().getDocument();
if (document != null) {
TextRange elseIfRange = atCaret.getTextRange();
int lineStart = document.getLineStartOffset(document.getLineNumber(elseIfRange.getStartOffset()));
CodeStyleManager.getInstance(atCaretElement.getProject()).reformatText(file, lineStart, elseIfRange.getEndOffset());
return;
}
}
super.reformat(atCaretElement);
}
private void doEnter(PsiElement atCaret, Editor editor, boolean afterCompletion) throws IncorrectOperationException {
final PsiFile psiFile = atCaret.getContainingFile();
if (myFirstErrorOffset != Integer.MAX_VALUE) {
editor.getCaretModel().moveToOffset(myFirstErrorOffset);
reformat(editor, atCaret);
return;
}
final RangeMarker rangeMarker = createRangeMarker(atCaret);
reformat(editor, atCaret);
commit(editor);
if (!mySkipEnter) {
ASTNode atCaretNode = BasicJavaAstTreeUtil.findElementInRange(psiFile, rangeMarker.getStartOffset(), rangeMarker.getEndOffset(),
atCaret.getNode().getElementType());
for (EnterProcessor processor : afterCompletion ? ourAfterCompletionEnterProcessors : ourEnterProcessors) {
if (atCaretNode == null) {
// Can't restore element at caret after enter processor execution!
break;
}
if (processor.doEnter(editor, BasicJavaAstTreeUtil.toPsi(atCaretNode), isModified(editor))) {
rangeMarker.dispose();
return;
}
}
if (!isModified(editor) && !afterCompletion) {
plainEnter(editor);
}
else {
if (myFirstErrorOffset == Integer.MAX_VALUE) {
editor.getCaretModel().moveToOffset(rangeMarker.getEndOffset());
}
else {
editor.getCaretModel().moveToOffset(myFirstErrorOffset);
}
}
}
rangeMarker.dispose();
}
private static void collectAllElements(ASTNode atCaret, List<? super ASTNode> res, boolean recurse) {
res.add(0, atCaret);
if (doNotStepInto(atCaret)) {
if (!recurse) return;
recurse = false;
}
final List<ASTNode> children = BasicJavaAstTreeUtil.getChildren(atCaret);
for (ASTNode child : children) {
if (BasicJavaAstTreeUtil.is(atCaret, STATEMENT_SET) &&
BasicJavaAstTreeUtil.is(child, STATEMENT_SET) &&
!(BasicJavaAstTreeUtil.is(atCaret, BASIC_FOR_STATEMENT)
&& child == (BasicJavaAstTreeUtil.getForInitialization(atCaret)))) {
continue;
}
collectAllElements(child, res, recurse);
}
}
private static boolean doNotStepInto(ASTNode element) {
return BasicJavaAstTreeUtil.is(element, CLASS_SET) ||
BasicJavaAstTreeUtil.is(element, BASIC_CODE_BLOCK) ||
BasicJavaAstTreeUtil.is(element, STATEMENT_SET) ||
BasicJavaAstTreeUtil.is(element, BASIC_METHOD);
}
@Override
@Nullable
protected PsiElement getStatementAtCaret(Editor editor, PsiFile psiFile) {
PsiElement atCaretElement = super.getStatementAtCaret(editor, psiFile);
if (atCaretElement == null) {
return null;
}
ASTNode atCaret = atCaretElement.getNode();
if (BasicJavaAstTreeUtil.isWhiteSpace(atCaret)) return null;
if (BasicJavaAstTreeUtil.is(atCaret, JavaTokenType.RBRACE)) {
atCaret = atCaret.getTreeParent();
boolean expressionEndingWithBrace = BasicJavaAstTreeUtil.is(atCaret, BASIC_ANONYMOUS_CLASS) ||
BasicJavaAstTreeUtil.is(atCaret, BASIC_ARRAY_INITIALIZER_EXPRESSION) ||
BasicJavaAstTreeUtil.is(atCaret, BASIC_CODE_BLOCK) && (
BasicJavaAstTreeUtil.is(atCaret.getTreeParent(), BASIC_LAMBDA_EXPRESSION) ||
BasicJavaAstTreeUtil.is(atCaret.getTreeParent(), BASIC_SWITCH_EXPRESSION));
if (!expressionEndingWithBrace) return null;
}
for (ASTNode each : SyntaxTraverser.astApi().parents(atCaret).skip(1)) {
PsiElement psiElement = BasicJavaAstTreeUtil.toPsi(each);
if (BasicJavaAstTreeUtil.is(each, MEMBER_SET) ||
isImportStatementBase(psiElement) ||
BasicJavaAstTreeUtil.is(each, BASIC_PACKAGE_STATEMENT) ||
BasicJavaAstTreeUtil.is(each, BASIC_ANNOTATION) &&
psiElement != null &&
PsiTreeUtil.hasErrorElements(psiElement)) {
return each.getPsi();
}
if (BasicJavaAstTreeUtil.is(each, BASIC_CODE_BLOCK) ||
BasicJavaAstTreeUtil.is(each, JAVA_COMMENT_BIT_SET)) {
return null;
}
if (BasicJavaAstTreeUtil.is(each, STATEMENT_SET)) {
return BasicJavaAstTreeUtil.is(each.getTreeParent(), BASIC_FOR_STATEMENT) &&
!PsiTreeUtil.hasErrorElements(each.getPsi()) ? each.getPsi().getParent() : each.getPsi();
}
if (BasicJavaAstTreeUtil.is(each, BASIC_CONDITIONAL_EXPRESSION) &&
PsiUtilCore.hasErrorElementChild(each.getPsi())) {
return each.getPsi();
}
}
return null;
}
protected abstract boolean isImportStatementBase(PsiElement el);
protected void moveCaretInsideBracesIfAny(@NotNull final Editor editor, @NotNull final PsiFile file) throws IncorrectOperationException {
int caretOffset = editor.getCaretModel().getOffset();
final CharSequence chars = editor.getDocument().getCharsSequence();
if (CharArrayUtil.regionMatches(chars, caretOffset, "{}")) {
caretOffset += 2;
}
else if (CharArrayUtil.regionMatches(chars, caretOffset, "{\n}")) {
caretOffset += 3;
}
caretOffset = CharArrayUtil.shiftBackward(chars, caretOffset - 1, " \t") + 1;
if (CharArrayUtil.regionMatches(chars, caretOffset - "{}".length(), "{}") ||
CharArrayUtil.regionMatches(chars, caretOffset - "{\n}".length(), "{\n}")) {
commit(editor);
final CommonCodeStyleSettings settings = CodeStyle.getSettings(file).getCommonSettings(JavaLanguage.INSTANCE);
final boolean old = settings.KEEP_SIMPLE_BLOCKS_IN_ONE_LINE;
settings.KEEP_SIMPLE_BLOCKS_IN_ONE_LINE = false;
PsiElement leaf = file.findElementAt(caretOffset - 1);
PsiElement elt = BasicJavaAstTreeUtil.toPsi(
BasicJavaAstTreeUtil.getParentOfType(BasicJavaAstTreeUtil.toNode(leaf), BASIC_CODE_BLOCK));
if (elt == null &&
leaf != null &&
leaf.getParent() != null &&
BasicJavaAstTreeUtil.is(leaf.getParent().getNode(), CLASS_SET)) {
elt = leaf.getParent();
}
reformatAndMove(editor, elt, caretOffset);
settings.KEEP_SIMPLE_BLOCKS_IN_ONE_LINE = old;
reformatBlockParentIfNeeded(editor, file);
}
}
protected void reformatAndMove(@NotNull Editor editor, @Nullable PsiElement elt, int caretOffset) {
reformat(elt);
editor.getCaretModel().moveToOffset(caretOffset - 1);
}
protected void reformat(@NotNull Editor editor, @Nullable PsiElement elt) {
reformat(elt);
}
private void reformatBlockParentIfNeeded(@NotNull Editor editor, @NotNull PsiFile file) {
commit(editor);
ASTNode block =
BasicJavaAstTreeUtil.findElementOfClassAtOffset(file, editor.getCaretModel().getOffset(), BASIC_CODE_BLOCK, false);
if (block != null &&
BasicJavaAstTreeUtil.is(block.getTreeParent(), BASIC_BLOCK_STATEMENT) &&
BasicJavaAstTreeUtil.is(block.getTreeParent().getTreeParent(), BASIC_FOR_STATEMENT)) {
reformat(block.getTreeParent().getTreeParent().getPsi());
}
}
public void registerUnresolvedError(int offset) {
if (myFirstErrorOffset > offset) {
myFirstErrorOffset = offset;
}
}
public void setSkipEnter(boolean skipEnter) {
mySkipEnter = skipEnter;
}
protected static void plainEnter(@NotNull final Editor editor) {
getEnterHandler().execute(editor, editor.getCaretModel().getCurrentCaret(), EditorUtil.getEditorDataContext(editor));
}
protected static EditorActionHandler getEnterHandler() {
return EditorActionManager.getInstance().getActionHandler(IdeActions.ACTION_EDITOR_START_NEW_LINE);
}
protected static boolean isModified(@NotNull final Editor editor) {
final Long timestamp = editor.getUserData(SMART_ENTER_TIMESTAMP);
return timestamp != null && editor.getDocument().getModificationStamp() != timestamp.longValue();
}
}
@@ -0,0 +1,113 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.javadoc.AbstractBasicJavadocHelper;
import com.intellij.openapi.editor.CaretModel;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.LogicalPosition;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
/**
* Serves as a facade for javadoc smart completion.
* <p/>
* Thread-safe.
*/
public abstract class AbstractBasicJavadocFixer {
private final AbstractBasicJavadocHelper myHelper;;
public AbstractBasicJavadocFixer(@NotNull AbstractBasicJavadocHelper helper) { myHelper = helper; }
/**
* Checks if caret of the given editor is located inside javadoc and tries to perform smart completion there in case of the positive
* answer.
*
* @param editor target editor
* @param psiFile PSI file for the document exposed via the given editor
* @return {@code true} if smart completion was performed; {@code false} otherwise
*/
public boolean process(@NotNull Editor editor, @NotNull PsiFile psiFile) {
// Check parameter description completion.
final CaretModel caretModel = editor.getCaretModel();
final Pair<AbstractBasicJavadocHelper.JavadocParameterInfo,List<AbstractBasicJavadocHelper.JavadocParameterInfo>> pair =
myHelper.parse(psiFile, editor, caretModel.getOffset());
if (pair.first == null) {
return false;
}
final AbstractBasicJavadocHelper.JavadocParameterInfo next = findNext(pair.second, pair.first);
if (next == null) {
final int line = pair.first.lastLine + 1;
final Document document = editor.getDocument();
if (line < document.getLineCount()) {
StringBuilder indent = new StringBuilder();
boolean insertIndent = true;
final CharSequence text = document.getCharsSequence();
for (int i = document.getLineStartOffset(line), max = document.getLineEndOffset(line); i < max; i++) {
final char c = text.charAt(i);
if (c == ' ' || c == '\t') {
indent.append(c);
continue;
}
else if (c == '*') {
indent.append("* ");
if (i < max - 1 && text.charAt(i + 1) != '/') {
insertIndent = false;
}
}
indent.append("\n");
break;
}
if (insertIndent) {
document.insertString(document.getLineStartOffset(line), indent);
}
}
moveCaretToTheLineEndIfPossible(editor, line);
return true;
}
if (next.parameterDescriptionStartPosition != null) {
myHelper.navigate(next.parameterDescriptionStartPosition, editor, psiFile.getProject());
}
else {
final LogicalPosition position = myHelper.calculateDescriptionStartPosition(psiFile, pair.second, next);
myHelper.navigate(position, editor, psiFile.getProject());
}
return true;
}
private static void moveCaretToTheLineEndIfPossible(@NotNull Editor editor, int line) {
final Document document = editor.getDocument();
final CaretModel caretModel = editor.getCaretModel();
int offset;
if (line >= document.getLineCount()) {
offset = document.getTextLength();
}
else {
offset = document.getLineEndOffset(line);
}
caretModel.moveToOffset(offset);
}
@Nullable
private static AbstractBasicJavadocHelper.JavadocParameterInfo findNext(@NotNull Collection<? extends AbstractBasicJavadocHelper.JavadocParameterInfo> data,
@NotNull AbstractBasicJavadocHelper.JavadocParameterInfo anchor)
{
boolean returnNow = false;
for (AbstractBasicJavadocHelper.JavadocParameterInfo info : data) {
if (returnNow) {
return info;
}
returnNow = info == anchor;
}
return null;
}
}
@@ -0,0 +1,173 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.TokenType;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.psi.impl.source.tree.TreeUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static com.intellij.psi.impl.source.BasicElementTypes.JAVA_COMMENT_OR_WHITESPACE_BIT_SET;
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
public abstract class AbstractBasicSemicolonFixer implements Fixer {
@Override
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
PsiElement psiElement = BasicJavaAstTreeUtil.toPsi(astNode);
if (fixReturn(editor, psiElement)) return;
if (fixForUpdate(editor, astNode)) return;
fixAfterLastValidElement(editor, astNode);
}
protected abstract boolean fixReturn(@NotNull Editor editor, @Nullable PsiElement astNode);
protected abstract boolean getSpaceAfterSemicolon(@NotNull PsiElement psiElement);
private boolean fixForUpdate(@NotNull Editor editor, @Nullable ASTNode astNode) {
if (!(BasicJavaAstTreeUtil.is(astNode, BASIC_FOR_STATEMENT))) {
return false;
}
ASTNode condition = BasicJavaAstTreeUtil.getForCondition(astNode);
if (BasicJavaAstTreeUtil.getForUpdate(astNode) != null || condition == null) {
return false;
}
TextRange range = condition.getTextRange();
Document document = editor.getDocument();
CharSequence text = document.getCharsSequence();
for (int i = range.getEndOffset() - 1, max = astNode.getTextRange().getEndOffset(); i < max; i++) {
if (text.charAt(i) == ';') {
return false;
}
}
String toInsert = ";";
PsiElement psiElement = BasicJavaAstTreeUtil.toPsi(astNode);
if (psiElement != null && getSpaceAfterSemicolon(psiElement)) {
toInsert += " ";
}
document.insertString(range.getEndOffset(), toInsert);
return true;
}
private void fixAfterLastValidElement(@NotNull Editor editor, @Nullable ASTNode astNode) {
PsiElement psiElement = BasicJavaAstTreeUtil.toPsi(astNode);
if (astNode == null || psiElement == null) {
return;
}
if (
BasicJavaAstTreeUtil.is(astNode, BASIC_EXPRESSION_STATEMENT) ||
BasicJavaAstTreeUtil.is(astNode, BASIC_DECLARATION_STATEMENT) ||
isImportStatementBase(psiElement) ||
BasicJavaAstTreeUtil.is(astNode, BASIC_DO_WHILE_STATEMENT) ||
BasicJavaAstTreeUtil.is(astNode, BASIC_RETURN_STATEMENT) ||
BasicJavaAstTreeUtil.is(astNode, BASIC_THROW_STATEMENT) ||
BasicJavaAstTreeUtil.is(astNode, BASIC_BREAK_STATEMENT) ||
BasicJavaAstTreeUtil.is(astNode, BASIC_CONTINUE_STATEMENT) ||
BasicJavaAstTreeUtil.is(astNode, BASIC_YIELD_STATEMENT) ||
BasicJavaAstTreeUtil.is(astNode, BASIC_ASSERT_STATEMENT) ||
BasicJavaAstTreeUtil.is(astNode, BASIC_PACKAGE_STATEMENT) ||
isStandaloneField(psiElement) ||
BasicJavaAstTreeUtil.is(astNode, BASIC_METHOD) &&
BasicJavaAstTreeUtil.getCodeBlock(astNode) == null &&
!isMethodShouldHaveBody(psiElement) ||
BasicJavaAstTreeUtil.is(astNode, BASIC_REQUIRES_STATEMENT) ||
BasicJavaAstTreeUtil.is(astNode, BASIC_OPENS_STATEMENT) ||
BasicJavaAstTreeUtil.is(astNode, BASIC_EXPORTS_STATEMENT) ||
BasicJavaAstTreeUtil.is(astNode, BASIC_USES_STATEMENT) ||
BasicJavaAstTreeUtil.is(astNode, BASIC_PROVIDES_STATEMENT)) {
String text = astNode.getText();
int tailLength = 0;
ASTNode leaf = TreeUtil.findLastLeaf(astNode);
while (leaf != null && JAVA_COMMENT_OR_WHITESPACE_BIT_SET.contains(leaf.getElementType())) {
tailLength += leaf.getTextLength();
leaf = TreeUtil.prevLeaf(leaf);
}
if (leaf == null) {
return;
}
if (tailLength > 0) {
text = text.substring(0, text.length() - tailLength);
}
int insertionOffset = leaf.getTextRange().getEndOffset();
Document doc = editor.getDocument();
if (BasicJavaAstTreeUtil.is(astNode, BASIC_FIELD) &&
(BasicJavaAstTreeUtil.hasModifierProperty(astNode, JavaTokenType.ABSTRACT_KEYWORD))) {
// abstract rarely seem to be field. It is rather incomplete method.
doc.insertString(insertionOffset, "()");
insertionOffset += "()".length();
}
// Like:
// assert x instanceof Type
// String s = "hello";
// Here, String is parsed as name of the pattern variable, and we have an assignment, instead of declaration
ASTNode error = astNode.getLastChildNode();
if (BasicJavaAstTreeUtil.is(error, TokenType.ERROR_ELEMENT) &&
BasicJavaAstTreeUtil.is(error.getTreePrev(), BASIC_INSTANCE_OF_EXPRESSION) &&
BasicJavaAstTreeUtil.is(error.getTreePrev().getLastChildNode(), BASIC_TYPE_TEST_PATTERN)) {
ASTNode variable = BasicJavaAstTreeUtil.getPatternVariable(error.getTreePrev().getLastChildNode());
PsiElement skipWhitespacesForward = PsiTreeUtil.skipWhitespacesForward(psiElement);
ASTNode assignmentExpr = BasicJavaAstTreeUtil.getExpression(BasicJavaAstTreeUtil.toNode(skipWhitespacesForward));
if (variable != null &&
BasicJavaAstTreeUtil.is(BasicJavaAstTreeUtil.toNode(skipWhitespacesForward), BASIC_EXPRESSION_STATEMENT) &&
BasicJavaAstTreeUtil.is(assignmentExpr, BASIC_ASSIGNMENT_EXPRESSION) &&
JavaTokenType.EQ.equals(BasicJavaAstTreeUtil.getAssignmentOperationTokenType(assignmentExpr))) {
ASTNode identifier = BasicJavaAstTreeUtil.getNameIdentifier(variable);
if (identifier != null &&
BasicJavaAstTreeUtil.toPsi(identifier.getTreePrev()) instanceof PsiWhiteSpace ws &&
ws.getText().contains("\n") &&
editor.getCaretModel().getOffset() < identifier.getTextRange().getStartOffset()) {
insertionOffset = ws.getTextRange().getStartOffset();
}
}
}
if (!StringUtil.endsWithChar(text, ';')) {
ASTNode parent = astNode.getTreeParent();
String toInsert = ";";
if (BasicJavaAstTreeUtil.is(parent, BASIC_FOR_STATEMENT)) {
if (BasicJavaAstTreeUtil.getForUpdate(parent) == astNode) {
return;
}
if (getSpaceAfterSemicolon(psiElement)) {
toInsert += " ";
}
}
doc.insertString(insertionOffset, toInsert);
}
}
}
private static boolean isMethodShouldHaveBody(@Nullable PsiElement psiElement){
return AfterSemicolonEnterProcessor.shouldHaveBody(BasicJavaAstTreeUtil.toNode(psiElement));
}
protected abstract boolean isImportStatementBase(@Nullable PsiElement psiElement);
private static boolean isStandaloneField(@Nullable PsiElement psiElement) {
if (psiElement == null || !BasicJavaAstTreeUtil.is(BasicJavaAstTreeUtil.toNode(psiElement), BASIC_FIELD)) return false;
PsiElement node = PsiTreeUtil.nextLeaf(psiElement, true);
if (node == null) {
return false;
}
return !",".equals(node.getText());
}
}
@@ -0,0 +1,105 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiErrorElement;
import com.intellij.psi.PsiRecursiveElementWalkingVisitor;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.psi.impl.source.BasicJavaElementType;
import com.intellij.util.text.CharArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Set;
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
public class AfterSemicolonEnterProcessor implements ASTNodeEnterProcessor {
@Override
public boolean doEnter(@NotNull Editor editor, @NotNull ASTNode astNode, boolean isModified) {
PsiElement psiElement = BasicJavaAstTreeUtil.toPsi(astNode);
if (psiElement == null) {
return false;
}
if (BasicJavaAstTreeUtil.is(astNode, BasicJavaElementType.BASIC_EXPRESSION_STATEMENT) ||
BasicJavaAstTreeUtil.is(astNode, BasicJavaElementType.BASIC_DECLARATION_STATEMENT) ||
BasicJavaAstTreeUtil.is(astNode, BasicJavaElementType.BASIC_DO_WHILE_STATEMENT) ||
BasicJavaAstTreeUtil.is(astNode, BasicJavaElementType.BASIC_RETURN_STATEMENT) ||
BasicJavaAstTreeUtil.is(astNode, BasicJavaElementType.BASIC_THROW_STATEMENT) ||
BasicJavaAstTreeUtil.is(astNode, BasicJavaElementType.BASIC_BREAK_STATEMENT) ||
BasicJavaAstTreeUtil.is(astNode, BasicJavaElementType.BASIC_CONTINUE_STATEMENT) ||
BasicJavaAstTreeUtil.is(astNode, BasicJavaElementType.BASIC_YIELD_STATEMENT) ||
BasicJavaAstTreeUtil.is(astNode, BasicJavaElementType.BASIC_ASSERT_STATEMENT) ||
BasicJavaAstTreeUtil.is(astNode, Set.of(BasicJavaElementType.BASIC_FIELD, BasicJavaElementType.BASIC_ENUM_CONSTANT)) ||
isImportStatementBase(psiElement) ||
isMethodWithoutBody(psiElement)) {
int errorOffset = getErrorElementOffset(psiElement);
int elementEndOffset = astNode.getTextRange().getEndOffset();
if (BasicJavaAstTreeUtil.is(astNode, BasicJavaElementType.BASIC_ENUM_CONSTANT)) {
final CharSequence text = editor.getDocument().getCharsSequence();
final int commaOffset = CharArrayUtil.shiftForwardUntil(text, elementEndOffset, ",");
if (commaOffset < text.length()) {
elementEndOffset = commaOffset + 1;
}
}
if (errorOffset >= 0 && errorOffset < elementEndOffset) {
final CharSequence text = editor.getDocument().getCharsSequence();
if (text.charAt(errorOffset) == ' ' && text.charAt(errorOffset + 1) == ';') {
errorOffset++;
}
}
editor.getCaretModel().moveToOffset(errorOffset >= 0 ? errorOffset : elementEndOffset);
return isModified;
}
return false;
}
static boolean shouldHaveBody(@Nullable ASTNode element) {
if (element == null) {
return false;
}
ASTNode containingClass = BasicJavaAstTreeUtil.getParentOfType(element, CLASS_SET);
if (containingClass == null) return false;
if (BasicJavaAstTreeUtil.hasModifierProperty(element, JavaTokenType.ABSTRACT_KEYWORD) ||
BasicJavaAstTreeUtil.hasModifierProperty(element, JavaTokenType.NATIVE_KEYWORD)) {
return false;
}
if (BasicJavaAstTreeUtil.hasModifierProperty(element, JavaTokenType.PRIVATE_KEYWORD)) return true;
if (BasicJavaAstTreeUtil.isInterfaceEnumClassOrRecord(containingClass, JavaTokenType.INTERFACE_KEYWORD) &&
!BasicJavaAstTreeUtil.hasModifierProperty(element, JavaTokenType.DEFAULT_KEYWORD) &&
!BasicJavaAstTreeUtil.hasModifierProperty(element, JavaTokenType.STATIC_KEYWORD)) {
return false;
}
return true;
}
private boolean isMethodWithoutBody(@Nullable PsiElement psiElement){
ASTNode node = BasicJavaAstTreeUtil.toNode(psiElement);
return BasicJavaAstTreeUtil.is(node, BASIC_METHOD) &&
!shouldHaveBody(node);
}
private boolean isImportStatementBase(@Nullable PsiElement psiElement){
ASTNode node = BasicJavaAstTreeUtil.toNode(psiElement);
return
BasicJavaAstTreeUtil.is(node, BASIC_IMPORT_STATEMENT) ||
BasicJavaAstTreeUtil.is(node, BASIC_IMPORT_STATIC_STATEMENT);
}
private static int getErrorElementOffset(PsiElement elt) {
final int[] offset = {-1};
elt.accept(new PsiRecursiveElementWalkingVisitor() {
@Override
public void visitErrorElement(@NotNull PsiErrorElement element) {
if (offset[0] == -1) offset[0] = element.getTextRange().getStartOffset();
}
});
return offset[0];
}
}
@@ -0,0 +1,40 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.codeInsight.editorActions.enter.EnterAfterUnmatchedBraceHandler;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.psi.PsiElement;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_CODE_BLOCK;
import static com.intellij.psi.impl.source.BasicJavaElementType.STATEMENT_SET;
public class BlockBraceFixer implements Fixer{
@Override
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
PsiElement psiElement = BasicJavaAstTreeUtil.toPsi(astNode);
if (psiElement == null) {
return;
}
if (BasicJavaAstTreeUtil.is(astNode, BASIC_CODE_BLOCK) && afterUnmatchedBrace(editor, psiElement.getContainingFile().getFileType())) {
int stopOffset = astNode.getTextRange().getEndOffset();
List<ASTNode> statements = BasicJavaAstTreeUtil.getChildren(astNode).stream().filter(node->
BasicJavaAstTreeUtil.is(node, STATEMENT_SET)
).toList();
if (!statements.isEmpty()) {
stopOffset = statements.get(0).getTextRange().getEndOffset();
}
editor.getDocument().insertString(stopOffset, "}");
}
}
static boolean afterUnmatchedBrace(Editor editor, FileType fileType) {
return EnterAfterUnmatchedBraceHandler.isAfterUnmatchedLBrace(editor, editor.getCaretModel().getOffset(), fileType);
}
}
@@ -1,39 +1,40 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiCatchSection;
import com.intellij.psi.PsiCodeBlock;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiJavaToken;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_CATCH_SECTION;
@SuppressWarnings({"HardCodedStringLiteral"})
public class CatchDeclarationFixer implements Fixer {
@Override
public void apply(Editor editor, JavaSmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException {
if (psiElement instanceof PsiCatchSection catchSection) {
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
if (BasicJavaAstTreeUtil.is(astNode, BASIC_CATCH_SECTION)) {
final Document doc = editor.getDocument();
final int catchStart = catchSection.getTextRange().getStartOffset();
final int catchStart = astNode.getTextRange().getStartOffset();
int stopOffset = doc.getLineEndOffset(doc.getLineNumber(catchStart));
final PsiCodeBlock catchBlock = catchSection.getCatchBlock();
final ASTNode catchBlock = BasicJavaAstTreeUtil.getCatchBlock(astNode);
if (catchBlock != null) {
stopOffset = Math.min(stopOffset, catchBlock.getTextRange().getStartOffset());
}
stopOffset = Math.min(stopOffset, catchSection.getTextRange().getEndOffset());
stopOffset = Math.min(stopOffset, astNode.getTextRange().getEndOffset());
final PsiJavaToken lParenth = catchSection.getLParenth();
final ASTNode lParenth = BasicJavaAstTreeUtil.getLParenth(astNode);
if (lParenth == null) {
doc.replaceString(catchStart, stopOffset, "catch ()");
processor.registerUnresolvedError(catchStart + "catch (".length());
} else {
if (catchSection.getParameter() == null) {
}
else {
if (BasicJavaAstTreeUtil.getParameter(astNode) == null) {
processor.registerUnresolvedError(lParenth.getTextRange().getEndOffset());
}
if (catchSection.getRParenth() == null) {
if (BasicJavaAstTreeUtil.getRParenth(astNode) == null) {
doc.insertString(stopOffset, ")");
}
}
@@ -15,27 +15,39 @@
*/
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.actionSystem.IdeActions;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.EditorModificationUtil;
import com.intellij.openapi.editor.EditorModificationUtilEx;
import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
import com.intellij.openapi.editor.actionSystem.EditorActionManager;
import com.intellij.openapi.editor.ex.util.EditorUtil;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.PsiComment;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.impl.source.BasicElementTypes;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.psi.impl.source.BasicJavaTokenSet;
import org.jetbrains.annotations.NotNull;
import static com.intellij.psi.impl.source.BasicJavaDocElementType.DOC_COMMENT;
public class CommentBreakerEnterProcessor implements ASTNodeEnterProcessor {
private final BasicJavaTokenSet myCommentTypes = BasicJavaTokenSet.orSet(
BasicElementTypes.JAVA_PLAIN_COMMENT_BIT_SET, BasicJavaTokenSet.create(DOC_COMMENT)
);
public class CommentBreakerEnterProcessor implements EnterProcessor {
@Override
public boolean doEnter(Editor editor, PsiElement psiElement, boolean isModified) {
if (isModified) return false;
public boolean doEnter(@NotNull Editor editor, @NotNull ASTNode astNode, boolean isModified) {
PsiElement psiElement = BasicJavaAstTreeUtil.toPsi(astNode);
if (isModified || psiElement == null) return false;
final PsiElement atCaret = psiElement.getContainingFile().findElementAt(editor.getCaretModel().getOffset());
final PsiComment comment = PsiTreeUtil.getParentOfType(atCaret, PsiComment.class, false);
if (atCaret == null) return false;
final ASTNode comment = BasicJavaAstTreeUtil.getParentOfType(atCaret.getNode(), myCommentTypes, false);
if (comment != null) {
plainEnter(editor);
if (comment.getTokenType() == JavaTokenType.END_OF_LINE_COMMENT) {
EditorModificationUtil.insertStringAtCaret(editor, "// ");
if (BasicJavaAstTreeUtil.is(comment, JavaTokenType.END_OF_LINE_COMMENT)) {
EditorModificationUtilEx.insertStringAtCaret(editor, "// ");
}
return true;
}
@@ -49,5 +61,4 @@ public class CommentBreakerEnterProcessor implements EnterProcessor {
private static EditorActionHandler getEnterHandler() {
return EditorActionManager.getInstance().getActionHandler(IdeActions.ACTION_EDITOR_START_NEW_LINE);
}
}
@@ -0,0 +1,47 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_BLOCK_STATEMENT;
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_DO_WHILE_STATEMENT;
public class DoWhileConditionFixer implements Fixer {
@Override
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
if (BasicJavaAstTreeUtil.is(astNode, BASIC_DO_WHILE_STATEMENT)) {
final Document doc = editor.getDocument();
ASTNode whileKeyword = BasicJavaAstTreeUtil.getWhileKeyword(astNode);
ASTNode doWhileBody = BasicJavaAstTreeUtil.getDoWhileBody(astNode);
if (doWhileBody == null || !(BasicJavaAstTreeUtil.is(doWhileBody, BASIC_BLOCK_STATEMENT)) && whileKeyword == null) {
final int startOffset = astNode.getTextRange().getStartOffset();
doc.replaceString(startOffset, startOffset + "do".length(), "do {} while()");
return;
}
if (BasicJavaAstTreeUtil.getWhileCondition(astNode) == null) {
if (whileKeyword == null) {
final int endOffset = astNode.getTextRange().getEndOffset();
doc.insertString(endOffset, "while()");
}
else if (BasicJavaAstTreeUtil.getLParenth(astNode) == null || BasicJavaAstTreeUtil.getRParenth(astNode) == null) {
final TextRange whileRange = whileKeyword.getTextRange();
doc.replaceString(whileRange.getStartOffset(), whileRange.getEndOffset(), "while()");
}
else {
ASTNode lParenth = BasicJavaAstTreeUtil.getLParenth(astNode);
if (lParenth != null) {
processor.registerUnresolvedError(lParenth.getTextRange().getEndOffset());
}
}
}
}
}
}
@@ -15,18 +15,21 @@
*/
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiEnumConstant;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.text.CharArrayUtil;
import org.jetbrains.annotations.NotNull;
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_ENUM_CONSTANT;
public class EnumFieldFixer implements Fixer {
@Override
public void apply(Editor editor, JavaSmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException {
if (psiElement instanceof PsiEnumConstant) {
int insertionOffset = psiElement.getTextRange().getEndOffset();
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
if (BasicJavaAstTreeUtil.is(astNode, BASIC_ENUM_CONSTANT)) {
int insertionOffset = astNode.getTextRange().getEndOffset();
Document doc = editor.getDocument();
final CharSequence text = doc.getCharsSequence();
final int probableCommaOffset = CharArrayUtil.shiftForward(text, insertionOffset, " \t");
@@ -1,10 +1,11 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiElement;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
public interface Fixer {
void apply(Editor editor, JavaSmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException;
void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException;
}
@@ -0,0 +1,107 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.BaseJavaJspElementType;
import com.intellij.application.options.CodeStyle;
import com.intellij.lang.ASTNode;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_EMPTY_STATEMENT;
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_FOR_STATEMENT;
/**
* {@link Fixer} that handles use-cases like below:
* <b>before:</b>
* <pre>
* void foo() {
* for[caret]
* }
* </pre>
*
* <b>after:</b>
* <pre>
* void foo() {
* for ([caret]) {
*
* }
* }
* </pre>
*/
public class ForStatementFixer implements Fixer {
@Override
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
if (!(BasicJavaAstTreeUtil.is(astNode, BASIC_FOR_STATEMENT))) {
return;
}
final ASTNode lParenth = BasicJavaAstTreeUtil.getLParenth(astNode);
final ASTNode rParenth = BasicJavaAstTreeUtil.getRParenth(astNode);
if (lParenth == null || rParenth == null) {
final TextRange textRange = astNode.getTextRange();
editor.getDocument().replaceString(textRange.getStartOffset(), textRange.getEndOffset(), "for () {\n}");
processor.registerUnresolvedError(textRange.getStartOffset() + "for (".length());
return;
}
final ASTNode initialization = BasicJavaAstTreeUtil.getForInitialization(astNode);
if (initialization == null) {
processor.registerUnresolvedError(lParenth.getTextRange().getEndOffset());
return;
}
final ASTNode condition = BasicJavaAstTreeUtil.getForCondition(astNode);
if (condition == null) {
boolean endlessLoop = BasicJavaAstTreeUtil.is(initialization, BASIC_EMPTY_STATEMENT) &&
BasicJavaAstTreeUtil.getForUpdate(astNode) == null;
if (!endlessLoop) {
registerErrorOffset(editor, processor, initialization, astNode);
}
return;
}
if (BasicJavaAstTreeUtil.getForUpdate(astNode) == null) {
registerErrorOffset(editor, processor, condition, astNode);
}
}
/**
* {@link JavaSmartEnterProcessor#registerUnresolvedError(int) registers target offset} taking care of the situation when
* current code style implies white space after 'for' part's semicolon.
*
* @param editor target editor
* @param processor target smart enter processor
* @param lastValidForPart last valid element of the target 'for' loop
* @param forStatement PSI element for the target 'for' loop
*/
private void registerErrorOffset(@NotNull Editor editor, @NotNull AbstractBasicJavaSmartEnterProcessor processor,
@NotNull ASTNode lastValidForPart, @NotNull ASTNode forStatement) {
final Project project = editor.getProject();
int offset = lastValidForPart.getTextRange().getEndOffset();
if (project != null && CodeStyle.getSettings(editor).getCommonSettings(JavaLanguage.INSTANCE).SPACE_AFTER_COMMA) {
if (editor.getDocument().getCharsSequence().charAt(lastValidForPart.getTextRange().getEndOffset() - 1) != ';') {
offset++;
}
for (ASTNode element = lastValidForPart.getTreeNext();
element != null && element != BasicJavaAstTreeUtil.getRParenth(forStatement) && element.getTreeParent() == forStatement;
element = element.getTreeNext()) {
if (isWhiteSpaceIncludingJsp(element) && element.getTextLength() > 0) {
offset++;
break;
}
}
}
processor.registerUnresolvedError(offset);
}
private static boolean isWhiteSpaceIncludingJsp(@NotNull ASTNode node) {
return BaseJavaJspElementType.WHITE_SPACE_BIT_SET.contains(node.getElementType());
}
}
@@ -0,0 +1,77 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiKeyword;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import static com.intellij.psi.impl.source.BasicElementTypes.JAVA_PLAIN_COMMENT_BIT_SET;
import static com.intellij.psi.impl.source.BasicJavaDocElementType.DOC_COMMENT;
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
public class IfConditionFixer implements Fixer {
@Override
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
if (BasicJavaAstTreeUtil.is(astNode, BASIC_IF_STATEMENT)) {
final Document doc = editor.getDocument();
final ASTNode rParen = BasicJavaAstTreeUtil.getRParenth(astNode);
final ASTNode lParen = BasicJavaAstTreeUtil.getLParenth(astNode);
final ASTNode condition = BasicJavaAstTreeUtil.getIfCondition(astNode);
if (condition == null) {
if (lParen == null || rParen == null) {
int stopOffset = doc.getLineEndOffset(doc.getLineNumber(astNode.getTextRange().getStartOffset()));
final ASTNode then = BasicJavaAstTreeUtil.getThenBranch(astNode);
if (then != null) {
stopOffset = Math.min(stopOffset, then.getTextRange().getStartOffset());
}
stopOffset = Math.min(stopOffset, astNode.getTextRange().getEndOffset());
ASTNode lastChild = astNode.getLastChildNode();
String innerComment = "";
String lastComment = "";
if (lParen != null && PsiUtilCore.getElementType(lastChild) == JavaTokenType.C_STYLE_COMMENT) {
innerComment = lastChild.getText();
}
else if (BasicJavaAstTreeUtil.is(lastChild, DOC_COMMENT) ||
BasicJavaAstTreeUtil.is(lastChild, JAVA_PLAIN_COMMENT_BIT_SET)
) {
lastComment = lastChild.getText();
}
String prefix = "if (" + innerComment;
doc.replaceString(astNode.getTextRange().getStartOffset(), stopOffset, prefix + ")" + lastComment);
processor.registerUnresolvedError(astNode.getTextRange().getStartOffset() + prefix.length());
}
else {
processor.registerUnresolvedError(lParen.getTextRange().getEndOffset());
}
}
else if (rParen == null) {
doc.insertString(condition.getTextRange().getEndOffset(), ")");
}
}
else if (BasicJavaAstTreeUtil.is(astNode, EXPRESSION_SET) &&
BasicJavaAstTreeUtil.is(astNode.getTreeParent(), BASIC_EXPRESSION_STATEMENT)) {
PsiElement psi = BasicJavaAstTreeUtil.toPsi(astNode);
if (psi != null) {
PsiElement prevLeaf = PsiTreeUtil.prevVisibleLeaf(psi);
if (prevLeaf != null && prevLeaf.textMatches(PsiKeyword.IF)) {
Document doc = editor.getDocument();
doc.insertString(astNode.getTextRange().getEndOffset(), ")");
doc.insertString(astNode.getTextRange().getStartOffset(), "(");
}
}
}
}
}
@@ -1,18 +1,4 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.ide.DataManager;
@@ -22,41 +8,38 @@ import com.intellij.openapi.actionSystem.IdeActions;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
import com.intellij.openapi.editor.actionSystem.EditorActionManager;
import com.intellij.psi.PsiCodeBlock;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReturnStatement;
import com.intellij.psi.PsiThrowStatement;
import com.intellij.psi.impl.source.tree.JavaElementType;
import com.intellij.psi.tree.TokenSet;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.psi.impl.source.BasicJavaTokenSet;
import com.intellij.util.text.CharArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class LeaveCodeBlockEnterProcessor implements EnterProcessor {
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
public class LeaveCodeBlockEnterProcessor implements ASTNodeEnterProcessor {
private final BasicJavaTokenSet CONTROL_FLOW_ELEMENT_TYPES =
BasicJavaTokenSet.create(BASIC_IF_STATEMENT, BASIC_WHILE_STATEMENT, BASIC_DO_WHILE_STATEMENT, BASIC_FOR_STATEMENT,
BASIC_FOREACH_STATEMENT);
private static final TokenSet CONTROL_FLOW_ELEMENT_TYPES = TokenSet.create(
JavaElementType.IF_STATEMENT, JavaElementType.WHILE_STATEMENT, JavaElementType.DO_WHILE_STATEMENT, JavaElementType.FOR_STATEMENT,
JavaElementType.FOREACH_STATEMENT
);
@Override
public boolean doEnter(Editor editor, PsiElement psiElement, boolean isModified) {
PsiElement parent = psiElement.getParent();
if (!(parent instanceof PsiCodeBlock)) {
public boolean doEnter(@NotNull Editor editor, @NotNull ASTNode astNode, boolean isModified) {
ASTNode parent = astNode.getTreeParent();
if (!(BasicJavaAstTreeUtil.is(parent, BASIC_CODE_BLOCK))) {
return false;
}
final ASTNode node = psiElement.getNode();
if (node != null && CONTROL_FLOW_ELEMENT_TYPES.contains(node.getElementType())) {
if (CONTROL_FLOW_ELEMENT_TYPES.contains(astNode.getElementType())) {
return false;
}
boolean leaveCodeBlock = isControlFlowBreak(psiElement);
}
boolean leaveCodeBlock = isControlFlowBreak(astNode);
if (!leaveCodeBlock) {
return false;
}
final int offset = parent.getTextRange().getEndOffset();
// Check if there is empty line after the code block. Just move caret there in the case of the positive answer.
final CharSequence text = editor.getDocument().getCharsSequence();
if (offset < text.length() - 1) {
@@ -70,7 +53,7 @@ public class LeaveCodeBlockEnterProcessor implements EnterProcessor {
return true;
}
}
editor.getCaretModel().moveToOffset(offset);
return false;
}
@@ -84,7 +67,7 @@ public class LeaveCodeBlockEnterProcessor implements EnterProcessor {
* }
* }
* </pre>
*
*
* <b>Output:</b>
* <pre>
* void foo(int i) {
@@ -94,9 +77,9 @@ public class LeaveCodeBlockEnterProcessor implements EnterProcessor {
* [caret]
* }
* </pre>
*
*/
private static boolean isControlFlowBreak(@Nullable PsiElement element) {
return element instanceof PsiReturnStatement || element instanceof PsiThrowStatement;
private static boolean isControlFlowBreak(@Nullable ASTNode element) {
return BasicJavaAstTreeUtil.is(element, BASIC_RETURN_STATEMENT) ||
BasicJavaAstTreeUtil.is(element, BASIC_THROW_STATEMENT);
}
}
@@ -0,0 +1,24 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.JavaTokenType;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
public class LiteralFixer implements Fixer {
@Override
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode)
throws IncorrectOperationException {
if (astNode.getElementType() == JavaTokenType.STRING_LITERAL &&
!StringUtil.endsWithChar(astNode.getText(), '\"')) {
editor.getDocument().insertString(astNode.getTextRange().getEndOffset(), "\"");
}
else if (astNode.getElementType() == JavaTokenType.CHARACTER_LITERAL &&
!StringUtil.endsWithChar(astNode.getText(), '\'')) {
editor.getDocument().insertString(astNode.getTextRange().getEndOffset(), "'");
}
}
}
@@ -0,0 +1,31 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_NEW_EXPRESSION;
public class MissingArrayConstructorBracketFixer implements Fixer {
@Override
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
if (!(BasicJavaAstTreeUtil.is(astNode, BASIC_NEW_EXPRESSION))) return;
int count = 0;
for (ASTNode element = astNode.getFirstChildNode(); element != null; element = element.getTreeNext()) {
if (BasicJavaAstTreeUtil.is(element, JavaTokenType.LBRACKET)) {
count++;
}
else if (BasicJavaAstTreeUtil.is(element, JavaTokenType.RBRACKET)) {
count--;
}
}
if (count > 0) {
editor.getDocument().insertString(astNode.getTextRange().getEndOffset(), "]");
}
}
}
@@ -0,0 +1,50 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.codeInsight.editorActions.enter.EnterAfterUnmatchedBraceHandler;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.PsiElement;
import com.intellij.psi.TokenType;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_ANNOTATION_ARRAY_INITIALIZER;
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_ARRAY_INITIALIZER_EXPRESSION;
public class MissingArrayInitializerBraceFixer implements Fixer {
@Override
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
if (!(
BasicJavaAstTreeUtil.is(astNode, BASIC_ARRAY_INITIALIZER_EXPRESSION) ||
BasicJavaAstTreeUtil.is(astNode, BASIC_ANNOTATION_ARRAY_INITIALIZER))) {
return;
}
ASTNode child = astNode.getFirstChildNode();
if (!child.getElementType().equals(JavaTokenType.LBRACE)) return;
PsiElement psi = BasicJavaAstTreeUtil.toPsi(astNode);
if (psi == null) {
return;
}
if (!EnterAfterUnmatchedBraceHandler.isAfterUnmatchedLBrace(editor, child.getTextRange().getEndOffset(),
psi.getContainingFile().getFileType())) {
return;
}
ASTNode anchor = BasicJavaAstTreeUtil.findChildByType(astNode, TokenType.ERROR_ELEMENT);
if (anchor == null) {
PsiElement last = PsiTreeUtil.getDeepestVisibleLast(psi);
while (last != null && last.getNode().getElementType().equals(JavaTokenType.RBRACE)) {
last = PsiTreeUtil.prevCodeLeaf(last);
}
if (last != null && PsiTreeUtil.isAncestor(psi, last, true)) {
anchor = last.getNode();
}
}
int endOffset = (anchor != null ? anchor : astNode).getTextRange().getEndOffset();
editor.getDocument().insertString(endOffset, "}");
}
}
@@ -0,0 +1,26 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_CATCH_SECTION;
public class MissingCatchBodyFixer implements Fixer {
@Override
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
if (!(BasicJavaAstTreeUtil.is(astNode, BASIC_CATCH_SECTION))) return;
ASTNode body = BasicJavaAstTreeUtil.getCatchBlock(astNode);
if (body != null && BasicJavaAstTreeUtil.getLBrace(body) != null && BasicJavaAstTreeUtil.getRBrace(body) != null) return;
final ASTNode rParenth = BasicJavaAstTreeUtil.getRParenth(astNode);
if (rParenth == null) return;
processor.insertBraces(editor, rParenth.getTextRange().getEndOffset());
}
}
@@ -0,0 +1,50 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.TokenType;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
public class MissingClassBodyFixer implements Fixer {
@Override
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
if (BasicJavaAstTreeUtil.is(astNode, BASIC_RECORD_COMPONENT)) {
astNode = BasicJavaAstTreeUtil.getRecordComponentContainingClass(astNode);
}
if (!(BasicJavaAstTreeUtil.is(astNode, CLASS_SET)) ||
BasicJavaAstTreeUtil.is(astNode, BASIC_TYPE_PARAMETER)) {
return;
}
if (BasicJavaAstTreeUtil.getLBrace(astNode) == null && astNode != null) {
ASTNode lastChild = astNode.getLastChildNode();
int offset = astNode.getTextRange().getEndOffset();
if (BasicJavaAstTreeUtil.is(lastChild, TokenType.ERROR_ELEMENT)) {
ASTNode previous = lastChild.getTreePrev();
if (BasicJavaAstTreeUtil.isWhiteSpace(previous)) {
offset = previous.getTextRange().getStartOffset();
}
else {
offset = lastChild.getTextRange().getStartOffset();
}
}
if (BasicJavaAstTreeUtil.isInterfaceEnumClassOrRecord(astNode, JavaTokenType.RECORD_KEYWORD) &&
BasicJavaAstTreeUtil.getRecordHeader(astNode) == null) {
editor.getDocument().insertString(offset, "() {}");
editor.getCaretModel().moveToOffset(offset + 1);
processor.setSkipEnter(true);
}
else {
processor.insertBracesWithNewLine(editor, offset);
editor.getCaretModel().moveToOffset(offset + 1);
}
}
}
}
@@ -0,0 +1,60 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiKeyword;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_BLOCK_STATEMENT;
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_IF_STATEMENT;
public class MissingIfBranchesFixer implements Fixer {
@Override
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
if (!(BasicJavaAstTreeUtil.is(astNode, BASIC_IF_STATEMENT))) return;
final Document doc = editor.getDocument();
final ASTNode elseElement = BasicJavaAstTreeUtil.getElseElement(astNode);
if (elseElement != null) {
handleBranch(doc, astNode, elseElement, BasicJavaAstTreeUtil.getElseBranch(astNode));
}
ASTNode rParenth = BasicJavaAstTreeUtil.getRParenth(astNode);
assert rParenth != null;
handleBranch(doc, astNode, rParenth, BasicJavaAstTreeUtil.getThenBranch(astNode));
}
private static void handleBranch(@NotNull Document doc,
@NotNull ASTNode ifStatement,
@NotNull ASTNode beforeBranch,
@Nullable ASTNode branch) {
if (BasicJavaAstTreeUtil.is(branch, BASIC_BLOCK_STATEMENT) ||
PsiKeyword.ELSE.equals(beforeBranch.getText()) && BasicJavaAstTreeUtil.is(branch, BASIC_IF_STATEMENT)) {
return;
}
boolean transformingOneLiner = branch != null && (startLine(doc, beforeBranch) == startLine(doc, branch) ||
startCol(doc, ifStatement) < startCol(doc, branch));
if (!transformingOneLiner) {
doc.insertString(beforeBranch.getTextRange().getEndOffset(), "{}");
}
else {
doc.insertString(beforeBranch.getTextRange().getEndOffset(), "{");
doc.insertString(branch.getTextRange().getEndOffset() + 1, "}");
}
}
private static int startLine(Document doc, @NotNull ASTNode astNode) {
return doc.getLineNumber(astNode.getTextRange().getStartOffset());
}
private static int startCol(Document doc, @NotNull ASTNode astNode) {
int offset = astNode.getTextRange().getStartOffset();
return offset - doc.getLineStartOffset(doc.getLineNumber(offset));
}
}
@@ -0,0 +1,62 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.PsiElement;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
public class MissingLambdaBodyFixer implements Fixer {
@Override
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
ASTNode body = null;
if (BasicJavaAstTreeUtil.is(astNode, BASIC_LAMBDA_EXPRESSION)) {
final ASTNode lastChild = astNode.getLastChildNode();
if (BasicJavaAstTreeUtil.is(lastChild, EXPRESSION_SET) ||
BasicJavaAstTreeUtil.is(lastChild, BASIC_CODE_BLOCK)) {
body = lastChild;
}
}
else if (BasicJavaAstTreeUtil.is(astNode, BASIC_SWITCH_LABELED_RULE)) {
body = BasicJavaAstTreeUtil.getRuleBody(astNode);
}
else {
return;
}
if (body != null) return;
PsiElement psiElement = BasicJavaAstTreeUtil.toPsi(astNode);
if (psiElement == null) {
return;
}
PsiElement arrow = PsiTreeUtil.getDeepestVisibleLast(psiElement);
if (arrow == null || !arrow.getNode().getElementType().equals(JavaTokenType.ARROW)) return;
int offset = arrow.getTextRange().getEndOffset();
processor.insertBracesWithNewLine(editor, offset);
editor.getCaretModel().moveToOffset(offset + 1);
processor.commit(editor);
processor.reformat(editor, psiElement);
processor.setSkipEnter(BasicJavaAstTreeUtil.is(astNode, BASIC_LAMBDA_EXPRESSION));
}
}
@@ -0,0 +1,162 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.psi.impl.source.BasicJavaTokenSet;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.ObjectUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
public class MissingLoopBodyFixer implements Fixer {
@Override
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
ASTNode loopStatement = getLoopParent(astNode);
if (loopStatement == null) return;
final Document doc = editor.getDocument();
ASTNode body;
if (BasicJavaAstTreeUtil.is(loopStatement, BASIC_FOR_STATEMENT)) {
body = BasicJavaAstTreeUtil.getForBody(loopStatement);
}
else if (BasicJavaAstTreeUtil.is(loopStatement, BASIC_FOREACH_STATEMENT)) {
body = BasicJavaAstTreeUtil.getForeachBody(loopStatement);
}
else if (BasicJavaAstTreeUtil.is(loopStatement, BASIC_WHILE_STATEMENT)) {
body = BasicJavaAstTreeUtil.getWhileBody(loopStatement);
}
else {
return;
}
if (BasicJavaAstTreeUtil.is(body, BASIC_BLOCK_STATEMENT)) return;
if (body != null && startLine(doc, body) == startLine(doc, loopStatement)) return;
ASTNode eltToInsertAfter = BasicJavaAstTreeUtil.getRParenth(loopStatement);
fixLoopBody(editor, processor, loopStatement, doc, body, eltToInsertAfter);
}
private static ASTNode getLoopParent(@NotNull ASTNode element) {
ASTNode statement = BasicJavaAstTreeUtil.getParentOfType(element, BasicJavaTokenSet.create(BASIC_FOREACH_STATEMENT,
BASIC_FOR_STATEMENT,
BASIC_WHILE_STATEMENT));
if (statement == null) return null;
if (BasicJavaAstTreeUtil.is(statement, BASIC_FOREACH_STATEMENT)) {
return isForEachApplicable(statement, element) ? statement : null;
}
if (BasicJavaAstTreeUtil.is(statement, BASIC_FOR_STATEMENT)) {
return isForApplicable(statement, element) ? statement : null;
}
if (BasicJavaAstTreeUtil.is(statement, BASIC_WHILE_STATEMENT)) {
return statement;
}
return null;
}
private static boolean isForApplicable(ASTNode statement, ASTNode astNode) {
ASTNode init = BasicJavaAstTreeUtil.getForInitialization(statement);
ASTNode update = BasicJavaAstTreeUtil.getForUpdate(statement);
ASTNode check = BasicJavaAstTreeUtil.getForCondition(statement);
return isValidChild(init, astNode) || isValidChild(update, astNode) || isValidChild(check, astNode);
}
private static boolean isValidChild(ASTNode ancestorNode, ASTNode node) {
PsiElement ancestor = BasicJavaAstTreeUtil.toPsi(ancestorNode);
PsiElement element = BasicJavaAstTreeUtil.toPsi(node);
if (ancestor != null && element != null) {
if (PsiTreeUtil.isAncestor(ancestor, element, false)) {
if (PsiTreeUtil.hasErrorElements(ancestor)) return false;
return true;
}
}
return false;
}
private static boolean isForEachApplicable(ASTNode statement, ASTNode astNode) {
ASTNode iterated = BasicJavaAstTreeUtil.getForEachIteratedValue(statement);
ASTNode parameter = BasicJavaAstTreeUtil.getForEachIterationParameter(statement);
PsiElement iteratedElement = BasicJavaAstTreeUtil.toPsi(iterated);
PsiElement parameterElement = BasicJavaAstTreeUtil.toPsi(parameter);
PsiElement element = BasicJavaAstTreeUtil.toPsi(astNode);
return element != null &&
(PsiTreeUtil.isAncestor(iteratedElement, element, false) ||
PsiTreeUtil.isAncestor(parameterElement, element, false));
}
private static int startLine(Document doc, ASTNode astNode) {
return doc.getLineNumber(astNode.getTextRange().getStartOffset());
}
private static void fixLoopBody(@NotNull Editor editor,
@NotNull AbstractBasicJavaSmartEnterProcessor processor,
@NotNull ASTNode loop,
@NotNull Document doc,
@Nullable ASTNode body,
@Nullable ASTNode eltToInsertAfter) {
PsiElement loopElement = BasicJavaAstTreeUtil.toPsi(loop);
if (body != null && eltToInsertAfter != null) {
PsiElement bodyElement = BasicJavaAstTreeUtil.toPsi(body);
if (loopElement != null && bodyElement != null && bodyIsIndented(loopElement, bodyElement)) {
int endOffset = body.getTextRange().getEndOffset();
doc.insertString(endOffset, "\n");
processor.insertCloseBrace(editor, endOffset + 1);
int offset = eltToInsertAfter.getTextRange().getEndOffset();
doc.insertString(offset, "{");
editor.getCaretModel().moveToOffset(endOffset + "{".length());
processor.setSkipEnter(true);
processor.reformat(loopElement);
return;
}
}
boolean needToClose = false;
if (eltToInsertAfter == null) {
eltToInsertAfter = loop;
needToClose = true;
}
int offset = eltToInsertAfter.getTextRange().getEndOffset();
if (needToClose) {
doc.insertString(offset, ")");
offset++;
}
processor.insertBraces(editor, offset);
editor.getCaretModel().moveToOffset(offset);
}
private static boolean bodyIsIndented(@NotNull PsiElement loop, @NotNull PsiElement body) {
PsiWhiteSpace beforeBody = ObjectUtils.tryCast(body.getPrevSibling(), PsiWhiteSpace.class);
if (beforeBody == null) return false;
PsiWhiteSpace beforeLoop = ObjectUtils.tryCast(loop.getPrevSibling(), PsiWhiteSpace.class);
if (beforeLoop == null) return false;
String beforeBodyText = beforeBody.getText();
String beforeLoopText = beforeLoop.getText();
int beforeBodyLineBreak = beforeBodyText.lastIndexOf('\n');
if (beforeBodyLineBreak == -1) return false;
int beforeLoopLineBreak = beforeLoopText.lastIndexOf('\n');
if (beforeLoopLineBreak == -1) return false;
return beforeBodyText.length() - beforeBodyLineBreak > beforeLoopText.length() - beforeLoopLineBreak;
}
}
@@ -0,0 +1,85 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.core.JavaPsiBundle;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiErrorElement;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
public class MissingMethodBodyFixer implements Fixer {
@Override
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
if (BasicJavaAstTreeUtil.is(astNode, BASIC_FIELD)) {
// replace something like `void x` with `void x() {...}`
// while it's ambiguous whether user wants a field or a method, declaring a field is easier (just append a semicolon),
// so completing a method looks more useful
if (BasicJavaAstTreeUtil.getInitializer(astNode) != null) return;
ASTNode lastChild = astNode.getLastChildNode();
PsiElement psiElement = BasicJavaAstTreeUtil.toPsi(lastChild);
if (!(psiElement instanceof PsiErrorElement)) return;
if (!((PsiErrorElement)psiElement).getErrorDescription().equals(JavaPsiBundle.message("expected.semicolon"))) return;
// Impossible modifiers for a method
if (BasicJavaAstTreeUtil.hasModifierProperty(astNode, JavaTokenType.TRANSIENT_KEYWORD) ||
BasicJavaAstTreeUtil.hasModifierProperty(astNode, JavaTokenType.VOLATILE_KEYWORD)) {
return;
}
ASTNode typeElement = BasicJavaAstTreeUtil.getTypeElement(astNode);
if (typeElement == null || !typeElement.getText().equals("void")) return;
int endOffset = astNode.getTextRange().getEndOffset();
editor.getDocument().insertString(endOffset, "()");
editor.getDocument().insertString(endOffset + 2, "{}");
editor.getCaretModel().moveToOffset(endOffset + 1);
processor.registerUnresolvedError(endOffset + 1);
processor.setSkipEnter(true);
return;
}
if (!(BasicJavaAstTreeUtil.is(astNode, BASIC_METHOD))) return;
if (!shouldMethodHaveBody(BasicJavaAstTreeUtil.toPsi(astNode))) return;
final ASTNode body = BasicJavaAstTreeUtil.getCodeBlock(astNode);
final Document doc = editor.getDocument();
if (body != null) {
// See IDEADEV-1093. This is quite hacky heuristic but it seem to be best we can do.
String bodyText = body.getText();
if (bodyText.startsWith("{")) {
final ASTNode statement = BasicJavaAstTreeUtil.findChildByType(body, STATEMENT_SET);
if (statement != null) {
if (BasicJavaAstTreeUtil.is(statement, BASIC_DECLARATION_STATEMENT)) {
PsiElement psiElement = BasicJavaAstTreeUtil.toPsi(statement);
if (psiElement != null && PsiTreeUtil.getDeepestLast(psiElement) instanceof PsiErrorElement) {
ASTNode containingClass = BasicJavaAstTreeUtil.getParentOfType(astNode, CLASS_SET);
if (containingClass != null && BasicJavaAstTreeUtil.getRBrace(containingClass) == null) {
doc.insertString(body.getTextRange().getStartOffset() + 1, "\n}");
}
}
}
}
}
return;
}
ASTNode throwList = BasicJavaAstTreeUtil.findChildByType(astNode, BASIC_THROWS_LIST);
if (throwList != null) {
int endOffset = throwList.getTextRange().getEndOffset();
if (endOffset < doc.getTextLength() && doc.getCharsSequence().charAt(endOffset) == ';') {
doc.deleteString(endOffset, endOffset + 1);
}
processor.insertBracesWithNewLine(editor, endOffset);
}
}
private static boolean shouldMethodHaveBody(@Nullable PsiElement method){
return AfterSemicolonEnterProcessor.shouldHaveBody(
BasicJavaAstTreeUtil.toNode(method));
}
}
@@ -0,0 +1,103 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiJavaToken;
import com.intellij.psi.TokenType;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.psi.impl.source.BasicJavaTokenSet;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
public class MissingReturnExpressionFixer implements Fixer {
@Override
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
if (!(BasicJavaAstTreeUtil.is(astNode, BASIC_RETURN_STATEMENT))) {
return;
}
if (!BasicJavaAstTreeUtil.hasErrorElements(astNode)) {
return;
}
if (fixMethodCallWithoutTrailingSemicolon(astNode, editor, processor)) {
return;
}
ASTNode returnValue = BasicJavaAstTreeUtil.getReturnValue(astNode);
if (returnValue != null
&& lineNumber(editor, editor.getCaretModel().getOffset()) == lineNumber(editor, returnValue.getTextRange().getStartOffset())) {
return;
}
ASTNode parent = BasicJavaAstTreeUtil.getParentOfType(astNode, BasicJavaTokenSet.create(BASIC_CLASS_INITIALIZER, BASIC_METHOD));
if (BasicJavaAstTreeUtil.is(parent, BASIC_METHOD)) {
ASTNode type = BasicJavaAstTreeUtil.findChildByType(parent, BASIC_TYPE);
if (type != null && !type.getText().equals("void")) {
final int startOffset = astNode.getTextRange().getStartOffset();
if (returnValue != null) {
editor.getDocument().insertString(startOffset + "return".length(), ";");
}
processor.registerUnresolvedError(startOffset + "return".length());
}
}
}
private static boolean fixMethodCallWithoutTrailingSemicolon(@Nullable ASTNode returnStatement, @NotNull Editor editor,
@NotNull AbstractBasicJavaSmartEnterProcessor processor) {
if (returnStatement == null) {
return false;
}
final ASTNode lastChild = returnStatement.getLastChildNode();
if (!(BasicJavaAstTreeUtil.is(lastChild, TokenType.ERROR_ELEMENT))) {
return false;
}
ASTNode prev = lastChild.getTreePrev();
if (BasicJavaAstTreeUtil.isWhiteSpace(prev)) {
prev = prev.getTreePrev();
}
if (!(prev instanceof PsiJavaToken prevToken)) {
int offset = returnStatement.getTextRange().getEndOffset();
final PsiElement psiMethod =
BasicJavaAstTreeUtil.getParentOfType(BasicJavaAstTreeUtil.toPsi(returnStatement), BASIC_METHOD, true,
BasicJavaTokenSet.create(BASIC_LAMBDA_EXPRESSION));
ASTNode method = BasicJavaAstTreeUtil.toNode(psiMethod);
ASTNode type = BasicJavaAstTreeUtil.findChildByType(method, BASIC_TYPE);
if (method != null && type != null && type.getText().equals("void")) {
offset = returnStatement.getTextRange().getStartOffset() + "return".length();
}
editor.getDocument().insertString(offset, ";");
//processor.setSkipEnter(true);
return true;
}
if (prevToken.getTokenType() == JavaTokenType.SEMICOLON) {
return false;
}
final int offset = returnStatement.getTextRange().getEndOffset();
editor.getDocument().insertString(offset, ";");
if (prevToken.getTokenType() == JavaTokenType.RETURN_KEYWORD) {
final ASTNode method = BasicJavaAstTreeUtil.getParentOfType(returnStatement, BASIC_METHOD);
ASTNode type = BasicJavaAstTreeUtil.findChildByType(method, BASIC_TYPE);
if (method != null && type != null && !type.getText().equals("void")) {
editor.getCaretModel().moveToOffset(offset);
processor.setSkipEnter(true);
}
}
return true;
}
private static int lineNumber(Editor editor, int offset) {
return editor.getDocument().getLineNumber(offset);
}
}
@@ -0,0 +1,28 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_SWITCH_EXPRESSION;
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_SWITCH_STATEMENT;
public class MissingSwitchBodyFixer implements Fixer {
@Override
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
if (!(BasicJavaAstTreeUtil.is(astNode, BASIC_SWITCH_EXPRESSION, BASIC_SWITCH_STATEMENT))) return;
final ASTNode body = BasicJavaAstTreeUtil.getCodeBlock(astNode);
if (body != null) return;
final ASTNode rParenth = BasicJavaAstTreeUtil.getRParenth(astNode);
assert rParenth != null;
int offset = rParenth.getTextRange().getEndOffset();
processor.insertBraces(editor, offset);
}
}
@@ -0,0 +1,23 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_SYNCHRONIZED_STATEMENT;
public class MissingSynchronizedBodyFixer implements Fixer {
@Override
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
if (!(BasicJavaAstTreeUtil.is(astNode, BASIC_SYNCHRONIZED_STATEMENT))) return;
ASTNode body = BasicJavaAstTreeUtil.getCodeBlock(astNode);
if (body != null) return;
processor.insertBraces(editor, astNode.getTextRange().getEndOffset());
}
}
@@ -0,0 +1,34 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_THROW_STATEMENT;
public class MissingThrowExpressionFixer implements Fixer {
@Override
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode)
throws IncorrectOperationException {
if (BasicJavaAstTreeUtil.is(astNode, BASIC_THROW_STATEMENT)) {
ASTNode expression = BasicJavaAstTreeUtil.getExpression(astNode);
if (expression != null &&
startLine(editor, astNode) == startLine(editor, expression)) {
return;
}
final int startOffset = astNode.getTextRange().getStartOffset();
if (expression != null) {
editor.getDocument().insertString(startOffset + "throw".length(), ";");
}
processor.registerUnresolvedError(startOffset + "throw".length());
}
}
private static int startLine(Editor editor, @NotNull ASTNode psiElement) {
return editor.getDocument().getLineNumber(psiElement.getTextRange().getStartOffset());
}
}
@@ -0,0 +1,23 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_TRY_STATEMENT;
public class MissingTryBodyFixer implements Fixer {
@Override
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
if (!(BasicJavaAstTreeUtil.is(astNode, BASIC_TRY_STATEMENT))) return;
ASTNode body = BasicJavaAstTreeUtil.getCodeBlock(astNode);
if (body != null) return;
processor.insertBraces(editor, astNode.getTextRange().getEndOffset());
}
}
@@ -0,0 +1,36 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_ANNOTATION_PARAMETER_LIST;
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_PARAMETER_LIST;
public class ParameterListFixer implements Fixer {
@Override
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
if (BasicJavaAstTreeUtil.is(astNode, BASIC_PARAMETER_LIST) ||
BasicJavaAstTreeUtil.is(astNode, BASIC_ANNOTATION_PARAMETER_LIST)) {
String text = astNode.getText();
if (StringUtil.startsWithChar(text, '(') && !StringUtil.endsWithChar(text, ')')) {
ASTNode[] params = BasicJavaAstTreeUtil.is(astNode, BASIC_PARAMETER_LIST) ?
BasicJavaAstTreeUtil.getParameterListParameters(astNode) :
BasicJavaAstTreeUtil.getAnnotationParameterListAttributes(astNode);
int offset;
if (params.length == 0) {
offset = astNode.getTextRange().getStartOffset() + 1;
}
else {
offset = params[params.length - 1].getTextRange().getEndOffset();
}
editor.getDocument().insertString(offset, ")");
}
}
}
}
@@ -0,0 +1,23 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_PARENTH_EXPRESSION;
public class ParenthesizedFixer implements Fixer {
@Override
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
if (BasicJavaAstTreeUtil.is(astNode, BASIC_PARENTH_EXPRESSION)) {
final ASTNode lastChild = astNode.getLastChildNode();
if (lastChild != null && !")".equals(lastChild.getText())) {
editor.getDocument().insertString(astNode.getTextRange().getEndOffset(), ")");
}
}
}
}
@@ -1,18 +1,4 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.ide.DataManager;
@@ -26,25 +12,32 @@ import com.intellij.openapi.editor.actionSystem.EditorActionManager;
import com.intellij.openapi.editor.ex.util.EditorUtil;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.text.CharArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class PlainEnterProcessor implements EnterProcessor {
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
public class PlainEnterProcessor implements ASTNodeEnterProcessor {
@Override
public boolean doEnter(Editor editor, PsiElement psiElement, boolean isModified) {
if (expandCodeBlock(editor, psiElement)) return true;
public boolean doEnter(@NotNull Editor editor, @NotNull ASTNode astNode, boolean isModified) {
if (expandCodeBlock(editor, astNode)) return true;
getEnterHandler(IdeActions.ACTION_EDITOR_START_NEW_LINE).execute(editor, editor.getCaretModel().getCurrentCaret(),
EditorUtil.getEditorDataContext(editor));
return true;
}
static boolean expandCodeBlock(Editor editor, PsiElement psiElement) {
PsiCodeBlock block = getControlStatementBlock(editor.getCaretModel().getOffset(), psiElement);
if (processExistingBlankLine(editor, block, psiElement)) {
public static boolean expandCodeBlock(@NotNull Editor editor, @Nullable ASTNode astNode) {
ASTNode block = getControlStatementBlock(editor.getCaretModel().getOffset(), astNode);
PsiElement psiBlock = BasicJavaAstTreeUtil.toPsi(block);
if (processExistingBlankLine(editor, psiBlock, astNode)) {
return true;
}
if (block == null) {
@@ -52,9 +45,9 @@ public class PlainEnterProcessor implements EnterProcessor {
}
EditorActionHandler enterHandler = getEnterHandler(IdeActions.ACTION_EDITOR_START_NEW_LINE);
PsiElement firstElement = block.getFirstBodyElement();
ASTNode firstElement = BasicJavaAstTreeUtil.getFirstBodyElement(block);
if (firstElement == null) {
firstElement = block.getRBrace();
firstElement = BasicJavaAstTreeUtil.getRBrace(block);
// Plain enter processor inserts enter after the end of line, hence, we don't want to use it here because the line ends with
// the empty braces block. So, we get the following in case of default handler usage:
// Before:
@@ -76,52 +69,53 @@ public class PlainEnterProcessor implements EnterProcessor {
}
@Nullable
private static PsiCodeBlock getControlStatementBlock(int caret, PsiElement element) {
if (element instanceof PsiTryStatement) {
PsiCodeBlock tryBlock = ((PsiTryStatement)element).getTryBlock();
private static ASTNode getControlStatementBlock(int caret, ASTNode astNode) {
if (BasicJavaAstTreeUtil.is(astNode, BASIC_TRY_STATEMENT)) {
ASTNode tryBlock = BasicJavaAstTreeUtil.getCodeBlock(astNode);
if (tryBlock != null && caret < tryBlock.getTextRange().getEndOffset()) return tryBlock;
for (PsiCodeBlock catchBlock : ((PsiTryStatement)element).getCatchBlocks()) {
for (ASTNode catchBlock : BasicJavaAstTreeUtil.getCatchBlocks(astNode)) {
if (catchBlock != null && caret < catchBlock.getTextRange().getEndOffset()) return catchBlock;
}
return ((PsiTryStatement)element).getFinallyBlock();
return BasicJavaAstTreeUtil.getFinallyBlock(astNode);
}
if (element instanceof PsiSynchronizedStatement) {
return ((PsiSynchronizedStatement)element).getBody();
if (BasicJavaAstTreeUtil.is(astNode, BASIC_SYNCHRONIZED_STATEMENT)) {
return BasicJavaAstTreeUtil.getCodeBlock(astNode);
}
if (element instanceof PsiMethod) {
PsiCodeBlock methodBody = ((PsiMethod)element).getBody();
if (BasicJavaAstTreeUtil.is(astNode, BASIC_METHOD)) {
ASTNode methodBody = BasicJavaAstTreeUtil.getCodeBlock(astNode);
if (methodBody != null) return methodBody;
}
if (element instanceof PsiSwitchStatement) {
return ((PsiSwitchStatement)element).getBody();
if (BasicJavaAstTreeUtil.is(astNode, BASIC_SWITCH_STATEMENT)) {
return BasicJavaAstTreeUtil.getCodeBlock(astNode);
}
PsiStatement body = null;
if (element instanceof PsiIfStatement) {
body = ((PsiIfStatement)element).getThenBranch();
ASTNode body = null;
if (BasicJavaAstTreeUtil.is(astNode, BASIC_IF_STATEMENT)) {
body = BasicJavaAstTreeUtil.getThenBranch(astNode);
if (body != null && caret > body.getTextRange().getEndOffset()) {
body = ((PsiIfStatement)element).getElseBranch();
body = BasicJavaAstTreeUtil.getElseBranch(astNode);
}
}
else if (element instanceof PsiWhileStatement) {
body = ((PsiWhileStatement)element).getBody();
else if (BasicJavaAstTreeUtil.is(astNode, BASIC_WHILE_STATEMENT)) {
body = BasicJavaAstTreeUtil.getBlock(astNode);
}
else if (element instanceof PsiForStatement) {
body = ((PsiForStatement)element).getBody();
else if (BasicJavaAstTreeUtil.is(astNode, BASIC_FOR_STATEMENT)) {
body = BasicJavaAstTreeUtil.getForBody(astNode);
}
else if (element instanceof PsiForeachStatement) {
body = ((PsiForeachStatement)element).getBody();
else if (BasicJavaAstTreeUtil.is(astNode, BASIC_FOREACH_STATEMENT)) {
body = BasicJavaAstTreeUtil.getBlock(astNode);
}
else if (element instanceof PsiDoWhileStatement) {
body = ((PsiDoWhileStatement)element).getBody();
else if (BasicJavaAstTreeUtil.is(astNode, BASIC_DO_WHILE_STATEMENT)) {
body = BasicJavaAstTreeUtil.getDoWhileBody(astNode);
}
return body instanceof PsiBlockStatement ? ((PsiBlockStatement)body).getCodeBlock() : null;
return BasicJavaAstTreeUtil.is(body, BASIC_BLOCK_STATEMENT) ?
BasicJavaAstTreeUtil.getCodeBlock(body) : null;
}
/**
@@ -129,24 +123,25 @@ public class PlainEnterProcessor implements EnterProcessor {
* <pre>
* void test(int i) {
* if (i > 1[caret]) {
*
*
* }
* }
* </pre>
* We want just move caret to correct position at that empty line without creating additional empty line then.
*
* @param editor target editor
* @param codeBlock target code block to which new empty line is going to be inserted
* @param element target element under caret
* @return {@code true} if it was found out that the given code block starts with the empty line and caret
* is pointed to correct position there, i.e. no additional processing is required;
* {@code false} otherwise
*
* @param editor target editor
* @param codeBlock target code block to which new empty line is going to be inserted
* @param element target element under caret
* @return {@code true} if it was found out that the given code block starts with the empty line and caret
* is pointed to correct position there, i.e. no additional processing is required;
* {@code false} otherwise
*/
private static boolean processExistingBlankLine(@NotNull Editor editor, @Nullable PsiCodeBlock codeBlock, @Nullable PsiElement element) {
private static boolean processExistingBlankLine(@NotNull Editor editor, @Nullable PsiElement codeBlock, @Nullable ASTNode element) {
PsiWhiteSpace whiteSpace = null;
if (codeBlock == null) {
if (element != null && !(element instanceof PsiMember)) {
final PsiElement next = PsiTreeUtil.nextLeaf(element);
PsiElement psiElement = BasicJavaAstTreeUtil.toPsi(element);
if (psiElement != null && !(BasicJavaAstTreeUtil.is(element, MEMBER_SET))) {
final PsiElement next = PsiTreeUtil.nextLeaf(psiElement);
if (next instanceof PsiWhiteSpace) {
whiteSpace = (PsiWhiteSpace)next;
}
@@ -193,6 +188,6 @@ public class PlainEnterProcessor implements EnterProcessor {
EditorActionHandler actionHandler = actionManager.getActionHandler(IdeActions.ACTION_EDITOR_MOVE_LINE_END);
final DataContext dataContext = DataManager.getInstance().getDataContext(editor.getComponent());
actionHandler.execute(editor, editor.getCaretModel().getCurrentCaret(), dataContext);
return true;
return true;
}
}
@@ -0,0 +1,44 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_SWITCH_EXPRESSION;
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_SWITCH_STATEMENT;
public class SwitchExpressionFixer implements Fixer {
@Override
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
if (BasicJavaAstTreeUtil.is(astNode, BASIC_SWITCH_EXPRESSION) ||
BasicJavaAstTreeUtil.is(astNode, BASIC_SWITCH_STATEMENT)
) {
final Document doc = editor.getDocument();
final ASTNode rParenth = BasicJavaAstTreeUtil.getRParenth(astNode);
final ASTNode lParenth = BasicJavaAstTreeUtil.getLParenth(astNode);
final ASTNode condition = BasicJavaAstTreeUtil.getExpression(astNode);
if (condition == null) {
if (lParenth == null || rParenth == null) {
int stopOffset = doc.getLineEndOffset(doc.getLineNumber(astNode.getTextRange().getStartOffset()));
final ASTNode block = BasicJavaAstTreeUtil.getCodeBlock(astNode);
if (block != null) {
stopOffset = Math.min(stopOffset, block.getTextRange().getStartOffset());
}
doc.replaceString(astNode.getTextRange().getStartOffset(), stopOffset, "switch ()");
}
else {
processor.registerUnresolvedError(lParenth.getTextRange().getEndOffset());
}
}
else if (rParenth == null) {
doc.insertString(condition.getTextRange().getEndOffset(), ")");
}
}
}
}
@@ -0,0 +1,32 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.EditorModificationUtilEx;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_CONDITIONAL_EXPRESSION;
public class TernaryColonFixer implements Fixer {
@Override
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
if (!(BasicJavaAstTreeUtil.is(astNode, BASIC_CONDITIONAL_EXPRESSION))) {
return;
}
if (BasicJavaAstTreeUtil.getConditionalExpressionThenExpression(astNode) == null ||
astNode.findChildByType(JavaTokenType.COLON) != null) {
return;
}
editor.getCaretModel().moveToOffset(astNode.getTextRange().getEndOffset());
EditorModificationUtilEx.insertStringAtCaret(editor, ": ");
processor.setSkipEnter(true);
}
}
@@ -0,0 +1,41 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_WHILE_STATEMENT;
public class WhileConditionFixer implements Fixer {
@Override
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
if (BasicJavaAstTreeUtil.is(astNode, BASIC_WHILE_STATEMENT)) {
final Document doc = editor.getDocument();
final ASTNode rParenth = BasicJavaAstTreeUtil.getRParenth(astNode);
final ASTNode lParenth = BasicJavaAstTreeUtil.getLParenth(astNode);
final ASTNode condition = BasicJavaAstTreeUtil.getWhileCondition(astNode);
if (condition == null) {
if (lParenth == null || rParenth == null) {
int stopOffset = doc.getLineEndOffset(doc.getLineNumber(astNode.getTextRange().getStartOffset()));
final ASTNode block = BasicJavaAstTreeUtil.getWhileBody(astNode);
if (block != null) {
stopOffset = Math.min(stopOffset, block.getTextRange().getStartOffset());
}
stopOffset = Math.min(stopOffset, astNode.getTextRange().getEndOffset());
doc.replaceString(astNode.getTextRange().getStartOffset(), stopOffset, "while ()");
processor.registerUnresolvedError(astNode.getTextRange().getStartOffset() + "while (".length());
} else {
processor.registerUnresolvedError(lParenth.getTextRange().getEndOffset());
}
} else if (rParenth == null) {
doc.insertString(condition.getTextRange().getEndOffset(), ")");
}
}
}
}
@@ -0,0 +1,45 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.wordSelection;
import com.intellij.codeInsight.editorActions.ExtendWordSelectionHandlerBase;
import com.intellij.lang.ASTNode;
import com.intellij.lang.Language;
import com.intellij.lang.xml.XMLLanguage;
import com.intellij.psi.PsiElement;
import com.intellij.psi.TokenType;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import org.jetbrains.annotations.NotNull;
import java.util.function.Predicate;
import static com.intellij.psi.impl.source.BasicElementTypes.JAVA_COMMENT_BIT_SET;
import static com.intellij.psi.impl.source.BasicJavaDocElementType.*;
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
public abstract class AbstractBasicBackBasicSelectioner extends ExtendWordSelectionHandlerBase {
private static Predicate<PsiElement> getElementPredicate() {
return (e) -> {
Language language = e.getLanguage();
return !(language instanceof XMLLanguage || language.isKindOf(XMLLanguage.INSTANCE));
};
}
@Override
public boolean canSelect(@NotNull final PsiElement e) {
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
return
!BasicJavaAstTreeUtil.is(node, TokenType.WHITE_SPACE) &&
!BasicJavaAstTreeUtil.is(node, JAVA_COMMENT_BIT_SET) &&
!BasicJavaAstTreeUtil.is(node, BASIC_CODE_BLOCK) &&
!BasicJavaAstTreeUtil.is(node, BASIC_ARRAY_INITIALIZER_EXPRESSION) &&
!BasicJavaAstTreeUtil.is(node, BASIC_PARAMETER_LIST) &&
!BasicJavaAstTreeUtil.is(node, BASIC_EXPRESSION_LIST) &&
!BasicJavaAstTreeUtil.is(node, BASIC_BLOCK_STATEMENT) &&
!BasicJavaAstTreeUtil.is(node, JAVA_CODE_REFERENCE_ELEMENT_SET) &&
!(BasicJavaAstTreeUtil.isJavaToken(node) &&
!BasicJavaAstTreeUtil.isKeyword(node)) &&
!BasicJavaAstTreeUtil.is(node, DOC_TAG, DOC_SNIPPET_TAG, DOC_INLINE_TAG) &&
getElementPredicate().test(e);
}
}
@@ -1,18 +1,4 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.wordSelection;
import com.intellij.codeInsight.editorActions.ExtendWordSelectionHandlerBase;
@@ -23,6 +9,7 @@ import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiComment;
import com.intellij.psi.PsiElement;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
@@ -30,18 +17,9 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static com.intellij.psi.impl.source.BasicElementTypes.JAVA_COMMENT_BIT_SET;
public class AntLikePropertySelectionHandler extends ExtendWordSelectionHandlerBase {
@Override
public boolean canSelect(@NotNull PsiElement e) {
Language l = e.getLanguage();
if (!(l.equals(JavaLanguage.INSTANCE)
|| l.equals(XMLLanguage.INSTANCE))) {
return false;
}
return PsiTreeUtil.getParentOfType(e, PsiComment.class) == null;
}
@Override
public List<TextRange> select(@NotNull PsiElement e, @NotNull CharSequence editorText, int cursorOffset, @NotNull Editor editor) {
TextRange range = e.getTextRange();
@@ -62,4 +40,18 @@ public class AntLikePropertySelectionHandler extends ExtendWordSelectionHandlerB
}
return Collections.emptyList();
}
@Override
public boolean canSelect(@NotNull PsiElement e) {
Language l = e.getLanguage();
if (!(l.equals(JavaLanguage.INSTANCE)
|| l.equals(XMLLanguage.INSTANCE))) {
return false;
}
if (BasicJavaAstTreeUtil.getParentOfType(BasicJavaAstTreeUtil.toNode(e), JAVA_COMMENT_BIT_SET) == null) {
return true;
}
return PsiTreeUtil.getParentOfType(e, PsiComment.class) == null;
}
}
@@ -0,0 +1,92 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.wordSelection;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.TokenType;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.util.DocumentUtil;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
public class CaseStatementsSelectioner extends AbstractBasicBackBasicSelectioner {
@Override
public boolean canSelect(@NotNull PsiElement e) {
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
return node != null &&
BasicJavaAstTreeUtil.is(node.getTreeParent(), BASIC_CODE_BLOCK) &&
BasicJavaAstTreeUtil.is(node.getTreeParent().getTreeParent(), BASIC_SWITCH_STATEMENT);
}
@Override
public List<TextRange> select(@NotNull PsiElement elementStatement,
@NotNull CharSequence editorText,
int cursorOffset,
@NotNull Editor editor) {
ASTNode statement = BasicJavaAstTreeUtil.toNode(elementStatement);
List<TextRange> result = new ArrayList<>();
ASTNode caseStart = statement;
ASTNode caseEnd = statement;
if (statement == null ||
BasicJavaAstTreeUtil.is(statement, BASIC_SWITCH_STATEMENT)) {
return result;
}
ASTNode labelStatement = BasicJavaAstTreeUtil.is(statement, BASIC_SWITCH_LABEL_STATEMENT) ? statement : null;
ASTNode sibling;
if (labelStatement == null) {
sibling = statement.getTreePrev();
while (sibling != null && !BasicJavaAstTreeUtil.is(sibling, BASIC_SWITCH_LABEL_STATEMENT)) {
if (!BasicJavaAstTreeUtil.isWhiteSpace(sibling)) caseStart = sibling;
sibling = sibling.getTreePrev();
}
labelStatement = sibling;
}
if (labelStatement != null) {
ASTNode nextLabel = BasicJavaAstTreeUtil.skipSiblingsBackward(labelStatement, TokenType.WHITE_SPACE);
while (BasicJavaAstTreeUtil.is(nextLabel, BASIC_SWITCH_LABEL_STATEMENT)) {
labelStatement = nextLabel;
nextLabel = BasicJavaAstTreeUtil.skipSiblingsBackward(labelStatement, TokenType.WHITE_SPACE);
}
}
sibling = BasicJavaAstTreeUtil.isWhiteSpace(statement) ? statement.getTreeNext() : statement;
while (BasicJavaAstTreeUtil.is(sibling, BASIC_SWITCH_LABEL_STATEMENT)) {
sibling = BasicJavaAstTreeUtil.skipSiblingsForward(sibling, TokenType.WHITE_SPACE);
}
while (sibling != null && !BasicJavaAstTreeUtil.is(sibling, BASIC_SWITCH_LABEL_STATEMENT)) {
if (!BasicJavaAstTreeUtil.isWhiteSpace(sibling) &&
!BasicJavaAstTreeUtil.isJavaToken(sibling) // end of switch
) {
caseEnd = sibling;
}
sibling = sibling.getTreeNext();
}
Document document = editor.getDocument();
int endOffset =
DocumentUtil.getLineEndOffset(BasicJavaAstTreeUtil.getTextOffset(caseEnd) + caseEnd.getTextLength(), document) + 1;
if (!BasicJavaAstTreeUtil.is(caseStart, BASIC_SWITCH_LABEL_STATEMENT)) {
result.add(new TextRange(DocumentUtil.getLineStartOffset(BasicJavaAstTreeUtil.getTextOffset(caseStart), document),
endOffset));
}
if (labelStatement != null) {
result.add(
new TextRange(DocumentUtil.getLineStartOffset(BasicJavaAstTreeUtil.getTextOffset(labelStatement), document),
endOffset));
}
return result;
}
}
@@ -0,0 +1,98 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.wordSelection;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.PsiElement;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
public class CodeBlockOrInitializerSelectioner extends AbstractBasicBackBasicSelectioner {
@Override
public boolean canSelect(@NotNull PsiElement e) {
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
return BasicJavaAstTreeUtil.is(node, BASIC_CODE_BLOCK) ||
BasicJavaAstTreeUtil.is(node, BASIC_ARRAY_INITIALIZER_EXPRESSION) ||
BasicJavaAstTreeUtil.is(node, CLASS_SET) &&
!BasicJavaAstTreeUtil.is(node, BASIC_TYPE_PARAMETER);
}
@Override
public List<TextRange> select(@NotNull PsiElement e, @NotNull CharSequence editorText, int cursorOffset, @NotNull Editor editor) {
List<TextRange> result = new ArrayList<>();
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
if (node == null) {
return null;
}
result.add(getElementRange(node));
List<ASTNode> children = BasicJavaAstTreeUtil.getChildren(node);
if (!children.isEmpty()) {
int start = findOpeningBrace(children);
// in non-Java PsiClasses, there can be no opening brace
if (start != 0) {
int end = findClosingBrace(children, start);
result.addAll(expandToWholeLine(editorText, new TextRange(start, end)));
}
}
return result;
}
public TextRange getElementRange(@NotNull ASTNode astNode) {
if (BasicJavaAstTreeUtil.is(astNode, CLASS_SET)) {
ASTNode lBrace = BasicJavaAstTreeUtil.getLBrace(astNode);
ASTNode rBrace = BasicJavaAstTreeUtil.getRBrace(astNode);
if (lBrace != null && rBrace != null) {
return new TextRange(BasicJavaAstTreeUtil.getTextOffset(lBrace), rBrace.getTextRange().getEndOffset());
}
}
return astNode.getTextRange();
}
public static int findOpeningBrace(List<ASTNode> children) {
int start = 0;
for (int i = 0; i < children.size(); i++) {
ASTNode child = children.get(i);
if (BasicJavaAstTreeUtil.is(child, JavaTokenType.LBRACE)) {
int j = i + 1;
while (BasicJavaAstTreeUtil.isWhiteSpace(children.get(j))) {
j++;
}
start = children.get(j).getTextRange().getStartOffset();
}
}
return start;
}
public static int findClosingBrace(List<ASTNode> children, int startOffset) {
int end = children.get(children.size() - 1).getTextRange().getEndOffset();
for (int i = 0; i < children.size(); i++) {
ASTNode child = children.get(i);
if (BasicJavaAstTreeUtil.is(child, JavaTokenType.RBRACE)) {
int j = i - 1;
while (BasicJavaAstTreeUtil.isWhiteSpace(children.get(j)) && children.get(j).getTextRange().getStartOffset() > startOffset) {
j--;
}
end = children.get(j).getTextRange().getEndOffset();
}
}
return end;
}
}
@@ -1,35 +1,45 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.wordSelection;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.JavaDocTokenType;
import com.intellij.psi.PsiElement;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.javadoc.PsiDocToken;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.util.text.CharArrayUtil;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import static com.intellij.psi.impl.source.BasicJavaDocElementType.DOC_COMMENT;
public class DocCommentSelectioner extends LineCommentSelectioner {
@Override
public boolean canSelect(@NotNull PsiElement e) {
return e instanceof PsiDocComment;
return BasicJavaAstTreeUtil.is(BasicJavaAstTreeUtil.toNode(e), DOC_COMMENT);
}
@Override
public List<TextRange> select(@NotNull PsiElement e, @NotNull CharSequence editorText, int cursorOffset, @NotNull Editor editor) {
List<TextRange> result = super.select(e, editorText, cursorOffset, editor);
if (result == null) {
return null;
}
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
if (node == null) {
return null;
}
PsiElement[] children = e.getChildren();
List<ASTNode> children = BasicJavaAstTreeUtil.getChildren(node);
int startOffset = e.getTextRange().getStartOffset();
int endOffset = e.getTextRange().getEndOffset();
for (PsiElement child : children) {
if (child instanceof PsiDocToken token && token.getTokenType() == JavaDocTokenType.DOC_COMMENT_DATA) {
char[] chars = token.getText().toCharArray();
for (ASTNode child : children) {
if (BasicJavaAstTreeUtil.is(child, JavaDocTokenType.DOC_COMMENT_DATA)) {
char[] chars = child.getText().toCharArray();
if (CharArrayUtil.shiftForward(chars, 0, " *\n\t\r") != chars.length) {
break;
@@ -39,9 +49,9 @@ public class DocCommentSelectioner extends LineCommentSelectioner {
startOffset = child.getTextRange().getEndOffset();
}
for (PsiElement child : children) {
if (child instanceof PsiDocToken token && token.getTokenType() == JavaDocTokenType.DOC_COMMENT_DATA) {
char[] chars = token.getText().toCharArray();
for (ASTNode child : children) {
if (BasicJavaAstTreeUtil.is(child, JavaDocTokenType.DOC_COMMENT_DATA)) {
char[] chars = child.getText().toCharArray();
if (CharArrayUtil.shiftForward(chars, 0, " *\n\t\r") != chars.length) {
endOffset = child.getTextRange().getEndOffset();
@@ -1,42 +1,51 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.wordSelection;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.JavaDocTokenType;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.javadoc.PsiDocTag;
import com.intellij.psi.javadoc.PsiDocToken;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.util.text.CharArrayUtil;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import static com.intellij.psi.impl.source.BasicJavaDocElementType.*;
public class DocTagSelectioner extends WordSelectioner {
@Override
public boolean canSelect(@NotNull PsiElement e) {
return e instanceof PsiDocTag;
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
return BasicJavaAstTreeUtil.is(node, DOC_TAG, DOC_SNIPPET_TAG, DOC_INLINE_TAG);
}
@Override
public List<TextRange> select(@NotNull PsiElement e, @NotNull CharSequence editorText, int cursorOffset, @NotNull Editor editor) {
List<TextRange> result = super.select(e, editorText, cursorOffset, editor);
result.add(getDocTagRange((PsiDocTag)e, editorText, cursorOffset));
if (result == null) {
return null;
}
result.add(getDocTagRange(e, editorText, cursorOffset));
return result;
}
public static TextRange getDocTagRange(PsiDocTag e, CharSequence documentText, int minOffset) {
public static TextRange getDocTagRange(@NotNull PsiElement e, @NotNull CharSequence documentText, int minOffset) {
TextRange range = e.getTextRange();
int endOffset = range.getEndOffset();
int startOffset = range.getStartOffset();
PsiElement[] children = e.getChildren();
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
if (node == null) {
return null;
}
for (int i = children.length - 1; i >= 0; i--) {
PsiElement child = children[i];
List<ASTNode> children = BasicJavaAstTreeUtil.getChildren(node);
for (int i = children.size() - 1; i >= 0; i--) {
ASTNode child = children.get(i);
int childStartOffset = child.getTextRange().getStartOffset();
@@ -44,16 +53,15 @@ public class DocTagSelectioner extends WordSelectioner {
break;
}
if (child instanceof PsiDocToken token) {
IElementType type = token.getTokenType();
char[] chars = token.textToCharArray();
if (BasicJavaAstTreeUtil.isDocToken(child)) {
CharSequence chars = child.getChars();
int shift = CharArrayUtil.shiftForward(chars, 0, " \t\n\r");
if (shift != chars.length && type != JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS) {
if (shift != chars.length() && !BasicJavaAstTreeUtil.is(child, JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS)) {
break;
}
}
else if (!(child instanceof PsiWhiteSpace)) {
else if (!(BasicJavaAstTreeUtil.isWhiteSpace(child))) {
break;
}
@@ -1,40 +1,31 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.wordSelection;
import com.intellij.lang.ASTNode;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiIdentifier;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_ENUM_CONSTANT;
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_FIELD;
public class FieldSelectioner extends WordSelectioner {
@Override
public boolean canSelect(@NotNull PsiElement e) {
return e instanceof PsiField && e.getLanguage() == JavaLanguage.INSTANCE;
return BasicJavaAstTreeUtil.is(BasicJavaAstTreeUtil.toNode(e), BASIC_FIELD, BASIC_ENUM_CONSTANT) &&
e.getLanguage() == JavaLanguage.INSTANCE;
}
private static void addRangeElem(final List<? super TextRange> result,
CharSequence editorText,
final PsiElement first,
final ASTNode first,
final int end) {
if (first != null) {
result.addAll(expandToWholeLine(editorText,
@@ -45,21 +36,28 @@ public class FieldSelectioner extends WordSelectioner {
@Override
public List<TextRange> select(@NotNull PsiElement e, @NotNull CharSequence editorText, int cursorOffset, @NotNull Editor editor) {
List<TextRange> result = super.select(e, editorText, cursorOffset, editor);
PsiField field = (PsiField)e;
TextRange fieldRange = field.getTextRange();
PsiIdentifier nameId = field.getNameIdentifier();
if (result == null) {
return null;
}
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
if (node == null) {
return null;
}
TextRange fieldRange = node.getTextRange();
ASTNode nameId = BasicJavaAstTreeUtil.getNameIdentifier(node);
if (nameId == null) return new ArrayList<>();
TextRange nameRange = nameId.getTextRange();
PsiElement last = field.getInitializer();
ASTNode last = BasicJavaAstTreeUtil.getInitializer(node);
int end = last == null ? nameRange.getEndOffset() : last.getTextRange().getEndOffset();
PsiDocComment comment = field.getDocComment();
ASTNode comment = BasicJavaAstTreeUtil.getDocComment(node);
if (comment != null) {
TextRange commentTextRange = comment.getTextRange();
addRangeElem(result, editorText, comment, commentTextRange.getEndOffset());
}
addRangeElem(result, editorText, nameId, end);
addRangeElem(result, editorText, field.getTypeElement(), nameRange.getEndOffset());
addRangeElem(result, editorText, field.getModifierList(), fieldRange.getEndOffset());
addRangeElem(result, editorText, BasicJavaAstTreeUtil.getTypeElement(node), nameRange.getEndOffset());
addRangeElem(result, editorText, BasicJavaAstTreeUtil.getModifierList(node), fieldRange.getEndOffset());
result.addAll(expandToWholeLine(editorText, fieldRange));
return result;
}
@@ -1,31 +1,36 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.wordSelection;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiCodeBlock;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiKeyword;
import com.intellij.psi.PsiTryStatement;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
public class FinallyBlockSelectioner extends BasicSelectioner {
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_TRY_STATEMENT;
public class FinallyBlockSelectioner extends AbstractBasicBackBasicSelectioner {
@Override
public boolean canSelect(@NotNull PsiElement e) {
return e instanceof PsiKeyword && PsiKeyword.FINALLY.equals(e.getText());
return BasicJavaAstTreeUtil.is(BasicJavaAstTreeUtil.toNode(e), JavaTokenType.FINALLY_KEYWORD);
}
@Override
public List<TextRange> select(@NotNull PsiElement e, @NotNull CharSequence editorText, int cursorOffset, @NotNull Editor editor) {
List<TextRange> result = new ArrayList<>();
final PsiElement parent = e.getParent();
if (parent instanceof PsiTryStatement tryStatement) {
final PsiCodeBlock finallyBlock = tryStatement.getFinallyBlock();
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
if (node == null) {
return null;
}
final ASTNode parent = node.getTreeParent();
if (BasicJavaAstTreeUtil.is(parent, BASIC_TRY_STATEMENT)) {
final ASTNode finallyBlock = BasicJavaAstTreeUtil.getFinallyBlock(parent);
if (finallyBlock != null) {
result.add(new TextRange(e.getTextRange().getStartOffset(), finallyBlock.getTextRange().getEndOffset()));
}
@@ -0,0 +1,40 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.wordSelection;
import com.intellij.codeInsight.editorActions.ExtendWordSelectionHandler;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.List;
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_FOREACH_STATEMENT;
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_FOR_STATEMENT;
public class ForStatementHeaderSelectioner implements ExtendWordSelectionHandler {
@Override
public boolean canSelect(@NotNull PsiElement e) {
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
return
BasicJavaAstTreeUtil.is(node, BASIC_FOR_STATEMENT) ||
BasicJavaAstTreeUtil.is(node, BASIC_FOREACH_STATEMENT);
}
@Override
public List<TextRange> select(@NotNull PsiElement e, @NotNull CharSequence editorText, int cursorOffset, @NotNull Editor editor) {
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
if (node == null) {
return null;
}
ASTNode lParen = BasicJavaAstTreeUtil.getLParenth(node);
ASTNode rParen = BasicJavaAstTreeUtil.getRParenth(node);
if (lParen == null || rParen == null) return null;
TextRange result = new TextRange(lParen.getTextRange().getEndOffset(), rParen.getTextRange().getStartOffset());
return result.containsOffset(cursorOffset) ? Collections.singletonList(result) : null;
}
}
@@ -1,34 +1,37 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.wordSelection;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiIfStatement;
import com.intellij.psi.PsiKeyword;
import com.intellij.psi.PsiStatement;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
public class IfStatementSelectioner extends BasicSelectioner {
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_IF_STATEMENT;
public class IfStatementSelectioner extends AbstractBasicBackBasicSelectioner {
@Override
public boolean canSelect(@NotNull PsiElement e) {
return e instanceof PsiIfStatement;
return BasicJavaAstTreeUtil.is(BasicJavaAstTreeUtil.toNode(e), BASIC_IF_STATEMENT);
}
@Override
public List<TextRange> select(@NotNull PsiElement e, @NotNull CharSequence editorText, int cursorOffset, @NotNull Editor editor) {
List<TextRange> result = new ArrayList<>(expandToWholeLine(editorText, e.getTextRange(), false));
PsiIfStatement statement = (PsiIfStatement)e;
final PsiKeyword elseKeyword = statement.getElseElement();
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
if (node == null) {
return null;
}
final ASTNode elseKeyword = BasicJavaAstTreeUtil.getElseElement(node);
if (elseKeyword != null) {
final PsiStatement then = statement.getThenBranch();
final ASTNode then = BasicJavaAstTreeUtil.getThenBranch(node);
if (then != null) {
final TextRange thenRange = new TextRange(statement.getTextRange().getStartOffset(), then.getTextRange().getEndOffset());
final TextRange thenRange = new TextRange(e.getTextRange().getStartOffset(), then.getTextRange().getEndOffset());
if (thenRange.contains(cursorOffset)) {
result.addAll(expandToWholeLine(editorText, thenRange, false));
}
@@ -36,14 +39,14 @@ public class IfStatementSelectioner extends BasicSelectioner {
result.addAll(expandToWholeLine(editorText,
new TextRange(elseKeyword.getTextRange().getStartOffset(),
statement.getTextRange().getEndOffset()),
node.getTextRange().getEndOffset()),
false));
final PsiStatement branch = statement.getElseBranch();
if (branch instanceof PsiIfStatement elseIf) {
final PsiKeyword element = elseIf.getElseElement();
final ASTNode branch = BasicJavaAstTreeUtil.getElseBranch(node);
if (BasicJavaAstTreeUtil.is(branch, BASIC_IF_STATEMENT)) {
final ASTNode element = BasicJavaAstTreeUtil.getElseElement(branch);
if (element != null) {
final PsiStatement elseThen = elseIf.getThenBranch();
final ASTNode elseThen = BasicJavaAstTreeUtil.getThenBranch(branch);
if (elseThen != null) {
result.addAll(expandToWholeLine(editorText,
new TextRange(elseKeyword.getTextRange().getStartOffset(),
@@ -0,0 +1,33 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.wordSelection;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.Condition;
import com.intellij.psi.JavaDocTokenType;
import com.intellij.psi.PsiElement;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import static com.intellij.psi.impl.source.BasicJavaDocElementType.*;
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
public class JavaBasicWordSelectionFilter implements Condition<PsiElement> {
public JavaBasicWordSelectionFilter() {
}
@Override
public boolean value(final PsiElement e) {
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
return !BasicJavaAstTreeUtil.is(node, BASIC_CODE_BLOCK) &&
!BasicJavaAstTreeUtil.is(node, BASIC_ARRAY_INITIALIZER_EXPRESSION) &&
!BasicJavaAstTreeUtil.is(node, BASIC_PARAMETER_LIST) &&
!BasicJavaAstTreeUtil.is(node, BASIC_EXPRESSION_LIST) &&
!BasicJavaAstTreeUtil.is(node, BASIC_BLOCK_STATEMENT) &&
!BasicJavaAstTreeUtil.is(node, JAVA_CODE_REFERENCE_ELEMENT_SET) &&
!BasicJavaAstTreeUtil.isJavaToken(node) &&
!BasicJavaAstTreeUtil.is(node, DOC_TAG, DOC_SNIPPET_TAG, DOC_INLINE_TAG) &&
!(BasicJavaAstTreeUtil.isDocToken(node) &&
BasicJavaAstTreeUtil.is(node, JavaDocTokenType.DOC_COMMENT_DATA));
}
}
@@ -0,0 +1,42 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.wordSelection;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.PsiElement;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_CODE_BLOCK;
import static com.intellij.psi.impl.source.BasicJavaElementType.CLASS_SET;
public class JavaTokenSelectioner extends AbstractBasicBackBasicSelectioner {
@Override
public boolean canSelect(@NotNull PsiElement e) {
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
return BasicJavaAstTreeUtil.isJavaToken(node) && !BasicJavaAstTreeUtil.isKeyword(node) &&
!BasicJavaAstTreeUtil.is(node.getTreeParent(), BASIC_CODE_BLOCK) &&
!BasicJavaAstTreeUtil.is(node.getTreeParent(), CLASS_SET);
}
@Override
public List<TextRange> select(@NotNull PsiElement e, @NotNull CharSequence editorText, int cursorOffset, @NotNull Editor editor) {
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
if (node == null) {
return null;
}
if (!BasicJavaAstTreeUtil.is(node, JavaTokenType.SEMICOLON) &&
!BasicJavaAstTreeUtil.is(node, JavaTokenType.LPARENTH)) {
return super.select(e, editorText, cursorOffset, editor);
}
else {
return null;
}
}
}
@@ -1,28 +1,12 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.wordSelection;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiJavaToken;
import com.intellij.psi.PsiKeyword;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import org.jetbrains.annotations.NotNull;
import java.util.Iterator;
@@ -31,12 +15,12 @@ import java.util.List;
public class JavaWordSelectioner extends AbstractWordSelectioner {
@Override
public boolean canSelect(@NotNull PsiElement e) {
if (e instanceof PsiKeyword) {
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
if (BasicJavaAstTreeUtil.isKeyword(node)) {
return true;
}
if (e instanceof PsiJavaToken) {
IElementType tokenType = ((PsiJavaToken)e).getTokenType();
return tokenType == JavaTokenType.IDENTIFIER || tokenType == JavaTokenType.STRING_LITERAL;
if (BasicJavaAstTreeUtil.isJavaToken(node)) {
return BasicJavaAstTreeUtil.is(node, JavaTokenType.IDENTIFIER) || BasicJavaAstTreeUtil.is(node, JavaTokenType.STRING_LITERAL);
}
return false;
}
@@ -44,7 +28,10 @@ public class JavaWordSelectioner extends AbstractWordSelectioner {
@Override
public List<TextRange> select(@NotNull PsiElement e, @NotNull CharSequence editorText, int cursorOffset, @NotNull Editor editor) {
List<TextRange> ranges = super.select(e, editorText, cursorOffset, editor);
if (PsiUtil.isJavaToken(e, JavaTokenType.STRING_LITERAL)) {
if (ranges == null) {
return null;
}
if (BasicJavaAstTreeUtil.is(BasicJavaAstTreeUtil.toNode(e), JavaTokenType.STRING_LITERAL)) {
killRangesBreakingEscapes(e, ranges, e.getTextRange());
}
return ranges;
@@ -0,0 +1,55 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.wordSelection;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.PsiElement;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
public class ListSelectioner extends AbstractBasicBackBasicSelectioner {
@Override
public boolean canSelect(@NotNull PsiElement e) {
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
return BasicJavaAstTreeUtil.is(node, BASIC_PARAMETER_LIST) ||
BasicJavaAstTreeUtil.is(node, BASIC_EXPRESSION_LIST) ||
BasicJavaAstTreeUtil.is(node, BASIC_RECORD_HEADER);
}
@Override
public List<TextRange> select(@NotNull PsiElement e, @NotNull CharSequence editorText, int cursorOffset, @NotNull Editor editor) {
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
if (node == null) {
return null;
}
List<ASTNode> children = BasicJavaAstTreeUtil.getChildren(node);
int start = 0;
int end = 0;
for (ASTNode child : children) {
if (BasicJavaAstTreeUtil.isJavaToken(child)) {
if (BasicJavaAstTreeUtil.is(child, JavaTokenType.LPARENTH)) {
start = BasicJavaAstTreeUtil.getTextOffset(child) + 1;
}
if (BasicJavaAstTreeUtil.is(child, JavaTokenType.RPARENTH)) {
end = BasicJavaAstTreeUtil.getTextOffset(child);
}
}
}
List<TextRange> result = new ArrayList<>();
if (start != 0 && end != 0) {
result.add(new TextRange(start, end));
}
return result;
}
}
@@ -1,54 +1,67 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.wordSelection;
import com.intellij.codeInsight.editorActions.SelectWordUtil;
import com.intellij.lang.ASTNode;
import com.intellij.lexer.StringLiteralLexer;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.BasicLiteralUtil;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiLiteralExpression;
import com.intellij.psi.PsiType;
import com.intellij.psi.util.PsiLiteralUtil;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import static com.intellij.psi.CommonClassNames.JAVA_LANG_STRING;
import static com.intellij.util.ObjectUtils.tryCast;
import static com.intellij.psi.impl.source.BasicElementTypes.STRING_LITERALS;
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_LITERAL_EXPRESSION;
public class LiteralSelectioner extends AbstractBasicBackBasicSelectioner {
public class LiteralSelectioner extends BasicSelectioner {
@Override
public boolean canSelect(@NotNull PsiElement e) {
PsiElement parent = e.getParent();
return
isStringLiteral(e) || isStringLiteral(parent);
return isStringLiteral(e) || isStringLiteral(parent);
}
private static boolean isStringLiteral(PsiElement element) {
final PsiType type = element instanceof PsiLiteralExpression ? ((PsiLiteralExpression)element).getType() : null;
return type != null && type.equalsToText(JAVA_LANG_STRING)
&& element.getText().startsWith("\"")
&& element.getText().endsWith("\"");
return BasicJavaAstTreeUtil.is(BasicJavaAstTreeUtil.toNode(element), STRING_LITERALS)
&& element.getText().startsWith("\"")
&& element.getText().endsWith("\"");
}
@Override
public List<TextRange> select(@NotNull PsiElement e, @NotNull CharSequence editorText, int cursorOffset, @NotNull Editor editor) {
List<TextRange> result = super.select(e, editorText, cursorOffset, editor);
TextRange range = e.getTextRange();
if (result == null) {
return null;
}
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
if (node == null) {
return null;
}
TextRange range = node.getTextRange();
SelectWordUtil.addWordHonoringEscapeSequences(editorText, range, cursorOffset,
new StringLiteralLexer('\"', JavaTokenType.STRING_LITERAL),
result);
PsiLiteralExpression literalExpression = tryCast(e, PsiLiteralExpression.class);
if (literalExpression == null) literalExpression = tryCast(e.getParent(), PsiLiteralExpression.class);
if (literalExpression != null && literalExpression.isTextBlock()) {
ASTNode literalExpression = null;
if (BasicJavaAstTreeUtil.is(node, BASIC_LITERAL_EXPRESSION)) {
literalExpression = node;
}
if (literalExpression == null) {
ASTNode parent = node.getTreeParent();
if (BasicJavaAstTreeUtil.is(parent, BASIC_LITERAL_EXPRESSION)) {
literalExpression = parent;
}
}
PsiElement literalPsiExpression = BasicJavaAstTreeUtil.toPsi(literalExpression);
if (literalExpression != null && literalPsiExpression != null && BasicJavaAstTreeUtil.isTextBlock(literalExpression)) {
int contentStart = StringUtil.indexOf(editorText, '\n', range.getStartOffset());
if (contentStart == -1) return result;
contentStart += 1;
int indent = PsiLiteralUtil.getTextBlockIndent(literalExpression);
int indent = BasicLiteralUtil.getTextBlockIndent(literalPsiExpression);
if (indent == -1) return result;
for (int i = 0; i < indent; i++) {
if (editorText.charAt(contentStart + i) == '\n') return result;
@@ -0,0 +1,43 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.wordSelection;
import com.intellij.codeInsight.editorActions.ExtendWordSelectionHandler;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.List;
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_METHOD_CALL_EXPRESSION;
public class MethodCallSelectioner implements ExtendWordSelectionHandler {
@Override
public boolean canSelect(@NotNull PsiElement e) {
return BasicJavaAstTreeUtil.is(BasicJavaAstTreeUtil.toNode(e), BASIC_METHOD_CALL_EXPRESSION);
}
@Override
public List<TextRange> select(@NotNull PsiElement e, @NotNull CharSequence editorText, int cursorOffset, @NotNull Editor editor) {
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
if (node == null) {
return null;
}
ASTNode methodExpression = BasicJavaAstTreeUtil.getMethodExpression(node);
if (methodExpression == null) {
return null;
}
ASTNode referenceNameElement = BasicJavaAstTreeUtil.getReferenceNameElement(methodExpression);
if (referenceNameElement == null) {
return null;
}
else {
return Arrays.asList(new TextRange(referenceNameElement.getTextRange().getStartOffset(), e.getTextRange().getEndOffset()),
e.getTextRange());
}
}
}
@@ -0,0 +1,118 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.wordSelection;
import com.intellij.lang.ASTNode;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static com.intellij.psi.impl.source.BasicElementTypes.JAVA_COMMENT_BIT_SET;
import static com.intellij.psi.impl.source.BasicElementTypes.JAVA_COMMENT_OR_WHITESPACE_BIT_SET;
import static com.intellij.psi.impl.source.BasicJavaDocElementType.DOC_COMMENT;
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
public class MethodOrClassSelectioner extends AbstractBasicBackBasicSelectioner {
@Override
public boolean canSelect(@NotNull PsiElement e) {
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
return (
BasicJavaAstTreeUtil.is(node, CLASS_SET) &&
!BasicJavaAstTreeUtil.is(node, BASIC_TYPE_PARAMETER) ||
BasicJavaAstTreeUtil.is(node, BASIC_METHOD)) &&
e.getLanguage() == JavaLanguage.INSTANCE;
}
@Override
public List<TextRange> select(@NotNull PsiElement e, @NotNull CharSequence editorText, int cursorOffset, @NotNull Editor editor) {
List<TextRange> result = new ArrayList<>();
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
if (node == null) {
return result;
}
ASTNode firstChild = node.getFirstChildNode();
List<ASTNode> children = BasicJavaAstTreeUtil.getChildren(node);
int i = 1;
if (BasicJavaAstTreeUtil.is(firstChild, DOC_COMMENT)) {
while (BasicJavaAstTreeUtil.isWhiteSpace(children.get(i))) {
i++;
}
TextRange range = new TextRange(children.get(i).getTextRange().getStartOffset(), e.getTextRange().getEndOffset());
result.add(range);
result.addAll(expandToWholeLinesWithBlanks(editorText, range));
range = firstChild.getTextRange();
result.addAll(expandToWholeLinesWithBlanks(editorText, range));
firstChild = children.get(i++);
}
if (BasicJavaAstTreeUtil.is(firstChild, JAVA_COMMENT_BIT_SET)) {
while (BasicJavaAstTreeUtil.is(children.get(i), JAVA_COMMENT_OR_WHITESPACE_BIT_SET)) {
i++;
}
ASTNode last = BasicJavaAstTreeUtil.isWhiteSpace(children.get(i - 1)) ? children.get(i - 2) : children.get(i - 1);
TextRange range = new TextRange(firstChild.getTextRange().getStartOffset(), last.getTextRange().getEndOffset());
if (range.contains(cursorOffset)) {
result.addAll(expandToWholeLinesWithBlanks(editorText, range));
}
range = new TextRange(children.get(i).getTextRange().getStartOffset(), e.getTextRange().getEndOffset());
result.add(range);
result.addAll(expandToWholeLinesWithBlanks(editorText, range));
}
result.add(node.getTextRange());
result.addAll(expandToWholeLinesWithBlanks(editorText, node.getTextRange()));
if (BasicJavaAstTreeUtil.is(node, CLASS_SET)) {
result.addAll(selectWithTypeParameters(node));
result.addAll(selectBetweenBracesLines(children, editorText));
}
if (BasicJavaAstTreeUtil.is(node, BASIC_ANONYMOUS_CLASS)) {
result.addAll(selectWholeBlock(node));
}
return result;
}
private static Collection<TextRange> selectWithTypeParameters(@NotNull ASTNode astClass) {
final ASTNode identifier = BasicJavaAstTreeUtil.getNameIdentifier(astClass);
final ASTNode list = BasicJavaAstTreeUtil.getTypeParameterList(astClass);
if (identifier != null && list != null) {
return Collections.singletonList(new TextRange(identifier.getTextRange().getStartOffset(), list.getTextRange().getEndOffset()));
}
return Collections.emptyList();
}
private static Collection<TextRange> selectBetweenBracesLines(List<ASTNode> children,
@NotNull CharSequence editorText) {
int start = CodeBlockOrInitializerSelectioner.findOpeningBrace(children);
// in non-Java PsiClasses, there can be no opening brace
if (start != 0) {
int end = CodeBlockOrInitializerSelectioner.findClosingBrace(children, start);
return expandToWholeLinesWithBlanks(editorText, new TextRange(start, end));
}
return Collections.emptyList();
}
private static Collection<TextRange> selectWholeBlock(ASTNode clazz) {
ASTNode lBrace = BasicJavaAstTreeUtil.getLBrace(clazz);
ASTNode rBrace = BasicJavaAstTreeUtil.getRBrace(clazz);
if (lBrace != null && rBrace != null) {
return Collections.singleton(new TextRange(lBrace.getTextRange().getStartOffset(), rBrace.getTextRange().getEndOffset()));
}
return Collections.emptyList();
}
}
@@ -0,0 +1,83 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.wordSelection;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
public class ReferenceSelectioner extends AbstractBasicBackBasicSelectioner {
@Override
public boolean canSelect(@NotNull PsiElement e) {
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
return BasicJavaAstTreeUtil.is(node, JAVA_CODE_REFERENCE_ELEMENT_SET);
}
@Override
public List<TextRange> select(@NotNull PsiElement e, @NotNull CharSequence editorText, int cursorOffset, @NotNull Editor editor) {
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
ASTNode endElement = node;
if (endElement == null) {
return null;
}
while (BasicJavaAstTreeUtil.is(endElement, JAVA_CODE_REFERENCE_ELEMENT_SET) &&
endElement.getTreeNext() != null) {
endElement = endElement.getTreeNext();
}
if (!(BasicJavaAstTreeUtil.is(endElement, JAVA_CODE_REFERENCE_ELEMENT_SET)) &&
!(BasicJavaAstTreeUtil.is(endElement.getTreePrev(), REFERENCE_EXPRESSION_SET) &&
BasicJavaAstTreeUtil.is(endElement, BASIC_EXPRESSION_LIST))) {
endElement = endElement.getTreePrev();
}
ASTNode element = node;
List<TextRange> result = new ArrayList<>();
while (BasicJavaAstTreeUtil.is(element, JAVA_CODE_REFERENCE_ELEMENT_SET)) {
ASTNode firstChild = element.getFirstChildNode();
ASTNode referenceName = BasicJavaAstTreeUtil.getReferenceNameElement(element);
if (referenceName != null) {
result.addAll(expandToWholeLine(editorText, new TextRange(referenceName.getTextRange().getStartOffset(),
endElement.getTextRange().getEndOffset())));
if (BasicJavaAstTreeUtil.is(endElement, JAVA_CODE_REFERENCE_ELEMENT_SET)) {
final ASTNode endReferenceName = BasicJavaAstTreeUtil.getReferenceNameElement(endElement);
if (endReferenceName != null) {
result.addAll(expandToWholeLine(editorText, new TextRange(referenceName.getTextRange().getStartOffset(),
endReferenceName.getTextRange().getEndOffset())));
}
}
}
if (firstChild == null) break;
element = firstChild;
}
TextRange range = new TextRange(element.getTextRange().getStartOffset(),
endElement.getTextRange().getEndOffset());
result.add(range);
result.addAll(expandToWholeLine(editorText, range));
if (!(BasicJavaAstTreeUtil.is(node.getTreeParent(), JAVA_CODE_REFERENCE_ELEMENT_SET))) {
if (BasicJavaAstTreeUtil.isJavaToken(node.getTreeNext()) ||
BasicJavaAstTreeUtil.isWhiteSpace(node.getTreeNext()) ||
BasicJavaAstTreeUtil.is(node.getTreeNext(), BASIC_EXPRESSION_LIST)) {
List<TextRange> superSelect = super.select(e, editorText, cursorOffset, editor);
if (superSelect != null) {
result.addAll(superSelect);
}
}
}
return result;
}
}
@@ -0,0 +1,92 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.wordSelection;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.LineTokenizer;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.PsiElement;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
public class StatementGroupSelectioner extends AbstractBasicBackBasicSelectioner {
@Override
public boolean canSelect(@NotNull PsiElement e) {
return BasicJavaAstTreeUtil.is(BasicJavaAstTreeUtil.toNode(e), STATEMENT_SET);
}
@Override
public List<TextRange> select(@NotNull PsiElement e, @NotNull CharSequence editorText, int cursorOffset, @NotNull Editor editor) {
List<TextRange> result = new ArrayList<>();
PsiElement parentElement = e.getParent();
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
if (node == null) {
return result;
}
ASTNode parentNode = BasicJavaAstTreeUtil.toNode(parentElement);
if (!BasicJavaAstTreeUtil.is(parentNode, BASIC_CODE_BLOCK) &&
!BasicJavaAstTreeUtil.is(parentNode, BASIC_BLOCK_STATEMENT) ||
BasicJavaAstTreeUtil.is(node, BASIC_SWITCH_LABEL_STATEMENT)) {
return result;
}
ASTNode startElement = node;
ASTNode endElement = node;
while (startElement.getTreePrev() != null) {
ASTNode sibling = startElement.getTreePrev();
if (BasicJavaAstTreeUtil.is(sibling, JavaTokenType.LBRACE)) break;
if (BasicJavaAstTreeUtil.isWhiteSpace(sibling)) {
String[] strings = LineTokenizer.tokenize(sibling.getText().toCharArray(), false);
if (strings.length > 2) {
break;
}
}
if (BasicJavaAstTreeUtil.is(sibling, BASIC_SWITCH_LABEL_STATEMENT)) break;
startElement = sibling;
}
while (BasicJavaAstTreeUtil.isWhiteSpace(startElement)) {
startElement = startElement.getTreeNext();
}
while (endElement.getTreeNext() != null) {
ASTNode sibling = endElement.getTreeNext();
if (BasicJavaAstTreeUtil.is(sibling, JavaTokenType.RBRACE)) break;
if (BasicJavaAstTreeUtil.isWhiteSpace(sibling)) {
String[] strings = LineTokenizer.tokenize(sibling.getText().toCharArray(), false);
if (strings.length > 2) {
break;
}
}
if (BasicJavaAstTreeUtil.is(sibling, BASIC_SWITCH_LABEL_STATEMENT)) break;
endElement = sibling;
}
while (BasicJavaAstTreeUtil.isWhiteSpace(endElement)) {
endElement = endElement.getTreePrev();
}
result.addAll(expandToWholeLine(editorText, new TextRange(startElement.getTextRange().getStartOffset(),
endElement.getTextRange().getEndOffset())));
return result;
}
}
@@ -1,36 +1,36 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.wordSelection;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiJavaToken;
import com.intellij.psi.PsiTypeCastExpression;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
public class TypeCastSelectioner extends BasicSelectioner {
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_TYPE_CAST_EXPRESSION;
public class TypeCastSelectioner extends AbstractBasicBackBasicSelectioner {
@Override
public boolean canSelect(@NotNull PsiElement e) {
return e instanceof PsiTypeCastExpression;
return BasicJavaAstTreeUtil.is(BasicJavaAstTreeUtil.toNode(e), BASIC_TYPE_CAST_EXPRESSION);
}
@Override
public List<TextRange> select(@NotNull PsiElement e, @NotNull CharSequence editorText, int cursorOffset, @NotNull Editor editor) {
List<TextRange> result = new ArrayList<>(expandToWholeLine(editorText, e.getTextRange(), false));
PsiTypeCastExpression expression = (PsiTypeCastExpression)e;
PsiElement[] children = expression.getChildren();
PsiElement lParen = null;
PsiElement rParen = null;
for (PsiElement child : children) {
if (child instanceof PsiJavaToken token) {
if (token.getTokenType() == JavaTokenType.LPARENTH) lParen = token;
if (token.getTokenType() == JavaTokenType.RPARENTH) rParen = token;
}
List<ASTNode> children = BasicJavaAstTreeUtil.getChildren(BasicJavaAstTreeUtil.toNode(e));
ASTNode lParen = null;
ASTNode rParen = null;
for (ASTNode child : children) {
if (BasicJavaAstTreeUtil.is(child, JavaTokenType.LPARENTH)) lParen = child;
if (BasicJavaAstTreeUtil.is(child, JavaTokenType.RPARENTH)) rParen = child;
}
if (lParen != null && rParen != null) {
@@ -1,31 +1,23 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.highlighting;
import com.intellij.codeInsight.hint.DeclarationRangeUtil;
import com.intellij.lang.ASTNode;
import com.intellij.lang.BracePair;
import com.intellij.lang.PairedBraceMatcher;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.tree.ElementType;
import com.intellij.psi.JavaDocTokenType;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.java.IJavaElementType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static com.intellij.psi.impl.source.BasicElementTypes.*;
public class JavaBraceMatcher implements PairedBraceMatcher {
private final BracePair[] pairs = new BracePair[] {
new BracePair(JavaTokenType.LPARENTH, JavaTokenType.RPARENTH, false),
@@ -34,6 +26,8 @@ public class JavaBraceMatcher implements PairedBraceMatcher {
new BracePair(JavaDocTokenType.DOC_INLINE_TAG_START, JavaDocTokenType.DOC_INLINE_TAG_END, false),
new BracePair(JavaTokenType.LT, JavaTokenType.GT, false)
};
public JavaBraceMatcher() {
}
@Override
public BracePair @NotNull [] getPairs() {
@@ -47,14 +41,14 @@ public class JavaBraceMatcher implements PairedBraceMatcher {
}
private static boolean isPairedBracesAllowedBeforeTypeInJava(final IElementType tokenType) {
return ElementType.JAVA_COMMENT_OR_WHITESPACE_BIT_SET.contains(tokenType)
|| tokenType == JavaTokenType.SEMICOLON
|| tokenType == JavaTokenType.COMMA
|| tokenType == JavaTokenType.RPARENTH
|| tokenType == JavaTokenType.RBRACKET
|| tokenType == JavaTokenType.RBRACE
|| tokenType == JavaTokenType.LBRACE
|| tokenType == JavaTokenType.DOT;
return JAVA_COMMENT_OR_WHITESPACE_BIT_SET.contains(tokenType)
|| tokenType == JavaTokenType.SEMICOLON
|| tokenType == JavaTokenType.COMMA
|| tokenType == JavaTokenType.RPARENTH
|| tokenType == JavaTokenType.RBRACKET
|| tokenType == JavaTokenType.RBRACE
|| tokenType == JavaTokenType.LBRACE
|| tokenType == JavaTokenType.DOT;
}
@Override
@@ -62,20 +56,24 @@ public class JavaBraceMatcher implements PairedBraceMatcher {
PsiElement element = file.findElementAt(openingBraceOffset);
if (element == null || element instanceof PsiFile) return openingBraceOffset;
PsiElement parent = element.getParent();
if (parent instanceof PsiCodeBlock) {
parent = parent.getParent();
if (parent instanceof PsiMethod || parent instanceof PsiClassInitializer) {
TextRange range = DeclarationRangeUtil.getDeclarationRange(parent);
if(parent==null) return openingBraceOffset;
ASTNode parentNode = parent.getNode();
if (BasicJavaAstTreeUtil.is(parentNode, BASIC_CODE_BLOCK)) {
parentNode = parentNode.getTreeParent();
if (BasicJavaAstTreeUtil.is(parentNode, BASIC_METHOD) ||
BasicJavaAstTreeUtil.is(parentNode, BASIC_CLASS_INITIALIZER)) {
TextRange range = DeclarationRangeUtil.getDeclarationRange(parentNode.getPsi());
return range.getStartOffset();
}
else if (parent instanceof PsiStatement) {
if (parent instanceof PsiBlockStatement && parent.getParent() instanceof PsiStatement) {
parent = parent.getParent();
else if (BasicJavaAstTreeUtil.is(parentNode, JAVA_STATEMENT_BIT_SET)) {
if (BasicJavaAstTreeUtil.is(parentNode, BASIC_BLOCK_STATEMENT) &&
BasicJavaAstTreeUtil.is(parentNode.getTreeParent(), JAVA_STATEMENT_BIT_SET)) {
parentNode = parentNode.getTreeParent();
}
return parent.getTextRange().getStartOffset();
return parentNode.getTextRange().getStartOffset();
}
}
else if (parent instanceof PsiClass) {
else if (BasicJavaAstTreeUtil.is(parentNode, CLASS_KEYWORD_BIT_SET)) {
TextRange range = DeclarationRangeUtil.getDeclarationRange(parent);
return range.getStartOffset();
}
@@ -0,0 +1,37 @@
package com.intellij.codeInsight.highlighting;
import com.intellij.BaseJavaJspElementType;
import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.impl.source.BasicElementTypes;
import com.intellij.psi.impl.source.BasicJavaTokenSet;
import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.NotNull;
public class JavaPairedBraceMatcher extends PairedBraceAndAnglesMatcher {
private static class Holder {
private static final BasicJavaTokenSet TYPE_TOKENS =
BasicJavaTokenSet.orSet(BaseJavaJspElementType.WHITE_SPACE_BIT_SET,
BasicElementTypes.JAVA_COMMENT_BIT_SET,
BasicJavaTokenSet.create(JavaTokenType.IDENTIFIER, JavaTokenType.COMMA,
JavaTokenType.AT,//anno
JavaTokenType.RBRACKET, JavaTokenType.LBRACKET, //arrays
JavaTokenType.QUEST, JavaTokenType.EXTENDS_KEYWORD, JavaTokenType.SUPER_KEYWORD));//wildcards
}
public JavaPairedBraceMatcher() {
super(new JavaBraceMatcher(), JavaLanguage.INSTANCE, JavaFileType.INSTANCE, Holder.TYPE_TOKENS.toTokenSet());
}
@Override
public @NotNull IElementType lt() {
return JavaTokenType.LT;
}
@Override
public @NotNull IElementType gt() {
return JavaTokenType.GT;
}
}
@@ -1,4 +1,4 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.highlighting;
import com.intellij.lang.BracePair;
@@ -55,7 +55,7 @@ public final class JavaHighlightingColors {
public static final TextAttributesKey ANNOTATION_NAME_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("ANNOTATION_NAME_ATTRIBUTES", DefaultLanguageHighlighterColors.METADATA);
public static final TextAttributesKey ANNOTATION_ATTRIBUTE_NAME_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("ANNOTATION_ATTRIBUTE_NAME_ATTRIBUTES", DefaultLanguageHighlighterColors.METADATA);
public static final TextAttributesKey ANNOTATION_ATTRIBUTE_VALUE_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("ANNOTATION_ATTRIBUTE_VALUE_ATTRIBUTES", DefaultLanguageHighlighterColors.METADATA);
//visibility
public static final TextAttributesKey PUBLIC_REFERENCE_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("PUBLIC_REFERENCE", (TextAttributesKey)null);
public static final TextAttributesKey PROTECTED_REFERENCE_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("PROTECTED_REFERENCE", (TextAttributesKey)null);
@@ -0,0 +1,230 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.javadoc;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.CaretModel;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.LogicalPosition;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.psi.impl.source.BasicJavaTokenSet;
import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static com.intellij.psi.impl.source.BasicJavaDocElementType.*;
public abstract class AbstractBasicJavadocHelper {
private static final String PARAM_TEXT = "param";
public static final Pair<JavadocParameterInfo, List<JavadocParameterInfo>> EMPTY
= new Pair<>(null, Collections.emptyList());
private static final @NotNull BasicJavaTokenSet TAG_TOKEN_SET = BasicJavaTokenSet.create(DOC_TAG,
DOC_SNIPPET_TAG,
DOC_INLINE_TAG);
protected AbstractBasicJavadocHelper() {
}
/**
* Tries to navigate caret at the given editor to the target position inserting missing white spaces if necessary.
*
* @param position target caret position
* @param editor target editor
* @param project target project
*/
public void navigate(@NotNull LogicalPosition position, @NotNull Editor editor, final @NotNull Project project) {
final Document document = editor.getDocument();
final CaretModel caretModel = editor.getCaretModel();
final int endLineOffset = document.getLineEndOffset(position.line);
final LogicalPosition endLinePosition = editor.offsetToLogicalPosition(endLineOffset);
if (endLinePosition.column < position.column && !editor.getSettings().isVirtualSpace() && !editor.isViewer()) {
final String toInsert = StringUtil.repeat(" ", position.column - endLinePosition.column);
ApplicationManager.getApplication().runWriteAction(() -> {
document.insertString(endLineOffset, toInsert);
PsiDocumentManager.getInstance(project).commitDocument(document);
});
}
caretModel.moveToLogicalPosition(position);
}
/**
* Calculates desired position of target javadoc parameter's description start.
*
* @param psiFile PSI holder
* @param data parsed adjacent javadoc parameters
* @param anchor descriptor for the target parameter
* @return logical position that points to the desired parameter description start location
*/
public @NotNull LogicalPosition calculateDescriptionStartPosition(@NotNull PsiFile psiFile,
@NotNull Collection<? extends JavadocParameterInfo> data,
@NotNull JavadocParameterInfo anchor) {
int descriptionStartColumn = -1;
int parameterNameEndColumn = -1;
for (JavadocParameterInfo parameterInfo : data) {
parameterNameEndColumn = Math.max(parameterNameEndColumn, parameterInfo.parameterNameEndPosition.column);
if (parameterInfo.parameterDescriptionStartPosition != null) {
descriptionStartColumn = Math.max(descriptionStartColumn, parameterInfo.parameterDescriptionStartPosition.column);
}
}
int column;
if (getJdAlignParamComments(psiFile)) {
column = Math.max(descriptionStartColumn, parameterNameEndColumn);
if (column <= parameterNameEndColumn) {
column = parameterNameEndColumn + 1;
}
}
else {
column = anchor.parameterNameEndPosition.column + 1;
}
return new LogicalPosition(anchor.parameterNameEndPosition.line, column);
}
protected abstract boolean getJdAlignParamComments(@NotNull PsiFile psiFile);
/**
* Returns information about all lines that contain javadoc parameters and are adjacent to the one that holds given offset.
*
* @param psiFile PSI holder for the document exposed the given editor
* @param editor target editor
* @param offset target offset that identifies anchor line to check
* @return pair like (javadoc info for the line identified by the given offset; list of javadoc parameter infos for
* adjacent lines if any
*/
public @NotNull Pair<JavadocParameterInfo, List<JavadocParameterInfo>> parse(@NotNull PsiFile psiFile,
@NotNull Editor editor,
int offset) {
List<JavadocParameterInfo> result = new ArrayList<>();
PsiDocumentManager.getInstance(psiFile.getProject()).commitDocument(editor.getDocument());
final PsiElement elementAtCaret = psiFile.findElementAt(offset);
if (elementAtCaret == null) {
return EMPTY;
}
ASTNode nodeAtCaret = BasicJavaAstTreeUtil.toNode(elementAtCaret);
ASTNode tag = BasicJavaAstTreeUtil.getParentOfType(nodeAtCaret, TAG_TOKEN_SET);
if (tag == null) {
// Due to javadoc PSI specifics.
if (BasicJavaAstTreeUtil.isWhiteSpace(nodeAtCaret)) {
for (ASTNode e = nodeAtCaret.getTreePrev(); e != null && tag == null; e = e.getTreePrev()) {
tag = BasicJavaAstTreeUtil.getParentOfType(e, TAG_TOKEN_SET, false);
if (e instanceof PsiWhiteSpace
|| (e.getElementType() == JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS)) {
continue;
}
break;
}
}
}
if (tag == null) {
return EMPTY;
}
JavadocParameterInfo anchorInfo = parse(tag, editor);
if (anchorInfo == null) {
return EMPTY;
}
// Parse previous parameters.
for (ASTNode n = tag.getTreePrev(); n != null; n = n.getTreePrev()) {
JavadocParameterInfo info = parse(n, editor);
if (info == null) {
break;
}
result.add(0, info);
}
result.add(anchorInfo);
// Parse subsequent parameters.
for (ASTNode n = tag.getTreeNext(); n != null; n = n.getTreeNext()) {
JavadocParameterInfo info = parse(n, editor);
if (info == null) {
break;
}
result.add(info);
}
return Pair.create(anchorInfo, result);
}
private static @Nullable JavadocParameterInfo parse(@NotNull ASTNode astNode, @NotNull Editor editor) {
final ASTNode tag = BasicJavaAstTreeUtil.getParentOfType(astNode, TAG_TOKEN_SET, false);
if (tag == null || !PARAM_TEXT.equals(BasicJavaAstTreeUtil.getTagName(tag))) {
return null;
}
final ASTNode paramRef = BasicJavaAstTreeUtil.findChildByType(tag, DOC_TAG_VALUE_ELEMENT,
DOC_METHOD_OR_FIELD_REF,
DOC_PARAMETER_REF,
DOC_SNIPPET_TAG_VALUE);
if (paramRef == null) {
return null;
}
for (ASTNode node = paramRef.getTreeNext(); node != null; node = node.getTreeNext()) {
final IElementType elementType = node.getElementType();
if (elementType == JavaDocTokenType.DOC_COMMENT_DATA) {
return new JavadocParameterInfo(
editor.offsetToLogicalPosition(paramRef.getTextRange().getEndOffset()),
editor.offsetToLogicalPosition(node.getTextRange().getStartOffset()),
editor.getDocument().getLineNumber(node.getTextRange().getEndOffset())
);
}
else if (elementType == JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS) {
break;
}
}
return new JavadocParameterInfo(
editor.offsetToLogicalPosition(paramRef.getTextRange().getEndOffset()),
null,
editor.getDocument().getLineNumber(paramRef.getTextRange().getEndOffset())
);
}
/**
* Encapsulates information about source code line that holds javadoc parameter.
*/
public static class JavadocParameterInfo {
/**
* Logical position that points to location just after javadoc parameter name.
* <p/>
* Example:
* <pre>
* /**
* * @param i[X] description
* *&#47;
* </pre>
*/
public final @NotNull LogicalPosition parameterNameEndPosition;
public final @Nullable LogicalPosition parameterDescriptionStartPosition;
/** Last logical line occupied by the current javadoc parameter. */
public final int lastLine;
public JavadocParameterInfo(@NotNull LogicalPosition parameterNameEndPosition,
@Nullable LogicalPosition parameterDescriptionStartPosition,
int lastLine) {
this.parameterNameEndPosition = parameterNameEndPosition;
this.parameterDescriptionStartPosition = parameterDescriptionStartPosition;
this.lastLine = lastLine;
}
@Override
public String toString() {
return "name end: " + parameterNameEndPosition + ", description start: " + parameterDescriptionStartPosition;
}
}
}
@@ -0,0 +1,199 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.openapi.options.colors.pages;
import com.intellij.application.options.colors.InspectionColorSettingsPage;
import com.intellij.codeHighlighting.RainbowHighlighter;
import com.intellij.core.JavaOptionBundle;
import com.intellij.ide.highlighter.JavaHighlightingColors;
import com.intellij.openapi.editor.colors.CodeInsightColors;
import com.intellij.openapi.editor.colors.TextAttributesKey;
import com.intellij.openapi.options.OptionsBundle;
import com.intellij.openapi.options.colors.AttributesDescriptor;
import com.intellij.openapi.options.colors.ColorDescriptor;
import com.intellij.openapi.options.colors.ColorSettingsPage;
import com.intellij.psi.codeStyle.DisplayPriority;
import com.intellij.psi.codeStyle.DisplayPrioritySortable;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.util.Map;
public abstract class AbstractBasicJavaColorSettingsPage
implements ColorSettingsPage, InspectionColorSettingsPage, DisplayPrioritySortable {
private static final AttributesDescriptor[] ourDescriptors = {
new AttributesDescriptor(OptionsBundle.message("options.java.attribute.descriptor.keyword"), JavaHighlightingColors.KEYWORD),
new AttributesDescriptor(OptionsBundle.message("options.java.attribute.descriptor.number"), JavaHighlightingColors.NUMBER),
new AttributesDescriptor(OptionsBundle.message("options.java.attribute.descriptor.string"), JavaHighlightingColors.STRING),
new AttributesDescriptor(OptionsBundle.message("options.java.attribute.descriptor.valid.escape.in.string"), JavaHighlightingColors.VALID_STRING_ESCAPE),
new AttributesDescriptor(OptionsBundle.message("options.java.attribute.descriptor.invalid.escape.in.string"), JavaHighlightingColors.INVALID_STRING_ESCAPE),
new AttributesDescriptor(OptionsBundle.message("options.java.attribute.descriptor.operator.sign"), JavaHighlightingColors.OPERATION_SIGN),
new AttributesDescriptor(OptionsBundle.message("options.java.attribute.descriptor.parentheses"), JavaHighlightingColors.PARENTHESES),
new AttributesDescriptor(OptionsBundle.message("options.java.attribute.descriptor.braces"), JavaHighlightingColors.BRACES),
new AttributesDescriptor(OptionsBundle.message("options.java.attribute.descriptor.brackets"), JavaHighlightingColors.BRACKETS),
new AttributesDescriptor(OptionsBundle.message("options.java.attribute.descriptor.comma"), JavaHighlightingColors.COMMA),
new AttributesDescriptor(OptionsBundle.message("options.java.attribute.descriptor.semicolon"), JavaHighlightingColors.JAVA_SEMICOLON),
new AttributesDescriptor(OptionsBundle.message("options.java.attribute.descriptor.dot"), JavaHighlightingColors.DOT),
new AttributesDescriptor(OptionsBundle.message("options.java.attribute.descriptor.line.comment"), JavaHighlightingColors.LINE_COMMENT),
new AttributesDescriptor(OptionsBundle.message("options.java.attribute.descriptor.block.comment"), JavaHighlightingColors.JAVA_BLOCK_COMMENT),
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.javadoc.comment"), JavaHighlightingColors.DOC_COMMENT),
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.javadoc.tag"), JavaHighlightingColors.DOC_COMMENT_TAG),
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.javadoc.tag.value"), JavaHighlightingColors.DOC_COMMENT_TAG_VALUE),
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.javadoc.markup"), JavaHighlightingColors.DOC_COMMENT_MARKUP),
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.class"), JavaHighlightingColors.CLASS_NAME_ATTRIBUTES),
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.anonymous.class"), JavaHighlightingColors.ANONYMOUS_CLASS_NAME_ATTRIBUTES),
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.type.parameter"), JavaHighlightingColors.TYPE_PARAMETER_NAME_ATTRIBUTES),
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.abstract.class"), JavaHighlightingColors.ABSTRACT_CLASS_NAME_ATTRIBUTES),
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.interface"), JavaHighlightingColors.INTERFACE_NAME_ATTRIBUTES),
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.enum"), JavaHighlightingColors.ENUM_NAME_ATTRIBUTES),
new AttributesDescriptor(OptionsBundle.message("options.java.attribute.descriptor.local.variable"), JavaHighlightingColors.LOCAL_VARIABLE_ATTRIBUTES),
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.reassigned.local.variable"), JavaHighlightingColors.REASSIGNED_LOCAL_VARIABLE_ATTRIBUTES),
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.reassigned.parameter"), JavaHighlightingColors.REASSIGNED_PARAMETER_ATTRIBUTES),
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.implicit.anonymous.parameter"), JavaHighlightingColors.IMPLICIT_ANONYMOUS_CLASS_PARAMETER_ATTRIBUTES),
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.instance.field"), JavaHighlightingColors.INSTANCE_FIELD_ATTRIBUTES),
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.instance.final.field"), JavaHighlightingColors.INSTANCE_FINAL_FIELD_ATTRIBUTES),
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.static.field"), JavaHighlightingColors.STATIC_FIELD_ATTRIBUTES),
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.static.imported.field"), JavaHighlightingColors.STATIC_FIELD_IMPORTED_ATTRIBUTES),
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.static.final.field"), JavaHighlightingColors.STATIC_FINAL_FIELD_ATTRIBUTES),
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.static.final.imported.field"), JavaHighlightingColors.STATIC_FINAL_FIELD_IMPORTED_ATTRIBUTES),
new AttributesDescriptor(OptionsBundle.message("options.java.attribute.descriptor.parameter"), JavaHighlightingColors.PARAMETER_ATTRIBUTES),
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.lambda.parameter"), JavaHighlightingColors.LAMBDA_PARAMETER_ATTRIBUTES),
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.method.call"), JavaHighlightingColors.METHOD_CALL_ATTRIBUTES),
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.method.imported.call"), JavaHighlightingColors.STATIC_METHOD_CALL_IMPORTED_ATTRIBUTES),
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.method.declaration"), JavaHighlightingColors.METHOD_DECLARATION_ATTRIBUTES),
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.constructor.call"), JavaHighlightingColors.CONSTRUCTOR_CALL_ATTRIBUTES),
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.constructor.declaration"), JavaHighlightingColors.CONSTRUCTOR_DECLARATION_ATTRIBUTES),
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.static.method"), JavaHighlightingColors.STATIC_METHOD_ATTRIBUTES),
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.abstract.method"), JavaHighlightingColors.ABSTRACT_METHOD_ATTRIBUTES),
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.inherited.method"), JavaHighlightingColors.INHERITED_METHOD_ATTRIBUTES),
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.public"), JavaHighlightingColors.PUBLIC_REFERENCE_ATTRIBUTES),
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.protected"), JavaHighlightingColors.PROTECTED_REFERENCE_ATTRIBUTES),
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.package.private"), JavaHighlightingColors.PACKAGE_PRIVATE_REFERENCE_ATTRIBUTES),
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.private"), JavaHighlightingColors.PRIVATE_REFERENCE_ATTRIBUTES),
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.annotation.name"), JavaHighlightingColors.ANNOTATION_NAME_ATTRIBUTES),
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.annotation.attribute.name"), JavaHighlightingColors.ANNOTATION_ATTRIBUTE_NAME_ATTRIBUTES)
};
@NonNls private static final Map<String, TextAttributesKey> ourTags = RainbowHighlighter.createRainbowHLM();
static {
ourTags.put("field", JavaHighlightingColors.INSTANCE_FIELD_ATTRIBUTES);
ourTags.put("unusedField", CodeInsightColors.NOT_USED_ELEMENT_ATTRIBUTES);
ourTags.put("error", CodeInsightColors.ERRORS_ATTRIBUTES);
ourTags.put("warning", CodeInsightColors.WARNINGS_ATTRIBUTES);
ourTags.put("weak_warning", CodeInsightColors.WEAK_WARNING_ATTRIBUTES);
ourTags.put("server_problems", CodeInsightColors.GENERIC_SERVER_ERROR_OR_WARNING);
ourTags.put("server_duplicate", CodeInsightColors.DUPLICATE_FROM_SERVER);
ourTags.put("unknownType", CodeInsightColors.WRONG_REFERENCES_ATTRIBUTES);
ourTags.put("localVar", JavaHighlightingColors.LOCAL_VARIABLE_ATTRIBUTES);
ourTags.put("reassignedLocalVar", JavaHighlightingColors.REASSIGNED_LOCAL_VARIABLE_ATTRIBUTES);
ourTags.put("implicitAnonymousParameter", JavaHighlightingColors.IMPLICIT_ANONYMOUS_CLASS_PARAMETER_ATTRIBUTES);
ourTags.put("static", JavaHighlightingColors.STATIC_FIELD_ATTRIBUTES);
ourTags.put("static_final", JavaHighlightingColors.STATIC_FINAL_FIELD_ATTRIBUTES);
ourTags.put("deprecated", CodeInsightColors.DEPRECATED_ATTRIBUTES);
ourTags.put("for_removal", CodeInsightColors.MARKED_FOR_REMOVAL_ATTRIBUTES);
ourTags.put("constructorCall", JavaHighlightingColors.CONSTRUCTOR_CALL_ATTRIBUTES);
ourTags.put("constructorDeclaration", JavaHighlightingColors.CONSTRUCTOR_DECLARATION_ATTRIBUTES);
ourTags.put("methodCall", JavaHighlightingColors.METHOD_CALL_ATTRIBUTES);
ourTags.put("methodDeclaration", JavaHighlightingColors.METHOD_DECLARATION_ATTRIBUTES);
ourTags.put("static_method", JavaHighlightingColors.STATIC_METHOD_ATTRIBUTES);
ourTags.put("abstract_method", JavaHighlightingColors.ABSTRACT_METHOD_ATTRIBUTES);
ourTags.put("inherited_method", JavaHighlightingColors.INHERITED_METHOD_ATTRIBUTES);
ourTags.put("param", JavaHighlightingColors.PARAMETER_ATTRIBUTES);
ourTags.put("lambda_param", JavaHighlightingColors.LAMBDA_PARAMETER_ATTRIBUTES);
ourTags.put("class", JavaHighlightingColors.CLASS_NAME_ATTRIBUTES);
ourTags.put("anonymousClass", JavaHighlightingColors.ANONYMOUS_CLASS_NAME_ATTRIBUTES);
ourTags.put("typeParameter", JavaHighlightingColors.TYPE_PARAMETER_NAME_ATTRIBUTES);
ourTags.put("abstractClass", JavaHighlightingColors.ABSTRACT_CLASS_NAME_ATTRIBUTES);
ourTags.put("interface", JavaHighlightingColors.INTERFACE_NAME_ATTRIBUTES);
ourTags.put("enum", JavaHighlightingColors.ENUM_NAME_ATTRIBUTES);
ourTags.put("annotationName", JavaHighlightingColors.ANNOTATION_NAME_ATTRIBUTES);
ourTags.put("annotationAttributeName", JavaHighlightingColors.ANNOTATION_ATTRIBUTE_NAME_ATTRIBUTES);
ourTags.put("javadocTagValue", JavaHighlightingColors.DOC_COMMENT_TAG_VALUE);
ourTags.put("instanceFinalField", JavaHighlightingColors.INSTANCE_FINAL_FIELD_ATTRIBUTES);
ourTags.put("staticallyConstImported", JavaHighlightingColors.STATIC_FINAL_FIELD_IMPORTED_ATTRIBUTES);
ourTags.put("staticallyImported", JavaHighlightingColors.STATIC_FIELD_IMPORTED_ATTRIBUTES);
ourTags.put("static_imported_method", JavaHighlightingColors.STATIC_METHOD_CALL_IMPORTED_ATTRIBUTES);
ourTags.put("public", JavaHighlightingColors.PUBLIC_REFERENCE_ATTRIBUTES);
ourTags.put("protected", JavaHighlightingColors.PROTECTED_REFERENCE_ATTRIBUTES);
ourTags.put("package_private", JavaHighlightingColors.PACKAGE_PRIVATE_REFERENCE_ATTRIBUTES);
ourTags.put("private", JavaHighlightingColors.PRIVATE_REFERENCE_ATTRIBUTES);
}
@Override
@NotNull
public String getDisplayName() {
return JavaOptionBundle.message("options.java.display.name");
}
@Override
public abstract Icon getIcon();
@Override
public AttributesDescriptor @NotNull [] getAttributeDescriptors() {
return ourDescriptors;
}
@Override
public ColorDescriptor @NotNull [] getColorDescriptors() {
return ColorDescriptor.EMPTY_ARRAY;
}
@Override
@NotNull
public String getDemoText() {
return
"""
/* Block comment */
import <class>java.util.Date</class>;
/**
* Doc comment here for <code>SomeClass</code>
* @param <javadocTagValue>T</javadocTagValue> type parameter
* @see <class>Math</class>#<methodCall>sin</methodCall>(double)
*/
<annotationName>@Annotation</annotationName> (<annotationAttributeName>name</annotationAttributeName>=value)
public class <class>SomeClass</class><<typeParameter>T</typeParameter> extends <interface>Runnable</interface>> { // some comment
private <typeParameter>T</typeParameter> <field>field</field> = null;
private double <unusedField>unusedField</unusedField> = 12345.67890;
private <unknownType>UnknownType</unknownType> <field>anotherString</field> = "Another\\nStrin\\g";
public static int <static>staticField</static> = 0;
public final int <instanceFinalField>instanceFinalField</instanceFinalField> = 0;
public <constructorDeclaration>SomeClass</constructorDeclaration>(<interface>AnInterface</interface> <param>param</param>, int[] <reassignedParameter>reassignedParam</reassignedParameter>) {
<error>int <localVar>localVar</localVar> = "IntelliJ"</error>; // Error, incompatible types
<class>System</class>.<static>out</static>.<methodCall>println</methodCall>(<field>anotherString</field> + <inherited_method>toString</inherited_method>() + <localVar>localVar</localVar>);
long <localVar>time</localVar> = <class>Date</class>.<static_method><deprecated>parse</deprecated></static_method>("1.2.3"); // Method is deprecated
int <reassignedLocalVar>reassignedValue</reassignedLocalVar> = this.<warning>staticField</warning>;\s
<reassignedLocalVar>reassignedValue</reassignedLocalVar> ++;\s
<field>field</field>.<abstract_method>run</abstract_method>();\s
new <anonymousClass>SomeClass</anonymousClass>() {
{
int <localVar>a</localVar> = <implicitAnonymousParameter>localVar</implicitAnonymousParameter>;
}
};
<reassignedParameter>reassignedParam</reassignedParameter> = new <constructorCall>ArrayList</constructorCall><<class>String</class>>().toArray(new int[0]);
}
}
enum <enum>AnEnum</enum> { <static_final>CONST1</static_final>, <static_final>CONST2</static_final> }
interface <interface>AnInterface</interface> {
int <static_final>CONSTANT</static_final> = 2;
void <methodDeclaration>method</methodDeclaration>();
}
abstract class <abstractClass>SomeAbstractClass</abstractClass> {
}""";
}
@Override
public Map<String,TextAttributesKey> getAdditionalHighlightingTagToDescriptorMap() {
return ourTags;
}
@Override
public DisplayPriority getPriority() {
return DisplayPriority.KEY_LANGUAGE_SETTINGS;
}
}
@@ -0,0 +1,22 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight;
import com.intellij.codeInsight.definition.AbstractBasicJavaDefinitionService;
import com.intellij.openapi.project.Project;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiUtil;
import org.jetbrains.annotations.NotNull;
public class JavaDefinitionService extends AbstractBasicJavaDefinitionService {
@Override
public @NotNull LanguageLevel getLanguageLevel(@NotNull PsiElement psiElement) {
return PsiUtil.getLanguageLevel(psiElement);
}
@Override
public @NotNull LanguageLevel getLanguageLevel(@NotNull Project project) {
return PsiUtil.getLanguageLevel(project);
}
}
@@ -1,46 +0,0 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.editorActions;
import com.intellij.codeInsight.daemon.JavaErrorBundle;
import com.intellij.codeInsight.editorActions.enter.EnterAfterUnmatchedBraceHandler;
import com.intellij.psi.*;
import org.jetbrains.annotations.NotNull;
public class JavaEnterAfterUnmatchedBraceHandler extends EnterAfterUnmatchedBraceHandler {
@Override
public boolean isApplicable(@NotNull PsiFile file, int caretOffset) {
return file instanceof PsiJavaFile;
}
@Override
protected int calculateOffsetToInsertClosingBraceInsideElement(PsiElement element) {
if (element instanceof PsiErrorElement &&
((PsiErrorElement)element).getErrorDescription().equals(JavaErrorBundle.message("else.without.if"))) {
return element.getTextRange().getStartOffset();
}
if (element instanceof PsiExpressionListStatement) {
final PsiExpressionList list = ((PsiExpressionListStatement)element).getExpressionList();
if (list != null) {
final PsiExpression[] expressions = list.getExpressions();
if (expressions.length > 1) {
return expressions[0].getTextRange().getEndOffset();
}
}
}
return super.calculateOffsetToInsertClosingBraceInsideElement(element);
}
}
@@ -3,26 +3,18 @@ package com.intellij.codeInsight.editorActions;
import com.intellij.application.options.CodeStyle;
import com.intellij.codeInsight.AutoPopupController;
import com.intellij.codeInsight.CodeInsightSettings;
import com.intellij.codeInsight.completion.CompletionType;
import com.intellij.codeInsight.completion.JavaClassReferenceCompletionContributor;
import com.intellij.codeInsight.editorActions.smartEnter.JavaSmartEnterProcessor;
import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.java.JavaBundle;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.EditorModificationUtil;
import com.intellij.openapi.editor.EditorModificationUtilEx;
import com.intellij.openapi.editor.highlighter.HighlighterIterator;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.patterns.PsiJavaPatterns;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.jsp.JspFile;
import com.intellij.psi.tree.IElementType;
@@ -33,153 +25,37 @@ import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.text.CharArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class JavaTypedHandler extends TypedHandlerDelegate {
private boolean myJavaLTTyped;
public class JavaTypedHandler extends AbstractBasicJavaTypedHandler {
private static void autoPopupMemberLookup(Project project, final Editor editor) {
AutoPopupController.getInstance(project).autoPopupMemberLookup(editor, file -> {
int offset = editor.getCaretModel().getOffset();
PsiElement lastElement = file.findElementAt(offset - 1);
if (lastElement == null) {
return false;
}
//do not show lookup when typing varargs ellipsis
final PsiElement prevSibling = PsiTreeUtil.prevVisibleLeaf(lastElement);
if (prevSibling == null || ".".equals(prevSibling.getText())) return false;
PsiElement parent = prevSibling;
do {
parent = parent.getParent();
} while(parent instanceof PsiJavaCodeReferenceElement || parent instanceof PsiTypeElement);
if (parent instanceof PsiParameterList ||
(parent instanceof PsiParameter && !(parent instanceof PsiPatternVariable))) {
return false;
}
if (!".".equals(lastElement.getText()) && !"#".equals(lastElement.getText())) {
return JavaClassReferenceCompletionContributor.findJavaClassReference(file, offset - 1) != null;
}
else{
final PsiElement element = file.findElementAt(offset);
return element == null ||
!"#".equals(lastElement.getText()) ||
PsiTreeUtil.getParentOfType(element, PsiDocComment.class) != null;
}
});
@Override
protected boolean isJavaFile(@NotNull PsiFile file) {
return file instanceof PsiJavaFile;
}
@Override
public @NotNull Result checkAutoPopup(char charTyped, @NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
if (!(file instanceof PsiJavaFile)) return Result.CONTINUE;
int offset = editor.getCaretModel().getOffset();
if (charTyped == ' ' &&
StringUtil.endsWith(editor.getDocument().getImmutableCharSequence(), 0, offset, PsiKeyword.NEW)) {
AutoPopupController.getInstance(project).scheduleAutoPopup(editor, CompletionType.BASIC, f -> {
PsiElement leaf = f.findElementAt(offset - PsiKeyword.NEW.length());
return leaf instanceof PsiKeyword &&
leaf.textMatches(PsiKeyword.NEW) &&
!PsiJavaPatterns.psiElement().insideStarting(PsiJavaPatterns.psiExpressionStatement()).accepts(leaf);
});
return Result.STOP;
}
return super.checkAutoPopup(charTyped, project, editor, file);
protected boolean isJspFile(@NotNull PsiFile file) {
return file instanceof JspFile;
}
@NotNull
@Override
public Result beforeCharTyped(final char c, @NotNull final Project project, @NotNull final Editor editor, @NotNull final PsiFile file, @NotNull final FileType fileType) {
if (!(file instanceof PsiJavaFile)) return Result.CONTINUE;
if (c == '@') {
autoPopupJavadocLookup(project, editor);
}
else if (c == '#' || c == '.') {
autoPopupMemberLookup(project, editor);
}
int offsetBefore = editor.getCaretModel().getOffset();
//important to calculate before inserting charTyped
myJavaLTTyped = '<' == c &&
!(file instanceof JspFile) &&
CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET &&
PsiUtil.isLanguageLevel5OrHigher(file) &&
TypedHandlerUtil.isAfterClassLikeIdentifierOrDot(offsetBefore, editor, JavaTokenType.DOT, JavaTokenType.IDENTIFIER, true);
if ('>' == c) {
if (!(file instanceof JspFile) && CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET && PsiUtil.isLanguageLevel5OrHigher(file)) {
if (TypedHandlerUtil.handleGenericGT(editor, JavaTokenType.LT, JavaTokenType.GT, JavaTypingTokenSets.INVALID_INSIDE_REFERENCE)) return Result.STOP;
}
}
if (c == '?') {
if (handleQuestionMark(project, editor, file, offsetBefore)) {
return Result.STOP;
}
}
if (c == '=') {
if (handleEquality(project, editor, file, offsetBefore)) {
return Result.STOP;
}
}
if (c == ';') {
if (handleSemicolon(project, editor, file, fileType)) return Result.STOP;
}
if (fileType == JavaFileType.INSTANCE && c == '{') {
int offset = editor.getCaretModel().getOffset();
if (offset == 0) {
return Result.CONTINUE;
}
HighlighterIterator iterator = editor.getHighlighter().createIterator(offset - 1);
while (!iterator.atEnd() && iterator.getTokenType() == TokenType.WHITE_SPACE) {
iterator.retreat();
}
if (iterator.atEnd() || iterator.getTokenType() == JavaTokenType.RBRACKET || iterator.getTokenType() == JavaTokenType.EQ) {
return Result.CONTINUE;
}
Document doc = editor.getDocument();
PsiDocumentManager.getInstance(project).commitDocument(doc);
final PsiElement leaf = file.findElementAt(offset);
if (PsiTreeUtil.getParentOfType(leaf, PsiArrayInitializerExpression.class, false, PsiCodeBlock.class, PsiMember.class) != null) {
return Result.CONTINUE;
}
PsiElement st = leaf != null ? leaf.getParent() : null;
PsiElement prev = offset > 1 ? file.findElementAt(offset - 1) : null;
if (CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET && isRparenth(leaf) &&
(st instanceof PsiWhileStatement || st instanceof PsiIfStatement) && shouldInsertStatementBody(st, doc, prev)) {
CommandProcessor.getInstance().executeCommand(project, () -> new JavaSmartEnterProcessor().process(project, editor, file),
JavaBundle.message("command.name.insert.block.statement"), null);
return Result.STOP;
}
if (PsiTreeUtil.getParentOfType(leaf, PsiCodeBlock.class, false, PsiMember.class) != null && !shouldInsertPairedBrace(leaf)) {
EditorModificationUtilEx.insertStringAtCaret(editor, "{");
TypedHandler.indentOpenedBrace(project, editor);
return Result.STOP; // use case: manually wrapping part of method's code in 'if', 'while', etc
}
}
return Result.CONTINUE;
protected boolean isLanguageLevel5OrHigher(@NotNull PsiFile file) {
return PsiUtil.isLanguageLevel5OrHigher(file);
}
/**
* Automatically inserts parentheses if != or == was typed after a&b, a|b or a^b where a and b are numbers.
*
* @return true if the '=' char was processed
*/
private static boolean handleEquality(Project project, Editor editor, PsiFile file, int offsetBefore) {
@Override
protected @NotNull Result processWhileAndIfStatementBody(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
CommandProcessor.getInstance().executeCommand(project, () -> new JavaSmartEnterProcessor().process(project, editor, file),
JavaBundle.message("command.name.insert.block.statement"), null);
return Result.STOP;
}
@Override
public boolean handleEquality(Project project, Editor editor, PsiFile file, int offsetBefore) {
if (offsetBefore == 0) return false;
Document doc = editor.getDocument();
char prevChar = doc.getCharsSequence().charAt(offsetBefore - 1);
@@ -230,12 +106,14 @@ public class JavaTypedHandler extends TypedHandlerDelegate {
return true;
}
/**
* Automatically insert parentheses around the ?: when necessary.
*
* @return true if question mark was handled
*/
private static boolean handleQuestionMark(Project project, Editor editor, PsiFile file, int offsetBefore) {
private static void wrapWithParentheses(PsiFile file, Document doc, int openingOffset, int closingOffset) {
String space = CodeStyle.getLanguageSettings(file).SPACE_WITHIN_PARENTHESES ? " " : "";
doc.insertString(closingOffset, space + ")");
doc.insertString(openingOffset, "(" + space);
}
@Override
protected boolean handleQuestionMark(Project project, Editor editor, PsiFile file, int offsetBefore) {
if (offsetBefore == 0) return false;
HighlighterIterator it = editor.getHighlighter().createIterator(offsetBefore);
if (it.atEnd()) return false;
@@ -281,91 +159,13 @@ public class JavaTypedHandler extends TypedHandlerDelegate {
return true;
}
private static void wrapWithParentheses(PsiFile file, Document doc, int openingOffset, int closingOffset) {
String space = CodeStyle.getLanguageSettings(file).SPACE_WITHIN_PARENTHESES ? " " : "";
doc.insertString(closingOffset, space + ")");
doc.insertString(openingOffset, "(" + space);
}
private static boolean shouldInsertPairedBrace(@NotNull PsiElement leaf) {
PsiElement prevLeaf = PsiTreeUtil.prevVisibleLeaf(leaf);
// lambda
if (PsiUtil.isJavaToken(prevLeaf, JavaTokenType.ARROW)) return true;
// anonymous class
if (PsiTreeUtil.getParentOfType(prevLeaf, PsiNewExpression.class, true, PsiCodeBlock.class, PsiMember.class) != null) return true;
// local class
if (prevLeaf instanceof PsiIdentifier && prevLeaf.getParent() instanceof PsiClass) return true;
// local record
if (PsiUtil.isJavaToken(prevLeaf, JavaTokenType.RPARENTH) && prevLeaf.getParent() instanceof PsiRecordHeader) return true;
return false;
}
private static boolean shouldInsertStatementBody(@NotNull PsiElement statement, @NotNull Document doc, @Nullable PsiElement prev) {
PsiStatement block = statement instanceof PsiWhileStatement ? ((PsiWhileStatement)statement).getBody() : ((PsiIfStatement)statement).getThenBranch();
PsiExpression condition = PsiTreeUtil.getChildOfType(statement, PsiExpression.class);
PsiExpression latestExpression = PsiTreeUtil.getParentOfType(prev, PsiExpression.class);
if (latestExpression instanceof PsiNewExpression && ((PsiNewExpression)latestExpression).getAnonymousClass() == null) return false;
return !(block instanceof PsiBlockStatement) && (block == null || startLine(doc, block) != startLine(doc, statement) || condition == null);
}
private static boolean isRparenth(@Nullable PsiElement leaf) {
if (leaf == null) return false;
if (leaf.getNode().getElementType() == JavaTokenType.RPARENTH) return true;
PsiElement next = PsiTreeUtil.nextVisibleLeaf(leaf);
if (next == null) return false;
return next.getNode().getElementType() == JavaTokenType.RPARENTH;
}
private static int startLine(@NotNull Document doc, @NotNull PsiElement psiElement) {
return doc.getLineNumber(psiElement.getTextRange().getStartOffset());
}
@NotNull
@Override
public Result charTyped(final char c, @NotNull final Project project, @NotNull final Editor editor, @NotNull final PsiFile file) {
if (!(file instanceof PsiJavaFile)) return Result.CONTINUE;
if (myJavaLTTyped) {
myJavaLTTyped = false;
TypedHandlerUtil.handleAfterGenericLT(editor, JavaTokenType.LT, JavaTokenType.GT, JavaTypingTokenSets.INVALID_INSIDE_REFERENCE);
return Result.STOP;
}
else if (c == ':') {
if (autoIndentCase(editor, project, file)) {
return Result.STOP;
}
}
else if (c == ',' && handleAnnotationParameter(project, editor, file)) {
return Result.STOP;
}
else if (c == '.') {
if (handleDotTyped(project, editor, file)) {
return Result.STOP;
}
}
return Result.CONTINUE;
}
private static boolean handleDotTyped(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
int offset = editor.getCaretModel().getOffset() - 1;
if (offset >= 0) {
Document document = editor.getDocument();
int line = document.getLineNumber(offset);
int lineStart = document.getLineStartOffset(line);
if (StringUtil.isEmptyOrSpaces(document.getCharsSequence().subSequence(lineStart, offset))) {
PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument());
CodeStyleManager.getInstance(project).adjustLineIndent(file, offset);
return true;
}
}
return false;
}
private static boolean handleAnnotationParameter(Project project, @NotNull Editor editor, @NotNull PsiFile file) {
protected boolean handleAnnotationParameter(Project project, @NotNull Editor editor, @NotNull PsiFile file) {
int caret = editor.getCaretModel().getOffset();
if (mightBeInsideDefaultAnnotationAttribute(editor, caret - 2)) {
PsiDocumentManager.getInstance(project).commitAllDocuments();
PsiAnnotation anno = PsiTreeUtil.getParentOfType(file.findElementAt(caret), PsiAnnotation.class, false, PsiExpression.class, PsiComment.class);
PsiAnnotation anno =
PsiTreeUtil.getParentOfType(file.findElementAt(caret), PsiAnnotation.class, false, PsiExpression.class, PsiComment.class);
PsiNameValuePair attr = anno == null ? null : getTheOnlyDefaultAttribute(anno);
if (attr != null && hasDefaultArrayMethod(anno) && !(attr.getValue() instanceof PsiArrayInitializerMemberValue)) {
editor.getDocument().insertString(caret, "}");
@@ -376,7 +176,8 @@ public class JavaTypedHandler extends TypedHandlerDelegate {
return false;
}
@Nullable private static PsiNameValuePair getTheOnlyDefaultAttribute(@NotNull PsiAnnotation anno) {
@Nullable
private static PsiNameValuePair getTheOnlyDefaultAttribute(@NotNull PsiAnnotation anno) {
List<PsiNameValuePair> attributes = ContainerUtil.findAll(anno.getParameterList().getAttributes(), a -> !a.getTextRange().isEmpty());
return attributes.size() == 1 && attributes.get(0).getNameIdentifier() == null ? attributes.get(0) : null;
}
@@ -414,90 +215,62 @@ public class JavaTypedHandler extends TypedHandlerDelegate {
return false;
}
private static boolean handleSemicolon(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file, @NotNull FileType fileType) {
if (fileType != JavaFileType.INSTANCE) return false;
int offset = editor.getCaretModel().getOffset();
if (offset == editor.getDocument().getTextLength()) return false;
@Override
protected void autoPopupMemberLookup(@NotNull Project project, @NotNull final Editor editor) {
AutoPopupController.getInstance(project).autoPopupMemberLookup(editor, file -> {
int offset = editor.getCaretModel().getOffset();
if (moveSemicolonAtRParen(project, editor, file, offset)) return true;
char charAt = editor.getDocument().getCharsSequence().charAt(offset);
if (charAt != ';') return false;
HighlighterIterator hi = editor.getHighlighter().createIterator(offset);
if (hi.atEnd() || hi.getTokenType() != JavaTokenType.SEMICOLON) return false;
EditorModificationUtil.moveCaretRelatively(editor, 1);
return true;
}
private static boolean moveSemicolonAtRParen(@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
PsiElement lastElement = file.findElementAt(offset - 1);
if (lastElement == null) {
return false;
}
it.advance();
}
if (!it.atEnd() && afterLastParenOffset >= 0 && afterLastParenOffset >= caretOffset) {
PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument());
PsiElement curElement = file.findElementAt(caretOffset);
PsiStatement curStmt = PsiTreeUtil.getParentOfType(curElement, PsiStatement.class);
if (curStmt != null) {
if (curStmt instanceof PsiTryStatement) {
// try-with-resources can contain semicolons inside
return false;
}
if (curStmt instanceof PsiForStatement) {
// for loop can have semicolons inside
return false;
}
// It may worth to check if the error element is about expecting semicolon
if (PsiTreeUtil.getDeepestLast(curStmt) instanceof PsiErrorElement) {
int stmtEndOffset = curStmt.getTextRange().getEndOffset();
if (stmtEndOffset == afterLastParenOffset || stmtEndOffset == it.getStart()) {
editor.getDocument().insertString(stmtEndOffset, ";");
editor.getCaretModel().moveToOffset(stmtEndOffset + 1);
return true;
}
}
//do not show lookup when typing varargs ellipsis
final PsiElement prevSibling = PsiTreeUtil.prevVisibleLeaf(lastElement);
if (prevSibling == null || ".".equals(prevSibling.getText())) return false;
PsiElement parent = prevSibling;
do {
parent = parent.getParent();
}
while (parent instanceof PsiJavaCodeReferenceElement || parent instanceof PsiTypeElement);
if (parent instanceof PsiParameterList ||
(parent instanceof PsiParameter && !(parent instanceof PsiPatternVariable))) {
return false;
}
}
return false;
if (!".".equals(lastElement.getText()) && !"#".equals(lastElement.getText())) {
return JavaClassReferenceCompletionContributor.findJavaClassReference(file, offset - 1) != null;
}
else {
final PsiElement element = file.findElementAt(offset);
return element == null ||
!"#".equals(lastElement.getText()) ||
PsiTreeUtil.getParentOfType(element, PsiDocComment.class) != null;
}
});
}
private static boolean isAtLineEnd(HighlighterIterator it) {
if (it.getTokenType() == TokenType.WHITE_SPACE) {
CharSequence tokenText = it.getDocument().getImmutableCharSequence().subSequence(it.getStart(), it.getEnd());
return CharArrayUtil.containLineBreaks(tokenText);
@Override
public @NotNull Result checkAutoPopup(char charTyped, @NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
if (!(file instanceof PsiJavaFile)) return Result.CONTINUE;
int offset = editor.getCaretModel().getOffset();
if (charTyped == ' ' &&
StringUtil.endsWith(editor.getDocument().getImmutableCharSequence(), 0, offset, PsiKeyword.NEW)) {
AutoPopupController.getInstance(project).scheduleAutoPopup(editor, CompletionType.BASIC, f -> {
PsiElement leaf = f.findElementAt(offset - PsiKeyword.NEW.length());
return leaf instanceof PsiKeyword &&
leaf.textMatches(PsiKeyword.NEW) &&
!PsiJavaPatterns.psiElement().insideStarting(PsiJavaPatterns.psiExpressionStatement()).accepts(leaf);
});
return Result.STOP;
}
return false;
return super.checkAutoPopup(charTyped, project, editor, file);
}
private static void autoPopupJavadocLookup(final Project project, final Editor editor) {
@Override
protected void autoPopupJavadocLookup(@NotNull final Project project, @NotNull final Editor editor) {
AutoPopupController.getInstance(project).autoPopupMemberLookup(editor, file -> {
int offset = editor.getCaretModel().getOffset();
@@ -505,18 +278,4 @@ public class JavaTypedHandler extends TypedHandlerDelegate {
return lastElement != null && StringUtil.endsWithChar(lastElement.getText(), '@');
});
}
private static boolean autoIndentCase(Editor editor, Project project, PsiFile file) {
int offset = editor.getCaretModel().getOffset();
PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument());
PsiElement currElement = file.findElementAt(offset - 1);
if (currElement != null) {
PsiElement parent = currElement.getParent();
if (parent instanceof PsiSwitchLabelStatement) {
CodeStyleManager.getInstance(project).adjustLineIndent(file, parent.getTextOffset());
return true;
}
}
return false;
}
}
@@ -1,200 +1,20 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions;
import com.intellij.codeInsight.CodeInsightSettings;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.javadoc.PsiDocParamRef;
import com.intellij.psi.impl.source.tree.JavaDocElementType;
import com.intellij.psi.javadoc.PsiDocTag;
import com.intellij.psi.javadoc.PsiInlineDocTag;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiJavaFile;
import com.intellij.xml.util.HtmlUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static com.intellij.util.text.CharArrayUtil.containsOnlyWhiteSpaces;
/**
* Advises typing in javadoc if necessary.
*/
public class JavadocTypedHandler extends TypedHandlerDelegate {
private static final char START_TAG_SYMBOL = '<';
private static final char CLOSE_TAG_SYMBOL = '>';
private static final char SLASH = '/';
private static final String COMMENT_PREFIX = "!--";
@NotNull
public class JavadocTypedHandler extends AbstractBasicJavadocTypedHandler {
@Override
public Result charTyped(char c, @NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
if (file instanceof PsiJavaFile) {
if (!insertClosingTagIfNecessary(c, project, editor, file)) {
adjustStartTagIndent(c, editor, file);
}
}
return Result.CONTINUE;
public boolean isJavaFile(@Nullable PsiFile file) {
return file instanceof PsiJavaFile;
}
private static void adjustStartTagIndent(char c, @NotNull Editor editor, @NotNull PsiFile file) {
if (c == '@') {
final int offset = editor.getCaretModel().getOffset();
PsiElement currElement = file.findElementAt(offset);
if (currElement instanceof PsiWhiteSpace) {
PsiElement prev = currElement.getPrevSibling();
if (prev != null && prev.getNode().getElementType() == JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS) {
editor.getDocument().replaceString(currElement.getTextRange().getStartOffset(), offset - 1, " ");
}
}
}
@Override
public boolean isSingleHtmlTag(@NotNull String tagName) {
return HtmlUtil.isSingleHtmlTag(tagName, false);
}
/**
* Checks if it's necessary to insert closing tag on typed character.
*
* @param c typed symbol
* @param project current project
* @param editor current editor
* @param file current file
* @return {@code true} if closing tag is inserted; {@code false} otherwise
*/
private static boolean insertClosingTagIfNecessary(char c, @NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
if (c != CLOSE_TAG_SYMBOL || !CodeInsightSettings.getInstance().JAVADOC_GENERATE_CLOSING_TAG) {
return false;
}
PsiDocumentManager.getInstance(project).commitAllDocuments();
if (!isAppropriatePlace(editor, file)) {
return false;
}
// Inspect symbols to the left of the current caret position, insert closing tag only if valid tag is just typed
// (e.g. don't insert anything on single '>' symbol typing).
int offset = editor.getCaretModel().getOffset();
Document document = editor.getDocument();
String tagName = getTagName(document.getText(), offset);
if (tagName == null || HtmlUtil.isSingleHtmlTag(tagName, false) || tagName.startsWith(COMMENT_PREFIX)) {
return false;
}
document.insertString(offset, String.valueOf(START_TAG_SYMBOL) + SLASH + tagName + CLOSE_TAG_SYMBOL);
return true;
}
/**
* Tries to derive start tag name assuming that given offset points to position just after {@code '>'} symbol.
* <p/>
* Is expected to return {@code null} when offset is not located just after start tag, e.g. the following situations:
* <pre>
* <ul>
* <li>standalone {@code '>'} symbol (surrounded by white spaces);</li>
* <li>after end tag {@code <mytag><mytag>[caret]};</li>
* <li>after empty element tag {@code <p/>[caret]};</li>
* </ul>
* </pre>
*
* @param text target text
* @param afterTagOffset offset that points after
* @return tag name if the one is parsed; {@code null} otherwise
*/
@Nullable
public static String getTagName(@NotNull CharSequence text, int afterTagOffset) {
if (afterTagOffset > text.length()) {
return null;
}
int endOffset = afterTagOffset - 1;
// Check empty element like <p/>
if (endOffset > 0 && text.charAt(endOffset - 1) == SLASH) {
return null;
}
for (int i = endOffset - 1; i >= 0; i--) {
char c = text.charAt(i);
switch (c) {
case '\n' -> {
return null;
}
case CLOSE_TAG_SYMBOL -> {
return null;
}
case START_TAG_SYMBOL -> {
if (text.charAt(i + 1) == SLASH) {
// Handle situation like <tag></tag>[offset].
return null;
}
return text.subSequence(i + 1, endOffset).toString();
}
// There is a possible case that opening tag has attributes, e.g. <a href='bla-bla-bla'>[offset]. We want to extract
// only tag name then.
case ' ', '\t' -> endOffset = i;
}
}
return null;
}
private static boolean isAppropriatePlace(Editor editor, PsiFile file) {
FileViewProvider provider = file.getViewProvider();
int offset = editor.getCaretModel().getOffset();
final PsiElement elementAtCaret;
if (offset < editor.getDocument().getTextLength()) {
elementAtCaret = provider.findElementAt(offset);
}
else {
elementAtCaret = provider.findElementAt(editor.getDocument().getTextLength() - 1);
}
PsiElement element = elementAtCaret;
while(element instanceof PsiWhiteSpace || element != null && containsOnlyWhiteSpaces(element.getText())) {
element = element.getPrevSibling();
}
if (element == null) {
return false;
}
if (element instanceof PsiDocParamRef) {
element = element.getParent();
}
if (element instanceof PsiDocTag tag && "param".equals(tag.getName()) && isTypeParamBracketClosedAfterParamTag(tag, offset)) {
return false;
}
// The contents of inline tags is not HTML, so the paired tag completion isn't appropriate there.
if (PsiTreeUtil.getParentOfType(element, PsiInlineDocTag.class, false) != null) {
return false;
}
ASTNode node = element.getNode();
return node != null
&& (JavaDocTokenType.ALL_JAVADOC_TOKENS.contains(node.getElementType())
|| JavaDocElementType.ALL_JAVADOC_ELEMENTS.contains(node.getElementType()));
}
private static boolean isTypeParamBracketClosedAfterParamTag(PsiDocTag tag, int bracketOffset) {
PsiElement paramToDocument = getDocumentingParameter(tag);
if (paramToDocument == null) return false;
TextRange paramRange = paramToDocument.getTextRange();
return paramRange.getEndOffset() == bracketOffset;
}
@Nullable
private static PsiElement getDocumentingParameter(PsiDocTag tag) {
for(PsiElement element = tag.getFirstChild(); element != null; element = element.getNextSibling()) {
if (element instanceof PsiDocParamRef) {
return element;
}
}
return null;
}
}
}
@@ -1,56 +0,0 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.*;
import com.intellij.util.text.CharArrayUtil;
import org.jetbrains.annotations.NotNull;
public class AfterSemicolonEnterProcessor implements EnterProcessor {
@Override
public boolean doEnter(Editor editor, PsiElement psiElement, boolean isModified) {
if (psiElement instanceof PsiExpressionStatement ||
psiElement instanceof PsiDeclarationStatement ||
psiElement instanceof PsiDoWhileStatement ||
psiElement instanceof PsiReturnStatement ||
psiElement instanceof PsiThrowStatement ||
psiElement instanceof PsiBreakStatement ||
psiElement instanceof PsiContinueStatement ||
psiElement instanceof PsiYieldStatement ||
psiElement instanceof PsiAssertStatement ||
psiElement instanceof PsiField ||
psiElement instanceof PsiImportStatementBase ||
psiElement instanceof PsiMethod && !MissingMethodBodyFixer.shouldHaveBody((PsiMethod)psiElement)) {
int errorOffset = getErrorElementOffset(psiElement);
int elementEndOffset = psiElement.getTextRange().getEndOffset();
if (psiElement instanceof PsiEnumConstant) {
final CharSequence text = editor.getDocument().getCharsSequence();
final int commaOffset = CharArrayUtil.shiftForwardUntil(text, elementEndOffset, ",");
if (commaOffset < text.length()) {
elementEndOffset = commaOffset + 1;
}
}
if (errorOffset >= 0 && errorOffset < elementEndOffset) {
final CharSequence text = editor.getDocument().getCharsSequence();
if (text.charAt(errorOffset) == ' ' && text.charAt(errorOffset + 1) == ';') {
errorOffset++;
}
}
editor.getCaretModel().moveToOffset(errorOffset >= 0 ? errorOffset : elementEndOffset);
return isModified;
}
return false;
}
private static int getErrorElementOffset(PsiElement elt) {
final int[] offset = { -1 };
elt.accept(new PsiRecursiveElementWalkingVisitor() {
@Override public void visitErrorElement(@NotNull PsiErrorElement element) {
if (offset[0] == -1) offset[0] = element.getTextRange().getStartOffset();
}
});
return offset[0];
}
}
@@ -1,28 +0,0 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.codeInsight.editorActions.enter.EnterAfterUnmatchedBraceHandler;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.psi.PsiCodeBlock;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiStatement;
import com.intellij.util.IncorrectOperationException;
public class BlockBraceFixer implements Fixer{
@Override
public void apply(Editor editor, JavaSmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException {
if (psiElement instanceof PsiCodeBlock block && afterUnmatchedBrace(editor, psiElement.getContainingFile().getFileType())) {
int stopOffset = block.getTextRange().getEndOffset();
final PsiStatement[] statements = block.getStatements();
if (statements.length > 0) {
stopOffset = statements[0].getTextRange().getEndOffset();
}
editor.getDocument().insertString(stopOffset, "}");
}
}
static boolean afterUnmatchedBrace(Editor editor, FileType fileType) {
return EnterAfterUnmatchedBraceHandler.isAfterUnmatchedLBrace(editor, editor.getCaretModel().getOffset(), fileType);
}
}
@@ -1,37 +0,0 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiBlockStatement;
import com.intellij.psi.PsiDoWhileStatement;
import com.intellij.psi.PsiElement;
import com.intellij.util.IncorrectOperationException;
@SuppressWarnings({"HardCodedStringLiteral"})
public class DoWhileConditionFixer implements Fixer {
@Override
public void apply(Editor editor, JavaSmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException {
if (psiElement instanceof PsiDoWhileStatement stmt) {
final Document doc = editor.getDocument();
if (stmt.getBody() == null || !(stmt.getBody() instanceof PsiBlockStatement) && stmt.getWhileKeyword() == null) {
final int startOffset = stmt.getTextRange().getStartOffset();
doc.replaceString(startOffset, startOffset + "do".length(), "do {} while()");
return;
}
if (stmt.getCondition() == null) {
if (stmt.getWhileKeyword() == null) {
final int endOffset = stmt.getTextRange().getEndOffset();
doc.insertString(endOffset, "while()");
} else if (stmt.getLParenth() == null || stmt.getRParenth() == null) {
final TextRange whileRange = stmt.getWhileKeyword().getTextRange();
doc.replaceString(whileRange.getStartOffset(), whileRange.getEndOffset(), "while()");
} else {
processor.registerUnresolvedError(stmt.getLParenth().getTextRange().getEndOffset());
}
}
}
}
}
@@ -1,101 +0,0 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.application.options.CodeStyle;
import com.intellij.lang.ASTNode;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.tree.JavaJspElementType;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
/**
* {@link Fixer} that handles use-cases like below:
* <b>before:</b>
* <pre>
* void foo() {
* for[caret]
* }
* </pre>
*
* <b>after:</b>
* <pre>
* void foo() {
* for ([caret]) {
*
* }
* }
* </pre>
*/
public class ForStatementFixer implements Fixer {
@Override
public void apply(Editor editor, JavaSmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException {
if (!(psiElement instanceof PsiForStatement forStatement)) {
return;
}
final PsiJavaToken lParenth = forStatement.getLParenth();
final PsiJavaToken rParenth = forStatement.getRParenth();
if (lParenth == null || rParenth == null) {
final TextRange textRange = forStatement.getTextRange();
editor.getDocument().replaceString(textRange.getStartOffset(), textRange.getEndOffset(), "for () {\n}");
processor.registerUnresolvedError(textRange.getStartOffset() + "for (".length());
return;
}
final PsiStatement initialization = forStatement.getInitialization();
if (initialization == null) {
processor.registerUnresolvedError(lParenth.getTextRange().getEndOffset());
return;
}
final PsiExpression condition = forStatement.getCondition();
if (condition == null) {
boolean endlessLoop = initialization instanceof PsiEmptyStatement && forStatement.getUpdate() == null;
if (!endlessLoop) {
registerErrorOffset(editor, processor, initialization, forStatement);
}
return;
}
if (forStatement.getUpdate() == null) {
registerErrorOffset(editor, processor, condition, forStatement);
}
}
/**
* {@link JavaSmartEnterProcessor#registerUnresolvedError(int) registers target offset} taking care of the situation when
* current code style implies white space after 'for' part's semicolon.
*
* @param editor target editor
* @param processor target smart enter processor
* @param lastValidForPart last valid element of the target 'for' loop
* @param forStatement PSI element for the target 'for' loop
*/
private static void registerErrorOffset(@NotNull Editor editor, @NotNull JavaSmartEnterProcessor processor,
@NotNull PsiElement lastValidForPart, @NotNull PsiForStatement forStatement)
{
final Project project = editor.getProject();
int offset = lastValidForPart.getTextRange().getEndOffset();
if (project != null && CodeStyle.getSettings(editor).getCommonSettings(JavaLanguage.INSTANCE).SPACE_AFTER_COMMA) {
if (editor.getDocument().getCharsSequence().charAt(lastValidForPart.getTextRange().getEndOffset() - 1) != ';') {
offset++;
}
for (PsiElement element = lastValidForPart.getNextSibling();
element != null && element != forStatement.getRParenth() && element.getParent() == forStatement;
element = element.getNextSibling()) {
final ASTNode node = element.getNode();
if (node != null && JavaJspElementType.WHITE_SPACE_BIT_SET.contains(node.getElementType()) && element.getTextLength() > 0) {
offset++;
break;
}
}
}
processor.registerUnresolvedError(offset);
}
}
@@ -1,59 +0,0 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.util.IncorrectOperationException;
public class IfConditionFixer implements Fixer {
@Override
public void apply(Editor editor, JavaSmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException {
if (psiElement instanceof PsiIfStatement ifStatement) {
final Document doc = editor.getDocument();
final PsiJavaToken rParen = ifStatement.getRParenth();
final PsiJavaToken lParen = ifStatement.getLParenth();
final PsiExpression condition = ifStatement.getCondition();
if (condition == null) {
if (lParen == null || rParen == null) {
int stopOffset = doc.getLineEndOffset(doc.getLineNumber(ifStatement.getTextRange().getStartOffset()));
final PsiStatement then = ifStatement.getThenBranch();
if (then != null) {
stopOffset = Math.min(stopOffset, then.getTextRange().getStartOffset());
}
stopOffset = Math.min(stopOffset, ifStatement.getTextRange().getEndOffset());
PsiElement lastChild = ifStatement.getLastChild();
String innerComment = "";
String lastComment = "";
if (lParen != null && PsiUtilCore.getElementType(lastChild) == JavaTokenType.C_STYLE_COMMENT) {
innerComment = lastChild.getText();
}
else if (lastChild instanceof PsiComment) {
lastComment = lastChild.getText();
}
String prefix = "if (" + innerComment;
doc.replaceString(ifStatement.getTextRange().getStartOffset(), stopOffset, prefix + ")" + lastComment);
processor.registerUnresolvedError(ifStatement.getTextRange().getStartOffset() + prefix.length());
} else {
processor.registerUnresolvedError(lParen.getTextRange().getEndOffset());
}
} else if (rParen == null) {
doc.insertString(condition.getTextRange().getEndOffset(), ")");
}
}
else if (psiElement instanceof PsiExpression && psiElement.getParent() instanceof PsiExpressionStatement) {
PsiElement prevLeaf = PsiTreeUtil.prevVisibleLeaf(psiElement);
if (prevLeaf != null && prevLeaf.textMatches(PsiKeyword.IF)) {
Document doc = editor.getDocument();
doc.insertString(psiElement.getTextRange().getEndOffset(), ")");
doc.insertString(psiElement.getTextRange().getStartOffset(), "(");
}
}
}
}
@@ -1,75 +1,54 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.application.options.CodeStyle;
import com.intellij.codeInsight.CodeInsightUtil;
import com.intellij.codeInsight.lookup.LookupManager;
import com.intellij.internal.statistic.eventLog.EventLogGroup;
import com.intellij.internal.statistic.eventLog.events.EventId1;
import com.intellij.internal.statistic.eventLog.events.StringEventField;
import com.intellij.internal.statistic.service.fus.collectors.CounterUsagesCollector;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.actionSystem.IdeActions;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
import com.intellij.openapi.editor.actionSystem.EditorActionManager;
import com.intellij.openapi.editor.ex.util.EditorUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.util.IncorrectOperationException;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiImportStatementBase;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.text.CharArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import static com.intellij.patterns.PsiJavaPatterns.psiElement;
public class JavaSmartEnterProcessor extends SmartEnterProcessor {
private static final Logger LOG = Logger.getInstance(JavaSmartEnterProcessor.class);
public class JavaSmartEnterProcessor extends AbstractBasicJavaSmartEnterProcessor {
private static final List<Fixer> ourFixers =
List.of(new LiteralFixer(),
new MethodCallFixer(),
new IfConditionFixer(),
new ForStatementFixer(),
new TernaryColonFixer(),
new WhileConditionFixer(),
new CatchDeclarationFixer(),
new SwitchExpressionFixer(),
new SwitchLabelColonFixer(),
new DoWhileConditionFixer(),
new BlockBraceFixer(),
new MissingIfBranchesFixer(),
new MissingTryBodyFixer(),
new MissingSwitchBodyFixer(),
new MissingLambdaBodyFixer(),
new MissingCatchBodyFixer(),
new MissingSynchronizedBodyFixer(),
new MissingLoopBodyFixer(),
new ParameterListFixer(),
new MissingCommaFixer(),
new MissingMethodBodyFixer(),
new MissingClassBodyFixer(),
new MissingReturnExpressionFixer(),
new MissingThrowExpressionFixer(),
new ParenthesizedFixer(),
new SemicolonFixer(),
new MissingArrayInitializerBraceFixer(),
new MissingArrayConstructorBracketFixer(),
new EnumFieldFixer());
List.of(
new LiteralFixer(),
new MethodCallFixer(),
new IfConditionFixer(),
new ForStatementFixer(),
new TernaryColonFixer(),
new WhileConditionFixer(),
new CatchDeclarationFixer(),
new SwitchExpressionFixer(),
new SwitchLabelColonFixer(),
new DoWhileConditionFixer(),
new BlockBraceFixer(),
new MissingIfBranchesFixer(),
new MissingTryBodyFixer(),
new MissingSwitchBodyFixer(),
new MissingLambdaBodyFixer(),
new MissingCatchBodyFixer(),
new MissingSynchronizedBodyFixer(),
new MissingLoopBodyFixer(),
new ParameterListFixer(),
new MissingCommaFixer(),
new MissingMethodBodyFixer(),
new MissingClassBodyFixer(),
new MissingReturnExpressionFixer(),
new MissingThrowExpressionFixer(),
new ParenthesizedFixer(),
new SemicolonFixer(),
new MissingArrayInitializerBraceFixer(),
new MissingArrayConstructorBracketFixer(),
new EnumFieldFixer());
private static final EnterProcessor[] ourEnterProcessors = {
new CommentBreakerEnterProcessor(),
new AfterSemicolonEnterProcessor(),
@@ -78,288 +57,41 @@ public class JavaSmartEnterProcessor extends SmartEnterProcessor {
};
private static final EnterProcessor[] ourAfterCompletionEnterProcessors = {
new AfterSemicolonEnterProcessor(),
new EnterProcessor() {
new ASTNodeEnterProcessor() {
@Override
public boolean doEnter(Editor editor, PsiElement psiElement, boolean isModified) {
return PlainEnterProcessor.expandCodeBlock(editor, psiElement);
public boolean doEnter(@NotNull Editor editor, @NotNull ASTNode astNode, boolean isModified) {
return PlainEnterProcessor.expandCodeBlock(editor, astNode);
}
}
};
private int myFirstErrorOffset = Integer.MAX_VALUE;
private boolean mySkipEnter;
private static final int MAX_ATTEMPTS = 20;
private static final Key<Long> SMART_ENTER_TIMESTAMP = Key.create("smartEnterOriginalTimestamp");
private static class TooManyAttemptsException extends Exception {}
private final JavadocFixer myJavadocFixer = new JavadocFixer();
@Override
public boolean process(@NotNull final Project project, @NotNull final Editor editor, @NotNull final PsiFile psiFile) {
return invokeProcessor(editor, psiFile, false);
public JavaSmartEnterProcessor() {
super(ourFixers, ourEnterProcessors, ourAfterCompletionEnterProcessors,
new JavadocFixer(),
new CommentBreakerEnterProcessor()
);
}
@Override
public boolean processAfterCompletion(@NotNull Editor editor, @NotNull PsiFile psiFile) {
return invokeProcessor(editor, psiFile, true);
}
private boolean invokeProcessor(Editor editor, PsiFile psiFile, boolean afterCompletion) {
final Document document = editor.getDocument();
final CharSequence textForRollback = document.getImmutableCharSequence();
try {
editor.putUserData(SMART_ENTER_TIMESTAMP, editor.getDocument().getModificationStamp());
myFirstErrorOffset = Integer.MAX_VALUE;
mySkipEnter = false;
process(editor, psiFile, 0, afterCompletion);
}
catch (TooManyAttemptsException e) {
document.replaceString(0, document.getTextLength(), textForRollback);
} finally {
editor.putUserData(SMART_ENTER_TIMESTAMP, null);
}
return true;
}
private void process(@NotNull final Editor editor, @NotNull final PsiFile file, final int attempt, boolean afterCompletion) throws TooManyAttemptsException {
if (attempt > MAX_ATTEMPTS) throw new TooManyAttemptsException();
try {
commit(editor);
if (myFirstErrorOffset != Integer.MAX_VALUE) {
editor.getCaretModel().moveToOffset(myFirstErrorOffset);
}
myFirstErrorOffset = Integer.MAX_VALUE;
PsiElement atCaret = getStatementAtCaret(editor, file);
if (atCaret == null) {
if (myJavadocFixer.process(editor, file)) {
return;
}
if (!new CommentBreakerEnterProcessor().doEnter(editor, file, false)) {
plainEnter(editor);
}
return;
}
List<PsiElement> queue = new ArrayList<>();
collectAllElements(atCaret, queue, true);
queue.add(atCaret);
for (PsiElement psiElement : queue) {
for (Fixer fixer : ourFixers) {
Document document = editor.getDocument();
int offset = myFirstErrorOffset;
long stamp = document.getModificationStamp();
fixer.apply(editor, this, psiElement);
Project project = file.getProject();
if (document.getModificationStamp() != stamp || offset != myFirstErrorOffset) {
FixerUsageCollector.log(project, fixer);
}
if (LookupManager.getInstance(project).getActiveLookup() != null) {
return;
}
if (isUncommited(project) || !psiElement.isValid()) {
moveCaretInsideBracesIfAny(editor, file);
process(editor, file, attempt + 1, afterCompletion);
return;
}
}
}
doEnter(atCaret, editor, afterCompletion);
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
}
@Override
protected void reformat(PsiElement atCaret) throws IncorrectOperationException {
if (atCaret == null) {
return;
}
PsiElement parent = atCaret.getParent();
if (parent instanceof PsiForStatement) {
atCaret = parent;
}
if (parent instanceof PsiIfStatement && atCaret == ((PsiIfStatement)parent).getElseBranch()) {
PsiFile file = atCaret.getContainingFile();
Document document = file.getViewProvider().getDocument();
if (document != null) {
TextRange elseIfRange = atCaret.getTextRange();
int lineStart = document.getLineStartOffset(document.getLineNumber(elseIfRange.getStartOffset()));
CodeStyleManager.getInstance(atCaret.getProject()).reformatText(file, lineStart, elseIfRange.getEndOffset());
return;
}
}
super.reformat(atCaret);
}
private void doEnter(PsiElement atCaret, Editor editor, boolean afterCompletion) throws IncorrectOperationException {
final PsiFile psiFile = atCaret.getContainingFile();
if (myFirstErrorOffset != Integer.MAX_VALUE) {
editor.getCaretModel().moveToOffset(myFirstErrorOffset);
reformat(atCaret);
return;
}
final RangeMarker rangeMarker = createRangeMarker(atCaret);
reformat(atCaret);
commit(editor);
if (!mySkipEnter) {
atCaret = CodeInsightUtil.findElementInRange(psiFile, rangeMarker.getStartOffset(), rangeMarker.getEndOffset(), atCaret.getClass());
for (EnterProcessor processor : afterCompletion ? ourAfterCompletionEnterProcessors : ourEnterProcessors) {
if (atCaret == null) {
// Can't restore element at caret after enter processor execution!
break;
}
if (processor.doEnter(editor, atCaret, isModified(editor))) {
rangeMarker.dispose();
return;
}
}
if (!isModified(editor) && !afterCompletion) {
plainEnter(editor);
} else {
if (myFirstErrorOffset == Integer.MAX_VALUE) {
editor.getCaretModel().moveToOffset(rangeMarker.getEndOffset());
} else {
editor.getCaretModel().moveToOffset(myFirstErrorOffset);
}
}
}
rangeMarker.dispose();
}
private static void collectAllElements(PsiElement atCaret, List<? super PsiElement> res, boolean recurse) {
res.add(0, atCaret);
if (doNotStepInto(atCaret)) {
if (!recurse) return;
recurse = false;
}
final PsiElement[] children = atCaret.getChildren();
for (PsiElement child : children) {
if (atCaret instanceof PsiStatement && child instanceof PsiStatement &&
!(atCaret instanceof PsiForStatement && child == ((PsiForStatement)atCaret).getInitialization())) continue;
collectAllElements(child, res, recurse);
}
}
private static boolean doNotStepInto(PsiElement element) {
return element instanceof PsiClass || element instanceof PsiCodeBlock || element instanceof PsiStatement || element instanceof PsiMethod;
protected void log(@NotNull Fixer fixer, @NotNull Project project) {
FixerUsageCollector.log(project, fixer);
}
@Override
@Nullable
protected PsiElement getStatementAtCaret(Editor editor, PsiFile psiFile) {
PsiElement atCaret = super.getStatementAtCaret(editor, psiFile);
if (atCaret instanceof PsiWhiteSpace) return null;
if (PsiUtil.isJavaToken(atCaret, JavaTokenType.RBRACE)) {
atCaret = atCaret.getParent();
boolean expressionEndingWithBrace = atCaret instanceof PsiAnonymousClass ||
atCaret instanceof PsiArrayInitializerExpression ||
atCaret instanceof PsiCodeBlock && (
atCaret.getParent() instanceof PsiLambdaExpression ||
atCaret.getParent() instanceof PsiSwitchExpression
);
if (!expressionEndingWithBrace) return null;
}
for (PsiElement each : SyntaxTraverser.psiApi().parents(atCaret).skip(1)) {
if (each instanceof PsiMember ||
each instanceof PsiImportStatementBase ||
each instanceof PsiPackageStatement ||
each instanceof PsiAnnotation && PsiTreeUtil.hasErrorElements(each)) {
return each;
}
if (each instanceof PsiCodeBlock || each instanceof PsiComment) {
return null;
}
if (each instanceof PsiStatement) {
return each.getParent() instanceof PsiForStatement && !PsiTreeUtil.hasErrorElements(each) ? each.getParent() : each;
}
if (each instanceof PsiConditionalExpression && PsiUtilCore.hasErrorElementChild(each)) {
return each;
}
}
return null;
}
protected void moveCaretInsideBracesIfAny(@NotNull final Editor editor, @NotNull final PsiFile file) throws IncorrectOperationException {
int caretOffset = editor.getCaretModel().getOffset();
final CharSequence chars = editor.getDocument().getCharsSequence();
if (CharArrayUtil.regionMatches(chars, caretOffset, "{}")) {
caretOffset+=2;
}
else if (CharArrayUtil.regionMatches(chars, caretOffset, "{\n}")) {
caretOffset+=3;
}
caretOffset = CharArrayUtil.shiftBackward(chars, caretOffset - 1, " \t") + 1;
if (CharArrayUtil.regionMatches(chars, caretOffset - "{}".length(), "{}") ||
CharArrayUtil.regionMatches(chars, caretOffset - "{\n}".length(), "{\n}")) {
commit(editor);
final CommonCodeStyleSettings settings = CodeStyle.getSettings(file).getCommonSettings(JavaLanguage.INSTANCE);
final boolean old = settings.KEEP_SIMPLE_BLOCKS_IN_ONE_LINE;
settings.KEEP_SIMPLE_BLOCKS_IN_ONE_LINE = false;
PsiElement leaf = file.findElementAt(caretOffset - 1);
PsiElement elt = PsiTreeUtil.getParentOfType(leaf, PsiCodeBlock.class);
if (elt == null && leaf != null && leaf.getParent() instanceof PsiClass) {
elt = leaf.getParent();
}
reformat(elt);
settings.KEEP_SIMPLE_BLOCKS_IN_ONE_LINE = old;
editor.getCaretModel().moveToOffset(caretOffset - 1);
reformatBlockParentIfNeeded(editor, file);
}
}
private void reformatBlockParentIfNeeded(@NotNull Editor editor, @NotNull PsiFile file) {
commit(editor);
PsiCodeBlock block = PsiTreeUtil.findElementOfClassAtOffset(file, editor.getCaretModel().getOffset(), PsiCodeBlock.class, false);
if (block != null && psiElement().withParents(PsiBlockStatement.class, PsiForStatement.class).accepts(block)) {
reformat(block.getParent().getParent());
}
}
public void registerUnresolvedError(int offset) {
if (myFirstErrorOffset > offset) {
myFirstErrorOffset = offset;
}
}
public void setSkipEnter(boolean skipEnter) {
mySkipEnter = skipEnter;
protected boolean isImportStatementBase(PsiElement el) {
return el instanceof PsiImportStatementBase;
}
protected static void plainEnter(@NotNull final Editor editor) {
getEnterHandler().execute(editor, editor.getCaretModel().getCurrentCaret(), EditorUtil.getEditorDataContext(editor));
AbstractBasicJavaSmartEnterProcessor.plainEnter(editor);
}
protected static EditorActionHandler getEnterHandler() {
return EditorActionManager.getInstance().getActionHandler(IdeActions.ACTION_EDITOR_START_NEW_LINE);
return AbstractBasicJavaSmartEnterProcessor.getEnterHandler();
}
protected static boolean isModified(@NotNull final Editor editor) {
final Long timestamp = editor.getUserData(SMART_ENTER_TIMESTAMP);
return editor.getDocument().getModificationStamp() != timestamp.longValue();
return AbstractBasicJavaSmartEnterProcessor.isModified(editor);
}
private static final class FixerUsageCollector extends CounterUsagesCollector {
@@ -372,7 +104,7 @@ public class JavaSmartEnterProcessor extends SmartEnterProcessor {
public EventLogGroup getGroup() {
return GROUP;
}
static void log(Project project, Fixer fixer) {
USED.log(project, fixer.getClass().getSimpleName());
}
@@ -1,110 +1,15 @@
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.javadoc.JavadocHelper;
import com.intellij.openapi.editor.CaretModel;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.LogicalPosition;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
/**
* Serves as a facade for javadoc smart completion.
* <p/>
* Thread-safe.
*/
public class JavadocFixer {
public class JavadocFixer extends AbstractBasicJavadocFixer {
private final JavadocHelper myHelper = new JavadocHelper();
/**
* Checks if caret of the given editor is located inside javadoc and tries to perform smart completion there in case of the positive
* answer.
*
* @param editor target editor
* @param psiFile PSI file for the document exposed via the given editor
* @return {@code true} if smart completion was performed; {@code false} otherwise
*/
public boolean process(@NotNull Editor editor, @NotNull PsiFile psiFile) {
// Check parameter description completion.
final CaretModel caretModel = editor.getCaretModel();
final Pair<JavadocHelper.JavadocParameterInfo,List<JavadocHelper.JavadocParameterInfo>> pair =
myHelper.parse(psiFile, editor, caretModel.getOffset());
if (pair.first == null) {
return false;
}
final JavadocHelper.JavadocParameterInfo next = findNext(pair.second, pair.first);
if (next == null) {
final int line = pair.first.lastLine + 1;
final Document document = editor.getDocument();
if (line < document.getLineCount()) {
StringBuilder indent = new StringBuilder();
boolean insertIndent = true;
final CharSequence text = document.getCharsSequence();
for (int i = document.getLineStartOffset(line), max = document.getLineEndOffset(line); i < max; i++) {
final char c = text.charAt(i);
if (c == ' ' || c == '\t') {
indent.append(c);
continue;
}
else if (c == '*') {
indent.append("* ");
if (i < max - 1 && text.charAt(i + 1) != '/') {
insertIndent = false;
}
}
indent.append("\n");
break;
}
if (insertIndent) {
document.insertString(document.getLineStartOffset(line), indent);
}
}
moveCaretToTheLineEndIfPossible(editor, line);
return true;
}
if (next.parameterDescriptionStartPosition != null) {
myHelper.navigate(next.parameterDescriptionStartPosition, editor, psiFile.getProject());
}
else {
final LogicalPosition position = myHelper.calculateDescriptionStartPosition(psiFile, pair.second, next);
myHelper.navigate(position, editor, psiFile.getProject());
}
return true;
}
private static void moveCaretToTheLineEndIfPossible(@NotNull Editor editor, int line) {
final Document document = editor.getDocument();
final CaretModel caretModel = editor.getCaretModel();
int offset;
if (line >= document.getLineCount()) {
offset = document.getTextLength();
}
else {
offset = document.getLineEndOffset(line);
}
caretModel.moveToOffset(offset);
}
@Nullable
private static JavadocHelper.JavadocParameterInfo findNext(@NotNull Collection<? extends JavadocHelper.JavadocParameterInfo> data,
@NotNull JavadocHelper.JavadocParameterInfo anchor)
{
boolean returnNow = false;
for (JavadocHelper.JavadocParameterInfo info : data) {
if (returnNow) {
return info;
}
returnNow = info == anchor;
}
return null;
public JavadocFixer() {
super( new JavadocHelper());
}
}
@@ -1,39 +0,0 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiJavaToken;
import com.intellij.util.IncorrectOperationException;
public class LiteralFixer implements Fixer {
@Override
public void apply(Editor editor, JavaSmartEnterProcessor processor, PsiElement psiElement)
throws IncorrectOperationException {
if (psiElement instanceof PsiJavaToken) {
if (((PsiJavaToken) psiElement).getTokenType() == JavaTokenType.STRING_LITERAL &&
!StringUtil.endsWithChar(psiElement.getText(), '\"')) {
editor.getDocument().insertString(psiElement.getTextRange().getEndOffset(), "\"");
} else if (((PsiJavaToken) psiElement).getTokenType() == JavaTokenType.CHARACTER_LITERAL &&
!StringUtil.endsWithChar(psiElement.getText(), '\'')) {
editor.getDocument().insertString(psiElement.getTextRange().getEndOffset(), "'");
}
}
}
}
@@ -3,10 +3,12 @@ package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeSet;
import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeType;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.psi.impl.source.jsp.jspJava.JspMethodCall;
import com.intellij.psi.infos.CandidateInfo;
import com.intellij.psi.util.PsiTreeUtil;
@@ -18,20 +20,23 @@ import org.jetbrains.annotations.NotNull;
public class MethodCallFixer implements Fixer {
@Override
public void apply(Editor editor, JavaSmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException {
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
PsiElement psiElement = BasicJavaAstTreeUtil.toPsi(astNode);
PsiExpressionList argList = null;
if (psiElement instanceof PsiMethodCallExpression && !(psiElement instanceof JspMethodCall)) {
argList = ((PsiMethodCallExpression) psiElement).getArgumentList();
} else if (psiElement instanceof PsiNewExpression) {
argList = ((PsiNewExpression) psiElement).getArgumentList();
argList = ((PsiMethodCallExpression)psiElement).getArgumentList();
}
else if (psiElement instanceof PsiNewExpression) {
argList = ((PsiNewExpression)psiElement).getArgumentList();
}
int caret = editor.getCaretModel().getOffset();
if (argList != null && !hasRParenth(argList)) {
PsiCallExpression innermostCall = PsiTreeUtil.findElementOfClassAtOffset(psiElement.getContainingFile(), caret - 1, PsiCallExpression.class, false);
PsiCallExpression innermostCall =
PsiTreeUtil.findElementOfClassAtOffset(psiElement.getContainingFile(), caret - 1, PsiCallExpression.class, false);
while (innermostCall != null && innermostCall != psiElement && hasRParenth(innermostCall.getArgumentList())
&& innermostCall.resolveMethodGenerics().isValidResult()) {
&& innermostCall.resolveMethodGenerics().isValidResult()) {
innermostCall = PsiTreeUtil.getParentOfType(innermostCall, PsiCallExpression.class);
}
if (innermostCall == null) return;
@@ -1,26 +0,0 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiNewExpression;
import com.intellij.util.IncorrectOperationException;
public class MissingArrayConstructorBracketFixer implements Fixer {
@Override
public void apply(Editor editor, JavaSmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException {
if (!(psiElement instanceof PsiNewExpression expr)) return;
int count = 0;
for (PsiElement element = expr.getFirstChild(); element != null; element = element.getNextSibling()) {
if (element.getNode().getElementType() == JavaTokenType.LBRACKET) {
count++;
} else if (element.getNode().getElementType() == JavaTokenType.RBRACKET) {
count--;
}
}
if (count > 0) {
editor.getDocument().insertString(psiElement.getTextRange().getEndOffset(), "]");
}
}
}
@@ -1,47 +0,0 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.codeInsight.editorActions.enter.EnterAfterUnmatchedBraceHandler;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.ContainerUtil;
public class MissingArrayInitializerBraceFixer implements Fixer {
@Override
public void apply(Editor editor, JavaSmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException {
if (!(psiElement instanceof PsiArrayInitializerExpression || psiElement instanceof PsiArrayInitializerMemberValue)) return;
PsiElement child = psiElement.getFirstChild();
if (!PsiUtil.isJavaToken(child, JavaTokenType.LBRACE)) return;
if (!EnterAfterUnmatchedBraceHandler.isAfterUnmatchedLBrace(editor, child.getTextRange().getEndOffset(),
psiElement.getContainingFile().getFileType())) return;
PsiElement anchor = PsiTreeUtil.getChildOfType(psiElement, PsiErrorElement.class);
if (anchor == null) {
PsiElement last = PsiTreeUtil.getDeepestVisibleLast(psiElement);
while (PsiUtil.isJavaToken(last, JavaTokenType.RBRACE)) {
last = PsiTreeUtil.prevCodeLeaf(last);
}
if (last != null && PsiTreeUtil.isAncestor(psiElement, last, true)) {
anchor = last;
}
}
int endOffset = (anchor != null ? anchor : psiElement).getTextRange().getEndOffset();
editor.getDocument().insertString(endOffset, "}");
}
}
@@ -1,28 +0,0 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiCatchSection;
import com.intellij.psi.PsiCodeBlock;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiJavaToken;
import com.intellij.util.IncorrectOperationException;
public class MissingCatchBodyFixer implements Fixer {
@Override
public void apply(Editor editor, JavaSmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException {
if (!(psiElement instanceof PsiCatchSection catchSection)) return;
final Document doc = editor.getDocument();
PsiCodeBlock body = catchSection.getCatchBlock();
if (body != null && body.getLBrace() != null && body.getRBrace() != null) return;
final PsiJavaToken rParenth = catchSection.getRParenth();
if (rParenth == null) return;
doc.insertString(rParenth.getTextRange().getEndOffset(), "{}");
}
}
@@ -1,37 +0,0 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.*;
import com.intellij.util.IncorrectOperationException;
public class MissingClassBodyFixer implements Fixer {
@Override
public void apply(Editor editor, JavaSmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException {
if (psiElement instanceof PsiRecordComponent recordComponent) {
psiElement = recordComponent.getContainingClass();
}
if (!(psiElement instanceof PsiClass psiClass) || psiElement instanceof PsiTypeParameter) return;
if (psiClass.getLBrace() == null) {
PsiElement lastChild = psiClass.getLastChild();
int offset = psiClass.getTextRange().getEndOffset();
if (lastChild instanceof PsiErrorElement) {
PsiElement previous = lastChild.getPrevSibling();
if (previous instanceof PsiWhiteSpace) {
offset = previous.getTextRange().getStartOffset();
} else {
offset = lastChild.getTextRange().getStartOffset();
}
}
if (psiClass.isRecord() && psiClass.getRecordHeader() == null) {
editor.getDocument().insertString(offset, "() {}");
editor.getCaretModel().moveToOffset(offset + 1);
processor.setSkipEnter(true);
} else {
editor.getDocument().insertString(offset, "{\n}");
editor.getCaretModel().moveToOffset(offset);
}
}
}
}
@@ -2,15 +2,19 @@
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.core.JavaPsiBundle;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
public class MissingCommaFixer implements Fixer {
@Override
public void apply(Editor editor, JavaSmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException {
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
PsiElement psiElement = BasicJavaAstTreeUtil.toPsi(astNode);
if (!(psiElement instanceof PsiErrorElement) ||
!((PsiErrorElement)psiElement).getErrorDescription().equals(JavaPsiBundle.message("expected.comma.or.rparen"))) {
return;
@@ -1,50 +0,0 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.*;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class MissingIfBranchesFixer implements Fixer {
@Override
public void apply(Editor editor, JavaSmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException {
if (!(psiElement instanceof PsiIfStatement ifStatement)) return;
final Document doc = editor.getDocument();
final PsiKeyword elseElement = ifStatement.getElseElement();
if (elseElement != null) {
handleBranch(doc, ifStatement, elseElement, ifStatement.getElseBranch());
}
PsiJavaToken rParenth = ifStatement.getRParenth();
assert rParenth != null;
handleBranch(doc, ifStatement, rParenth, ifStatement.getThenBranch());
}
private static void handleBranch(@NotNull Document doc, @NotNull PsiIfStatement ifStatement, @NotNull PsiElement beforeBranch, @Nullable PsiStatement branch) {
if (branch instanceof PsiBlockStatement || beforeBranch.textMatches(PsiKeyword.ELSE) && branch instanceof PsiIfStatement) return;
boolean transformingOneLiner = branch != null && (startLine(doc, beforeBranch) == startLine(doc, branch) ||
startCol(doc, ifStatement) < startCol(doc, branch));
if (!transformingOneLiner) {
doc.insertString(beforeBranch.getTextRange().getEndOffset(), "{}");
}
else {
doc.insertString(beforeBranch.getTextRange().getEndOffset(), "{");
doc.insertString(branch.getTextRange().getEndOffset() + 1, "}");
}
}
private static int startLine(Document doc, @NotNull PsiElement psiElement) {
return doc.getLineNumber(psiElement.getTextRange().getStartOffset());
}
private static int startCol(Document doc, @NotNull PsiElement psiElement) {
int offset = psiElement.getTextRange().getStartOffset();
return offset - doc.getLineStartOffset(doc.getLineNumber(offset));
}
}
@@ -1,49 +0,0 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiLambdaExpression;
import com.intellij.psi.PsiSwitchLabeledRuleStatement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.IncorrectOperationException;
public class MissingLambdaBodyFixer implements Fixer {
@Override
public void apply(Editor editor, JavaSmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException {
PsiElement body;
if (psiElement instanceof PsiLambdaExpression lambda) {
body = lambda.getBody();
} else if (psiElement instanceof PsiSwitchLabeledRuleStatement rule) {
body = rule.getBody();
} else return;
if (body != null) return;
Document doc = editor.getDocument();
PsiElement arrow = PsiTreeUtil.getDeepestVisibleLast(psiElement);
if (!PsiUtil.isJavaToken(arrow, JavaTokenType.ARROW)) return;
int offset = arrow.getTextRange().getEndOffset();
doc.insertString(offset, "{\n}");
editor.getCaretModel().moveToOffset(offset + 1);
processor.commit(editor);
processor.reformat(psiElement);
processor.setSkipEnter(psiElement instanceof PsiLambdaExpression);
}
}
@@ -1,142 +0,0 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.ObjectUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class MissingLoopBodyFixer implements Fixer {
@Override
public void apply(Editor editor, JavaSmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException {
PsiLoopStatement loopStatement = getLoopParent(psiElement);
if (loopStatement == null) return;
final Document doc = editor.getDocument();
PsiStatement body = loopStatement.getBody();
if (body instanceof PsiBlockStatement) return;
if (body != null && startLine(doc, body) == startLine(doc, loopStatement)) return;
PsiElement eltToInsertAfter;
if (loopStatement instanceof PsiWhileStatement) {
eltToInsertAfter = ((PsiWhileStatement)loopStatement).getRParenth();
}
else if (loopStatement instanceof PsiForStatement) {
eltToInsertAfter = ((PsiForStatement)loopStatement).getRParenth();
}
else if (loopStatement instanceof PsiForeachStatement) {
eltToInsertAfter = ((PsiForeachStatement)loopStatement).getRParenth();
}
else {
return;
}
fixLoopBody(editor, processor, loopStatement, doc, body, eltToInsertAfter);
}
private static PsiLoopStatement getLoopParent(PsiElement element) {
PsiLoopStatement statement = PsiTreeUtil.getParentOfType(element, PsiLoopStatement.class);
if (statement == null) return null;
if (statement instanceof PsiForeachStatement) {
return isForEachApplicable((PsiForeachStatement)statement, element) ? statement : null;
}
if (statement instanceof PsiForStatement) {
return isForApplicable((PsiForStatement)statement, element) ? statement : null;
}
if (statement instanceof PsiWhileStatement) {
return statement;
}
return null;
}
private static boolean isForApplicable(PsiForStatement statement, PsiElement psiElement) {
PsiStatement init = statement.getInitialization();
PsiStatement update = statement.getUpdate();
PsiExpression check = statement.getCondition();
return isValidChild(init, psiElement) || isValidChild(update, psiElement) || isValidChild(check, psiElement);
}
private static boolean isValidChild(PsiElement ancestor, PsiElement psiElement) {
if (ancestor != null) {
if (PsiTreeUtil.isAncestor(ancestor, psiElement, false)) {
if (PsiTreeUtil.hasErrorElements(ancestor)) return false;
return true;
}
}
return false;
}
private static boolean isForEachApplicable(PsiForeachStatement statement, PsiElement psiElement) {
PsiExpression iterated = statement.getIteratedValue();
PsiParameter parameter = statement.getIterationParameter();
return PsiTreeUtil.isAncestor(iterated, psiElement, false) || PsiTreeUtil.isAncestor(parameter, psiElement, false);
}
private static int startLine(Document doc, PsiElement psiElement) {
return doc.getLineNumber(psiElement.getTextRange().getStartOffset());
}
static void fixLoopBody(@NotNull Editor editor,
@NotNull JavaSmartEnterProcessor processor,
@NotNull PsiLoopStatement loop,
@NotNull Document doc,
@Nullable PsiStatement body,
@Nullable PsiElement eltToInsertAfter) {
if (body != null && eltToInsertAfter != null) {
if (bodyIsIndented(loop, body)) {
int endOffset = body.getTextRange().getEndOffset();
doc.insertString(endOffset, "\n}");
int offset = eltToInsertAfter.getTextRange().getEndOffset();
doc.insertString(offset, "{");
editor.getCaretModel().moveToOffset(endOffset + "{".length());
processor.setSkipEnter(true);
processor.reformat(loop);
return;
}
}
String prefix = "{}";
String text = prefix;
if (eltToInsertAfter == null) {
eltToInsertAfter = loop;
text = ")" + text;
}
int offset = eltToInsertAfter.getTextRange().getEndOffset();
doc.insertString(offset, text);
editor.getCaretModel().moveToOffset(offset + text.length() - prefix.length());
}
private static boolean bodyIsIndented(@NotNull PsiLoopStatement loop, @NotNull PsiElement body) {
PsiWhiteSpace beforeBody = ObjectUtils.tryCast(body.getPrevSibling(), PsiWhiteSpace.class);
if (beforeBody == null) return false;
PsiWhiteSpace beforeLoop = ObjectUtils.tryCast(loop.getPrevSibling(), PsiWhiteSpace.class);
if (beforeLoop == null) return false;
String beforeBodyText = beforeBody.getText();
String beforeLoopText = beforeLoop.getText();
int beforeBodyLineBreak = beforeBodyText.lastIndexOf('\n');
if (beforeBodyLineBreak == -1) return false;
int beforeLoopLineBreak = beforeLoopText.lastIndexOf('\n');
if (beforeLoopLineBreak == -1) return false;
return beforeBodyText.length() - beforeBodyLineBreak > beforeLoopText.length() - beforeLoopLineBreak;
}
}
@@ -1,75 +0,0 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.codeInsight.daemon.JavaErrorBundle;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import java.util.Objects;
import static com.intellij.psi.PsiModifier.*;
public class MissingMethodBodyFixer implements Fixer {
@Override
public void apply(Editor editor, JavaSmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException {
if (psiElement instanceof PsiField field) {
// replace something like `void x` with `void x() {...}`
// while it's ambiguous whether user wants a field or a method, declaring a field is easier (just append a semicolon),
// so completing a method looks more useful
if (field.hasInitializer()) return;
PsiElement lastChild = field.getLastChild();
if (!(lastChild instanceof PsiErrorElement)) return;
if (!((PsiErrorElement)lastChild).getErrorDescription().equals(JavaErrorBundle.message("expected.semicolon"))) return;
PsiModifierList modifiers = field.getModifierList();
if (modifiers == null) return;
// Impossible modifiers for a method
if (modifiers.hasExplicitModifier(TRANSIENT) || modifiers.hasExplicitModifier(VOLATILE)) return;
if (!PsiTypes.voidType().equals(field.getType())) return;
int endOffset = field.getTextRange().getEndOffset();
editor.getDocument().insertString(endOffset, "(){}");
editor.getCaretModel().moveToOffset(endOffset + 1);
processor.registerUnresolvedError(endOffset + 1);
processor.setSkipEnter(true);
return;
}
if (!(psiElement instanceof PsiMethod method)) return;
if (!shouldHaveBody(method)) return;
final PsiCodeBlock body = method.getBody();
final Document doc = editor.getDocument();
if (body != null) {
// See IDEADEV-1093. This is quite hacky heuristic but it seem to be best we can do.
String bodyText = body.getText();
if (bodyText.startsWith("{")) {
final PsiStatement[] statements = body.getStatements();
if (statements.length > 0) {
if (statements[0] instanceof PsiDeclarationStatement) {
if (PsiTreeUtil.getDeepestLast(statements[0]) instanceof PsiErrorElement) {
if (Objects.requireNonNull(method.getContainingClass()).getRBrace() == null) {
doc.insertString(body.getTextRange().getStartOffset() + 1, "\n}");
}
}
}
}
}
return;
}
int endOffset = method.getThrowsList().getTextRange().getEndOffset();
if (endOffset < doc.getTextLength() && doc.getCharsSequence().charAt(endOffset) == ';') {
doc.deleteString(endOffset, endOffset + 1);
}
doc.insertString(endOffset, "{\n}");
}
static boolean shouldHaveBody(PsiMethod method) {
PsiClass containingClass = method.getContainingClass();
if (containingClass == null) return false;
if (method.hasModifierProperty(ABSTRACT) || method.hasModifierProperty(NATIVE)) return false;
if (method.hasModifierProperty(PRIVATE)) return true;
if (containingClass.isInterface() && !method.hasModifierProperty(DEFAULT) && !method.hasModifierProperty(STATIC)) return false;
return true;
}
}
@@ -1,92 +0,0 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@SuppressWarnings({"HardCodedStringLiteral"})
public class MissingReturnExpressionFixer implements Fixer {
@Override
public void apply(Editor editor, JavaSmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException {
if (!(psiElement instanceof PsiReturnStatement retStatement)) {
return;
}
if (!PsiTreeUtil.hasErrorElements(psiElement)) {
return;
}
if (fixMethodCallWithoutTrailingSemicolon(retStatement, editor, processor)) {
return;
}
PsiExpression returnValue = retStatement.getReturnValue();
if (returnValue != null
&& lineNumber(editor, editor.getCaretModel().getOffset()) == lineNumber(editor, returnValue.getTextRange().getStartOffset()))
{
return;
}
PsiElement parent = PsiTreeUtil.getParentOfType(psiElement, PsiClassInitializer.class, PsiMethod.class);
if (parent instanceof PsiMethod) {
final PsiType returnType = ((PsiMethod)parent).getReturnType();
if (returnType != null && !PsiTypes.voidType().equals(returnType)) {
final int startOffset = retStatement.getTextRange().getStartOffset();
if (returnValue != null) {
editor.getDocument().insertString(startOffset + "return".length(), ";");
}
processor.registerUnresolvedError(startOffset + "return".length());
}
}
}
private static boolean fixMethodCallWithoutTrailingSemicolon(@Nullable PsiReturnStatement returnStatement, @NotNull Editor editor,
@NotNull JavaSmartEnterProcessor processor)
{
if (returnStatement == null) {
return false;
}
final PsiElement lastChild = returnStatement.getLastChild();
if (!(lastChild instanceof PsiErrorElement)) {
return false;
}
PsiElement prev = lastChild.getPrevSibling();
if (prev instanceof PsiWhiteSpace) {
prev = prev.getPrevSibling();
}
if (!(prev instanceof PsiJavaToken prevToken)) {
int offset = returnStatement.getTextRange().getEndOffset();
final PsiMethod method = PsiTreeUtil.getParentOfType(returnStatement, PsiMethod.class, true, PsiLambdaExpression.class);
if (method != null && PsiTypes.voidType().equals(method.getReturnType())) {
offset = returnStatement.getTextRange().getStartOffset() + "return".length();
}
editor.getDocument().insertString(offset, ";");
//processor.setSkipEnter(true);
return true;
}
if (prevToken.getTokenType() == JavaTokenType.SEMICOLON) {
return false;
}
final int offset = returnStatement.getTextRange().getEndOffset();
editor.getDocument().insertString(offset, ";");
if (prevToken.getTokenType() == JavaTokenType.RETURN_KEYWORD) {
final PsiMethod method = PsiTreeUtil.getParentOfType(returnStatement, PsiMethod.class);
if (method != null && !PsiTypes.voidType().equals(method.getReturnType())) {
editor.getCaretModel().moveToOffset(offset);
processor.setSkipEnter(true);
}
}
return true;
}
private static int lineNumber(Editor editor, int offset) {
return editor.getDocument().getLineNumber(offset);
}
}
@@ -1,28 +0,0 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiCodeBlock;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiJavaToken;
import com.intellij.psi.PsiSwitchBlock;
import com.intellij.util.IncorrectOperationException;
public class MissingSwitchBodyFixer implements Fixer {
@Override
public void apply(Editor editor, JavaSmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException {
if (!(psiElement instanceof PsiSwitchBlock switchStatement)) return;
final Document doc = editor.getDocument();
final PsiCodeBlock body = switchStatement.getBody();
if (body != null) return;
final PsiJavaToken rParenth = switchStatement.getRParenth();
assert rParenth != null;
doc.insertString(rParenth.getTextRange().getEndOffset(), "{}");
}
}
@@ -1,22 +0,0 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiSynchronizedStatement;
import com.intellij.util.IncorrectOperationException;
public class MissingSynchronizedBodyFixer implements Fixer {
@Override
public void apply(Editor editor, JavaSmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException {
if (!(psiElement instanceof PsiSynchronizedStatement syncStatement)) return;
final Document doc = editor.getDocument();
PsiElement body = syncStatement.getBody();
if (body != null) return;
doc.insertString(syncStatement.getTextRange().getEndOffset(), "{}");
}
}
@@ -1,16 +1,19 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiThrowStatement;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
@SuppressWarnings({"HardCodedStringLiteral"})
public class MissingThrowExpressionFixer implements Fixer {
@Override
public void apply(Editor editor, JavaSmartEnterProcessor processor, PsiElement psiElement)
throws IncorrectOperationException {
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode)
throws IncorrectOperationException {
PsiElement psiElement = BasicJavaAstTreeUtil.toPsi(astNode);
if (psiElement instanceof PsiThrowStatement throwStatement) {
if (throwStatement.getException() != null &&
startLine(editor, throwStatement) == startLine(editor, throwStatement.getException())) {
@@ -1,23 +0,0 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.editorActions.smartEnter;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiTryStatement;
import com.intellij.util.IncorrectOperationException;
public class MissingTryBodyFixer implements Fixer {
@Override
public void apply(Editor editor, JavaSmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException {
if (!(psiElement instanceof PsiTryStatement tryStatement)) return;
final Document doc = editor.getDocument();
PsiElement body = tryStatement.getTryBlock();
if (body != null) return;
doc.insertString(tryStatement.getTextRange().getEndOffset(), "{}");
}
}

Some files were not shown because too many files have changed in this diff Show More