From 024f3f5039eb7c17050ad4c2c6bbf4d9327fe571 Mon Sep 17 00:00:00 2001 From: Alexander Zolotov Date: Fri, 30 Jan 2015 15:11:36 +0300 Subject: [PATCH] Provide suppression fixes in injections for suppressing inspection in host file --- .../InjectionAwareSuppressQuickFix.java | 18 +++++ .../InspectionProfileEntry.java | 81 ++++++++++++++----- ...BatchSuppressByNoInspectionCommentFix.java | 15 +++- .../SuppressIntentionActionFromFix.java | 11 ++- .../impl/ShowIntentionActionsHandler.java | 12 +++ 5 files changed, 112 insertions(+), 25 deletions(-) create mode 100644 platform/analysis-api/src/com/intellij/codeInspection/InjectionAwareSuppressQuickFix.java diff --git a/platform/analysis-api/src/com/intellij/codeInspection/InjectionAwareSuppressQuickFix.java b/platform/analysis-api/src/com/intellij/codeInspection/InjectionAwareSuppressQuickFix.java new file mode 100644 index 000000000000..27baddcd3220 --- /dev/null +++ b/platform/analysis-api/src/com/intellij/codeInspection/InjectionAwareSuppressQuickFix.java @@ -0,0 +1,18 @@ +package com.intellij.codeInspection; + +import com.intellij.psi.PsiElement; +import com.intellij.util.ThreeState; + +/** + * This kind of suppression fix allows to clients to specify whether the fix should + * be invoked on injected elements or on elements of host files. + *

+ * By default suppression fixes on injected elements are able to make suppression inside injection only. + * Whereas implementation of this interface will be provided for suppressing inside injection and in injection host. + * See {@link InspectionProfileEntry#getBatchSuppressActions(PsiElement)} for details. + */ +public interface InjectionAwareSuppressQuickFix extends SuppressQuickFix { + ThreeState isShouldBeAppliedToInjectionHost(); + + void setShouldBeAppliedToInjectionHost(ThreeState shouldBeAppliedToInjectionHost); +} diff --git a/platform/analysis-api/src/com/intellij/codeInspection/InspectionProfileEntry.java b/platform/analysis-api/src/com/intellij/codeInspection/InspectionProfileEntry.java index af1e65c67598..886d02aa55a2 100644 --- a/platform/analysis-api/src/com/intellij/codeInspection/InspectionProfileEntry.java +++ b/platform/analysis-api/src/com/intellij/codeInspection/InspectionProfileEntry.java @@ -17,6 +17,7 @@ package com.intellij.codeInspection; import com.intellij.codeHighlighting.HighlightDisplayLevel; import com.intellij.lang.Language; +import com.intellij.lang.injection.InjectedLanguageManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.InvalidDataException; @@ -24,8 +25,10 @@ import com.intellij.openapi.util.WriteExternalException; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.FileViewProvider; import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiLanguageInjectionHost; import com.intellij.psi.templateLanguages.TemplateLanguageFileViewProvider; import com.intellij.util.ResourceUtil; +import com.intellij.util.ThreeState; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.xmlb.SerializationFilter; import com.intellij.util.xmlb.SkipDefaultValuesSerializationFilters; @@ -44,14 +47,16 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.URL; -import java.util.*; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Set; /** * @author anna * @since 28-Nov-2005 */ -@SuppressWarnings("JavadocReference") -public abstract class InspectionProfileEntry implements BatchSuppressableTool{ +public abstract class InspectionProfileEntry implements BatchSuppressableTool { public static final String GENERAL_GROUP_NAME = InspectionsBundle.message("inspection.general.tools.group.name"); private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.InspectionProfileEntry"); @@ -93,22 +98,52 @@ public abstract class InspectionProfileEntry implements BatchSuppressableTool{ Set fixes = new THashSet(new TObjectHashingStrategy() { @Override public int computeHashCode(SuppressQuickFix object) { - return object.getName().hashCode(); + int result = object instanceof InjectionAwareSuppressQuickFix + ? ((InjectionAwareSuppressQuickFix)object).isShouldBeAppliedToInjectionHost().hashCode() + : 0; + return 31 * result + object.getName().hashCode(); } @Override public boolean equals(SuppressQuickFix o1, SuppressQuickFix o2) { + if (o1 instanceof InjectionAwareSuppressQuickFix && o2 instanceof InjectionAwareSuppressQuickFix) { + if (((InjectionAwareSuppressQuickFix)o1).isShouldBeAppliedToInjectionHost() != ((InjectionAwareSuppressQuickFix)o2).isShouldBeAppliedToInjectionHost()) { + return false; + } + } return o1.getName().equals(o2.getName()); } }); + Set suppressors = getSuppressors(element); + final PsiLanguageInjectionHost injectionHost = InjectedLanguageManager.getInstance(element.getProject()).getInjectionHost(element); + if (injectionHost != null) { + Set injectionHostSuppressors = getSuppressors(injectionHost); + for (InspectionSuppressor suppressor : injectionHostSuppressors) { + addAllSuppressActions(fixes, injectionHost, suppressor, ThreeState.YES, getShortName()); + } + } + for (InspectionSuppressor suppressor : suppressors) { - SuppressQuickFix[] actions = suppressor.getSuppressActions(element, getShortName()); - fixes.addAll(Arrays.asList(actions)); + addAllSuppressActions(fixes, element, suppressor, injectionHost != null ? ThreeState.NO : ThreeState.UNSURE, getShortName()); } return fixes.toArray(new SuppressQuickFix[fixes.size()]); } + private static void addAllSuppressActions(Set fixes, + PsiElement element, + InspectionSuppressor suppressor, + ThreeState appliedToInjectionHost, + String toolShortName) { + final SuppressQuickFix[] actions = suppressor.getSuppressActions(element, toolShortName); + for (SuppressQuickFix action : actions) { + if (action instanceof InjectionAwareSuppressQuickFix) { + ((InjectionAwareSuppressQuickFix)action).setShouldBeAppliedToInjectionHost(appliedToInjectionHost); + } + fixes.add(action); + } + } + private boolean isSuppressed(@NotNull String toolId, @NotNull InspectionSuppressor suppressor, @NotNull PsiElement element) { @@ -132,7 +167,9 @@ public abstract class InspectionProfileEntry implements BatchSuppressableTool{ ContainerUtil.addIfNotNull(suppressors, elementLanguageSuppressor); return suppressors; } - return elementLanguageSuppressor != null ? Collections.singleton(elementLanguageSuppressor) : Collections.emptySet(); + return elementLanguageSuppressor != null + ? Collections.singleton(elementLanguageSuppressor) + : Collections.emptySet(); } public void cleanup(Project project) { @@ -148,9 +185,9 @@ public abstract class InspectionProfileEntry implements BatchSuppressableTool{ protected volatile DefaultNameProvider myNameProvider = null; /** - * @see com.intellij.codeInspection.InspectionEP#groupDisplayName - * @see com.intellij.codeInspection.InspectionEP#groupKey - * @see com.intellij.codeInspection.InspectionEP#groupBundle + * @see InspectionEP#groupDisplayName + * @see InspectionEP#groupKey + * @see InspectionEP#groupBundle */ @Nls @NotNull @@ -166,7 +203,7 @@ public abstract class InspectionProfileEntry implements BatchSuppressableTool{ } /** - * @see com.intellij.codeInspection.InspectionEP#groupPath + * @see InspectionEP#groupPath */ @NotNull public String[] getGroupPath() { @@ -178,9 +215,9 @@ public abstract class InspectionProfileEntry implements BatchSuppressableTool{ } /** - * @see com.intellij.codeInspection.InspectionEP#displayName - * @see com.intellij.codeInspection.InspectionEP#key - * @see com.intellij.codeInspection.InspectionEP#bundle + * @see InspectionEP#displayName + * @see InspectionEP#key + * @see InspectionEP#bundle */ @Nls @NotNull @@ -198,7 +235,7 @@ public abstract class InspectionProfileEntry implements BatchSuppressableTool{ /** * DO NOT OVERRIDE this method. * - * @see com.intellij.codeInspection.InspectionEP#shortName + * @see InspectionEP#shortName */ @NonNls @NotNull @@ -214,13 +251,13 @@ public abstract class InspectionProfileEntry implements BatchSuppressableTool{ @NotNull public static String getShortName(@NotNull String className) { - return StringUtil.trimEnd(StringUtil.trimEnd(className, "Inspection"),"InspectionBase"); + return StringUtil.trimEnd(StringUtil.trimEnd(className, "Inspection"), "InspectionBase"); } /** * DO NOT OVERRIDE this method. * - * @see com.intellij.codeInspection.InspectionEP#level + * @see InspectionEP#level */ @NotNull public HighlightDisplayLevel getDefaultLevel() { @@ -230,7 +267,7 @@ public abstract class InspectionProfileEntry implements BatchSuppressableTool{ /** * DO NOT OVERRIDE this method. * - * @see com.intellij.codeInspection.InspectionEP#enabledByDefault + * @see InspectionEP#enabledByDefault */ public boolean isEnabledByDefault() { return false; @@ -238,6 +275,7 @@ public abstract class InspectionProfileEntry implements BatchSuppressableTool{ /** * This method is called each time UI is shown. + * * @return null if no UI options required. */ @Nullable @@ -348,7 +386,7 @@ public abstract class InspectionProfileEntry implements BatchSuppressableTool{ * Initialize inspection with project. Is called on project opened for all profiles as well as on profile creation. * * @param project to be associated with this entry - * @deprecated this won't work for inspections configured via {@link com.intellij.codeInspection.InspectionEP} + * @deprecated this won't work for inspections configured via {@link InspectionEP} */ public void projectOpened(@NotNull Project project) { } @@ -357,7 +395,7 @@ public abstract class InspectionProfileEntry implements BatchSuppressableTool{ * Cleanup inspection settings corresponding to the project. Is called on project closed for all profiles as well as on profile deletion. * * @param project to be disassociated from this entry - * @deprecated this won't work for inspections configured via {@link com.intellij.codeInspection.InspectionEP} + * @deprecated this won't work for inspections configured via {@link InspectionEP} */ public void projectClosed(@NotNull Project project) { } @@ -411,7 +449,8 @@ public abstract class InspectionProfileEntry implements BatchSuppressableTool{ if (descriptionUrl == null) return null; return ResourceUtil.loadText(descriptionUrl); } - catch (IOException ignored) { } + catch (IOException ignored) { + } return null; } diff --git a/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/actions/AbstractBatchSuppressByNoInspectionCommentFix.java b/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/actions/AbstractBatchSuppressByNoInspectionCommentFix.java index 7a8c3e3c6cde..6bc8511bd5b5 100644 --- a/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/actions/AbstractBatchSuppressByNoInspectionCommentFix.java +++ b/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/actions/AbstractBatchSuppressByNoInspectionCommentFix.java @@ -17,9 +17,9 @@ package com.intellij.codeInsight.daemon.impl.actions; import com.intellij.codeInsight.FileModificationService; +import com.intellij.codeInspection.InjectionAwareSuppressQuickFix; import com.intellij.codeInspection.InspectionsBundle; import com.intellij.codeInspection.ProblemDescriptor; -import com.intellij.codeInspection.SuppressQuickFix; import com.intellij.codeInspection.SuppressionUtil; import com.intellij.icons.AllIcons; import com.intellij.lang.Language; @@ -32,6 +32,7 @@ import com.intellij.psi.PsiManager; import com.intellij.psi.PsiWhiteSpace; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.IncorrectOperationException; +import com.intellij.util.ThreeState; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -43,9 +44,10 @@ import java.util.List; * @author Roman.Chernyatchik * @date Aug 13, 2009 */ -public abstract class AbstractBatchSuppressByNoInspectionCommentFix implements SuppressQuickFix, Iconable { +public abstract class AbstractBatchSuppressByNoInspectionCommentFix implements InjectionAwareSuppressQuickFix, Iconable { @NotNull protected final String myID; private final boolean myReplaceOtherSuppressionIds; + private ThreeState myShouldBeAppliedToInjectionHost = ThreeState.UNSURE; @Nullable public abstract PsiElement getContainer(final PsiElement context); @@ -60,6 +62,15 @@ public abstract class AbstractBatchSuppressByNoInspectionCommentFix implements S myReplaceOtherSuppressionIds = replaceOtherSuppressionIds; } + public void setShouldBeAppliedToInjectionHost(ThreeState shouldBeAppliedToInjectionHost) { + myShouldBeAppliedToInjectionHost = shouldBeAppliedToInjectionHost; + } + + @Override + public ThreeState isShouldBeAppliedToInjectionHost() { + return myShouldBeAppliedToInjectionHost; + } + @NotNull @Override public String getName() { diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/SuppressIntentionActionFromFix.java b/platform/analysis-impl/src/com/intellij/codeInspection/SuppressIntentionActionFromFix.java index 3fc800e87879..d92101c386fb 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/SuppressIntentionActionFromFix.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/SuppressIntentionActionFromFix.java @@ -21,6 +21,7 @@ import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; import com.intellij.util.Function; import com.intellij.util.IncorrectOperationException; +import com.intellij.util.ThreeState; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; @@ -59,9 +60,15 @@ public class SuppressIntentionActionFromFix extends SuppressIntentionAction { } } + public ThreeState isShouldBeAppliedToInjectionHost() { + return myFix instanceof InjectionAwareSuppressQuickFix + ? ((InjectionAwareSuppressQuickFix)myFix).isShouldBeAppliedToInjectionHost() + : ThreeState.UNSURE; + } + public PsiElement getContainer(PsiElement element) { return myFix instanceof AbstractBatchSuppressByNoInspectionCommentFix - ? ((AbstractBatchSuppressByNoInspectionCommentFix )myFix).getContainer(element) : null; + ? ((AbstractBatchSuppressByNoInspectionCommentFix)myFix).getContainer(element) : null; } @Override @@ -72,7 +79,7 @@ public class SuppressIntentionActionFromFix extends SuppressIntentionAction { @NotNull @Override public String getText() { - return myFix.getName(); + return myFix.getName() + (isShouldBeAppliedToInjectionHost() == ThreeState.NO ? " in injection" : ""); } @NotNull diff --git a/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/ShowIntentionActionsHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/ShowIntentionActionsHandler.java index 8ddebd7c383f..31c1ecd533d1 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/ShowIntentionActionsHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/ShowIntentionActionsHandler.java @@ -27,6 +27,7 @@ import com.intellij.codeInsight.lookup.LookupEx; import com.intellij.codeInsight.lookup.LookupManager; import com.intellij.codeInsight.template.impl.TemplateManagerImpl; import com.intellij.codeInsight.template.impl.TemplateState; +import com.intellij.codeInspection.SuppressIntentionActionFromFix; import com.intellij.featureStatistics.FeatureUsageTracker; import com.intellij.featureStatistics.FeatureUsageTrackerImpl; import com.intellij.injected.editor.EditorWindow; @@ -45,6 +46,7 @@ import com.intellij.psi.PsiFile; import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil; import com.intellij.util.IncorrectOperationException; import com.intellij.util.PairProcessor; +import com.intellij.util.ThreeState; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -107,6 +109,16 @@ public class ShowIntentionActionsHandler implements CodeInsightActionHandler { private static boolean isAvailableHere(Editor editor, PsiFile psiFile, PsiElement psiElement, boolean inProject, IntentionAction action) { try { Project project = psiFile.getProject(); + if (action instanceof SuppressIntentionActionFromFix) { + final ThreeState shouldBeAppliedToInjectionHost = ((SuppressIntentionActionFromFix)action).isShouldBeAppliedToInjectionHost(); + if (editor instanceof EditorWindow && shouldBeAppliedToInjectionHost == ThreeState.YES) { + return false; + } + if (!(editor instanceof EditorWindow) && shouldBeAppliedToInjectionHost == ThreeState.NO) { + return false; + } + } + if (action instanceof PsiElementBaseIntentionAction) { if (!inProject || psiElement == null || !((PsiElementBaseIntentionAction)action).isAvailable(project, editor, psiElement)) return false; }