api to provide cumulative quick fixes which can show one dialog for all selected problems in batch mode

This commit is contained in:
anna
2011-10-28 13:45:46 +02:00
parent 7b42368c86
commit 78b77d65a2
4 changed files with 168 additions and 92 deletions
@@ -21,8 +21,6 @@ import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.daemon.GroupNames;
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInspection.*;
import com.intellij.codeInspection.ex.InspectionTool;
import com.intellij.codeInspection.ex.QuickFixAction;
import com.intellij.codeInspection.reference.*;
import com.intellij.codeInspection.util.SpecialAnnotationsUtil;
import com.intellij.openapi.application.ApplicationManager;
@@ -33,7 +31,7 @@ import com.intellij.openapi.util.*;
import com.intellij.psi.*;
import com.intellij.psi.search.searches.AllOverridingMethodsSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtilBase;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.refactoring.safeDelete.SafeDeleteHandler;
import com.intellij.util.Processor;
import com.intellij.util.Query;
@@ -319,7 +317,7 @@ public class EmptyMethodInspection extends GlobalJavaInspectionTool {
ApplicationManager.getApplication().invokeLater(new Runnable() {
public void run() {
SafeDeleteHandler.invoke(project, PsiUtilBase.toPsiElementArray(psiElements), false);
SafeDeleteHandler.invoke(project, PsiUtilCore.toPsiElementArray(psiElements), false);
}
});
}
@@ -327,13 +325,13 @@ public class EmptyMethodInspection extends GlobalJavaInspectionTool {
}
private class DeleteMethodQuickFix implements LocalQuickFix {
private class DeleteMethodQuickFix implements LocalQuickFix, BatchQuickFix<CommonProblemDescriptor> {
private final ProblemDescriptionsProcessor myProcessor;
private final boolean myNeedToDEleteHierarchy;
private final boolean myNeedToDeleteHierarchy;
public DeleteMethodQuickFix(final ProblemDescriptionsProcessor processor, final boolean needToDeleteHierarchy) {
myProcessor = processor;
myNeedToDEleteHierarchy = needToDeleteHierarchy;
myNeedToDeleteHierarchy = needToDeleteHierarchy;
}
@NotNull
@@ -342,28 +340,7 @@ public class EmptyMethodInspection extends GlobalJavaInspectionTool {
}
public void applyFix(final @NotNull Project project, @NotNull ProblemDescriptor descriptor) {
RefElement refElement = (RefElement)myProcessor.getElement(descriptor);
if (refElement.isValid() && refElement instanceof RefMethod) {
final List<RefElement> refElements = new ArrayList<RefElement>(1);
RefMethod refMethod = (RefMethod)refElement;
final List<PsiElement> psiElements = new ArrayList<PsiElement>();
if (myNeedToDEleteHierarchy) {
deleteHierarchy(refMethod, psiElements, refElements);
} else {
deleteMethod(refMethod, psiElements, refElements);
}
ApplicationManager.getApplication().invokeLater(new Runnable() {
public void run() {
SafeDeleteHandler.invoke(project, PsiUtilBase.toPsiElementArray(psiElements), false, new Runnable() {
public void run() {
QuickFixAction
.removeElements(refElements.toArray(new RefElement[refElements.size()]), project, (InspectionTool)myProcessor);
}
});
}
});
}
applyFix(project, new ProblemDescriptor[]{descriptor}, new ArrayList<PsiElement>(), null);
}
@NotNull
@@ -371,20 +348,43 @@ public class EmptyMethodInspection extends GlobalJavaInspectionTool {
return getName();
}
private void deleteHierarchy(RefMethod refMethod, List<PsiElement> result, List<RefElement> refElements) {
private void deleteHierarchy(RefMethod refMethod, List<PsiElement> result) {
Collection<RefMethod> derivedMethods = refMethod.getDerivedMethods();
RefMethod[] refMethods = derivedMethods.toArray(new RefMethod[derivedMethods.size()]);
for (RefMethod refDerived : refMethods) {
deleteMethod(refDerived, result, refElements);
deleteMethod(refDerived, result);
}
deleteMethod(refMethod, result, refElements);
deleteMethod(refMethod, result);
}
private void deleteMethod(RefMethod refMethod, List<PsiElement> result, List<RefElement> refElements) {
refElements.add(refMethod);
private void deleteMethod(RefMethod refMethod, List<PsiElement> result) {
PsiElement psiElement = refMethod.getElement();
if (psiElement == null) return;
if (!result.contains(psiElement)) result.add(psiElement);
}
@Override
public void applyFix(@NotNull final Project project,
@NotNull final CommonProblemDescriptor[] descriptors,
final List<PsiElement> psiElementsToIgnore,
final Runnable refreshViews) {
for (CommonProblemDescriptor descriptor : descriptors) {
RefElement refElement = (RefElement)myProcessor.getElement(descriptor);
if (refElement.isValid() && refElement instanceof RefMethod) {
RefMethod refMethod = (RefMethod)refElement;
if (myNeedToDeleteHierarchy) {
deleteHierarchy(refMethod, psiElementsToIgnore);
}
else {
deleteMethod(refMethod, psiElementsToIgnore);
}
}
}
ApplicationManager.getApplication().invokeLater(new Runnable() {
public void run() {
SafeDeleteHandler.invoke(project, PsiUtilCore.toPsiElementArray(psiElementsToIgnore), false, refreshViews);
}
});
}
}
}
@@ -0,0 +1,41 @@
/*
* Copyright 2000-2011 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.codeInspection;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* User: anna
* Date: 10/27/11
*/
public interface BatchQuickFix<D extends CommonProblemDescriptor> {
/**
* Called to apply the cumulative fix. Is invoked in WriteAction
*
* @param project {@link com.intellij.openapi.project.Project}
* @param descriptors problem reported by the tool on which fix should work
* @param psiElementsToIgnore elements to be excluded from view during post-refresh
* @param refreshViews post-refresh inspection results view; would remove collected elements from the view
*/
void applyFix(@NotNull final Project project,
@NotNull final D[] descriptors,
final List<PsiElement> psiElementsToIgnore,
final Runnable refreshViews);
}
@@ -16,10 +16,20 @@
package com.intellij.codeInspection.ex;
import com.intellij.codeInspection.QuickFix;
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
import com.intellij.codeInspection.*;
import com.intellij.codeInspection.reference.RefElement;
import com.intellij.codeInspection.reference.RefEntity;
import com.intellij.codeInspection.reference.RefManager;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiManager;
import com.intellij.psi.util.PsiModificationTracker;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Set;
/**
* @author max
@@ -27,6 +37,7 @@ import com.intellij.openapi.actionSystem.AnActionEvent;
public class LocalQuickFixWrapper extends QuickFixAction {
private final QuickFix myFix;
private String myText;
public LocalQuickFixWrapper(QuickFix fix, DescriptorProviderInspection tool) {
super(fix.getName(), tool);
myTool = tool;
@@ -48,26 +59,6 @@ public class LocalQuickFixWrapper extends QuickFixAction {
myText = text;
}
protected boolean applyFix(RefElement[] refElements) {
/* dead code ?!
for (RefElement refElement : refElements) {
ProblemDescriptor[] problems = myTool.getDescriptions(refElement);
if (problems != null) {
PsiElement psiElement = refElement.getElement();
if (psiElement != null) {
for (ProblemDescriptor problem : problems) {
LocalQuickFix fix = problem.getFix();
if (fix != null) {
fix.applyFix(psiElement.getProject(), problem);
myTool.ignoreProblem(refElement, problem);
}
}
}
}
}*/
return true;
}
protected boolean isProblemDescriptorsAcceptable() {
return true;
@@ -76,4 +67,79 @@ public class LocalQuickFixWrapper extends QuickFixAction {
public QuickFix getFix() {
return myFix;
}
}
@Nullable
protected QuickFix getWorkingQuickFix(QuickFix[] fixes) {
for (QuickFix fix : fixes) {
if (!myFix.getClass().isInstance(fix)) continue;
if (myFix instanceof IntentionWrapper && fix instanceof IntentionWrapper &&
!(((IntentionWrapper)myFix).getAction().getClass().isInstance(((IntentionWrapper)fix).getAction()))) {
continue;
}
return fix;
}
return null;
}
protected boolean applyFix(RefElement[] refElements) {
throw new UnsupportedOperationException("");
}
@Override
protected void applyFix(final Project project, final CommonProblemDescriptor[] descriptors, final Set<PsiElement> ignoredElements) {
final PsiModificationTracker tracker = PsiManager.getInstance(project).getModificationTracker();
if (myFix instanceof BatchQuickFix) {
final ArrayList<PsiElement> collectedElementsToIgnore = new ArrayList<PsiElement>();
final Runnable refreshViews = new Runnable() {
@Override
public void run() {
DaemonCodeAnalyzer.getInstance(project).restart();
for (CommonProblemDescriptor descriptor : descriptors) {
ignore(ignoredElements, descriptor, getWorkingQuickFix(descriptor.getFixes()));
}
final RefManager refManager = myTool.getContext().getRefManager();
final RefElement[] refElements = new RefElement[collectedElementsToIgnore.size()];
for (int i = 0, collectedElementsToIgnoreSize = collectedElementsToIgnore.size(); i < collectedElementsToIgnoreSize; i++) {
refElements[i] = refManager.getReference(collectedElementsToIgnore.get(i));
}
removeElements(refElements, project, myTool);
}
};
((BatchQuickFix)myFix).applyFix(project, descriptors, collectedElementsToIgnore, refreshViews);
return;
}
boolean restart = false;
for (CommonProblemDescriptor descriptor : descriptors) {
if (descriptor == null) continue;
final QuickFix[] fixes = descriptor.getFixes();
if (fixes != null) {
final QuickFix fix = getWorkingQuickFix(fixes);
if (fix != null) {
final long startCount = tracker.getModificationCount();
//CCE here means QuickFix was incorrectly inherited, is there a way to signal (plugin) it is wrong?
fix.applyFix(project, descriptor);
if (startCount != tracker.getModificationCount()) {
restart = true;
ignore(ignoredElements, descriptor, fix);
}
}
}
}
if (restart) {
DaemonCodeAnalyzer.getInstance(project).restart();
}
}
private void ignore(Set<PsiElement> ignoredElements, CommonProblemDescriptor descriptor, QuickFix fix) {
if (fix != null) {
((DescriptorProviderInspection)myTool).ignoreProblem(descriptor, fix);
}
if (descriptor instanceof ProblemDescriptor) {
ignoredElements.add(((ProblemDescriptor)descriptor).getPsiElement());
}
}
}
@@ -16,8 +16,9 @@
package com.intellij.codeInspection.ex;
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
import com.intellij.codeInspection.*;
import com.intellij.codeInspection.CommonProblemDescriptor;
import com.intellij.codeInspection.InspectionManager;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.codeInspection.reference.RefElement;
import com.intellij.codeInspection.reference.RefEntity;
import com.intellij.codeInspection.reference.RefManagerImpl;
@@ -36,8 +37,6 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.util.PsiModificationTracker;
import gnu.trove.THashSet;
import javax.swing.*;
@@ -110,6 +109,7 @@ public class QuickFixAction extends AnAction {
}
protected void applyFix(Project project, CommonProblemDescriptor[] descriptors, Set<PsiElement> ignoredElements) {}
private void doApplyFix(final Project project,
final CommonProblemDescriptor[] descriptors) {
final Set<VirtualFile> readOnlyFiles = new THashSet<VirtualFile>();
@@ -139,38 +139,7 @@ public class QuickFixAction extends AnAction {
CommandProcessor.getInstance().markCurrentCommandAsGlobal(project);
ApplicationManager.getApplication().runWriteAction(new Runnable() {
public void run() {
final PsiModificationTracker tracker = PsiManager.getInstance(project).getModificationTracker();
for (CommonProblemDescriptor descriptor : descriptors) {
if (descriptor == null) continue;
final QuickFix[] fixes = descriptor.getFixes();
if (fixes != null) {
for (QuickFix fix : fixes) {
if (fix != null) {
final QuickFixAction quickFixAction = QuickFixAction.this;
if (quickFixAction instanceof LocalQuickFixWrapper) {
QuickFix unwrapped = ((LocalQuickFixWrapper)quickFixAction).getFix();
if (!unwrapped.getClass().isInstance(fix)) continue;
if (unwrapped instanceof IntentionWrapper && fix instanceof IntentionWrapper &&
!(((IntentionWrapper) unwrapped).getAction().getClass().isInstance(((IntentionWrapper) fix).getAction()))) {
continue;
}
}
final long startCount = tracker.getModificationCount();
//CCE here means QuickFix was incorrectly inherited, is there a way to signal (plugin) it is wrong?
fix.applyFix(project, descriptor);
if (startCount != tracker.getModificationCount()) {
DaemonCodeAnalyzer.getInstance(project).restart();
((DescriptorProviderInspection)myTool).ignoreProblem(descriptor, fix);
if (descriptor instanceof ProblemDescriptor) {
ignoredElements.add(((ProblemDescriptor)descriptor).getPsiElement());
}
}
}
}
}
}
applyFix(project, descriptors, ignoredElements);
}
});
}