[java-intention] New ModCommands

ChangeExtendsToImplementsFix, ClassMayBeInterfaceInspection, MethodRefCanBeReplacedWithLambdaInspection

GitOrigin-RevId: 1ef6864503e633a37c1f1dda1f5604cc9ae978de
This commit is contained in:
Tagir Valeev
2023-07-21 11:17:38 +00:00
committed by intellij-monorepo-bot
parent 2c12f7c579
commit 1a157d9e68
5 changed files with 99 additions and 83 deletions
@@ -17,33 +17,70 @@
package com.intellij.codeInsight.daemon.impl.quickfix;
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInsight.intention.HighPriorityAction;
import com.intellij.codeInspection.util.IntentionName;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiClassType;
import com.intellij.psi.PsiKeyword;
import com.intellij.codeInsight.intention.PriorityAction;
import com.intellij.modcommand.ModPsiUpdater;
import com.intellij.modcommand.PsiUpdateModCommandAction;
import com.intellij.psi.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* changes 'class a extends b' to 'class a implements b' or vice versa
*/
public class ChangeExtendsToImplementsFix extends ExtendsListFix implements HighPriorityAction {
private final @IntentionName String myName;
public class ChangeExtendsToImplementsFix extends PsiUpdateModCommandAction<PsiClass> {
@Nullable
protected final SmartPsiElementPointer<PsiClass> myClassToExtendFromPointer;
private final boolean myToAdd;
private final PsiClassType myTypeToExtendFrom;
public ChangeExtendsToImplementsFix(@NotNull PsiClass aClass, @NotNull PsiClassType classToExtendFrom) {
super(aClass, classToExtendFrom, true);
PsiClass classToExtendFromPointer = myClassToExtendFromPointer != null ? myClassToExtendFromPointer.getElement() : null;
myName = classToExtendFromPointer == null ? getFamilyName() :
QuickFixBundle.message("exchange.extends.implements.keyword",
aClass.isInterface() == classToExtendFromPointer.isInterface() ? PsiKeyword.IMPLEMENTS : PsiKeyword.EXTENDS,
aClass.isInterface() == classToExtendFromPointer.isInterface() ? PsiKeyword.EXTENDS : PsiKeyword.IMPLEMENTS,
classToExtendFromPointer.getName());
public ChangeExtendsToImplementsFix(@NotNull PsiClass aClass, @NotNull PsiClassType classTypeToExtendFrom) {
super(aClass);
PsiClass classToExtendFrom = classTypeToExtendFrom.resolve();
myClassToExtendFromPointer = classToExtendFrom == null ? null : SmartPointerManager.createPointer(classToExtendFrom);
myToAdd = true;
myTypeToExtendFrom = aClass instanceof PsiTypeParameter ? classTypeToExtendFrom
: (PsiClassType)GenericsUtil.eliminateWildcards(classTypeToExtendFrom);
}
@Override
@NotNull
public String getText() {
return myName;
public String getFamilyName() {
return QuickFixBundle.message("change.extends.list.family");
}
@Override
protected @Nullable Presentation getPresentation(@NotNull ActionContext context, @NotNull PsiClass myClass) {
if (!myTypeToExtendFrom.isValid()) return null;
PsiClass classToExtendFrom = myClassToExtendFromPointer != null ? myClassToExtendFromPointer.getElement() : null;
boolean available = classToExtendFrom != null && classToExtendFrom.isValid()
&& !classToExtendFrom.hasModifierProperty(PsiModifier.FINAL)
&& (classToExtendFrom.isInterface() ||
!myClass.isInterface() && myClass.getExtendsList() != null
&& (myClass.getExtendsList().getReferencedTypes().length == 0) == myToAdd);
if (!available) return null;
String name = QuickFixBundle.message(
"exchange.extends.implements.keyword",
myClass.isInterface() == classToExtendFrom.isInterface() ? PsiKeyword.IMPLEMENTS : PsiKeyword.EXTENDS,
myClass.isInterface() == classToExtendFrom.isInterface() ? PsiKeyword.EXTENDS : PsiKeyword.IMPLEMENTS,
classToExtendFrom.getName());
return Presentation.of(name).withPriority(PriorityAction.Priority.HIGH);
}
@Override
protected void invoke(@NotNull ActionContext context, @NotNull PsiClass myClass, @NotNull ModPsiUpdater updater) {
PsiClass classToExtendFrom = myClassToExtendFromPointer != null ? myClassToExtendFromPointer.getElement() : null;
PsiReferenceList extendsList = !(myClass instanceof PsiTypeParameter) && classToExtendFrom != null &&
myClass.isInterface() != classToExtendFrom.isInterface() ?
myClass.getImplementsList() : myClass.getExtendsList();
PsiReferenceList otherList = extendsList == myClass.getImplementsList() ?
myClass.getExtendsList() : myClass.getImplementsList();
if (extendsList != null) {
ExtendsListFix.modifyList(extendsList, myToAdd, -1, myTypeToExtendFrom);
}
if (otherList != null) {
ExtendsListFix.modifyList(otherList, false, -1, myTypeToExtendFrom);
}
}
}
@@ -439,7 +439,7 @@ public final class QuickFixFactoryImpl extends QuickFixFactory {
@NotNull
@Override
public IntentionAction createChangeExtendsToImplementsFix(@NotNull PsiClass aClass, @NotNull PsiClassType classToExtendFrom) {
return new ChangeExtendsToImplementsFix(aClass, classToExtendFrom);
return new ChangeExtendsToImplementsFix(aClass, classToExtendFrom).asIntention();
}
@NotNull
@@ -1,7 +1,7 @@
// "Change 'extends Runnable' to 'implements Runnable'" "true-preview"
import java.io.*;
class a <caret>implements Runnable {
class a implements <caret>Runnable {
public void run() {
}
}
@@ -15,12 +15,13 @@
*/
package com.siyeh.ig.classlayout;
import com.intellij.codeInsight.FileModificationService;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightingFeature;
import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.codeInspection.options.OptPane;
import com.intellij.openapi.application.WriteAction;
import com.intellij.modcommand.ModCommand;
import com.intellij.modcommand.ModCommandQuickFix;
import com.intellij.modcommand.ModCommands;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.psi.*;
@@ -32,7 +33,6 @@ import com.intellij.util.containers.ContainerUtil;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.BaseInspection;
import com.siyeh.ig.BaseInspectionVisitor;
import com.siyeh.ig.InspectionGadgetsFix;
import com.siyeh.ig.memory.InnerClassReferenceVisitor;
import com.siyeh.ig.psiutils.ClassUtils;
import com.siyeh.ig.psiutils.MethodUtils;
@@ -71,11 +71,11 @@ public class ClassMayBeInterfaceInspection extends BaseInspection {
}
@Override
protected InspectionGadgetsFix buildFix(Object... infos) {
protected LocalQuickFix buildFix(Object... infos) {
return new ClassMayBeInterfaceFix();
}
private static class ClassMayBeInterfaceFix extends InspectionGadgetsFix {
private static class ClassMayBeInterfaceFix extends ModCommandQuickFix {
@Override
@NotNull
@@ -84,12 +84,7 @@ public class ClassMayBeInterfaceInspection extends BaseInspection {
}
@Override
public boolean startInWriteAction() {
return false;
}
@Override
public void doFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
public @NotNull ModCommand perform(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
final PsiIdentifier classNameIdentifier = (PsiIdentifier)descriptor.getPsiElement();
final PsiClass interfaceClass = (PsiClass)classNameIdentifier.getParent();
final SearchScope searchScope = interfaceClass.getUseScope();
@@ -98,30 +93,13 @@ public class ClassMayBeInterfaceInspection extends BaseInspection {
for (final PsiClass inheritor : ClassInheritorsSearch.search(interfaceClass, searchScope, false)) {
elements.add(inheritor);
}
if (!FileModificationService.getInstance().preparePsiElementsForWrite(elements)) {
return;
}
WriteAction.run(() -> {
moveSubClassExtendsToImplements(elements);
changeClassToInterface(interfaceClass);
moveImplementsToExtends(interfaceClass);
return ModCommands.psiUpdate(interfaceClass, (cls, updater) -> {
moveSubClassExtendsToImplements(ContainerUtil.map(elements, updater::getWritable));
changeClassToInterface(cls);
moveImplementsToExtends(cls);
});
}
@Override
public @NotNull IntentionPreviewInfo generatePreview(@NotNull Project project, @NotNull ProblemDescriptor previewDescriptor) {
final PsiIdentifier classNameIdentifier = (PsiIdentifier)previewDescriptor.getPsiElement();
final PsiClass interfaceClass = (PsiClass)classNameIdentifier.getParent();
// In preview, limit search to current file only
List<PsiClass> inheritorsInThisFile = SyntaxTraverser.psiTraverser(classNameIdentifier.getContainingFile()).filter(PsiClass.class)
.filter(cls -> cls.isInheritor(interfaceClass, false))
.toList();
moveSubClassExtendsToImplements(ContainerUtil.prepend(inheritorsInThisFile, interfaceClass));
changeClassToInterface(interfaceClass);
moveImplementsToExtends(interfaceClass);
return IntentionPreviewInfo.DIFF;
}
private static void changeClassToInterface(PsiClass aClass) {
for (PsiMethod method : aClass.getMethods()) {
if (isEmptyConstructor(method)) {
@@ -258,7 +236,7 @@ public class ClassMayBeInterfaceInspection extends BaseInspection {
return allMethodsPublicAbstract(aClass) && allFieldsPublicStaticFinal(aClass) && allInnerClassesPublic(aClass);
}
private boolean allFieldsPublicStaticFinal(PsiClass aClass) {
private static boolean allFieldsPublicStaticFinal(PsiClass aClass) {
boolean allFieldsStaticFinal = true;
final PsiField[] fields = aClass.getFields();
for (final PsiField field : fields) {
@@ -292,7 +270,7 @@ public class ClassMayBeInterfaceInspection extends BaseInspection {
return true;
}
private boolean allInnerClassesPublic(PsiClass aClass) {
private static boolean allInnerClassesPublic(PsiClass aClass) {
final PsiClass[] innerClasses = aClass.getInnerClasses();
for (PsiClass innerClass : innerClasses) {
if (!innerClass.hasModifierProperty(PsiModifier.PUBLIC)) {
@@ -3,8 +3,11 @@ package com.siyeh.ig.style;
import com.intellij.codeInsight.FileModificationService;
import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.ide.DataManager;
import com.intellij.modcommand.ModPsiUpdater;
import com.intellij.modcommand.PsiUpdateModCommandQuickFix;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.WriteAction;
@@ -39,7 +42,7 @@ public class MethodRefCanBeReplacedWithLambdaInspection extends BaseInspection {
@Nullable
@Override
protected InspectionGadgetsFix buildFix(Object... infos) {
protected LocalQuickFix buildFix(Object... infos) {
final PsiMethodReferenceExpression methodReferenceExpression = (PsiMethodReferenceExpression)infos[0];
final boolean onTheFly = (Boolean)infos[1];
if (LambdaRefactoringUtil.canConvertToLambdaWithoutSideEffects(methodReferenceExpression)) {
@@ -61,7 +64,7 @@ public class MethodRefCanBeReplacedWithLambdaInspection extends BaseInspection {
}
}
private static class MethodRefToLambdaFix extends InspectionGadgetsFix {
private static class MethodRefToLambdaFix extends PsiUpdateModCommandQuickFix {
@Nls
@NotNull
@Override
@@ -70,26 +73,22 @@ public class MethodRefCanBeReplacedWithLambdaInspection extends BaseInspection {
}
@Override
protected void doFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
final PsiElement element = descriptor.getPsiElement();
if (element instanceof PsiMethodReferenceExpression) {
doFix(project, (PsiMethodReferenceExpression)element);
protected void applyFix(@NotNull Project project, @NotNull PsiElement element, @NotNull ModPsiUpdater updater) {
if (element instanceof PsiMethodReferenceExpression methodRef) {
LambdaRefactoringUtil.convertMethodReferenceToLambda(methodRef, false, true);
}
}
protected void doFix(Project project, @NotNull PsiMethodReferenceExpression methodReferenceExpression) {
LambdaRefactoringUtil.convertMethodReferenceToLambda(methodReferenceExpression, false, true);
}
}
private static class SideEffectsMethodRefToLambdaFix extends MethodRefToLambdaFix {
private static class SideEffectsMethodRefToLambdaFix extends InspectionGadgetsFix {
@Nls
@NotNull
@Override
public String getFamilyName() {
return ApplicationManager.getApplication().isUnitTestMode() ? (InspectionGadgetsBundle
.message("side.effects.method.ref.to.lambda.fix.family.name",
super.getFamilyName())) : super.getFamilyName();
return ApplicationManager.getApplication().isUnitTestMode() ?
(InspectionGadgetsBundle.message("side.effects.method.ref.to.lambda.fix.family.name",
InspectionGadgetsBundle.message("method.ref.can.be.replaced.with.lambda.quickfix"))) :
InspectionGadgetsBundle.message("method.ref.can.be.replaced.with.lambda.quickfix");
}
@Override
@@ -97,20 +96,6 @@ public class MethodRefCanBeReplacedWithLambdaInspection extends BaseInspection {
return false;
}
@Override
protected void doFix(Project project, @NotNull PsiMethodReferenceExpression methodReferenceExpression) {
DataManager.getInstance()
.getDataContextFromFocusAsync()
.onSuccess(context -> {
final Editor editor = CommonDataKeys.EDITOR.getData(context);
if (editor != null) {
CommandProcessor.getInstance()
.executeCommand(project, () -> doFixAndRemoveSideEffects(editor, methodReferenceExpression),
getFamilyName(), null);
}
});
}
@Override
public @NotNull IntentionPreviewInfo generatePreview(@NotNull Project project, @NotNull ProblemDescriptor previewDescriptor) {
PsiMethodReferenceExpression methodRef = ObjectUtils.tryCast(previewDescriptor.getPsiElement(), PsiMethodReferenceExpression.class);
@@ -121,6 +106,22 @@ public class MethodRefCanBeReplacedWithLambdaInspection extends BaseInspection {
return IntentionPreviewInfo.DIFF;
}
@Override
protected void doFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
final PsiElement element = descriptor.getPsiElement();
if (element instanceof PsiMethodReferenceExpression methodRef) {
DataManager.getInstance()
.getDataContextFromFocusAsync()
.onSuccess(context -> {
final Editor editor = CommonDataKeys.EDITOR.getData(context);
if (editor != null) {
CommandProcessor.getInstance()
.executeCommand(project, () -> doFixAndRemoveSideEffects(editor, methodRef), getFamilyName(), null);
}
});
}
}
private static void doFixAndRemoveSideEffects(@NotNull Editor editor, @NotNull PsiMethodReferenceExpression methodReferenceExpression) {
if (!FileModificationService.getInstance().preparePsiElementsForWrite(methodReferenceExpression)) return;
final PsiLambdaExpression lambdaExpression =