From feb932c66e6950b3dfc905f16dbc98cd92620db6 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Thu, 15 Jun 2023 17:49:21 +0200 Subject: [PATCH] [java-intentions] More ModCommand actions GitOrigin-RevId: a336391bae84d99c45b3529cf404905dcf0635dd --- .../intention/QuickFixFactory.java | 18 +-- .../analysis/AdaptExpressionTypeFixUtil.java | 3 +- .../daemon/impl/quickfix/AddTypeCastFix.java | 45 ++----- .../quickfix/AddDefaultConstructorFix.java | 22 +++- ...ieldInitializerToConstructorThrowsFix.java | 52 +++----- .../daemon/impl/quickfix/AddMethodFix.java | 80 ++++------- .../ConvertToStringLiteralAction.java | 54 ++++---- ...reateConstructorParameterFromFieldFix.java | 9 +- .../daemon/impl/quickfix/DeleteReturnFix.java | 45 ++----- .../InitializeFinalFieldInConstructorFix.java | 13 +- .../impl/quickfix/MakeClassInterfaceFix.java | 60 +++------ .../NavigateToAlreadyDeclaredVariableFix.java | 68 ++-------- .../NavigateToDuplicateElementFix.java | 63 +++------ .../quickfix/ReuseVariableDeclarationFix.java | 65 +++------ .../generation/GenerateMembersUtil.java | 124 ++++++++++++------ .../MoveInitializerToConstructorAction.java | 15 +-- .../ReplaceAssignmentWithComparisonFix.java | 30 ++--- .../impl/config/QuickFixFactoryImpl.java | 45 ++----- .../quickFix/addTypeCast/after3.java | 2 +- .../quickFix/addTypeCast/afterTernary2.java | 2 +- ...igateToAlreadyDeclaredVariableFixTest.java | 5 +- .../NavigateToDuplicateElementFixTest.java | 27 ++-- ...uickFixAndIntentionActionOnPsiElement.java | 80 ++++++++--- .../intellij/modcommand/ModCommandAction.java | 4 + .../modcommand/ModCommandServiceImpl.java | 9 +- ...ArgumentToVariableArgMethodInspection.java | 4 +- ...ArgumentToVariableArgMethodInspection.java | 2 +- ...onversionCompoundAssignmentInspection.java | 2 +- .../ClassInitializerInspection.java | 6 +- ...thoutPublicNoArgConstructorInspection.java | 4 +- 30 files changed, 385 insertions(+), 573 deletions(-) rename plugins/InspectionGadgets/{InspectionGadgetsAnalysis => }/src/com/siyeh/ig/classlayout/ClassInitializerInspection.java (94%) diff --git a/java/java-analysis-api/src/com/intellij/codeInsight/intention/QuickFixFactory.java b/java/java-analysis-api/src/com/intellij/codeInsight/intention/QuickFixFactory.java index d2ac4dfdd042..e7530aa1ee8a 100644 --- a/java/java-analysis-api/src/com/intellij/codeInsight/intention/QuickFixFactory.java +++ b/java/java-analysis-api/src/com/intellij/codeInsight/intention/QuickFixFactory.java @@ -63,14 +63,6 @@ public abstract class QuickFixFactory { @NotNull PsiType toReturn, boolean fromDefaultValue); - @NotNull - public abstract LocalQuickFixAndIntentionActionOnPsiElement createAddMethodFix(@NotNull PsiMethod method, @NotNull PsiClass toClass); - - @NotNull - public abstract LocalQuickFixAndIntentionActionOnPsiElement createAddMethodFix(@NotNull String methodText, - @NotNull PsiClass toClass, - String @NotNull ... exceptions); - /** * @param psiElement psiClass or enum constant without class initializer */ @@ -78,7 +70,7 @@ public abstract class QuickFixFactory { public abstract LocalQuickFixAndIntentionActionOnPsiElement createImplementMethodsFix(@NotNull PsiElement psiElement); @NotNull - public abstract LocalQuickFixAndIntentionActionOnPsiElement createAssignmentToComparisonFix(@NotNull PsiAssignmentExpression expr); + public abstract IntentionAction createAssignmentToComparisonFix(@NotNull PsiAssignmentExpression expr); @NotNull public abstract LocalQuickFixAndIntentionActionOnPsiElement createImplementMethodsFix(@NotNull PsiClass psiElement); @@ -92,20 +84,12 @@ public abstract class QuickFixFactory { @NotNull public abstract LocalQuickFixAndIntentionActionOnPsiElement createAddDefaultConstructorFix(@NotNull PsiClass aClass); - @Nullable - public abstract LocalQuickFixAndIntentionActionOnPsiElement createAddConstructorFix(@NotNull PsiClass aClass, - @PsiModifier.ModifierConstant @NotNull String modifier); - @NotNull public abstract LocalQuickFixAndIntentionActionOnPsiElement createMethodParameterTypeFix(@NotNull PsiMethod method, int index, @NotNull PsiType newType, boolean fixWholeHierarchy); - @NotNull - public abstract LocalQuickFixAndIntentionActionOnPsiElement createMakeClassInterfaceFix(@NotNull PsiClass aClass, - final boolean makeInterface); - @NotNull public abstract LocalQuickFixAndIntentionActionOnPsiElement createExtendsListFix(@NotNull PsiClass aClass, @NotNull PsiClassType typeToExtendFrom, diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/AdaptExpressionTypeFixUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/AdaptExpressionTypeFixUtil.java index 1ba5ddf8cca4..1ea1bd4774c9 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/AdaptExpressionTypeFixUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/AdaptExpressionTypeFixUtil.java @@ -6,6 +6,7 @@ import com.intellij.codeInsight.daemon.impl.HighlightInfo; import com.intellij.codeInsight.daemon.impl.quickfix.*; import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInsight.intention.QuickFixFactory; +import com.intellij.modcommand.ModCommandAction; import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; import com.intellij.psi.impl.source.resolve.graphInference.PsiPolyExpressionUtil; @@ -236,7 +237,7 @@ final class AdaptExpressionTypeFixUtil { info.registerFix(action1, null, null, null, null); PsiType castToType = suggestCastTo(expectedType, actualType); if (castToType != null) { - IntentionAction action = new AddTypeCastFix(castToType, expression, role); + ModCommandAction action = new AddTypeCastFix(castToType, expression, role); info.registerFix(action, null, null, null, null); } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddTypeCastFix.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddTypeCastFix.java index 045909b00185..2dfa0e71c882 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddTypeCastFix.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddTypeCastFix.java @@ -1,16 +1,14 @@ -// 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.daemon.impl.quickfix; import com.intellij.codeInsight.daemon.QuickFixActionRegistrar; import com.intellij.codeInsight.daemon.QuickFixBundle; -import com.intellij.codeInsight.daemon.impl.actions.IntentionActionWithFixAllOption; import com.intellij.codeInsight.guess.GuessManager; -import com.intellij.codeInsight.intention.HighPriorityAction; -import com.intellij.codeInsight.intention.impl.BaseIntentionAction; -import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement; +import com.intellij.codeInsight.intention.PriorityAction; +import com.intellij.codeInspection.EditorUpdater; +import com.intellij.codeInspection.PsiUpdateModCommandAction; import com.intellij.codeInspection.util.IntentionName; -import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; @@ -25,9 +23,7 @@ import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.Objects; -public class AddTypeCastFix extends LocalQuickFixAndIntentionActionOnPsiElement - implements HighPriorityAction, IntentionActionWithFixAllOption { - @SafeFieldForPreview +public class AddTypeCastFix extends PsiUpdateModCommandAction { private final PsiType myType; private final @IntentionName String myName; @@ -46,12 +42,6 @@ public class AddTypeCastFix extends LocalQuickFixAndIntentionActionOnPsiElement type.isValid() ? type.getCanonicalText() : "", role); } - @Override - @NotNull - public String getText() { - return myName; - } - @Override @NotNull public String getFamilyName() { @@ -59,24 +49,17 @@ public class AddTypeCastFix extends LocalQuickFixAndIntentionActionOnPsiElement } @Override - public boolean isAvailable(@NotNull Project project, - @NotNull PsiFile file, - @NotNull PsiElement startElement, - @NotNull PsiElement endElement) { - return myType.isValid() && - !PsiTypes.voidType().equals(myType) && - PsiTypesUtil.isDenotableType(myType, startElement) && - PsiTypesUtil.allTypeParametersResolved(startElement, myType) && - BaseIntentionAction.canModify(startElement); + protected @Nullable Presentation getPresentation(@NotNull ActionContext context, @NotNull PsiExpression expr) { + if (!myType.isValid() || + PsiTypes.voidType().equals(myType) || + !PsiTypesUtil.isDenotableType(myType, expr) || + !PsiTypesUtil.allTypeParametersResolved(expr, myType)) return null; + return Presentation.of(myName).withPriority(PriorityAction.Priority.HIGH).withFixAllOption(this); } @Override - public void invoke(@NotNull Project project, - @NotNull PsiFile file, - @Nullable Editor editor, - @NotNull PsiElement startElement, - @NotNull PsiElement endElement) { - addTypeCast(project, (PsiExpression)startElement, myType); + protected void invoke(@NotNull ActionContext context, @NotNull PsiExpression expression, @NotNull EditorUpdater updater) { + addTypeCast(context.project(), expression, myType); } public static void addTypeCast(Project project, PsiExpression originalExpression, PsiType type) { @@ -169,7 +152,7 @@ public class AddTypeCastFix extends LocalQuickFixAndIntentionActionOnPsiElement else if (psiClass.findFieldByName(referenceName, true) == null) { continue; } - registrar.register(fixRange, new AddTypeCastFix(conjunct, qualifier, QuickFixBundle.message("fix.expression.role.qualifier")), null); + registrar.register(fixRange, new AddTypeCastFix(conjunct, qualifier, QuickFixBundle.message("fix.expression.role.qualifier")).asIntention(), null); } } } diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddDefaultConstructorFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddDefaultConstructorFix.java index f413a078f26a..0650b35a3697 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddDefaultConstructorFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddDefaultConstructorFix.java @@ -1,21 +1,31 @@ -// 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.daemon.impl.quickfix; import com.intellij.codeInsight.daemon.QuickFixBundle; +import com.intellij.codeInspection.util.IntentionName; import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiModifier; import com.intellij.psi.util.PsiUtil; import com.intellij.util.VisibilityUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public class AddDefaultConstructorFix extends AddMethodFix { + @IntentionName private final String myText; + public AddDefaultConstructorFix(PsiClass aClass) { this(aClass, PsiUtil.getSuitableModifierForMember(aClass, true)); } public AddDefaultConstructorFix(PsiClass aClass, @NotNull @PsiModifier.ModifierConstant final String modifier) { super(generateConstructor(aClass.getName(), modifier), aClass); - setText(QuickFixBundle.message("add.default.constructor.text", VisibilityUtil.toPresentableText(modifier), aClass.getName())); + myText = QuickFixBundle.message("add.default.constructor.text", VisibilityUtil.toPresentableText(modifier), aClass.getName()); + } + + @Override + protected @Nullable Presentation getPresentation(@NotNull ActionContext context, @NotNull PsiClass myClass) { + return super.getPresentation(context, myClass) == null ? null : Presentation.of(myText); } private static String generateConstructor(final String className, @PsiModifier.ModifierConstant final String modifier) { @@ -30,4 +40,12 @@ public class AddDefaultConstructorFix extends AddMethodFix { public String getFamilyName() { return QuickFixBundle.message("add.default.constructor.family"); } + + /** + * @param psiClass PsiClass to add default constructor to + * @return added constructor + */ + public static @NotNull PsiMethod addDefaultConstructor(@NotNull PsiClass psiClass) { + return new AddDefaultConstructorFix(psiClass).createMethod(psiClass); + } } diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddExceptionFromFieldInitializerToConstructorThrowsFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddExceptionFromFieldInitializerToConstructorThrowsFix.java index fa938e0f8b1e..1a36f0020a75 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddExceptionFromFieldInitializerToConstructorThrowsFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddExceptionFromFieldInitializerToConstructorThrowsFix.java @@ -3,16 +3,14 @@ package com.intellij.codeInsight.daemon.impl.quickfix; import com.intellij.codeInsight.ExceptionUtil; import com.intellij.codeInsight.daemon.QuickFixBundle; -import com.intellij.codeInsight.intention.FileModifier; -import com.intellij.codeInsight.intention.impl.BaseIntentionAction; +import com.intellij.codeInspection.EditorUpdater; +import com.intellij.codeInspection.PsiUpdateModCommandAction; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.HashSet; import java.util.List; @@ -21,60 +19,50 @@ import java.util.Set; /** * @author Dmitry Batkovich */ -public final class AddExceptionFromFieldInitializerToConstructorThrowsFix extends BaseIntentionAction { +public final class AddExceptionFromFieldInitializerToConstructorThrowsFix extends PsiUpdateModCommandAction { private final static Logger LOG = Logger.getInstance(AddExceptionFromFieldInitializerToConstructorThrowsFix.class); - private final PsiElement myWrongElement; - public AddExceptionFromFieldInitializerToConstructorThrowsFix(PsiElement element) { - myWrongElement = element; + super(element); } @Override - public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { - if (!myWrongElement.isValid()) return false; + protected @Nullable Presentation getPresentation(@NotNull ActionContext context, @NotNull PsiElement element) { final NavigatablePsiElement maybeField = - PsiTreeUtil.getParentOfType(myWrongElement, PsiMethod.class, PsiFunctionalExpression.class, PsiField.class); - if (!(maybeField instanceof PsiField field)) return false; - if (field.hasModifierProperty(PsiModifier.STATIC)) return false; + PsiTreeUtil.getParentOfType(element, PsiMethod.class, PsiFunctionalExpression.class, PsiField.class); + if (!(maybeField instanceof PsiField field)) return null; + if (field.hasModifierProperty(PsiModifier.STATIC)) return null; final PsiClass containingClass = field.getContainingClass(); if (containingClass == null || containingClass instanceof PsiAnonymousClass || containingClass.isInterface()) { - return false; + return null; } final List exceptions = ExceptionUtil.getUnhandledExceptions(field); if (exceptions.isEmpty()) { - return false; + return null; } final PsiMethod[] existedConstructors = containingClass.getConstructors(); - setText(QuickFixBundle.message("add.exception.from.field.initializer.to.constructor.throws.text", existedConstructors.length)); - return true; + return Presentation.of(QuickFixBundle.message("add.exception.from.field.initializer.to.constructor.throws.text", existedConstructors.length)); } @Override - public FileModifier getFileModifierForPreview(@NotNull PsiFile target) { - return new AddExceptionFromFieldInitializerToConstructorThrowsFix(PsiTreeUtil.findSameElementInCopy(myWrongElement, target)); - } - - @Override - public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file) throws IncorrectOperationException { - final NavigatablePsiElement field = - PsiTreeUtil.getParentOfType(myWrongElement, PsiMethod.class, PsiFunctionalExpression.class, PsiField.class); - if (field instanceof PsiField) { - final PsiClass aClass = ((PsiField)field).getContainingClass(); + protected void invoke(@NotNull ActionContext context, @NotNull PsiElement element, @NotNull EditorUpdater updater) { + final NavigatablePsiElement e = + PsiTreeUtil.getParentOfType(element, PsiMethod.class, PsiFunctionalExpression.class, PsiField.class); + if (e instanceof PsiField field) { + final PsiClass aClass = field.getContainingClass(); if (aClass != null) { PsiMethod[] constructors = aClass.getConstructors(); if (constructors.length == 0) { - final AddDefaultConstructorFix defaultConstructorFix = new AddDefaultConstructorFix(aClass); - defaultConstructorFix.invoke(project, null, file); + AddDefaultConstructorFix.addDefaultConstructor(aClass); constructors = aClass.getConstructors(); LOG.assertTrue(constructors.length != 0); } - Set unhandledExceptions = new HashSet<>(ExceptionUtil.getUnhandledExceptions(field)); + Set unhandledExceptions = new HashSet<>(ExceptionUtil.getUnhandledExceptions(e)); for (PsiMethod constructor : constructors) { - AddExceptionToThrowsFix.processMethod(project, constructor, unhandledExceptions); + AddExceptionToThrowsFix.processMethod(context.project(), constructor, unhandledExceptions); } } } diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddMethodFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddMethodFix.java index 2b888814f22f..5a25ea2d05d5 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddMethodFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddMethodFix.java @@ -1,12 +1,10 @@ -// 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.daemon.impl.quickfix; import com.intellij.codeInsight.daemon.QuickFixBundle; import com.intellij.codeInsight.generation.GenerateMembersUtil; -import com.intellij.codeInsight.intention.impl.BaseIntentionAction; -import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement; -import com.intellij.codeInspection.util.IntentionName; -import com.intellij.openapi.editor.Editor; +import com.intellij.codeInspection.EditorUpdater; +import com.intellij.codeInspection.PsiUpdateModCommandAction; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleManager; @@ -22,17 +20,13 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; -public class AddMethodFix extends LocalQuickFixAndIntentionActionOnPsiElement { - @SafeFieldForPreview +public class AddMethodFix extends PsiUpdateModCommandAction { private final SmartPsiElementPointer myMethodPrototype; - @SafeFieldForPreview private final List myExceptions = new ArrayList<>(); - private @IntentionName String myText; public AddMethodFix(@NotNull PsiMethod methodPrototype, @NotNull PsiClass implClass) { super(implClass); myMethodPrototype = SmartPointerManager.createPointer(methodPrototype); - setText(QuickFixBundle.message("add.method.text", methodPrototype.getName(), implClass.getName())); } public AddMethodFix(@NonNls @NotNull String methodText, @NotNull PsiClass implClass, String @NotNull ... exceptions) { @@ -40,12 +34,6 @@ public class AddMethodFix extends LocalQuickFixAndIntentionActionOnPsiElement { ContainerUtil.addAll(myExceptions, exceptions); } - @Nullable - @Override - public PsiElement getElementToMakeWritable(@NotNull PsiFile currentFile) { - return myStartElement.getContainingFile(); - } - @NotNull private static PsiMethod createMethod(final String methodText, final PsiClass implClass) { return JavaPsiFacade.getElementFactory(implClass.getProject()).createMethodFromText(methodText, implClass); @@ -60,16 +48,6 @@ public class AddMethodFix extends LocalQuickFixAndIntentionActionOnPsiElement { return result; } - @NotNull - @Override - public String getText() { - return myText; - } - - protected void setText(@NotNull @IntentionName String text) { - myText = text; - } - @Override @NotNull public String getFamilyName() { @@ -77,48 +55,34 @@ public class AddMethodFix extends LocalQuickFixAndIntentionActionOnPsiElement { } @Override - public boolean isAvailable(@NotNull Project project, - @NotNull PsiFile file, - @NotNull PsiElement startElement, - @NotNull PsiElement endElement) { - final PsiClass myClass = (PsiClass)startElement; - + protected @Nullable Presentation getPresentation(@NotNull ActionContext context, @NotNull PsiClass myClass) { PsiMethod methodPrototype = myMethodPrototype.getElement(); - return methodPrototype != null && - methodPrototype.isValid() && - BaseIntentionAction.canModify(myClass) && - myText != null && - MethodSignatureUtil.findMethodBySignature(myClass, methodPrototype, false) == null - ; + if (methodPrototype == null || !methodPrototype.isValid() || + MethodSignatureUtil.findMethodBySignature(myClass, methodPrototype, false) != null) { + return null; + } + return Presentation.of(QuickFixBundle.message("add.method.text", methodPrototype.getName(), myClass.getName())); } @Override - public void invoke(@NotNull Project project, - @NotNull PsiFile file, - @Nullable Editor editor, - @NotNull PsiElement startElement, - @NotNull PsiElement endElement) { + protected void invoke(@NotNull ActionContext context, @NotNull PsiClass psiClass, @NotNull EditorUpdater updater) { + PsiMethod method = createMethod(psiClass); + if (method.getContainingFile().getOriginalFile() == context.file()) { + GenerateMembersUtil.positionCaret(updater, method, true); + } + } + + public PsiMethod createMethod(@NotNull PsiClass psiClass) { PsiMethod methodPrototype = myMethodPrototype.getElement(); - if (methodPrototype == null) return; - - PsiClass myClass = (PsiClass)startElement; + if (methodPrototype == null) return null; PsiCodeBlock body; - if (myClass.isInterface() && (body = methodPrototype.getBody()) != null) body.delete(); + if (psiClass.isInterface() && (body = methodPrototype.getBody()) != null) body.delete(); for (String exception : myExceptions) { PsiUtil.addException(methodPrototype, exception); } - PsiMethod method = (PsiMethod)myClass.add(methodPrototype); - method = (PsiMethod)method.replace(reformat(project, method)); - postAddAction(file, editor, method); - } - - protected void postAddAction(@NotNull PsiFile file, - @Nullable Editor editor, - PsiMethod newMethod) { - if (editor != null && newMethod.getContainingFile() == file) { - GenerateMembersUtil.positionCaret(editor, newMethod, true); - } + PsiMethod method = (PsiMethod)psiClass.add(methodPrototype); + return (PsiMethod)method.replace(reformat(psiClass.getProject(), method)); } } diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ConvertToStringLiteralAction.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ConvertToStringLiteralAction.java index 5b1d5640410a..e13d47d5356e 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ConvertToStringLiteralAction.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ConvertToStringLiteralAction.java @@ -1,21 +1,25 @@ -// Copyright 2000-2021 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. +// 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.daemon.impl.quickfix; import com.intellij.codeInsight.daemon.QuickFixBundle; -import com.intellij.codeInsight.daemon.impl.actions.IntentionActionWithFixAllOption; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.project.Project; +import com.intellij.codeInspection.EditorUpdater; +import com.intellij.codeInspection.PsiUpdateModCommandAction; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.util.PsiUtil; -import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class ConvertToStringLiteralAction extends PsiUpdateModCommandAction { + + public ConvertToStringLiteralAction() { + super(PsiJavaToken.class); + } -public class ConvertToStringLiteralAction implements IntentionActionWithFixAllOption { - @NotNull @Override - public String getText() { - return QuickFixBundle.message("convert.to.string.text"); + protected @Nullable Presentation getPresentation(@NotNull ActionContext context, @NotNull PsiJavaToken element) { + if (element.getTokenType() != JavaTokenType.CHARACTER_LITERAL) return null; + return Presentation.of(QuickFixBundle.message("convert.to.string.text")).withFixAllOption(this); } @NotNull @@ -25,30 +29,16 @@ public class ConvertToStringLiteralAction implements IntentionActionWithFixAllOp } @Override - public boolean isAvailable(@NotNull final Project project, final Editor editor, final PsiFile file) { - final PsiElement element = file.findElementAt(editor.getCaretModel().getOffset()); - return PsiUtil.isJavaToken(element, JavaTokenType.CHARACTER_LITERAL); - } - - @Override - public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file) throws IncorrectOperationException { - final PsiElement element = file.findElementAt(editor.getCaretModel().getOffset()); - if (element != null && PsiUtil.isJavaToken(element, JavaTokenType.CHARACTER_LITERAL)) { - final String text = StringUtil.unescapeStringCharacters(element.getText()); - final int length = text.length(); - if (length > 1 && text.charAt(0) == '\'' && text.charAt(length - 1) == '\'') { - final String value = StringUtil.escapeStringCharacters(text.substring(1, length - 1)); - final PsiExpression expression = JavaPsiFacade.getElementFactory(project).createExpressionFromText('"' + value + '"', null); - final PsiElement literal = expression.getFirstChild(); - if (literal != null && PsiUtil.isJavaToken(literal, JavaTokenType.STRING_LITERAL)) { - element.replace(literal); - } + protected void invoke(@NotNull ActionContext context, @NotNull PsiJavaToken element, @NotNull EditorUpdater updater) { + final String text = StringUtil.unescapeStringCharacters(element.getText()); + final int length = text.length(); + if (length > 1 && text.charAt(0) == '\'' && text.charAt(length - 1) == '\'') { + final String value = StringUtil.escapeStringCharacters(text.substring(1, length - 1)); + final PsiExpression expression = JavaPsiFacade.getElementFactory(context.project()).createExpressionFromText('"' + value + '"', null); + final PsiElement literal = expression.getFirstChild(); + if (literal != null && PsiUtil.isJavaToken(literal, JavaTokenType.STRING_LITERAL)) { + element.replace(literal); } } } - - @Override - public boolean startInWriteAction() { - return true; - } } diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateConstructorParameterFromFieldFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateConstructorParameterFromFieldFix.java index 7b156abecf4a..cd17373ff9f0 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateConstructorParameterFromFieldFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateConstructorParameterFromFieldFix.java @@ -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.daemon.impl.quickfix; import com.intellij.codeInsight.AnnotationTargetUtil; @@ -21,6 +21,7 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.UserDataHolderEx; import com.intellij.openapi.util.text.StringUtil; @@ -99,8 +100,7 @@ public class CreateConstructorParameterFromFieldFix implements IntentionAction { PsiField copyMyField = PsiTreeUtil.findSameElementInCopy(myFieldElement, copyFile); PsiMethod[] constructors = getNonSyntheticConstructors(copyMyClass); if (constructors.length == 0) { - final AddDefaultConstructorFix defaultConstructorFix = new AddDefaultConstructorFix(copyMyClass); - defaultConstructorFix.invoke(project, editor, copyFile); + AddDefaultConstructorFix.addDefaultConstructor(copyMyClass); constructors = copyMyClass.getConstructors(); } List filteredConstructors = getFilteredConstructors(constructors, copyMyField); @@ -145,8 +145,7 @@ public class CreateConstructorParameterFromFieldFix implements IntentionAction { PsiMethod[] constructors = getNonSyntheticConstructors(myClass); if (constructors.length == 0) { - final AddDefaultConstructorFix defaultConstructorFix = new AddDefaultConstructorFix(myClass); - ApplicationManager.getApplication().runWriteAction(() -> defaultConstructorFix.invoke(project, editor, file)); + ApplicationManager.getApplication().runWriteAction((Computable)() -> AddDefaultConstructorFix.addDefaultConstructor(myClass)); constructors = getNonSyntheticConstructors(myClass); } final List filtered = getFilteredConstructors(constructors, getField()); diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/DeleteReturnFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/DeleteReturnFix.java index 2c4be36a0867..b5be55ae9089 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/DeleteReturnFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/DeleteReturnFix.java @@ -1,50 +1,34 @@ -// 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. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.codeInsight.daemon.impl.quickfix; import com.intellij.codeInsight.BlockUtils; import com.intellij.codeInsight.daemon.QuickFixBundle; -import com.intellij.codeInsight.intention.FileModifier; -import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.project.Project; +import com.intellij.codeInspection.EditorUpdater; +import com.intellij.codeInspection.PsiUpdateModCommandAction; import com.intellij.psi.*; -import com.intellij.psi.util.PsiTreeUtil; import com.siyeh.ig.psiutils.*; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.Objects; -public final class DeleteReturnFix extends LocalQuickFixAndIntentionActionOnPsiElement { - private final SmartPsiElementPointer myStatementPtr; +public final class DeleteReturnFix extends PsiUpdateModCommandAction { private final boolean myIsLastStatement; private final boolean myHasSideEffects; public DeleteReturnFix(@NotNull PsiMethod method, @NotNull PsiReturnStatement returnStatement) { super(returnStatement); PsiCodeBlock codeBlock = Objects.requireNonNull(method.getBody()); - SmartPointerManager manager = SmartPointerManager.getInstance(returnStatement.getProject()); - myStatementPtr = manager.createSmartPsiElementPointer(returnStatement); myIsLastStatement = ControlFlowUtils.blockCompletesWithStatement(codeBlock, returnStatement); myHasSideEffects = SideEffectChecker.mayHaveSideEffects(Objects.requireNonNull(returnStatement.getReturnValue())); } - private DeleteReturnFix(@NotNull PsiReturnStatement returnStatement, boolean isLastStatement, boolean hasSideEffects) { - super(returnStatement); - SmartPointerManager manager = SmartPointerManager.getInstance(returnStatement.getProject()); - myStatementPtr = manager.createSmartPsiElementPointer(returnStatement); - myIsLastStatement = isLastStatement; - myHasSideEffects = hasSideEffects; - } - - @Nls(capitalization = Nls.Capitalization.Sentence) - @NotNull @Override - public String getText() { + protected Presentation getPresentation(@NotNull ActionContext context, @NotNull PsiReturnStatement element) { String toDelete = myIsLastStatement ? "statement" : "value"; - return QuickFixBundle.message(myHasSideEffects ? "delete.return.fix.side.effects.text" : "delete.return.fix.text", toDelete); + String message = QuickFixBundle.message(myHasSideEffects ? "delete.return.fix.side.effects.text" : "delete.return.fix.text", toDelete); + return Presentation.of(message); } @Nls(capitalization = Nls.Capitalization.Sentence) @@ -55,13 +39,7 @@ public final class DeleteReturnFix extends LocalQuickFixAndIntentionActionOnPsiE } @Override - public void invoke(@NotNull Project project, - @NotNull PsiFile file, - @Nullable Editor editor, - @NotNull PsiElement startElement, - @NotNull PsiElement endElement) { - PsiReturnStatement returnStatement = myStatementPtr.getElement(); - if (returnStatement == null) return; + protected void invoke(@NotNull ActionContext context, @NotNull PsiReturnStatement returnStatement, @NotNull EditorUpdater updater) { PsiExpression returnValue = returnStatement.getReturnValue(); if (returnValue == null) return; CommentTracker ct = new CommentTracker(); @@ -76,11 +54,4 @@ public final class DeleteReturnFix extends LocalQuickFixAndIntentionActionOnPsiE PsiElement toDelete = myIsLastStatement ? returnStatement : returnValue; ct.deleteAndRestoreComments(toDelete); } - - @Override - public @Nullable FileModifier getFileModifierForPreview(@NotNull PsiFile target) { - PsiReturnStatement returnStatement = myStatementPtr.getElement(); - if (returnStatement == null) return null; - return new DeleteReturnFix(PsiTreeUtil.findSameElementInCopy(returnStatement, target), myIsLastStatement, myHasSideEffects); - } } diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/InitializeFinalFieldInConstructorFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/InitializeFinalFieldInConstructorFix.java index 5cbd2ba52cbd..39ba68a9233f 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/InitializeFinalFieldInConstructorFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/InitializeFinalFieldInConstructorFix.java @@ -1,4 +1,4 @@ -// 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.daemon.impl.quickfix; import com.intellij.codeInsight.daemon.QuickFixBundle; @@ -13,6 +13,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Computable; import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.util.PsiTreeUtil; @@ -74,7 +75,8 @@ public class InitializeFinalFieldInConstructorFix extends LocalQuickFixAndIntent final PsiClass myClass = field.getContainingClass(); if (myClass == null) return; if (myClass.getConstructors().length == 0) { - createDefaultConstructor(myClass, project, editor, file); + ApplicationManager.getApplication().runWriteAction( + (Computable)() -> AddDefaultConstructorFix.addDefaultConstructor(myClass)); } PsiMethod[] ctors = CreateConstructorParameterFromFieldFix.filterConstructorsIfFieldAlreadyAssigned(myClass.getConstructors(), field) @@ -91,7 +93,7 @@ public class InitializeFinalFieldInConstructorFix extends LocalQuickFixAndIntent final PsiClass myClass = field.getContainingClass(); if (myClass == null) return IntentionPreviewInfo.EMPTY; if (myClass.getConstructors().length == 0) { - new AddDefaultConstructorFix(myClass).invoke(project, editor, file); + AddDefaultConstructorFix.addDefaultConstructor(myClass); } PsiMethod[] ctors = CreateConstructorParameterFromFieldFix.filterConstructorsIfFieldAlreadyAssigned(myClass.getConstructors(), field) @@ -199,11 +201,6 @@ public class InitializeFinalFieldInConstructorFix extends LocalQuickFixAndIntent return result; } - private static void createDefaultConstructor(PsiClass psiClass, @NotNull final Project project, final Editor editor, final PsiFile file) { - final AddDefaultConstructorFix defaultConstructorFix = new AddDefaultConstructorFix(psiClass); - ApplicationManager.getApplication().runWriteAction(() -> defaultConstructorFix.invoke(project, editor, file)); - } - @Nullable @Override public PsiElement getElementToMakeWritable(@NotNull PsiFile currentFile) { diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/MakeClassInterfaceFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/MakeClassInterfaceFix.java index 29a376130901..e1fd68e987e9 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/MakeClassInterfaceFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/MakeClassInterfaceFix.java @@ -2,21 +2,15 @@ package com.intellij.codeInsight.daemon.impl.quickfix; import com.intellij.codeInsight.daemon.QuickFixBundle; -import com.intellij.codeInsight.intention.impl.BaseIntentionAction; -import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement; -import com.intellij.openapi.command.undo.UndoUtil; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.project.Project; +import com.intellij.codeInspection.EditorUpdater; +import com.intellij.codeInspection.PsiUpdateModCommandAction; import com.intellij.psi.*; import com.intellij.psi.tree.IElementType; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public class MakeClassInterfaceFix extends LocalQuickFixAndIntentionActionOnPsiElement { - private static final Logger LOG = Logger.getInstance(MakeClassInterfaceFix.class); - +public class MakeClassInterfaceFix extends PsiUpdateModCommandAction { private final boolean myMakeInterface; private final String myName; @@ -26,10 +20,10 @@ public class MakeClassInterfaceFix extends LocalQuickFixAndIntentionActionOnPsiE myName = aClass.getName(); } - @NotNull @Override - public String getText() { - return QuickFixBundle.message(myMakeInterface? "make.class.an.interface.text":"make.interface.an.class.text", myName); + protected @Nullable Presentation getPresentation(@NotNull ActionContext context, @NotNull PsiClass element) { + return Presentation.of( + QuickFixBundle.message(myMakeInterface ? "make.class.an.interface.text" : "make.interface.an.class.text", myName)); } @Override @@ -39,41 +33,21 @@ public class MakeClassInterfaceFix extends LocalQuickFixAndIntentionActionOnPsiE } @Override - public boolean isAvailable(@NotNull Project project, - @NotNull PsiFile file, - @NotNull PsiElement startElement, - @NotNull PsiElement endElement) { - final PsiClass myClass = (PsiClass)startElement; - - return BaseIntentionAction.canModify(myClass); - } - - @Override - public void invoke(@NotNull Project project, - @NotNull PsiFile file, - @Nullable Editor editor, - @NotNull PsiElement startElement, - @NotNull PsiElement endElement) { - final PsiClass myClass = (PsiClass)startElement; - try { - final PsiReferenceList extendsList = myMakeInterface? myClass.getExtendsList() : myClass.getImplementsList(); - final PsiReferenceList implementsList = myMakeInterface? myClass.getImplementsList() : myClass.getExtendsList(); - if (extendsList != null) { - for (PsiJavaCodeReferenceElement referenceElement : extendsList.getReferenceElements()) { + protected void invoke(@NotNull ActionContext context, @NotNull PsiClass myClass, @NotNull EditorUpdater updater) { + final PsiReferenceList extendsList = myMakeInterface? myClass.getExtendsList() : myClass.getImplementsList(); + final PsiReferenceList implementsList = myMakeInterface? myClass.getImplementsList() : myClass.getExtendsList(); + if (extendsList != null) { + for (PsiJavaCodeReferenceElement referenceElement : extendsList.getReferenceElements()) { + referenceElement.delete(); + } + if (implementsList != null) { + for (PsiJavaCodeReferenceElement referenceElement : implementsList.getReferenceElements()) { + extendsList.addAfter(referenceElement, null); referenceElement.delete(); } - if (implementsList != null) { - for (PsiJavaCodeReferenceElement referenceElement : implementsList.getReferenceElements()) { - extendsList.addAfter(referenceElement, null); - referenceElement.delete(); - } - } } - convertPsiClass(myClass, myMakeInterface); - UndoUtil.markPsiFileForUndo(file); - } catch (IncorrectOperationException e) { - LOG.error(e); } + convertPsiClass(myClass, myMakeInterface); } private static void convertPsiClass(PsiClass aClass, final boolean makeInterface) throws IncorrectOperationException { diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/NavigateToAlreadyDeclaredVariableFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/NavigateToAlreadyDeclaredVariableFix.java index 87f9773d14ea..841d0e6a4d22 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/NavigateToAlreadyDeclaredVariableFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/NavigateToAlreadyDeclaredVariableFix.java @@ -1,37 +1,18 @@ -/* - * 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.daemon.impl.quickfix; import com.intellij.codeInsight.daemon.QuickFixBundle; -import com.intellij.codeInsight.intention.IntentionAction; -import com.intellij.codeInsight.intention.impl.BaseIntentionAction; -import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.project.Project; -import com.intellij.psi.NavigatablePsiElement; -import com.intellij.psi.PsiFile; +import com.intellij.codeInspection.ModCommands; +import com.intellij.modcommand.ModCommand; +import com.intellij.modcommand.PsiBasedModCommandAction; +import com.intellij.psi.PsiElement; import com.intellij.psi.PsiVariable; -import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -public class NavigateToAlreadyDeclaredVariableFix implements IntentionAction { - private final PsiVariable myVariable; - +public class NavigateToAlreadyDeclaredVariableFix extends PsiBasedModCommandAction { public NavigateToAlreadyDeclaredVariableFix(@NotNull PsiVariable variable) { - this.myVariable = variable; + super(variable); } @Override @@ -41,36 +22,13 @@ public class NavigateToAlreadyDeclaredVariableFix implements IntentionAction { } @Override - @NotNull - public String getText() { - return QuickFixBundle.message("navigate.variable.declaration.text", myVariable.getName()); + protected @Nullable Presentation getPresentation(@NotNull ActionContext context, @NotNull PsiVariable variable) { + return Presentation.of(QuickFixBundle.message("navigate.variable.declaration.text", variable.getName())); } @Override - public boolean isAvailable(@NotNull final Project project, final Editor editor, final PsiFile file) { - if (!myVariable.isValid()) { - return false; - } - return BaseIntentionAction.canModify(myVariable); - } - - @Override - public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file) throws IncorrectOperationException { - myVariable.navigate(true); - } - - @Override - public boolean startInWriteAction() { - return false; - } - - @Override - public @NotNull IntentionPreviewInfo generatePreview(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) { - if (myVariable.getNavigationElement() instanceof NavigatablePsiElement navigatablePsiElement) { - return IntentionPreviewInfo.navigate(navigatablePsiElement); - } - else { - return IntentionPreviewInfo.EMPTY; - } + protected @NotNull ModCommand perform(@NotNull ActionContext context, @NotNull PsiVariable variable) { + PsiElement element = variable.getNameIdentifier(); + return ModCommands.select(element == null ? variable : element); } } diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/NavigateToDuplicateElementFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/NavigateToDuplicateElementFix.java index d269279c4c12..5af53da01e22 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/NavigateToDuplicateElementFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/NavigateToDuplicateElementFix.java @@ -1,23 +1,18 @@ -// 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. +// 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.daemon.impl.quickfix; import com.intellij.codeInsight.daemon.QuickFixBundle; -import com.intellij.codeInsight.intention.impl.BaseIntentionAction; -import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo; -import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement; -import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.codeInspection.ModCommands; import com.intellij.codeInspection.util.IntentionName; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.project.Project; +import com.intellij.modcommand.ModCommand; +import com.intellij.modcommand.PsiBasedModCommandAction; import com.intellij.psi.NavigatablePsiElement; import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiNameIdentifierOwner; import com.intellij.psi.util.JavaElementKind; -import com.intellij.util.ObjectUtils; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -public class NavigateToDuplicateElementFix extends LocalQuickFixAndIntentionActionOnPsiElement { +public class NavigateToDuplicateElementFix extends PsiBasedModCommandAction { private final @IntentionName String myText; public NavigateToDuplicateElementFix(@NotNull NavigatablePsiElement element) { @@ -27,46 +22,18 @@ public class NavigateToDuplicateElementFix extends LocalQuickFixAndIntentionActi @Override @NotNull - public String getText() { + public String getFamilyName() { return myText; } @Override - @NotNull - public String getFamilyName() { - return getText(); - } - - @Override - public boolean isAvailable(@NotNull Project project, - @NotNull PsiFile file, - @NotNull PsiElement startElement, - @NotNull PsiElement endElement) { - NavigatablePsiElement navigatablePsiElement = ObjectUtils.tryCast(startElement, NavigatablePsiElement.class); - return navigatablePsiElement != null && - navigatablePsiElement.isValid() && - BaseIntentionAction.canModify(navigatablePsiElement); - } - - @Override - public void invoke(@NotNull Project project, - @NotNull PsiFile file, - @Nullable Editor editor, - @NotNull PsiElement startElement, - @NotNull PsiElement endElement) { - if (!(startElement instanceof NavigatablePsiElement)) return; - ((NavigatablePsiElement)startElement).navigate(true); - } - - @Override - public @NotNull IntentionPreviewInfo generatePreview(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) { - NavigatablePsiElement element = ObjectUtils.tryCast(getStartElement(), NavigatablePsiElement.class); - if (element == null) return IntentionPreviewInfo.EMPTY; - return IntentionPreviewInfo.navigate(element); - } - - @Override - public boolean startInWriteAction() { - return false; + protected @NotNull ModCommand perform(@NotNull ActionContext context, @NotNull NavigatablePsiElement element) { + if (element instanceof PsiNameIdentifierOwner owner) { + PsiElement identifier = owner.getNameIdentifier(); + if (identifier != null) { + return ModCommands.select(identifier); + } + } + return ModCommands.select(element); } } diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ReuseVariableDeclarationFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ReuseVariableDeclarationFix.java index c0882b512f8e..0244295eff24 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ReuseVariableDeclarationFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ReuseVariableDeclarationFix.java @@ -3,27 +3,20 @@ package com.intellij.codeInsight.daemon.impl.quickfix; import com.intellij.codeInsight.daemon.QuickFixBundle; import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil; -import com.intellij.codeInsight.intention.FileModifier; -import com.intellij.codeInsight.intention.IntentionAction; -import com.intellij.codeInsight.intention.impl.BaseIntentionAction; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.project.Project; +import com.intellij.codeInspection.EditorUpdater; +import com.intellij.codeInspection.PsiUpdateModCommandAction; import com.intellij.openapi.util.Comparing; import com.intellij.psi.*; import com.intellij.psi.scope.processor.VariablesNotProcessor; import com.intellij.psi.scope.util.PsiScopesUtil; -import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; -import com.intellij.util.IncorrectOperationException; import com.siyeh.ig.psiutils.ExpressionUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public class ReuseVariableDeclarationFix implements IntentionAction { - private final PsiLocalVariable myVariable; - +public class ReuseVariableDeclarationFix extends PsiUpdateModCommandAction { public ReuseVariableDeclarationFix(@NotNull PsiLocalVariable variable) { - this.myVariable = variable; + super(variable); } @Override @@ -33,46 +26,30 @@ public class ReuseVariableDeclarationFix implements IntentionAction { } @Override - @NotNull - public String getText() { - return QuickFixBundle.message("reuse.variable.declaration.text", myVariable.getName()); + protected @Nullable Presentation getPresentation(@NotNull ActionContext context, @NotNull PsiLocalVariable variable) { + final PsiVariable previousVariable = findPreviousVariable(variable); + if (previousVariable == null || !Comparing.equal(previousVariable.getType(), variable.getType())) return null; + return Presentation.of(QuickFixBundle.message("reuse.variable.declaration.text", variable.getName())); } @Override - public boolean isAvailable(@NotNull final Project project, final Editor editor, final PsiFile file) { - if (!myVariable.isValid()) { - return false; - } - final PsiVariable previousVariable = findPreviousVariable(myVariable); - return previousVariable != null && - Comparing.equal(previousVariable.getType(), myVariable.getType()) && - BaseIntentionAction.canModify(myVariable); - } - - @NotNull - @Override - public PsiElement getElementToMakeWritable(@NotNull PsiFile file) { - return myVariable; - } - - @Override - public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file) throws IncorrectOperationException { - final PsiVariable refVariable = findPreviousVariable(myVariable); + protected void invoke(@NotNull ActionContext context, @NotNull PsiLocalVariable variable, @NotNull EditorUpdater updater) { + final PsiVariable refVariable = findPreviousVariable(variable); if (refVariable == null) return; - final PsiExpression initializer = myVariable.getInitializer(); + final PsiExpression initializer = variable.getInitializer(); if (initializer == null) { - myVariable.delete(); + variable.delete(); return; } boolean wasFinal = refVariable.hasModifierProperty(PsiModifier.FINAL); PsiUtil.setModifierProperty(refVariable, PsiModifier.FINAL, false); - final PsiElementFactory factory = JavaPsiFacade.getElementFactory(myVariable.getProject()); + final PsiElementFactory factory = JavaPsiFacade.getElementFactory(variable.getProject()); final PsiElement statement = factory.createStatementFromText( - myVariable.getName() + " = " + - ExpressionUtils.convertInitializerToExpression(initializer, factory, myVariable.getType()).getText() + ";", null); - myVariable.getParent().replace(statement); + variable.getName() + " = " + + ExpressionUtils.convertInitializerToExpression(initializer, factory, variable.getType()).getText() + ";", null); + variable.getParent().replace(statement); if (wasFinal && refVariable instanceof PsiLocalVariable && HighlightControlFlowUtil.isEffectivelyFinal(refVariable, initializer, null)) { @@ -98,14 +75,4 @@ public class ReuseVariableDeclarationFix implements IntentionAction { PsiScopesUtil.treeWalkUp(processor, nameIdentifier, scope); return processor.size() > 0 ? processor.getResult(0) : null; } - - @Override - public boolean startInWriteAction() { - return true; - } - - @Override - public @Nullable FileModifier getFileModifierForPreview(@NotNull PsiFile target) { - return new ReuseVariableDeclarationFix(PsiTreeUtil.findSameElementInCopy(myVariable, target)); - } } diff --git a/java/java-impl/src/com/intellij/codeInsight/generation/GenerateMembersUtil.java b/java/java-impl/src/com/intellij/codeInsight/generation/GenerateMembersUtil.java index af3b7f152c91..91fe545e96f8 100644 --- a/java/java-impl/src/com/intellij/codeInsight/generation/GenerateMembersUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/generation/GenerateMembersUtil.java @@ -7,6 +7,7 @@ import com.intellij.codeInsight.ExceptionUtil; import com.intellij.codeInsight.NullableNotNullManager; import com.intellij.codeInsight.daemon.impl.quickfix.CreateFromUsageUtils; import com.intellij.codeInsight.intention.AddAnnotationPsiFix; +import com.intellij.codeInspection.EditorUpdater; import com.intellij.lang.ASTNode; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; @@ -15,6 +16,7 @@ import com.intellij.openapi.editor.RangeMarker; import com.intellij.openapi.editor.ScrollType; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.codeStyle.*; @@ -141,31 +143,10 @@ public final class GenerateMembersUtil { PsiMethod method = (PsiMethod)firstMember; PsiCodeBlock body = method.getBody(); if (body != null) { - PsiElement firstBodyElement = body.getFirstBodyElement(); - PsiElement l = firstBodyElement; - while (l instanceof PsiWhiteSpace) l = l.getNextSibling(); - if (l == null) l = body; - PsiElement lastBodyElement = body.getLastBodyElement(); - PsiElement r = lastBodyElement; - while (r instanceof PsiWhiteSpace) r = r.getPrevSibling(); - if (r == null) r = body; - - int start = l.getTextRange().getStartOffset(); - int end = r.getTextRange().getEndOffset(); - - boolean adjustLineIndent = false; - - // body is whitespace - if (start > end && - firstBodyElement == lastBodyElement && - firstBodyElement instanceof PsiWhiteSpaceImpl - ) { - CharSequence chars = ((PsiWhiteSpaceImpl)firstBodyElement).getChars(); - if (chars.length() > 1 && chars.charAt(0) == '\n' && chars.charAt(1) == '\n') { - start = end = firstBodyElement.getTextRange().getStartOffset() + 1; - adjustLineIndent = true; - } - } + PositionInfo info = getPositionInfo(body); + int start = info.start(); + int end = info.end(); + boolean adjustLineIndent = info.adjustLineIndent(); editor.getCaretModel().moveToOffset(Math.min(start, end)); editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); @@ -184,28 +165,89 @@ public final class GenerateMembersUtil { } } - int offset; - if (firstMember instanceof PsiMethod) { - PsiMethod method = (PsiMethod)firstMember; - PsiCodeBlock body = method.getBody(); - if (body == null) { - offset = method.getTextRange().getStartOffset(); - } - else { - PsiJavaToken lBrace = body.getLBrace(); - assert lBrace != null : firstMember.getText(); - offset = lBrace.getTextRange().getEndOffset(); - } - } - else { - offset = firstMember.getTextRange().getStartOffset(); - } + int offset = getOffsetInMethod(firstMember); editor.getCaretModel().moveToOffset(offset); editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); editor.getSelectionModel().removeSelection(); } + public static void positionCaret(@NotNull EditorUpdater updater, @NotNull PsiElement firstMember, boolean toEditMethodBody) { + LOG.assertTrue(firstMember.isValid()); + Project project = firstMember.getProject(); + + if (toEditMethodBody) { + PsiMethod method = (PsiMethod)firstMember; + PsiCodeBlock body = method.getBody(); + if (body != null) { + PositionInfo info = getPositionInfo(body); + + updater.moveTo(Math.min(info.start(), info.end())); + if (info.start() < info.end()) { + //Not an empty body + updater.select(TextRange.create(info.start(), info.end())); + } else if (info.adjustLineIndent()) { + Document document = firstMember.getContainingFile().getViewProvider().getDocument(); + RangeMarker marker = document.createRangeMarker(info.start(), info.start()); + PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(document); + if (marker.isValid()) { + CodeStyleManager.getInstance(project).adjustLineIndent(document, marker.getStartOffset()); + } + } + return; + } + } + + int offset = getOffsetInMethod(firstMember); + updater.moveTo(offset); + } + + @NotNull + private static PositionInfo getPositionInfo(PsiCodeBlock body) { + PsiElement firstBodyElement = body.getFirstBodyElement(); + PsiElement l = firstBodyElement; + while (l instanceof PsiWhiteSpace) l = l.getNextSibling(); + if (l == null) l = body; + PsiElement lastBodyElement = body.getLastBodyElement(); + PsiElement r = lastBodyElement; + while (r instanceof PsiWhiteSpace) r = r.getPrevSibling(); + if (r == null) r = body; + + int start = l.getTextRange().getStartOffset(); + int end = r.getTextRange().getEndOffset(); + + boolean adjustLineIndent = false; + + // body is whitespace + if (start > end && + firstBodyElement == lastBodyElement && + firstBodyElement instanceof PsiWhiteSpaceImpl + ) { + CharSequence chars = ((PsiWhiteSpaceImpl)firstBodyElement).getChars(); + if (chars.length() > 1 && chars.charAt(0) == '\n' && chars.charAt(1) == '\n') { + start = end = firstBodyElement.getTextRange().getStartOffset() + 1; + adjustLineIndent = true; + } + } + return new PositionInfo(start, end, adjustLineIndent); + } + + private record PositionInfo(int start, int end, boolean adjustLineIndent) { + } + + private static int getOffsetInMethod(@NotNull PsiElement member) { + if (member instanceof PsiMethod method) { + PsiCodeBlock body = method.getBody(); + if (body == null) { + return method.getTextRange().getStartOffset(); + } + PsiJavaToken lBrace = body.getLBrace(); + assert lBrace != null : member.getText(); + return lBrace.getTextRange().getEndOffset(); + } + return member.getTextRange().getStartOffset(); + } + public static PsiElement insert(@NotNull PsiClass aClass, @NotNull PsiMember member, @Nullable PsiElement anchor, boolean before) throws IncorrectOperationException { if (member instanceof PsiMethod) { if (!aClass.isInterface()) { diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/MoveInitializerToConstructorAction.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/MoveInitializerToConstructorAction.java index dc9f4d5410f6..57d2c57e3862 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/MoveInitializerToConstructorAction.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/MoveInitializerToConstructorAction.java @@ -2,8 +2,7 @@ package com.intellij.codeInsight.intention.impl; import com.intellij.codeInsight.daemon.impl.analysis.JavaHighlightUtil; -import com.intellij.codeInsight.intention.IntentionAction; -import com.intellij.codeInsight.intention.QuickFixFactory; +import com.intellij.codeInsight.daemon.impl.quickfix.AddDefaultConstructorFix; import com.intellij.java.JavaBundle; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; @@ -64,7 +63,7 @@ public class MoveInitializerToConstructorAction extends BaseMoveInitializerToMet protected Collection getOrCreateMethods(@NotNull Project project, @NotNull Editor editor, PsiFile file, @NotNull PsiClass aClass) { final Collection constructors = Arrays.asList(aClass.getConstructors()); if (constructors.isEmpty()) { - return createConstructor(project, editor, file, aClass); + return createConstructor(aClass); } return removeChainedConstructors(constructors); @@ -78,14 +77,8 @@ public class MoveInitializerToConstructorAction extends BaseMoveInitializerToMet } @NotNull - private static Collection createConstructor(@NotNull Project project, - @NotNull Editor editor, - PsiFile file, - @NotNull PsiClass aClass) { - final IntentionAction addDefaultConstructorFix = QuickFixFactory.getInstance().createAddDefaultConstructorFix(aClass); - final int offset = editor.getCaretModel().getOffset(); - addDefaultConstructorFix.invoke(project, editor, file); - editor.getCaretModel().moveToOffset(offset); //restore caret + private static Collection createConstructor(@NotNull PsiClass aClass) { + AddDefaultConstructorFix.addDefaultConstructor(aClass); return Arrays.asList(aClass.getConstructors()); } } \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/ReplaceAssignmentWithComparisonFix.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/ReplaceAssignmentWithComparisonFix.java index e7ba877f4b4c..0804dbd7ce71 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/ReplaceAssignmentWithComparisonFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/ReplaceAssignmentWithComparisonFix.java @@ -1,32 +1,29 @@ -// 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.intention.impl; -import com.intellij.codeInsight.daemon.impl.actions.IntentionActionWithFixAllOption; import com.intellij.codeInspection.CommonQuickFixBundle; -import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement; -import com.intellij.openapi.editor.Editor; +import com.intellij.codeInspection.EditorUpdater; +import com.intellij.codeInspection.PsiUpdateModCommandAction; import com.intellij.openapi.project.Project; -import com.intellij.psi.*; +import com.intellij.psi.JavaPsiFacade; +import com.intellij.psi.PsiAssignmentExpression; +import com.intellij.psi.PsiBinaryExpression; +import com.intellij.psi.PsiExpression; import com.intellij.psi.codeStyle.CodeStyleManager; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public class ReplaceAssignmentWithComparisonFix extends LocalQuickFixAndIntentionActionOnPsiElement - implements IntentionActionWithFixAllOption { +public class ReplaceAssignmentWithComparisonFix extends PsiUpdateModCommandAction { public ReplaceAssignmentWithComparisonFix(@NotNull PsiAssignmentExpression expr) { super(expr); } @Override - public void invoke(@NotNull Project project, - @NotNull PsiFile file, - @Nullable Editor editor, - @NotNull PsiElement startElement, - @NotNull PsiElement endElement) { + protected void invoke(@NotNull ActionContext context, @NotNull PsiAssignmentExpression assignmentExpression, @NotNull EditorUpdater updater) { + Project project = context.project(); PsiBinaryExpression - comparisonExpr = (PsiBinaryExpression)JavaPsiFacade.getElementFactory(project).createExpressionFromText("a==b", startElement); - PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)startElement; + comparisonExpr = (PsiBinaryExpression)JavaPsiFacade.getElementFactory(project).createExpressionFromText("a==b", assignmentExpression); comparisonExpr.getLOperand().replace(assignmentExpression.getLExpression()); PsiExpression rOperand = comparisonExpr.getROperand(); assert rOperand != null; @@ -36,10 +33,9 @@ public class ReplaceAssignmentWithComparisonFix extends LocalQuickFixAndIntentio CodeStyleManager.getInstance(project).reformat(assignmentExpression.replace(comparisonExpr)); } - @NotNull @Override - public String getText() { - return getFamilyName(); + protected @Nullable Presentation getPresentation(@NotNull ActionContext context, @NotNull PsiAssignmentExpression element) { + return Presentation.of(getFamilyName()).withFixAllOption(this); } @Nls diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/config/QuickFixFactoryImpl.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/config/QuickFixFactoryImpl.java index 9520a878d962..23534c455f17 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/config/QuickFixFactoryImpl.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/config/QuickFixFactoryImpl.java @@ -108,20 +108,6 @@ public final class QuickFixFactoryImpl extends QuickFixFactory { return new AnnotationMethodReturnTypeFix(method, toReturn, fromDefaultValue); } - @NotNull - @Override - public LocalQuickFixAndIntentionActionOnPsiElement createAddMethodFix(@NotNull PsiMethod method, @NotNull PsiClass toClass) { - return new AddMethodFix(method, toClass); - } - - @NotNull - @Override - public LocalQuickFixAndIntentionActionOnPsiElement createAddMethodFix(@NotNull String methodText, - @NotNull PsiClass toClass, - String @NotNull ... exceptions) { - return new AddMethodFix(methodText, toClass, exceptions); - } - @NotNull @Override public LocalQuickFixAndIntentionActionOnPsiElement createImplementMethodsFix(@NotNull PsiClass aClass) { @@ -136,8 +122,8 @@ public final class QuickFixFactoryImpl extends QuickFixFactory { @NotNull @Override - public LocalQuickFixAndIntentionActionOnPsiElement createAssignmentToComparisonFix(@NotNull PsiAssignmentExpression expr) { - return new ReplaceAssignmentWithComparisonFix(expr); + public IntentionAction createAssignmentToComparisonFix(@NotNull PsiAssignmentExpression expr) { + return new ReplaceAssignmentWithComparisonFix(expr).asIntention(); } @NotNull @@ -154,12 +140,7 @@ public final class QuickFixFactoryImpl extends QuickFixFactory { @NotNull @Override public LocalQuickFixAndIntentionActionOnPsiElement createAddDefaultConstructorFix(@NotNull PsiClass aClass) { - return new AddDefaultConstructorFix(aClass); - } - - @Override - public LocalQuickFixAndIntentionActionOnPsiElement createAddConstructorFix(@NotNull PsiClass aClass, @NotNull String modifier) { - return aClass.getName() != null ? new AddDefaultConstructorFix(aClass, modifier) : null; + return LocalQuickFixAndIntentionActionOnPsiElement.from(new AddDefaultConstructorFix(aClass), aClass); } @NotNull @@ -171,12 +152,6 @@ public final class QuickFixFactoryImpl extends QuickFixFactory { return new MethodParameterFix(method, newType, index, fixWholeHierarchy); } - @NotNull - @Override - public LocalQuickFixAndIntentionActionOnPsiElement createMakeClassInterfaceFix(@NotNull PsiClass aClass, final boolean makeInterface) { - return new MakeClassInterfaceFix(aClass, makeInterface); - } - @NotNull @Override public LocalQuickFixAndIntentionActionOnPsiElement createExtendsListFix(@NotNull PsiClass aClass, @@ -240,7 +215,7 @@ public final class QuickFixFactoryImpl extends QuickFixFactory { @NotNull @Override public IntentionAction createAddExceptionFromFieldInitializerToConstructorThrowsFix(@NotNull PsiElement element) { - return new AddExceptionFromFieldInitializerToConstructorThrowsFix(element); + return new AddExceptionFromFieldInitializerToConstructorThrowsFix(element).asIntention(); } @NotNull @@ -266,37 +241,37 @@ public final class QuickFixFactoryImpl extends QuickFixFactory { @NotNull @Override public IntentionAction createAddTypeCastFix(@NotNull PsiType type, @NotNull PsiExpression expression) { - return new AddTypeCastFix(type, expression); + return new AddTypeCastFix(type, expression).asIntention(); } @NotNull @Override public IntentionAction createReuseVariableDeclarationFix(@NotNull PsiLocalVariable variable) { - return new ReuseVariableDeclarationFix(variable); + return new ReuseVariableDeclarationFix(variable).asIntention(); } @NotNull @Override public IntentionAction createNavigateToAlreadyDeclaredVariableFix(@NotNull PsiVariable variable) { - return new NavigateToAlreadyDeclaredVariableFix(variable); + return new NavigateToAlreadyDeclaredVariableFix(variable).asIntention(); } @NotNull @Override public IntentionAction createNavigateToDuplicateElementFix(@NotNull NavigatablePsiElement element) { - return new NavigateToDuplicateElementFix(element); + return new NavigateToDuplicateElementFix(element).asIntention(); } @NotNull @Override public IntentionAction createConvertToStringLiteralAction() { - return new ConvertToStringLiteralAction(); + return new ConvertToStringLiteralAction().asIntention(); } @NotNull @Override public IntentionAction createDeleteReturnFix(@NotNull PsiMethod method, @NotNull PsiReturnStatement returnStatement) { - return new DeleteReturnFix(method, returnStatement); + return new DeleteReturnFix(method, returnStatement).asIntention(); } @NotNull diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/after3.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/after3.java index 4f6a648578b8..cc3cc85970e1 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/after3.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/after3.java @@ -1,6 +1,6 @@ // "Cast expression to 'int'" "true-preview" class a { void f() { - int[] ii = {(int) 1.3}; + int[] ii = {(int) 1.3}; } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/afterTernary2.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/afterTernary2.java index 8c51e21d22fe..4692186d3a19 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/afterTernary2.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/addTypeCast/afterTernary2.java @@ -1,7 +1,7 @@ // "Cast expression to 'B'" "true-preview" class A { void f(B b) { - B s =b == null ? null : (B) this; + B s =b == null ? null : (B) this; } } class B extends A {} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/NavigateToAlreadyDeclaredVariableFixTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/NavigateToAlreadyDeclaredVariableFixTest.java index c0a5cca5f8f6..c4d45e211868 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/NavigateToAlreadyDeclaredVariableFixTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/NavigateToAlreadyDeclaredVariableFixTest.java @@ -1,8 +1,9 @@ -// 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.daemon.impl.quickfix; import com.intellij.codeInsight.daemon.QuickFixBundle; import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.openapi.application.impl.NonBlockingReadActionImpl; import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase; public class NavigateToAlreadyDeclaredVariableFixTest extends LightJavaCodeInsightFixtureTestCase { @@ -11,6 +12,7 @@ public class NavigateToAlreadyDeclaredVariableFixTest extends LightJavaCodeInsig IntentionAction intention = myFixture.findSingleIntention(QuickFixBundle.message("navigate.variable.declaration.text", "i")); assertNotNull(intention); myFixture.launchAction(intention); + NonBlockingReadActionImpl.waitForAsyncTaskCompletion(); assertEquals(14, myFixture.getCaretOffset()); } @@ -22,6 +24,7 @@ public class NavigateToAlreadyDeclaredVariableFixTest extends LightJavaCodeInsig IntentionAction intention = myFixture.findSingleIntention(QuickFixBundle.message("navigate.variable.declaration.text", "element")); assertNotNull(intention); myFixture.launchAction(intention); + NonBlockingReadActionImpl.waitForAsyncTaskCompletion(); assertEquals(51, myFixture.getCaretOffset()); } } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/NavigateToDuplicateElementFixTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/NavigateToDuplicateElementFixTest.java index d0d64a59cdc5..798e2ba41b44 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/NavigateToDuplicateElementFixTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/quickfix/NavigateToDuplicateElementFixTest.java @@ -1,39 +1,36 @@ -// 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.daemon.impl.quickfix; import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.openapi.application.impl.NonBlockingReadActionImpl; import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase; public class NavigateToDuplicateElementFixTest extends LightJavaCodeInsightFixtureTestCase { public void testNavigateToSameLevelClass() { myFixture.configureByText("A.java", "class Foo {} class Foo {}"); - IntentionAction intention = myFixture.findSingleIntention("Navigate to duplicate class"); - assertNotNull(intention); - myFixture.launchAction(intention); - assertEquals(6, myFixture.getCaretOffset()); + doTest("Navigate to duplicate class", 6); } public void testNavigateToSameLevelInterface() { myFixture.configureByText("A.java", "interface Foo {} interface Foo {}"); - IntentionAction intention = myFixture.findSingleIntention("Navigate to duplicate interface"); - assertNotNull(intention); - myFixture.launchAction(intention); - assertEquals(10, myFixture.getCaretOffset()); + doTest("Navigate to duplicate interface", 10); } public void testNavigateFromInterfaceToClass() { myFixture.configureByText("A.java", "class Foo { interface Foo {} }"); - IntentionAction intention = myFixture.findSingleIntention("Navigate to duplicate class"); - assertNotNull(intention); - myFixture.launchAction(intention); - assertEquals(6, myFixture.getCaretOffset()); + doTest("Navigate to duplicate class", 6); } public void testNavigateFromClassToInterface() { myFixture.configureByText("A.java", "interface Foo {} class Foo {}"); - IntentionAction intention = myFixture.findSingleIntention("Navigate to duplicate interface"); + doTest("Navigate to duplicate interface", 10); + } + + private void doTest(String intentionName, int expectedOffset) { + IntentionAction intention = myFixture.findSingleIntention(intentionName); assertNotNull(intention); myFixture.launchAction(intention); - assertEquals(10, myFixture.getCaretOffset()); + NonBlockingReadActionImpl.waitForAsyncTaskCompletion(); + assertEquals(expectedOffset, myFixture.getCaretOffset()); } } diff --git a/platform/analysis-api/src/com/intellij/codeInspection/LocalQuickFixAndIntentionActionOnPsiElement.java b/platform/analysis-api/src/com/intellij/codeInspection/LocalQuickFixAndIntentionActionOnPsiElement.java index c70ee1a559a0..fd617a184369 100644 --- a/platform/analysis-api/src/com/intellij/codeInspection/LocalQuickFixAndIntentionActionOnPsiElement.java +++ b/platform/analysis-api/src/com/intellij/codeInspection/LocalQuickFixAndIntentionActionOnPsiElement.java @@ -1,26 +1,18 @@ -/* - * Copyright 2000-2013 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.codeInspection; import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo; +import com.intellij.codeInspection.util.IntentionName; +import com.intellij.modcommand.ModCommand; +import com.intellij.modcommand.ModCommandAction; +import com.intellij.modcommand.ModCommandService; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -82,4 +74,62 @@ public abstract class LocalQuickFixAndIntentionActionOnPsiElement extends LocalQ public boolean startInWriteAction() { return true; } + + /** + * This method exists to provide compatibility bridges. Use of it is discouraged. When possible, prefer + * {@link ModCommandAction#asIntention()} or {@link ModCommandAction#asQuickFix()} + * @param action action to delegate to + * @param psiElement some context element. Mostly unused but should remain valid in order the action to be executed + * @return a wrapper that extends {@link LocalQuickFixAndIntentionActionOnPsiElement} and delegates to the action + */ + @ApiStatus.Internal + public static LocalQuickFixAndIntentionActionOnPsiElement from(@NotNull ModCommandAction action, @NotNull PsiElement psiElement) { + return new LocalQuickFixAndIntentionActionOnPsiElement(psiElement) { + @IntentionName private String myText; + + @Override + public @NotNull String getFamilyName() { + return action.getFamilyName(); + } + + @Override + public @NotNull String getText() { + return myText == null ? getFamilyName() : myText; + } + + @Override + public boolean isAvailable(@NotNull Project project, + @NotNull PsiFile file, + @Nullable Editor editor, + @NotNull PsiElement startElement, + @NotNull PsiElement endElement) { + ModCommandAction.ActionContext context = ModCommandAction.ActionContext.from(editor, file).withElement(startElement); + ModCommandAction.Presentation presentation = action.getPresentation(context); + if (presentation != null) { + myText = presentation.name(); + } + return presentation != null; + } + + @Override + public void invoke(@NotNull Project project, + @NotNull PsiFile file, + @Nullable Editor editor, + @NotNull PsiElement startElement, + @NotNull PsiElement endElement) { + ModCommand command = action.perform(ModCommandAction.ActionContext.from(editor, file).withElement(startElement)); + ModCommandService.getInstance().execute(project, command); + } + + @Override + public @NotNull IntentionPreviewInfo generatePreview(@NotNull Project project, @NotNull ProblemDescriptor previewDescriptor) { + return action.generatePreview(ModCommandAction.ActionContext.from(previewDescriptor)); + } + + @Override + public @NotNull IntentionPreviewInfo generatePreview(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) { + return action.generatePreview(ModCommandAction.ActionContext.from(editor, file)); + } + }; + } } diff --git a/platform/analysis-api/src/com/intellij/modcommand/ModCommandAction.java b/platform/analysis-api/src/com/intellij/modcommand/ModCommandAction.java index 937e00ebf2e2..d12051cf9a8f 100644 --- a/platform/analysis-api/src/com/intellij/modcommand/ModCommandAction.java +++ b/platform/analysis-api/src/com/intellij/modcommand/ModCommandAction.java @@ -118,6 +118,10 @@ public interface ModCommandAction extends CommonIntentionAction { return file.findElementAt(offset); } + public ActionContext withElement(@NotNull PsiElement element) { + return new ActionContext(project, file, offset, selection, element); + } + /** * @param editor editor the action is invoked in * @param file file the action is invoked on diff --git a/platform/lang-impl/src/com/intellij/lang/impl/modcommand/ModCommandServiceImpl.java b/platform/lang-impl/src/com/intellij/lang/impl/modcommand/ModCommandServiceImpl.java index 0ccc9056ae5e..3f0c6a01be47 100644 --- a/platform/lang-impl/src/com/intellij/lang/impl/modcommand/ModCommandServiceImpl.java +++ b/platform/lang-impl/src/com/intellij/lang/impl/modcommand/ModCommandServiceImpl.java @@ -16,9 +16,8 @@ import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.fileEditor.FileDocumentManager; -import com.intellij.openapi.fileEditor.FileEditor; import com.intellij.openapi.fileEditor.FileEditorManager; -import com.intellij.openapi.fileEditor.TextEditor; +import com.intellij.openapi.fileEditor.OpenFileDescriptor; import com.intellij.openapi.progress.DumbProgressIndicator; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.NlsContexts; @@ -163,11 +162,7 @@ public class ModCommandServiceImpl implements ModCommandService { } private static Editor getEditor(@NotNull Project project, VirtualFile file) { - FileEditor fileEditor = FileEditorManager.getInstance(project).getSelectedEditor(file); - if (fileEditor instanceof TextEditor textEditor) { - return textEditor.getEditor(); - } - return null; + return FileEditorManager.getInstance(project).openTextEditor(new OpenFileDescriptor(project, file), true); } private boolean executeComposite(@NotNull Project project, ModCompositeCommand cmp) { diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/NullArgumentToVariableArgMethodInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/NullArgumentToVariableArgMethodInspection.java index c516f40a429d..7419d7238414 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/NullArgumentToVariableArgMethodInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/NullArgumentToVariableArgMethodInspection.java @@ -54,8 +54,8 @@ public class NullArgumentToVariableArgMethodInspection extends BaseInspection { final PsiType type1 = (PsiType)infos[1]; final PsiType type2 = (PsiType)infos[2]; return new LocalQuickFix[] { - new AddTypeCastFix(type1, argument), - new AddTypeCastFix(type2, argument), + new AddTypeCastFix(type1, argument).asQuickFix(), + new AddTypeCastFix(type2, argument).asQuickFix(), }; } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/PrimitiveArrayArgumentToVariableArgMethodInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/PrimitiveArrayArgumentToVariableArgMethodInspection.java index 7c08901154e0..89d9c1d0eaab 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/PrimitiveArrayArgumentToVariableArgMethodInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/PrimitiveArrayArgumentToVariableArgMethodInspection.java @@ -62,7 +62,7 @@ public class PrimitiveArrayArgumentToVariableArgMethodInspection extends BaseIns protected LocalQuickFix buildFix(Object... infos) { final PsiExpression argument = (PsiExpression)infos[0]; final PsiType type = (PsiType)infos[1]; - return new AddTypeCastFix(type, argument); + return new AddTypeCastFix(type, argument).asQuickFix(); } @Override diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/LossyConversionCompoundAssignmentInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/LossyConversionCompoundAssignmentInspection.java index 3f2888067135..436e6395f4ee 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/LossyConversionCompoundAssignmentInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/numeric/LossyConversionCompoundAssignmentInspection.java @@ -31,7 +31,7 @@ public class LossyConversionCompoundAssignmentInspection extends BaseInspection protected @Nullable LocalQuickFix buildFix(Object... infos) { PsiType lType = (PsiType)infos[1]; PsiExpression rExpression = (PsiExpression)infos[2]; - return new AddTypeCastFix(lType, rExpression); + return new AddTypeCastFix(lType, rExpression).asQuickFix(); } @Override diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/ClassInitializerInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/classlayout/ClassInitializerInspection.java similarity index 94% rename from plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/ClassInitializerInspection.java rename to plugins/InspectionGadgets/src/com/siyeh/ig/classlayout/ClassInitializerInspection.java index d5f3dc8dac52..5e5ba56e62b3 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/ClassInitializerInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/classlayout/ClassInitializerInspection.java @@ -17,8 +17,7 @@ package com.siyeh.ig.classlayout; import com.intellij.codeInsight.daemon.impl.analysis.HighlightingFeature; import com.intellij.codeInsight.daemon.impl.analysis.JavaHighlightUtil; -import com.intellij.codeInsight.intention.IntentionAction; -import com.intellij.codeInsight.intention.QuickFixFactory; +import com.intellij.codeInsight.daemon.impl.quickfix.AddDefaultConstructorFix; import com.intellij.codeInspection.EditorUpdater; import com.intellij.codeInspection.LocalQuickFix; import com.intellij.codeInspection.PsiUpdateModCommandQuickFix; @@ -128,8 +127,7 @@ public class ClassInitializerInspection extends BaseInspection { private static Collection getOrCreateConstructors(@NotNull PsiClass aClass) { PsiMethod[] constructors = aClass.getConstructors(); if (constructors.length == 0) { - final IntentionAction addDefaultConstructorFix = QuickFixFactory.getInstance().createAddDefaultConstructorFix(aClass); - addDefaultConstructorFix.invoke(aClass.getProject(), null, aClass.getContainingFile()); + AddDefaultConstructorFix.addDefaultConstructor(aClass); } constructors = aClass.getConstructors(); return ContainerUtil.filter(constructors, constructor -> JavaHighlightUtil.getChainedConstructors(constructor).isEmpty()); diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/serialization/ExternalizableWithoutPublicNoArgConstructorInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/serialization/ExternalizableWithoutPublicNoArgConstructorInspection.java index 216e5f2b6a00..49ef45ed1d4e 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/serialization/ExternalizableWithoutPublicNoArgConstructorInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/serialization/ExternalizableWithoutPublicNoArgConstructorInspection.java @@ -4,14 +4,12 @@ package com.siyeh.ig.serialization; import com.intellij.codeInsight.daemon.impl.quickfix.AddDefaultConstructorFix; import com.intellij.codeInspection.EditorUpdater; import com.intellij.codeInspection.LocalQuickFix; -import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.codeInspection.PsiUpdateModCommandQuickFix; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; -import com.siyeh.ig.InspectionGadgetsFix; import com.siyeh.ig.psiutils.ClassUtils; import com.siyeh.ig.psiutils.SerializationUtils; import org.jetbrains.annotations.NotNull; @@ -31,7 +29,7 @@ public class ExternalizableWithoutPublicNoArgConstructorInspection extends BaseI // can't create constructor for anonymous class return null; } - return new AddDefaultConstructorFix(aClass, PsiModifier.PUBLIC); + return new AddDefaultConstructorFix(aClass, PsiModifier.PUBLIC).asQuickFix(); } else { return new MakeConstructorPublicFix();