mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-02-04 23:39:07 +07:00
[mod-commands] ModShowConflicts; use in MakeClassFinalFix
GitOrigin-RevId: 50c4332cf67c60462446c4b470526301c076412b
This commit is contained in:
committed by
intellij-monorepo-bot
parent
edc2082508
commit
e26af76325
@@ -232,3 +232,4 @@ modcommand.result.action.completed.successfully=Action completed successfully
|
||||
modcommand.result.action.is.interactive.only.cannot.be.executed.in.batch=Action is interactive only; cannot be executed in batch
|
||||
modcommand.result.action.has.no.effect=Action has no effect
|
||||
modcommand.result.action.was.aborted=Action was aborted
|
||||
modcommand.result.conflict=There are conflicts; user interaction required
|
||||
|
||||
@@ -183,6 +183,9 @@ public final class IntentionPreviewUtils {
|
||||
else if (command instanceof ModChooseAction target) {
|
||||
return getChoosePreview(context, target);
|
||||
}
|
||||
else if (command instanceof ModShowConflicts showConflicts) {
|
||||
return getModCommandPreview(showConflicts.nextStep(), context);
|
||||
}
|
||||
else if (command instanceof ModDisplayMessage message) {
|
||||
if (message.kind() == ModDisplayMessage.MessageKind.ERROR) {
|
||||
return new IntentionPreviewInfo.Html(new HtmlBuilder().append(
|
||||
|
||||
@@ -16,7 +16,7 @@ import java.util.Set;
|
||||
*/
|
||||
public sealed interface ModCommand
|
||||
permits ModChooseAction, ModCompositeCommand, ModCopyToClipboard, ModCreateFile, ModDeleteFile, ModDisplayMessage, ModHighlight,
|
||||
ModNavigate, ModNothing, ModRenameSymbol, ModUpdateFileText {
|
||||
ModNavigate, ModNothing, ModRenameSymbol, ModShowConflicts, ModUpdateFileText {
|
||||
|
||||
/**
|
||||
* @return true if the command does nothing
|
||||
|
||||
@@ -44,8 +44,8 @@ public interface ModCommandExecutor {
|
||||
|
||||
sealed interface BatchExecutionResult {
|
||||
default @NotNull BatchExecutionResult compose(@NotNull BatchExecutionResult next) {
|
||||
if (next == Result.NOTHING || next.equals(this) || this instanceof Error) return this;
|
||||
if (this == Result.NOTHING || next instanceof Error) return next;
|
||||
if (next == Result.NOTHING || next.equals(this) || this instanceof Error || this == Result.CONFLICTS) return this;
|
||||
if (this == Result.NOTHING || next instanceof Error || next == Result.CONFLICTS) return next;
|
||||
if (this == Result.ABORT || next == Result.ABORT) return Result.ABORT;
|
||||
return Result.SUCCESS;
|
||||
}
|
||||
@@ -69,8 +69,11 @@ public interface ModCommandExecutor {
|
||||
/**
|
||||
* Action was aborted
|
||||
*/
|
||||
ABORT;
|
||||
|
||||
ABORT,
|
||||
/**
|
||||
* Conflicts should be displayed and confirmed
|
||||
*/
|
||||
CONFLICTS;
|
||||
|
||||
@Nls
|
||||
@Override
|
||||
@@ -80,6 +83,7 @@ public interface ModCommandExecutor {
|
||||
case INTERACTIVE -> AnalysisBundle.message("modcommand.result.action.is.interactive.only.cannot.be.executed.in.batch");
|
||||
case NOTHING -> AnalysisBundle.message("modcommand.result.action.has.no.effect");
|
||||
case ABORT -> AnalysisBundle.message("modcommand.result.action.was.aborted");
|
||||
case CONFLICTS -> AnalysisBundle.message("modcommand.result.conflict");
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
// 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.modcommand;
|
||||
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public record ModShowConflicts(@NotNull Map<@NotNull PsiElement, @NotNull Conflict> conflicts,
|
||||
@NotNull ModCommand nextStep) implements ModCommand {
|
||||
public record Conflict(@NotNull List<@NotNull @Nls String> messages) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return conflicts().isEmpty() && nextStep().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Set<@NotNull VirtualFile> modifiedFiles() {
|
||||
return nextStep.modifiedFiles();
|
||||
}
|
||||
}
|
||||
@@ -42,9 +42,11 @@ import com.intellij.psi.search.LocalSearchScope;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.refactoring.RefactoringBundle;
|
||||
import com.intellij.refactoring.ui.ConflictsDialog;
|
||||
import com.intellij.util.concurrency.AppExecutorUtil;
|
||||
import com.intellij.util.concurrency.annotations.RequiresEdt;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -106,6 +108,12 @@ public class ModCommandExecutorImpl implements ModCommandExecutor {
|
||||
command instanceof ModCopyToClipboard || command instanceof ModRenameSymbol) {
|
||||
return Result.INTERACTIVE;
|
||||
}
|
||||
if (command instanceof ModShowConflicts showConflicts) {
|
||||
if (showConflicts.conflicts().isEmpty()) {
|
||||
return executeInBatch(context, showConflicts.nextStep());
|
||||
}
|
||||
return Result.CONFLICTS;
|
||||
}
|
||||
if (command instanceof ModDisplayMessage message) {
|
||||
if (message.kind() == ModDisplayMessage.MessageKind.ERROR) {
|
||||
return new Error(message.messageText());
|
||||
@@ -174,9 +182,25 @@ public class ModCommandExecutorImpl implements ModCommandExecutor {
|
||||
if (command instanceof ModDeleteFile deleteFile) {
|
||||
return executeDelete(project, deleteFile);
|
||||
}
|
||||
if (command instanceof ModShowConflicts showConflicts) {
|
||||
return executeShowConflicts(context, showConflicts, editor);
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown command: " + command);
|
||||
}
|
||||
|
||||
private boolean executeShowConflicts(@NotNull ActionContext context, @NotNull ModShowConflicts conflicts, @Nullable Editor editor) {
|
||||
MultiMap<PsiElement, String> conflictData = new MultiMap<>();
|
||||
conflicts.conflicts().forEach((e, c) -> conflictData.put(e, c.messages()));
|
||||
if (!conflictData.isEmpty()) {
|
||||
var conflictsDialog =
|
||||
new ConflictsDialog(context.project(), conflictData, () -> doExecuteInteractively(context, conflicts.nextStep(), editor));
|
||||
if (!conflictsDialog.showAndGet()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return doExecuteInteractively(context, conflicts.nextStep(), editor);
|
||||
}
|
||||
|
||||
private static boolean executeCopyToClipboard(@NotNull ModCopyToClipboard clipboard) {
|
||||
CopyPasteManager.getInstance().setContents(new StringSelection(clipboard.content()));
|
||||
return true;
|
||||
|
||||
@@ -1,34 +1,35 @@
|
||||
/*
|
||||
* 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.siyeh.ig.fixes;
|
||||
|
||||
import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo;
|
||||
import com.intellij.codeInsight.intention.preview.IntentionPreviewUtils;
|
||||
import com.intellij.codeInspection.ModCommands;
|
||||
import com.intellij.codeInspection.ProblemDescriptor;
|
||||
import com.intellij.openapi.application.WriteAction;
|
||||
import com.intellij.modcommand.ModCommand;
|
||||
import com.intellij.modcommand.ModCommandQuickFix;
|
||||
import com.intellij.modcommand.ModShowConflicts;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiModifierList;
|
||||
import com.intellij.psi.search.searches.ClassInheritorsSearch;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.refactoring.ui.ConflictsDialog;
|
||||
import com.intellij.refactoring.util.RefactoringUIUtil;
|
||||
import com.intellij.util.Query;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import com.siyeh.InspectionGadgetsBundle;
|
||||
import com.siyeh.ig.InspectionGadgetsFix;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.intellij.psi.PsiModifier.ABSTRACT;
|
||||
import static com.intellij.psi.PsiModifier.FINAL;
|
||||
|
||||
/**
|
||||
* @author Bas Leijdekkers
|
||||
*/
|
||||
public class MakeClassFinalFix extends InspectionGadgetsFix {
|
||||
public class MakeClassFinalFix extends ModCommandQuickFix {
|
||||
|
||||
private final String className;
|
||||
|
||||
@@ -50,41 +51,26 @@ public class MakeClassFinalFix extends InspectionGadgetsFix {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
public @NotNull ModCommand perform(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
final PsiElement element = descriptor.getPsiElement();
|
||||
final PsiClass containingClass = findClassToFix(element);
|
||||
if (containingClass == null) {
|
||||
return;
|
||||
return ModCommands.nop();
|
||||
}
|
||||
final PsiModifierList modifierList = containingClass.getModifierList();
|
||||
assert modifierList != null;
|
||||
if (!isOnTheFly()) {
|
||||
if (ClassInheritorsSearch.search(containingClass).findFirst() != null) {
|
||||
return;
|
||||
}
|
||||
WriteAction.run(() -> doMakeFinal(modifierList));
|
||||
return;
|
||||
}
|
||||
final MultiMap<PsiElement, String> conflicts = new MultiMap<>();
|
||||
final Query<PsiClass> search = ClassInheritorsSearch.search(containingClass);
|
||||
search.forEach(aClass -> {
|
||||
conflicts.putValue(containingClass, InspectionGadgetsBundle
|
||||
.message("0.will.no.longer.be.overridable.by.1", RefactoringUIUtil.getDescription(containingClass, false),
|
||||
RefactoringUIUtil.getDescription(aClass, false)));
|
||||
return true;
|
||||
});
|
||||
final boolean conflictsDialogOK;
|
||||
if (!conflicts.isEmpty()) {
|
||||
ConflictsDialog conflictsDialog =
|
||||
new ConflictsDialog(element.getProject(), conflicts, () -> WriteAction.run(() -> doMakeFinal(modifierList)));
|
||||
conflictsDialogOK = conflictsDialog.showAndGet();
|
||||
}
|
||||
else {
|
||||
conflictsDialogOK = true;
|
||||
}
|
||||
if (conflictsDialogOK) {
|
||||
WriteAction.run(() -> doMakeFinal(modifierList));
|
||||
final List<String> conflictMessages = new ArrayList<>();
|
||||
if (!IntentionPreviewUtils.isIntentionPreviewActive()) {
|
||||
final Query<PsiClass> search = ClassInheritorsSearch.search(containingClass);
|
||||
search.forEach(aClass -> {
|
||||
conflictMessages.add(InspectionGadgetsBundle
|
||||
.message("0.will.no.longer.be.overridable.by.1", RefactoringUIUtil.getDescription(containingClass, false),
|
||||
RefactoringUIUtil.getDescription(aClass, false)));
|
||||
return true;
|
||||
});
|
||||
}
|
||||
ModShowConflicts.Conflict conflict = new ModShowConflicts.Conflict(conflictMessages);
|
||||
return new ModShowConflicts(Map.of(containingClass, conflict), ModCommands.psiUpdate(modifierList, list -> doMakeFinal(list)));
|
||||
}
|
||||
|
||||
private static void doMakeFinal(PsiModifierList modifierList) {
|
||||
@@ -99,27 +85,4 @@ public class MakeClassFinalFix extends InspectionGadgetsFix {
|
||||
}
|
||||
return containingClass.getModifierList() == null ? null : containingClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull IntentionPreviewInfo generatePreview(@NotNull Project project, @NotNull ProblemDescriptor previewDescriptor) {
|
||||
final PsiClass aClass = findClassToFix(previewDescriptor.getPsiElement());
|
||||
if (aClass == null) {
|
||||
return IntentionPreviewInfo.EMPTY;
|
||||
}
|
||||
final PsiModifierList modifierList = aClass.getModifierList();
|
||||
assert modifierList != null;
|
||||
doMakeFinal(modifierList);
|
||||
return IntentionPreviewInfo.DIFF;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiElement getElementToMakeWritable(@NotNull PsiFile currentFile) {
|
||||
return currentFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startInWriteAction() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.siyeh.ig.initialization;
|
||||
|
||||
import com.intellij.codeInspection.LocalQuickFix;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
@@ -35,21 +36,21 @@ import java.util.List;
|
||||
public class OverridableMethodCallDuringObjectConstructionInspection extends BaseInspection {
|
||||
|
||||
@Override
|
||||
protected InspectionGadgetsFix @NotNull [] buildFixes(Object... infos) {
|
||||
protected LocalQuickFix @NotNull [] buildFixes(Object... infos) {
|
||||
final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)infos[0];
|
||||
final PsiClass callClass = ClassUtils.getContainingClass(methodCallExpression);
|
||||
final PsiMethod method = methodCallExpression.resolveMethod();
|
||||
if (method == null || callClass == null || MethodUtils.isOverriddenInHierarchy(method, callClass)) {
|
||||
return InspectionGadgetsFix.EMPTY_ARRAY;
|
||||
}
|
||||
final List<InspectionGadgetsFix> fixes = new SmartList<>();
|
||||
final List<LocalQuickFix> fixes = new SmartList<>();
|
||||
if (!callClass.hasModifierProperty(PsiModifier.ABSTRACT)) {
|
||||
fixes.add(new MakeClassFinalFix(callClass));
|
||||
}
|
||||
if (!(method instanceof PsiCompiledElement) && !method.hasModifierProperty(PsiModifier.ABSTRACT)) {
|
||||
fixes.add(new MakeMethodFinalFix(method.getName()));
|
||||
}
|
||||
return fixes.toArray(InspectionGadgetsFix.EMPTY_ARRAY);
|
||||
return fixes.toArray(LocalQuickFix.EMPTY_ARRAY);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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.siyeh.ig.initialization;
|
||||
|
||||
import com.intellij.codeInsight.intention.IntentionAction;
|
||||
@@ -30,7 +30,11 @@ public class OverridableMethodCallDuringObjectConstructionInspectionTest extends
|
||||
final IntentionAction intention = myFixture.getAvailableIntention(InspectionGadgetsBundle.message("make.method.final.fix.name", "a"));
|
||||
assertNotNull(intention);
|
||||
String customPreviewText = myFixture.getIntentionPreviewText(intention);
|
||||
assertEquals("public final void a() {}", customPreviewText);
|
||||
assertEquals("""
|
||||
class One {
|
||||
public final void a() {}
|
||||
}
|
||||
""", customPreviewText);
|
||||
}
|
||||
|
||||
public void testNoQuickFixes() {
|
||||
|
||||
Reference in New Issue
Block a user