do not remove invoked quick fix from the list if it did nothing to fix IDEA-173961 "Interrupted" quick fix disappears from popup

This commit is contained in:
Alexey Kudravtsev
2017-06-08 16:02:45 +03:00
parent 77a1a74d39
commit 54783edafd
4 changed files with 78 additions and 9 deletions
@@ -27,6 +27,7 @@ import com.intellij.codeInsight.folding.JavaCodeFoldingSettings;
import com.intellij.codeInsight.hint.EditorHintListener;
import com.intellij.codeInsight.intention.AbstractIntentionAction;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.intention.IntentionActionDelegate;
import com.intellij.codeInsight.intention.IntentionManager;
import com.intellij.codeInsight.intention.impl.IntentionHintComponent;
import com.intellij.codeInspection.*;
@@ -36,6 +37,7 @@ import com.intellij.codeInspection.deadCode.UnusedDeclarationInspectionBase;
import com.intellij.codeInspection.ex.InspectionToolRegistrar;
import com.intellij.codeInspection.ex.InspectionToolWrapper;
import com.intellij.codeInspection.ex.LocalInspectionToolWrapper;
import com.intellij.codeInspection.ex.QuickFixWrapper;
import com.intellij.codeInspection.htmlInspections.RequiredAttributesInspectionBase;
import com.intellij.codeInspection.varScopeCanBeNarrowed.FieldCanBeLocalInspection;
import com.intellij.configurationStore.StorageUtilKt;
@@ -2651,7 +2653,32 @@ public class DaemonRespondToChangesTest extends DaemonAnalyzerTestCase {
assertEmpty(fixes);
}
public void testStupidQuickFixIsStillVisibleAfterAppliedButDidNothing() {
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<IntentionAction> fixes = findStupidFixes();
IntentionAction fix = assertOneElement(fixes);
((MyInspection.StupidQuickFixWhichDoesntCheckItsOwnApplicability)((QuickFixWrapper)((IntentionActionDelegate)fix).getDelegate()).getFix()).doFix = false;
fix.invoke(getProject(), getEditor(), getFile()); // did nothing
fixes = findStupidFixes();
assertOneElement(fixes);
assertEmpty(doHighlighting(HighlightSeverity.ERROR));
fixes = findStupidFixes();
assertOneElement(fixes);
}
private List<IntentionAction> findStupidFixes() {
UIUtil.dispatchAllInvocationEvents();
return CodeInsightTestFixtureImpl.getAvailableIntentions(getEditor(), getFile())
.stream().filter(f->f.getFamilyName().equals(new MyInspection.StupidQuickFixWhichDoesntCheckItsOwnApplicability().getFamilyName()))
.collect(Collectors.toList());
@@ -2692,6 +2719,7 @@ public class DaemonRespondToChangesTest extends DaemonAnalyzerTestCase {
};
}
private static class StupidQuickFixWhichDoesntCheckItsOwnApplicability implements LocalQuickFix {
private boolean doFix = true;
@Nls
@NotNull
@Override
@@ -2708,7 +2736,9 @@ public class DaemonRespondToChangesTest extends DaemonAnalyzerTestCase {
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
WriteCommandAction.runWriteCommandAction(project, () -> ((PsiIfStatement)descriptor.getPsiElement().getParent()).getElseBranch().delete());
if (doFix) {
WriteCommandAction.runWriteCommandAction(project, () -> ((PsiIfStatement)descriptor.getPsiElement().getParent()).getElseBranch().delete());
}
}
}
}
@@ -59,14 +59,12 @@ import com.intellij.openapi.roots.ModuleRootEvent;
import com.intellij.openapi.roots.ModuleRootListener;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.SimpleModificationTracker;
import com.intellij.openapi.util.UserDataHolderEx;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.vcs.*;
import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileListener;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.openapi.vfs.VirtualFilePropertyEvent;
import com.intellij.openapi.vfs.*;
import com.intellij.openapi.wm.StatusBar;
import com.intellij.openapi.wm.WindowManager;
import com.intellij.openapi.wm.impl.status.TogglePopupHintsPanel;
@@ -94,7 +92,10 @@ import java.util.List;
/**
* @author cdr
*/
public class DaemonListeners implements Disposable {
public class DaemonListeners
extends SimpleModificationTracker // modCount will change whenever daemon-worthy event happens
implements Disposable {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.DaemonListeners");
private final Project myProject;
@@ -280,7 +281,7 @@ public class DaemonListeners implements Disposable {
});
connection.subscribe(PowerSaveMode.TOPIC, () -> stopDaemon(true, "Power save mode change"));
connection.subscribe(EditorColorsManager.TOPIC, scheme -> stopDaemonAndRestartAllFiles("Editor color scheme changed"));
connection.subscribe(EditorColorsManager.TOPIC, __ -> stopDaemonAndRestartAllFiles("Editor color scheme changed"));
commandProcessor.addCommandListener(new MyCommandListener(), this);
application.addApplicationListener(new MyApplicationListener(), this);
@@ -291,6 +292,7 @@ public class DaemonListeners implements Disposable {
virtualFileManager.addVirtualFileListener(new VirtualFileListener() {
@Override
public void propertyChanged(@NotNull VirtualFilePropertyEvent event) {
incModificationCount();
String propertyName = event.getPropertyName();
if (VirtualFile.PROP_NAME.equals(propertyName)) {
stopDaemonAndRestartAllFiles("Virtual file name changed");
@@ -318,6 +320,31 @@ public class DaemonListeners implements Disposable {
stopDaemon(true, "Virtual file property change");
}
}
@Override
public void fileCreated(@NotNull VirtualFileEvent event) {
incModificationCount();
}
@Override
public void beforePropertyChange(@NotNull VirtualFilePropertyEvent event) {
incModificationCount();
}
@Override
public void beforeContentsChange(@NotNull VirtualFileEvent event) {
incModificationCount();
}
@Override
public void beforeFileDeletion(@NotNull VirtualFileEvent event) {
incModificationCount();
}
@Override
public void beforeFileMovement(@NotNull VirtualFileMoveEvent event) {
incModificationCount();
}
}, this);
((EditorEventMulticasterEx)eventMulticaster).addErrorStripeListener(new ErrorStripeHandler(myProject), this);
@@ -614,12 +641,14 @@ public class DaemonListeners implements Disposable {
}
private void stopDaemon(boolean toRestartAlarm, @NonNls @NotNull String reason) {
incModificationCount();
if (myDaemonCodeAnalyzer.stopProcess(toRestartAlarm, reason)) {
myDaemonEventPublisher.daemonCancelEventOccurred(reason);
}
}
private void stopDaemonAndRestartAllFiles(@NotNull String reason) {
incModificationCount();
if (myDaemonCodeAnalyzer.doRestart()) {
myDaemonEventPublisher.daemonCancelEventOccurred(reason);
}
@@ -16,11 +16,13 @@
package com.intellij.codeInsight.intention.impl;
import com.intellij.codeInsight.daemon.impl.DaemonListeners;
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.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.DumbService;
@@ -178,12 +180,19 @@ public class IntentionActionWithTextCaching implements Comparable<IntentionActio
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
// if anything changed after action executed, mark it invoked.
// alternatively, the action could have shown the dialog where user pressed cancel, so do nothing in this case
long startModificationCount = DaemonListeners.getInstance(project).getModificationCount();
try {
myAction.invoke(project, editor, file);
}
finally {
if (myMarkInvoked != null) {
myMarkInvoked.accept(IntentionActionWithTextCaching.this, myAction);
ApplicationManager.getApplication().invokeLater(() -> {
if (startModificationCount != DaemonListeners.getInstance(project).getModificationCount()) {
myMarkInvoked.accept(IntentionActionWithTextCaching.this, myAction);
}
}, project.getDisposed());
}
}
}
@@ -63,6 +63,7 @@ public class IntentionListStep implements ListPopupStep<IntentionActionWithTextC
@Nullable
private final Editor myEditor;
private final PsiFile myFile;
@NotNull
private final Project myProject;
private static final TObjectHashingStrategy<IntentionActionWithTextCaching> ACTION_TEXT_AND_CLASS_EQUALS = new TObjectHashingStrategy<IntentionActionWithTextCaching>() {
@Override
@@ -284,7 +285,7 @@ public class IntentionListStep implements ListPopupStep<IntentionActionWithTextC
}
private void markInvoked(@NotNull IntentionAction action) {
if (myEditor != null) {
if (myEditor != null && !myEditor.isDisposed() && !myProject.isDisposed()) {
ShowIntentionsPass.markActionInvoked(myFile.getProject(), myEditor, action);
}
}