diff --git a/java-language-api/src/main/java/com/intellij/java/language/psi/PsiExpression.java b/java-language-api/src/main/java/com/intellij/java/language/psi/PsiExpression.java index 9d8e380eb..4a2ccf4fd 100644 --- a/java-language-api/src/main/java/com/intellij/java/language/psi/PsiExpression.java +++ b/java-language-api/src/main/java/com/intellij/java/language/psi/PsiExpression.java @@ -18,26 +18,26 @@ import consulo.util.collection.ArrayFactory; import org.jspecify.annotations.Nullable; + import java.util.function.Function; /** * Represents a Java expression. */ public interface PsiExpression extends PsiAnnotationMemberValue, PsiCaseLabelElement { - /** - * The empty array of PSI expressions which can be reused to avoid unnecessary allocations. - */ - PsiExpression[] EMPTY_ARRAY = new PsiExpression[0]; + /** + * The empty array of PSI expressions which can be reused to avoid unnecessary allocations. + */ + PsiExpression[] EMPTY_ARRAY = new PsiExpression[0]; - ArrayFactory ARRAY_FACTORY = count -> count == 0 ? PsiExpression.EMPTY_ARRAY : new PsiExpression[count]; + ArrayFactory ARRAY_FACTORY = count -> count == 0 ? PsiExpression.EMPTY_ARRAY : new PsiExpression[count]; - Function EXPRESSION_TO_TYPE = PsiExpression::getType; + Function EXPRESSION_TO_TYPE = PsiExpression::getType; - /** - * Returns the type of the expression. - * - * @return the expression type, or null if the type is not known. - */ - @Nullable - PsiType getType(); + /** + * Returns the type of the expression. + * + * @return the expression type, or null if the type is not known. + */ + @Nullable PsiType getType(); } diff --git a/java-language-api/src/main/java/com/intellij/java/language/psi/util/InheritanceUtil.java b/java-language-api/src/main/java/com/intellij/java/language/psi/util/InheritanceUtil.java index 15a56a8c5..3c9ca403a 100644 --- a/java-language-api/src/main/java/com/intellij/java/language/psi/util/InheritanceUtil.java +++ b/java-language-api/src/main/java/com/intellij/java/language/psi/util/InheritanceUtil.java @@ -16,13 +16,12 @@ package com.intellij.java.language.psi.util; import com.intellij.java.language.psi.*; -import consulo.application.util.function.Processor; +import consulo.annotation.access.RequiredReadAction; import consulo.language.psi.PsiElement; import consulo.language.psi.PsiFile; import consulo.language.psi.PsiManager; -import consulo.util.lang.function.Condition; -import org.jspecify.annotations.Nullable; import org.jetbrains.annotations.Contract; +import org.jspecify.annotations.Nullable; import java.util.HashSet; import java.util.LinkedHashSet; @@ -64,12 +63,12 @@ private static boolean processSupers(PsiClass aClass, Predicate superP return true; } - for (final PsiClass intf : aClass.getInterfaces()) { + for (PsiClass intf : aClass.getInterfaces()) { if (!superProcessor.test(intf) || !processSupers(intf, superProcessor, visited)) { return false; } } - final PsiClass superClass = aClass.getSuperClass(); + PsiClass superClass = aClass.getSuperClass(); if (superClass != null) { if (!superProcessor.test(superClass) || !processSupers(superClass, superProcessor, visited)) { return false; @@ -79,13 +78,13 @@ private static boolean processSupers(PsiClass aClass, Predicate superP } @Contract("null, _ -> false") - public static boolean isInheritor(@Nullable PsiType type, final String baseClassName) { - if (type instanceof PsiClassType) { - return isInheritor(((PsiClassType) type).resolve(), baseClassName); + public static boolean isInheritor(@Nullable PsiType type, String baseClassName) { + if (type instanceof PsiClassType classType) { + return isInheritor(classType.resolve(), baseClassName); } - if (type instanceof PsiIntersectionType) { - for (PsiType conjunct : ((PsiIntersectionType) type).getConjuncts()) { + if (type instanceof PsiIntersectionType intersectionType) { + for (PsiType conjunct : intersectionType.getConjuncts()) { if (isInheritor(conjunct, baseClassName)) { return true; } @@ -96,17 +95,17 @@ public static boolean isInheritor(@Nullable PsiType type, final String baseClass } @Contract("null, _ -> false") - public static boolean isInheritor(@Nullable PsiClass psiClass, final String baseClassName) { + public static boolean isInheritor(@Nullable PsiClass psiClass, String baseClassName) { return isInheritor(psiClass, false, baseClassName); } @Contract("null, _, _ -> false") - public static boolean isInheritor(@Nullable PsiClass psiClass, final boolean strict, final String baseClassName) { + public static boolean isInheritor(@Nullable PsiClass psiClass, boolean strict, String baseClassName) { if (psiClass == null) { return false; } - final PsiClass base = JavaPsiFacade.getInstance(psiClass.getProject()).findClass(baseClassName, psiClass.getResolveScope()); + PsiClass base = JavaPsiFacade.getInstance(psiClass.getProject()).findClass(baseClassName, psiClass.getResolveScope()); if (base == null) { return false; } @@ -131,8 +130,13 @@ public static LinkedHashSet getSuperClasses(PsiClass aClass) { return result; } - - private static void getSuperClassesOfList(PsiClassType[] types, Set results, boolean includeNonProject, Set visited, PsiManager manager) { + private static void getSuperClassesOfList( + PsiClassType[] types, + Set results, + boolean includeNonProject, + Set visited, + PsiManager manager + ) { for (PsiClassType type : types) { PsiClass resolved = type.resolve(); if (resolved != null && visited.add(resolved)) { @@ -144,31 +148,42 @@ private static void getSuperClassesOfList(PsiClassType[] types, Set re } } - public static boolean hasEnclosingInstanceInScope(PsiClass aClass, PsiElement scope, boolean isSuperClassAccepted, boolean isTypeParamsAccepted) { + @RequiredReadAction + public static boolean hasEnclosingInstanceInScope( + PsiClass aClass, + PsiElement scope, + boolean isSuperClassAccepted, + boolean isTypeParamsAccepted + ) { return hasEnclosingInstanceInScope(aClass, scope, psiClass -> isSuperClassAccepted, isTypeParamsAccepted); } - public static boolean hasEnclosingInstanceInScope(PsiClass aClass, PsiElement scope, Condition isSuperClassAccepted, boolean isTypeParamsAccepted) { + @RequiredReadAction + public static boolean hasEnclosingInstanceInScope( + PsiClass aClass, + PsiElement scope, + Predicate isSuperClassAccepted, + boolean isTypeParamsAccepted + ) { PsiManager manager = aClass.getManager(); PsiElement place = scope; while (place != null && place != aClass && !(place instanceof PsiFile)) { - if (place instanceof PsiClass) { - if (isSuperClassAccepted.value((PsiClass) place)) { - if (isInheritorOrSelf((PsiClass) place, aClass, true)) { + if (place instanceof PsiClass psiClass) { + if (isSuperClassAccepted.test(psiClass)) { + if (isInheritorOrSelf(psiClass, aClass, true)) { return true; } } - else { - if (manager.areElementsEquivalent(place, aClass)) { - return true; - } + else if (manager.areElementsEquivalent(place, aClass)) { + return true; } + if (isTypeParamsAccepted && place instanceof PsiTypeParameter) { return true; } } - if (place instanceof PsiModifierListOwner) { - final PsiModifierList modifierList = ((PsiModifierListOwner) place).getModifierList(); + if (place instanceof PsiModifierListOwner modifierListOwner) { + PsiModifierList modifierList = modifierListOwner.getModifierList(); if (modifierList != null && modifierList.hasModifierProperty(PsiModifier.STATIC)) { return false; } @@ -178,24 +193,23 @@ public static boolean hasEnclosingInstanceInScope(PsiClass aClass, PsiElement sc return place == aClass; } - public static boolean processSuperTypes(PsiType type, boolean includeSelf, Processor processor) { - if (includeSelf && !processor.process(type)) { + public static boolean processSuperTypes(PsiType type, boolean includeSelf, Predicate processor) { + if (includeSelf && !processor.test(type)) { return false; } return processSuperTypes(type, processor, new HashSet<>()); } - private static boolean processSuperTypes(PsiType type, Processor processor, Set visited) { + private static boolean processSuperTypes(PsiType type, Predicate processor, Set visited) { if (!visited.add(type)) { return true; } for (PsiType superType : type.getSuperTypes()) { - if (!processor.process(superType)) { + if (!processor.test(superType)) { return false; } processSuperTypes(superType, processor, visited); } return true; } - } \ No newline at end of file diff --git a/plugin/src/main/java/com/intellij/java/impl/codeInsight/daemon/impl/quickfix/IterateOverIterableIntention.java b/plugin/src/main/java/com/intellij/java/impl/codeInsight/daemon/impl/quickfix/IterateOverIterableIntention.java index 882d28297..a16aaf523 100644 --- a/plugin/src/main/java/com/intellij/java/impl/codeInsight/daemon/impl/quickfix/IterateOverIterableIntention.java +++ b/plugin/src/main/java/com/intellij/java/impl/codeInsight/daemon/impl/quickfix/IterateOverIterableIntention.java @@ -17,16 +17,17 @@ import com.intellij.java.language.psi.*; import com.intellij.java.language.psi.util.InheritanceUtil; +import consulo.annotation.access.RequiredReadAction; import consulo.annotation.component.ExtensionImpl; import consulo.codeEditor.Editor; import consulo.codeEditor.SelectionModel; import consulo.document.util.TextRange; -import consulo.ide.impl.idea.codeInsight.template.impl.InvokeTemplateAction; import consulo.language.editor.intention.IntentionAction; import consulo.language.editor.intention.IntentionMetaData; import consulo.language.editor.template.Template; import consulo.language.editor.template.TemplateManager; import consulo.language.editor.template.TemplateSettings; +import consulo.language.editor.template.context.TemplateActionContext; import consulo.language.psi.PsiComment; import consulo.language.psi.PsiElement; import consulo.language.psi.PsiFile; @@ -34,115 +35,129 @@ import consulo.language.psi.util.PsiTreeUtil; import consulo.language.util.IncorrectOperationException; import consulo.localize.LocalizeValue; -import consulo.logging.Logger; import consulo.project.Project; +import consulo.ui.annotation.RequiredUIAccess; import org.jspecify.annotations.Nullable; -import java.util.HashSet; +import java.util.Objects; /** - * User: anna + * @author anna */ @ExtensionImpl @IntentionMetaData(ignoreId = "java.IterateOverIterableIntention", categories = {"Java", "Control Flow"}, fileExtensions = "java") public class IterateOverIterableIntention implements IntentionAction { - private static final Logger LOG = Logger.getInstance(IterateOverIterableIntention.class); + @Override + @RequiredReadAction + public boolean isAvailable(Project project, Editor editor, PsiFile file) { + Template template = getTemplate(); + if (template != null) { + int offset = editor.getCaretModel().getOffset(); + int startOffset = offset; + if (editor.getSelectionModel().hasSelection()) { + int selStart = editor.getSelectionModel().getSelectionStart(); + int selEnd = editor.getSelectionModel().getSelectionEnd(); + startOffset = (offset == selStart) ? selEnd : selStart; + } + PsiElement element = file.findElementAt(startOffset); + while (element instanceof PsiWhiteSpace whiteSpace) { + element = whiteSpace.getPrevSibling(); + } + PsiStatement psiStatement = PsiTreeUtil.getParentOfType(element, PsiStatement.class, false); + if (psiStatement != null) { + startOffset = psiStatement.getTextRange().getStartOffset(); + } + TemplateManager templateManager = TemplateManager.getInstance(project); + if (!template.isDeactivated() && + (templateManager.isApplicable(template, TemplateActionContext.expanding(file, offset)) || + templateManager.isApplicable(template, TemplateActionContext.expanding(file, startOffset)))) { + return getIterableExpression(editor, file) != null; + } + } + return false; + } - @Override - public boolean isAvailable(Project project, Editor editor, PsiFile file) { - Template template = getTemplate(); - if (template != null) { - int offset = editor.getCaretModel().getOffset(); - int startOffset = offset; - if (editor.getSelectionModel().hasSelection()) { - int selStart = editor.getSelectionModel().getSelectionStart(); - int selEnd = editor.getSelectionModel().getSelectionEnd(); - startOffset = (offset == selStart) ? selEnd : selStart; - } - PsiElement element = file.findElementAt(startOffset); - while (element instanceof PsiWhiteSpace) { - element = element.getPrevSibling(); - } - PsiStatement psiStatement = PsiTreeUtil.getParentOfType(element, PsiStatement.class, false); - if (psiStatement != null) { - startOffset = psiStatement.getTextRange().getStartOffset(); - } - if (!template.isDeactivated() && - (TemplateManager.getInstance(project).isApplicable(file, offset, template) || - (TemplateManager.getInstance(project).isApplicable(file, startOffset, template)))) { - return getIterableExpression(editor, file) != null; - } + private static @Nullable Template getTemplate() { + return TemplateSettings.getInstance().getTemplate("I", "surround"); } - return false; - } - @Nullable - private static Template getTemplate() { - return TemplateSettings.getInstance().getTemplate("I", "surround"); - } + @Override + public LocalizeValue getText() { + return LocalizeValue.localizeTODO("Iterate"); + } + @RequiredReadAction + private static @Nullable PsiExpression getIterableExpression(Editor editor, PsiFile file) { + SelectionModel selectionModel = editor.getSelectionModel(); + if (selectionModel.hasSelection()) { + PsiElement elementAtStart = file.findElementAt(selectionModel.getSelectionStart()); + PsiElement elementAtEnd = file.findElementAt(selectionModel.getSelectionEnd() - 1); + if (elementAtStart == null || elementAtStart instanceof PsiWhiteSpace || elementAtStart instanceof PsiComment) { + elementAtStart = PsiTreeUtil.skipSiblingsForward(elementAtStart, PsiWhiteSpace.class, PsiComment.class); + if (elementAtStart == null) { + return null; + } + } + if (elementAtEnd == null || elementAtEnd instanceof PsiWhiteSpace || elementAtEnd instanceof PsiComment) { + elementAtEnd = PsiTreeUtil.skipSiblingsBackward(elementAtEnd, PsiWhiteSpace.class, PsiComment.class); + if (elementAtEnd == null) { + return null; + } + } + PsiElement parent = PsiTreeUtil.findCommonParent(elementAtStart, elementAtEnd); + if (parent instanceof PsiExpression parentExpr) { + return isIterableType(parentExpr.getType()) ? parentExpr : null; + } + return null; + } - @Override - public LocalizeValue getText() { - return LocalizeValue.localizeTODO("Iterate"); - } - - @Nullable - private static PsiExpression getIterableExpression(Editor editor, PsiFile file) { - SelectionModel selectionModel = editor.getSelectionModel(); - if (selectionModel.hasSelection()) { - PsiElement elementAtStart = file.findElementAt(selectionModel.getSelectionStart()); - PsiElement elementAtEnd = file.findElementAt(selectionModel.getSelectionEnd() - 1); - if (elementAtStart == null || elementAtStart instanceof PsiWhiteSpace || elementAtStart instanceof PsiComment) { - elementAtStart = PsiTreeUtil.skipSiblingsForward(elementAtStart, PsiWhiteSpace.class, PsiComment.class); - if (elementAtStart == null) return null; - } - if (elementAtEnd == null || elementAtEnd instanceof PsiWhiteSpace || elementAtEnd instanceof PsiComment) { - elementAtEnd = PsiTreeUtil.skipSiblingsBackward(elementAtEnd, PsiWhiteSpace.class, PsiComment.class); - if (elementAtEnd == null) return null; - } - PsiElement parent = PsiTreeUtil.findCommonParent(elementAtStart, elementAtEnd); - if (parent instanceof PsiExpression) { - PsiType type = ((PsiExpression)parent).getType(); - return type instanceof PsiArrayType || InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_LANG_ITERABLE) - ? (PsiExpression)parent - : null; - } - return null; - } + PsiElement element = file.findElementAt(editor.getCaretModel().getOffset()); + while (element instanceof PsiWhiteSpace whiteSpace) { + element = whiteSpace.getPrevSibling(); + } + if (element instanceof PsiExpressionStatement exprStmt) { + element = exprStmt.getExpression().getLastChild(); + } - PsiElement element = file.findElementAt(editor.getCaretModel().getOffset()); - while (element instanceof PsiWhiteSpace) { - element = element.getPrevSibling(); - } - if (element instanceof PsiExpressionStatement) { - element = ((PsiExpressionStatement)element).getExpression().getLastChild(); + while ((element = PsiTreeUtil.getParentOfType(element, PsiExpression.class, true)) != null) { + PsiElement parent = element.getParent(); + if (parent instanceof PsiMethodCallExpression) { + continue; + } + if (!(parent instanceof PsiExpressionStatement)) { + return null; + } + PsiExpression expr = (PsiExpression) element; + if (isIterableType(expr.getType())) { + return expr; + } + } + return null; } - while ((element = PsiTreeUtil.getParentOfType(element, PsiExpression.class, true)) != null) { - PsiElement parent = element.getParent(); - if (parent instanceof PsiMethodCallExpression) continue; - if (!(parent instanceof PsiExpressionStatement)) return null; - PsiType type = ((PsiExpression)element).getType(); - if (type instanceof PsiArrayType || InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_LANG_ITERABLE)) return (PsiExpression)element; + + private static boolean isIterableType(@Nullable PsiType type) { + return type instanceof PsiArrayType || InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_LANG_ITERABLE); } - return null; - } - @Override - public void invoke(Project project, Editor editor, PsiFile file) throws IncorrectOperationException { - Template template = getTemplate(); - SelectionModel selectionModel = editor.getSelectionModel(); - if (!selectionModel.hasSelection()) { - PsiExpression iterableExpression = getIterableExpression(editor, file); - LOG.assertTrue(iterableExpression != null); - TextRange textRange = iterableExpression.getTextRange(); - selectionModel.setSelection(textRange.getStartOffset(), textRange.getEndOffset()); + @Override + @RequiredUIAccess + public void invoke(Project project, Editor editor, PsiFile file) throws IncorrectOperationException { + Template template = getTemplate(); + if (template == null) { + return; + } + + SelectionModel selectionModel = editor.getSelectionModel(); + if (!selectionModel.hasSelection()) { + PsiExpression iterableExpression = Objects.requireNonNull(getIterableExpression(editor, file)); + TextRange textRange = iterableExpression.getTextRange(); + selectionModel.setSelection(textRange.getStartOffset(), textRange.getEndOffset()); + } + TemplateManager.getInstance(project).startTemplateForAllCarets(editor, template); } - new InvokeTemplateAction(template, editor, project, new HashSet()).perform(); - } - @Override - public boolean startInWriteAction() { - return false; - } + @Override + public boolean startInWriteAction() { + return false; + } }