diff --git a/java/java-impl/src/com/intellij/codeInsight/ExternalAnnotationsLineMarkerProvider.java b/java/java-impl/src/com/intellij/codeInsight/ExternalAnnotationsLineMarkerProvider.java index d5d276c689ce..d71631b96e0f 100644 --- a/java/java-impl/src/com/intellij/codeInsight/ExternalAnnotationsLineMarkerProvider.java +++ b/java/java-impl/src/com/intellij/codeInsight/ExternalAnnotationsLineMarkerProvider.java @@ -20,10 +20,10 @@ import com.intellij.codeInsight.daemon.GutterIconNavigationHandler; import com.intellij.codeInsight.daemon.LineMarkerInfo; import com.intellij.codeInsight.daemon.LineMarkerProviderDescriptor; import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.codeInsight.intention.IntentionActionDelegate; import com.intellij.codeInsight.intention.IntentionManager; import com.intellij.codeInsight.intention.impl.AddAnnotationIntention; import com.intellij.codeInsight.intention.impl.DeannotateIntentionAction; -import com.intellij.codeInsight.intention.impl.config.IntentionActionWrapper; import com.intellij.codeInsight.javadoc.JavaDocInfoGenerator; import com.intellij.codeInspection.dataFlow.EditContractIntention; import com.intellij.icons.AllIcons; @@ -212,7 +212,7 @@ public class ExternalAnnotationsLineMarkerProvider extends LineMarkerProviderDes action instanceof EditContractIntention || action instanceof ToggleSourceInferredAnnotations || action instanceof MakeInferredAnnotationExplicit || - action instanceof IntentionActionWrapper && shouldShowInGutterPopup(((IntentionActionWrapper)action).getDelegate()); + action instanceof IntentionActionDelegate && shouldShowInGutterPopup(((IntentionActionDelegate)action).getDelegate()); } } } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/DaemonRespondToChangesTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/DaemonRespondToChangesTest.java index 06b13fee91d8..f3e88757e689 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/DaemonRespondToChangesTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/DaemonRespondToChangesTest.java @@ -19,7 +19,6 @@ import com.intellij.codeHighlighting.*; import com.intellij.codeInsight.EditorInfo; import com.intellij.codeInsight.completion.CompletionContributor; import com.intellij.codeInsight.daemon.*; -import com.intellij.codeInsight.daemon.impl.quickfix.DeleteCatchFix; import com.intellij.codeInsight.daemon.quickFix.LightQuickFixTestCase; import com.intellij.codeInsight.folding.CodeFoldingManager; import com.intellij.codeInsight.hint.EditorHintListener; @@ -27,9 +26,7 @@ import com.intellij.codeInsight.intention.AbstractIntentionAction; import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInsight.intention.IntentionManager; import com.intellij.codeInsight.intention.impl.IntentionHintComponent; -import com.intellij.codeInspection.InspectionProfile; -import com.intellij.codeInspection.LocalInspectionTool; -import com.intellij.codeInspection.ProblemsHolder; +import com.intellij.codeInspection.*; import com.intellij.codeInspection.accessStaticViaInstance.AccessStaticViaInstance; import com.intellij.codeInspection.deadCode.UnusedDeclarationInspection; import com.intellij.codeInspection.deadCode.UnusedDeclarationInspectionBase; @@ -977,8 +974,7 @@ public class DaemonRespondToChangesTest extends DaemonAnalyzerTestCase { new GotoNextErrorHandler(true).invoke(getProject(), getEditor(), getFile()); List fixes = LightQuickFixTestCase.getAvailableActions(getEditor(), getFile()); - IntentionAction fix = assertContainsOneOf(fixes, DeleteCatchFix.class); - assertEquals("Delete catch for 'java.io.IOException'", fix.getText()); + IntentionAction fix = assertContainsOneOf(fixes, "Delete catch for 'java.io.IOException'"); final IntentionAction finalFix = fix; WriteCommandAction.runWriteCommandAction(getProject(), () -> finalFix.invoke(getProject(), getEditor(), getFile())); @@ -988,8 +984,7 @@ public class DaemonRespondToChangesTest extends DaemonAnalyzerTestCase { new GotoNextErrorHandler(true).invoke(getProject(), getEditor(), getFile()); fixes = LightQuickFixTestCase.getAvailableActions(getEditor(), getFile()); - fix = assertContainsOneOf(fixes, DeleteCatchFix.class); - assertEquals("Delete catch for 'java.io.IOException'", fix.getText()); + fix = assertContainsOneOf(fixes, "Delete catch for 'java.io.IOException'"); final IntentionAction finalFix1 = fix; WriteCommandAction.runWriteCommandAction(getProject(), () -> finalFix1.invoke(getProject(), getEditor(), getFile())); @@ -999,8 +994,7 @@ public class DaemonRespondToChangesTest extends DaemonAnalyzerTestCase { new GotoNextErrorHandler(true).invoke(getProject(), getEditor(), getFile()); fixes = LightQuickFixTestCase.getAvailableActions(getEditor(), getFile()); - fix = assertContainsOneOf(fixes, DeleteCatchFix.class); - assertEquals("Delete catch for 'java.io.IOException'", fix.getText()); + fix = assertContainsOneOf(fixes, "Delete catch for 'java.io.IOException'"); final IntentionAction finalFix2 = fix; WriteCommandAction.runWriteCommandAction(getProject(), () -> finalFix2.invoke(getProject(), getEditor(), getFile())); @@ -1013,19 +1007,19 @@ public class DaemonRespondToChangesTest extends DaemonAnalyzerTestCase { } } - private static T assertContainsOneOf(@NotNull Collection collection, @NotNull Class aClass) { - T result = null; - for (T t : collection) { - if (aClass.isInstance(t)) { + private static IntentionAction assertContainsOneOf(@NotNull Collection collection, @NotNull String text) { + IntentionAction result = null; + for (IntentionAction action : collection) { + if (text.equals(action.getText())) { if (result != null) { - fail("multiple " + aClass.getName() + " objects present in collection " + collection); + fail("multiple " + " objects present in collection " + collection); } else { - result = t; + result = action; } } } - assertNotNull(aClass.getName() + " object not found in collection " + collection, result); + assertNotNull(" object not found in collection " + collection, result); return result; } @@ -1330,8 +1324,7 @@ public class DaemonRespondToChangesTest extends DaemonAnalyzerTestCase { CodeInsightTestFixtureImpl.invokeIntention(descriptor.getAction(), getFile(), getEditor(), ""); highlightErrors(); - actions = ShowIntentionsPass.getAvailableFixes(getEditor(), getFile(), -1); - assertEmpty(actions); + assertEmpty(ShowIntentionsPass.getAvailableFixes(getEditor(), getFile(), -1)); } @@ -2608,5 +2601,90 @@ public class DaemonRespondToChangesTest extends DaemonAnalyzerTestCase { } } } + + public void testDumbQuickFixIsNoLongerVisibleAfterApplied() { + MyInspection tool = new MyInspection(); + enableInspectionTool(tool); + disposeOnTearDown(() -> disableInspectionTool(tool.getShortName())); + + @Language("JAVA") + String text = "class X { void f() { if (this == null) {} else return; } }"; + configureByText(JavaFileType.INSTANCE, text); + WriteCommandAction.runWriteCommandAction(getProject(), () -> myEditor.getDocument().setText(text)); + getEditor().getCaretModel().moveToOffset(getFile().getText().indexOf("if (") + 1); + assertEmpty(doHighlighting(HighlightSeverity.ERROR)); + List fixes = findStupidFixes(); + IntentionAction fix = assertOneElement(fixes); + fix.invoke(getProject(), getEditor(), getFile()); + + fixes = findStupidFixes(); + assertEmpty(fixes); + + assertEmpty(doHighlighting(HighlightSeverity.ERROR)); + fixes = findStupidFixes(); + assertEmpty(fixes); + } + + private List findStupidFixes() { + return CodeInsightTestFixtureImpl.getAvailableIntentions(getEditor(), getFile()) + .stream().filter(f->f.getFamilyName().equals(new MyInspection.StupidQuickFixWhichDoesntCheckItsOwnApplicability().getFamilyName())) + .collect(Collectors.toList()); + } + + private static class MyInspection extends LocalInspectionTool { + @Nls + @NotNull + @Override + public String getGroupDisplayName() { + return "danuna"; + } + + @Nls + @NotNull + @Override + public String getDisplayName() { + return getGroupDisplayName(); + } + + @NotNull + @Override + public String getShortName() { + return getGroupDisplayName(); + } + + @NotNull + @Override + public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) { + return new JavaElementVisitor() { + @Override + public void visitIfStatement(PsiIfStatement statement) { + if (statement.getElseBranch() != null) { + PsiKeyword keyw = (PsiKeyword)statement.getChildren()[0]; + holder.registerProblem(keyw, "dododo", new StupidQuickFixWhichDoesntCheckItsOwnApplicability()); + } + } + }; + } + private static class StupidQuickFixWhichDoesntCheckItsOwnApplicability implements LocalQuickFix { + @Nls + @NotNull + @Override + public String getName() { + return "danu"; + } + + @Nls + @NotNull + @Override + public String getFamilyName() { + return getName(); + } + + @Override + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + WriteCommandAction.runWriteCommandAction(project, () -> ((PsiIfStatement)descriptor.getPsiElement().getParent()).getElseBranch().delete()); + } + } + } } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/AddMethodQualifierTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/AddMethodQualifierTest.java index 9791c0a89366..240497223dbc 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/AddMethodQualifierTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/AddMethodQualifierTest.java @@ -18,13 +18,16 @@ package com.intellij.codeInsight.daemon.quickFix; import com.intellij.JavaTestUtil; import com.intellij.codeInsight.daemon.impl.quickfix.AddMethodQualifierFix; import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.codeInsight.intention.IntentionActionDelegate; import com.intellij.psi.PsiNamedElement; import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.Nullable; -import java.util.*; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; /** * @author Dmitry Batkovich @@ -93,9 +96,10 @@ public class AddMethodQualifierTest extends JavaCodeInsightFixtureTestCase { private AddMethodQualifierFix getQuickFix() { final List availableIntentions = myFixture.getAvailableIntentions(); AddMethodQualifierFix addMethodQualifierFix = null; - for (final IntentionAction availableIntention : availableIntentions) { - if (availableIntention instanceof AddMethodQualifierFix) { - addMethodQualifierFix = (AddMethodQualifierFix)availableIntention; + for (IntentionAction action : availableIntentions) { + if (action instanceof IntentionActionDelegate) action = ((IntentionActionDelegate)action).getDelegate(); + if (action instanceof AddMethodQualifierFix) { + addMethodQualifierFix = (AddMethodQualifierFix)action; break; } } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/EmptyIntentionInspectionQuickFixTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/EmptyIntentionInspectionQuickFixTest.java index 50258fd8f302..82c226172eb1 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/EmptyIntentionInspectionQuickFixTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/EmptyIntentionInspectionQuickFixTest.java @@ -2,6 +2,7 @@ package com.intellij.codeInsight.daemon.quickFix; import com.intellij.codeInsight.intention.EmptyIntentionAction; import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.codeInsight.intention.IntentionActionDelegate; import com.intellij.codeInspection.LocalInspectionTool; import com.intellij.codeInspection.ProblemsHolder; import com.intellij.codeInspection.defUse.DefUseInspection; @@ -69,6 +70,7 @@ public class EmptyIntentionInspectionQuickFixTest extends LightQuickFixTestCase List emptyActions = getAvailableActions(); for (int i = emptyActions.size()-1; i>=0; i--) { IntentionAction action = emptyActions.get(i); + if (action instanceof IntentionActionDelegate) action = ((IntentionActionDelegate)action).getDelegate(); if (!(action instanceof EmptyIntentionAction)) emptyActions.remove(i); } assertEquals(emptyActions.toString(), 1, emptyActions.size()); @@ -79,17 +81,20 @@ public class EmptyIntentionInspectionQuickFixTest extends LightQuickFixTestCase List emptyActions = getAvailableActions(); int i = 0; for(;i < emptyActions.size(); i++) { - final IntentionAction intentionAction = emptyActions.get(i); - if ("Make 'i' not final".equals(intentionAction.getText())) { + IntentionAction action = emptyActions.get(i); + if (action instanceof IntentionActionDelegate) action = ((IntentionActionDelegate)action).getDelegate(); + if ("Make 'i' not final".equals(action.getText())) { break; } - if (intentionAction instanceof EmptyIntentionAction) { + if (action instanceof EmptyIntentionAction) { fail("Low priority action prior to quick fix"); } } assertTrue(i < emptyActions.size()); for (; i < emptyActions.size(); i++) { - if (emptyActions.get(i) instanceof EditInspectionToolsSettingsAction) { + IntentionAction action = emptyActions.get(i); + if (action instanceof IntentionActionDelegate) action = ((IntentionActionDelegate)action).getDelegate(); + if (action instanceof EditInspectionToolsSettingsAction) { return; } } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/intention/AddImportActionTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/intention/AddImportActionTest.groovy index ae700318abd8..46402e71ee78 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/intention/AddImportActionTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/intention/AddImportActionTest.groovy @@ -152,7 +152,7 @@ public class Foo { Map l = new HashMap<>(); } ''' - ImportClassFix intention = myFixture.findSingleIntention("Import class") as ImportClassFix + ImportClassFix intention = myFixture.findSingleIntention("Import class").delegate as ImportClassFix assert intention.classesToImport.collect { it.qualifiedName } == ['java.util.Map'] } diff --git a/java/testFramework/src/com/intellij/codeInsight/daemon/quickFix/ActionHint.java b/java/testFramework/src/com/intellij/codeInsight/daemon/quickFix/ActionHint.java index 15d0e5636a05..ec9ba3eedbc9 100644 --- a/java/testFramework/src/com/intellij/codeInsight/daemon/quickFix/ActionHint.java +++ b/java/testFramework/src/com/intellij/codeInsight/daemon/quickFix/ActionHint.java @@ -16,6 +16,7 @@ package com.intellij.codeInsight.daemon.quickFix; import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.codeInsight.intention.IntentionActionDelegate; import com.intellij.codeInspection.ProblemHighlightType; import com.intellij.codeInspection.ex.QuickFixWrapper; import com.intellij.lang.Commenter; @@ -88,7 +89,9 @@ public class ActionHint { fail("Action with text '" + myExpectedText + "' not found\nAvailable actions: " + actions.stream().map(IntentionAction::getText).collect(Collectors.joining(", ", "[", "]\n")) + infoSupplier.get()); - } else if(myHighlightType != null) { + } + else if(myHighlightType != null) { + if (result instanceof IntentionActionDelegate) result = ((IntentionActionDelegate)result).getDelegate(); if(!(result instanceof QuickFixWrapper)) { fail("Action with text '" + myExpectedText + "' is not a LocalQuickFix, but " + result.getClass().getName() + "\nExpected LocalQuickFix with ProblemHighlightType=" + myHighlightType + "\n" + @@ -144,8 +147,7 @@ public class ActionHint { String state = matcher.group(2); if(state.equals("true") || state.equals("false")) { return new ActionHint(text, Boolean.parseBoolean(state), null); - } else { - return new ActionHint(text, true, ProblemHighlightType.valueOf(state)); } + return new ActionHint(text, true, ProblemHighlightType.valueOf(state)); } } diff --git a/platform/analysis-api/src/com/intellij/codeInsight/intention/IntentionActionDelegate.java b/platform/analysis-api/src/com/intellij/codeInsight/intention/IntentionActionDelegate.java new file mode 100644 index 000000000000..47142414814f --- /dev/null +++ b/platform/analysis-api/src/com/intellij/codeInsight/intention/IntentionActionDelegate.java @@ -0,0 +1,23 @@ +/* + * 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. + */ +package com.intellij.codeInsight.intention; + +import org.jetbrains.annotations.NotNull; + +public interface IntentionActionDelegate { + @NotNull + IntentionAction getDelegate(); +} diff --git a/platform/lang-api/src/com/intellij/codeInspection/IntentionWrapper.java b/platform/lang-api/src/com/intellij/codeInspection/IntentionWrapper.java index 5f5fa138a32d..781421ecc77a 100644 --- a/platform/lang-api/src/com/intellij/codeInspection/IntentionWrapper.java +++ b/platform/lang-api/src/com/intellij/codeInspection/IntentionWrapper.java @@ -16,6 +16,7 @@ package com.intellij.codeInspection; import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.codeInsight.intention.IntentionActionDelegate; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.fileEditor.FileEditor; import com.intellij.openapi.fileEditor.FileEditorManager; @@ -34,7 +35,7 @@ import org.jetbrains.annotations.Nullable; * Date: 4/20/11 * Time: 9:27 PM */ -public class IntentionWrapper implements LocalQuickFix, IntentionAction, ActionClassHolder { +public class IntentionWrapper implements LocalQuickFix, IntentionAction, ActionClassHolder, IntentionActionDelegate { private final IntentionAction myAction; private final PsiFile myFile; @@ -101,5 +102,11 @@ public class IntentionWrapper implements LocalQuickFix, IntentionAction, ActionC public Class getActionClass() { return getAction().getClass(); } + + @NotNull + @Override + public IntentionAction getDelegate() { + return myAction; + } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/ShowIntentionsPass.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/ShowIntentionsPass.java index 863a16d99342..cecc91870338 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/ShowIntentionsPass.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/ShowIntentionsPass.java @@ -85,6 +85,19 @@ public class ShowIntentionsPass extends TextEditorHighlightingPass { private volatile boolean myShowBulb; private volatile boolean myHasToRecreate; + ShowIntentionsPass(@NotNull Project project, @NotNull Editor editor, int passId) { + super(project, editor.getDocument(), false); + myPassIdToShowIntentionsFor = passId; + ApplicationManager.getApplication().assertIsDispatchThread(); + + myEditor = editor; + + PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project); + + myFile = documentManager.getPsiFile(myEditor.getDocument()); + assert myFile != null : FileDocumentManager.getInstance().getFile(myEditor.getDocument()); + } + @NotNull public static List getAvailableFixes(@NotNull final Editor editor, @NotNull final PsiFile file, @@ -100,6 +113,29 @@ public class ShowIntentionsPass extends TextEditorHighlightingPass { return result; } + public static boolean markActionInvoked(@NotNull Project project, + @NotNull final Editor editor, + @NotNull IntentionAction action) { + final int offset = ((EditorEx)editor).getExpectedCaretOffset(); + + List infos = new ArrayList<>(); + DaemonCodeAnalyzerImpl.processHighlightsNearOffset(editor.getDocument(), project, HighlightSeverity.INFORMATION, offset, true, + new CommonProcessors.CollectProcessor<>(infos)); + boolean removed = false; + for (HighlightInfo info : infos) { + if (info.quickFixActionMarkers != null) { + for (Pair pair : info.quickFixActionMarkers) { + HighlightInfo.IntentionActionDescriptor actionInGroup = pair.first; + if (actionInGroup.getAction() == action) { + // no CME because the list is concurrent + removed |= info.quickFixActionMarkers.remove(pair); + } + } + } + } + return removed; + } + private static void addAvailableFixesForGroups(@NotNull HighlightInfo info, @NotNull Editor editor, @NotNull PsiFile file, @@ -181,7 +217,7 @@ public class ShowIntentionsPass extends TextEditorHighlightingPass { } public boolean isEmpty() { - return intentionsToShow.isEmpty() && errorFixesToShow.isEmpty() && inspectionFixesToShow.isEmpty() && guttersToShow.isEmpty() && + return intentionsToShow.isEmpty() && errorFixesToShow.isEmpty() && inspectionFixesToShow.isEmpty() && guttersToShow.isEmpty() && notificationActionsToShow.isEmpty(); } @@ -197,19 +233,6 @@ public class ShowIntentionsPass extends TextEditorHighlightingPass { } } - ShowIntentionsPass(@NotNull Project project, @NotNull Editor editor, int passId) { - super(project, editor.getDocument(), false); - myPassIdToShowIntentionsFor = passId; - ApplicationManager.getApplication().assertIsDispatchThread(); - - myEditor = editor; - - PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project); - - myFile = documentManager.getPsiFile(myEditor.getDocument()); - assert myFile != null : FileDocumentManager.getInstance().getFile(myEditor.getDocument()); - } - @Override public void doCollectInformation(@NotNull ProgressIndicator progress) { if (!ApplicationManager.getApplication().isUnitTestMode() && !myEditor.getContentComponent().hasFocus()) return; diff --git a/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/IntentionActionWithTextCaching.java b/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/IntentionActionWithTextCaching.java index 226d0cb0bab6..e32dc7d35791 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/IntentionActionWithTextCaching.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/IntentionActionWithTextCaching.java @@ -18,18 +18,26 @@ package com.intellij.codeInsight.intention.impl; import com.intellij.codeInsight.daemon.impl.HighlightInfo; import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.codeInsight.intention.IntentionActionDelegate; import com.intellij.openapi.actionSystem.ShortcutProvider; import com.intellij.openapi.actionSystem.ShortcutSet; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.PossiblyDumbAware; +import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.util.ArrayList; import java.util.List; +import java.util.function.BiConsumer; /** * @author cdr @@ -45,20 +53,20 @@ public class IntentionActionWithTextCaching implements Comparable markInvoked){ + this(descriptor.getAction(), descriptor.getDisplayName(), descriptor.getIcon(), markInvoked); } - private IntentionActionWithTextCaching(@NotNull IntentionAction action, String displayName, @Nullable Icon icon) { + private IntentionActionWithTextCaching(@NotNull IntentionAction action, String displayName, @Nullable Icon icon, @Nullable BiConsumer markInvoked) { myIcon = icon; myText = action.getText(); // needed for checking errors in user written actions //noinspection ConstantConditions LOG.assertTrue(myText != null, "action "+action.getClass()+" text returned null"); - myAction = action; + myAction = new MyIntentionAction(action, markInvoked); myDisplayName = displayName; } @@ -133,4 +141,68 @@ public class IntentionActionWithTextCaching implements Comparable myMarkInvoked; + + MyIntentionAction(IntentionAction action, BiConsumer markInvoked) { + myAction = action; + myMarkInvoked = markInvoked; + } + + @Nls + @NotNull + @Override + public String getText() { + return myAction.getText(); + } + + @Override + public String toString() { + return getDelegate().getClass()+": "+getDelegate(); + } + + @Nls + @NotNull + @Override + public String getFamilyName() { + return myAction.getFamilyName(); + } + + @Override + public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { + return myAction.isAvailable(project, editor, file); + } + + @Override + public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { + try { + myAction.invoke(project, editor, file); + } + finally { + if (myMarkInvoked != null) { + myMarkInvoked.accept(IntentionActionWithTextCaching.this, myAction); + } + } + } + + @Override + public boolean startInWriteAction() { + return myAction.startInWriteAction(); + } + + @NotNull + @Override + public IntentionAction getDelegate() { + return myAction; + } + + @Nullable + @Override + public PsiElement getElementToMakeWritable(@NotNull PsiFile currentFile) { + return myAction.getElementToMakeWritable(currentFile); + } + } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/IntentionListStep.java b/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/IntentionListStep.java index 6276b4ef8271..49acc91ccbcb 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/IntentionListStep.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/IntentionListStep.java @@ -20,9 +20,7 @@ import com.intellij.codeInsight.daemon.impl.HighlightInfo; import com.intellij.codeInsight.daemon.impl.ShowIntentionsPass; import com.intellij.codeInsight.hint.HintManager; import com.intellij.codeInsight.intention.*; -import com.intellij.codeInsight.intention.impl.config.IntentionActionWrapper; import com.intellij.codeInsight.intention.impl.config.IntentionManagerSettings; -import com.intellij.codeInspection.IntentionWrapper; import com.intellij.codeInspection.LocalQuickFix; import com.intellij.codeInspection.SuppressIntentionActionFromFix; import com.intellij.codeInspection.ex.QuickFixWrapper; @@ -190,7 +188,10 @@ public class IntentionListStep implements ListPopupStep{ + removeActionFromCached(cached); + markInvoked(action); + }); if (element == null) return cachedAction; final List options = descriptor.getOptions(element, containingEditor); if (options == null) return cachedAction; @@ -274,6 +275,20 @@ public class IntentionListStep implements ListPopupStep actions = myFixture.getAvailableIntentions(getTestName(false) + ".groovy"); - DynamicPropertyFix dynamicFix = ContainerUtil.findInstance(actions, DynamicPropertyFix.class); + DynamicPropertyFix dynamicFix = (DynamicPropertyFix)actions.stream() + .map(a->((IntentionActionDelegate)a).getDelegate()) + .filter(DynamicPropertyFix.class::isInstance).findFirst().orElse(null); if (dynamicFix != null) { dynamicFix.invoke(getProject()); return dynamicFix.getReferenceExpression(); } else { - final DynamicMethodFix fix = ContainerUtil.findInstance(actions, DynamicMethodFix.class); + DynamicMethodFix fix = (DynamicMethodFix)actions.stream() + .map(a->((IntentionActionDelegate)a).getDelegate()) + .filter(DynamicMethodFix.class::isInstance).findFirst().orElse(null); assertNotNull(fix); fix.invoke(getProject()); return fix.getReferenceExpression(); diff --git a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFXQuickfixTest.java b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFXQuickfixTest.java index 59c2bb35707a..3b7239fd5702 100644 --- a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFXQuickfixTest.java +++ b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFXQuickfixTest.java @@ -16,6 +16,7 @@ package org.jetbrains.plugins.javaFX.fxml; import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.codeInsight.intention.IntentionActionDelegate; import com.intellij.openapi.application.PluginPathManager; import com.intellij.openapi.module.Module; import com.intellij.openapi.roots.ContentEntry; @@ -121,7 +122,7 @@ public class JavaFXQuickfixTest extends LightCodeInsightFixtureTestCase { assertNotNull(intention); Set languages = JavaFxInjectPageLanguageIntention.getAvailableLanguages(getProject()); assertContainsElements(languages, "groovy"); - JavaFxInjectPageLanguageIntention languageIntention = (JavaFxInjectPageLanguageIntention)intention; + JavaFxInjectPageLanguageIntention languageIntention = (JavaFxInjectPageLanguageIntention)((IntentionActionDelegate)intention).getDelegate(); languageIntention.registerPageLanguage(getProject(), (XmlFile)myFixture.getFile(), "groovy"); myFixture.checkResultByFile(getTestName(true) + ".fxml", getTestName(true) + "_after.fxml", true); } diff --git a/plugins/maven/src/test/java/org/jetbrains/idea/maven/dom/MavenDependencyCompletionAndResolutionTest.java b/plugins/maven/src/test/java/org/jetbrains/idea/maven/dom/MavenDependencyCompletionAndResolutionTest.java index 45f62fb4893b..e67fe5c6d6bc 100644 --- a/plugins/maven/src/test/java/org/jetbrains/idea/maven/dom/MavenDependencyCompletionAndResolutionTest.java +++ b/plugins/maven/src/test/java/org/jetbrains/idea/maven/dom/MavenDependencyCompletionAndResolutionTest.java @@ -16,7 +16,7 @@ package org.jetbrains.idea.maven.dom; import com.intellij.codeInsight.intention.IntentionAction; -import com.intellij.codeInsight.intention.impl.config.IntentionActionWrapper; +import com.intellij.codeInsight.intention.IntentionActionDelegate; import com.intellij.openapi.application.Result; import com.intellij.openapi.application.WriteAction; import com.intellij.openapi.vfs.LocalFileSystem; @@ -25,7 +25,6 @@ import com.intellij.psi.codeStyle.CodeStyleSettingsManager; import com.intellij.psi.codeStyle.CommonCodeStyleSettings; import com.intellij.psi.formatter.xml.XmlCodeStyleSettings; import com.intellij.util.PathUtil; -import com.intellij.util.Producer; import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.maven.MavenCustomRepositoryHelper; import org.jetbrains.idea.maven.dom.intentions.ChooseFileIntentionAction; @@ -714,7 +713,7 @@ public class MavenDependencyCompletionAndResolutionTest extends MavenDomWithIndi String libPath = myIndicesFixture.getRepositoryHelper().getTestDataPath("local1/junit/junit/4.0/junit-4.0.jar"); final VirtualFile libFile = LocalFileSystem.getInstance().refreshAndFindFileByPath(libPath); - ((ChooseFileIntentionAction)((IntentionActionWrapper)action).getDelegate()).setFileChooser(() -> new VirtualFile[]{libFile}); + ((ChooseFileIntentionAction)((IntentionActionDelegate)action).getDelegate()).setFileChooser(() -> new VirtualFile[]{libFile}); XmlCodeStyleSettings xmlSettings = CodeStyleSettingsManager.getInstance(myProject).getCurrentSettings().getCustomSettings(XmlCodeStyleSettings.class); @@ -726,7 +725,7 @@ public class MavenDependencyCompletionAndResolutionTest extends MavenDomWithIndi } finally { xmlSettings.XML_TEXT_WRAP = prevValue; - ((ChooseFileIntentionAction)((IntentionActionWrapper)action).getDelegate()).setFileChooser(null); + ((ChooseFileIntentionAction)((IntentionActionDelegate)action).getDelegate()).setFileChooser(null); } MavenDomProjectModel model = MavenDomUtil.getMavenDomProjectModel(myProject, myProjectPom);