From 2944c84331435852e41111f195bb4f2d73dc149c Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Tue, 22 Aug 2017 11:17:21 +0300 Subject: [PATCH] inspection view: get rid of global lock in presentation --- .../UnusedDeclarationPresentation.java | 35 +-- .../ex/GlobalInspectionContextImpl.java | 33 +- .../ex/InspectionRVContentProvider.java | 13 +- .../ex/InspectionRVContentProviderImpl.java | 10 +- .../ex/LocalQuickFixWrapper.java | 12 +- .../codeInspection/ex/QuickFixAction.java | 16 +- .../OfflineInspectionRVContentProvider.java | 15 +- .../ui/DefaultInspectionToolPresentation.java | 283 ++++++------------ .../ui/InspectionResultsView.java | 11 +- .../ui/InspectionToolPresentation.java | 38 ++- .../ui/ProblemDescriptionNode.java | 30 +- .../codeInspection/ui/RefElementNode.java | 25 +- .../ui/SuppressableInspectionTreeNode.java | 31 +- .../SuppressActionSequentialTask.java | 41 +-- .../suppress/SuppressActionWrapper.java | 4 +- .../ui/util/SynchronizedBidiMultiMap.java | 103 +++++++ 16 files changed, 321 insertions(+), 379 deletions(-) create mode 100644 platform/lang-impl/src/com/intellij/codeInspection/ui/util/SynchronizedBidiMultiMap.java diff --git a/java/java-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationPresentation.java b/java/java-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationPresentation.java index b9e31193b064..4c9bda5d2818 100644 --- a/java/java-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationPresentation.java +++ b/java/java-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationPresentation.java @@ -78,7 +78,7 @@ import java.util.stream.Collectors; public class UnusedDeclarationPresentation extends DefaultInspectionToolPresentation { - private final Set myIgnoreElements = ConcurrentCollectionFactory.createConcurrentSet(ContainerUtil.identityStrategy()); + private final Set myResolvedElements = ConcurrentCollectionFactory.createConcurrentSet(ContainerUtil.identityStrategy()); private final Map myFixedElements = ConcurrentCollectionFactory.createMap(ContainerUtil.identityStrategy()); @@ -155,7 +155,7 @@ public class UnusedDeclarationPresentation extends DefaultInspectionToolPresenta @NotNull Predicate excludedDescriptions) { if (!(refEntity instanceof RefJavaElement)) return; final RefFilter filter = getFilter(); - if (!getIgnoredRefElements().contains(refEntity) && filter.accepts((RefJavaElement)refEntity)) { + if (!myResolvedElements.contains(refEntity) && filter.accepts((RefJavaElement)refEntity)) { refEntity = getRefManager().getRefinedElement(refEntity); if (!refEntity.isValid()) return; RefJavaElement refElement = (RefJavaElement)refEntity; @@ -199,7 +199,7 @@ public class UnusedDeclarationPresentation extends DefaultInspectionToolPresenta public QuickFixAction[] getQuickFixes(@NotNull final RefEntity[] refElements, @Nullable InspectionTree tree) { boolean showFixes = false; for (RefEntity element : refElements) { - if (!getIgnoredRefElements().contains(element) && element.isValid()) { + if (!myResolvedElements.contains(element) && element.isValid()) { showFixes = true; break; } @@ -458,6 +458,15 @@ public class UnusedDeclarationPresentation extends DefaultInspectionToolPresenta }; } + public void resolveElement(@NotNull RefEntity entity) { + myProblemElements.remove(entity); + myResolvedElements.add(entity); + } + + public boolean isProblemResolved(@Nullable RefEntity entity) { + return myResolvedElements.contains(entity); + } + @Override public void updateContent() { getTool().checkForReachableRefs(getContext()); @@ -468,7 +477,7 @@ public class UnusedDeclarationPresentation extends DefaultInspectionToolPresenta if (!(refEntity instanceof RefJavaElement)) return;//dead code doesn't work with refModule | refPackage RefJavaElement refElement = (RefJavaElement)refEntity; if (!compareVisibilities(refElement, localInspectionTool)) return; - if (!(getContext().getUIOptions().FILTER_RESOLVED_ITEMS && getIgnoredRefElements().contains(refElement)) && refElement.isValid() && getFilter().accepts(refElement)) { + if (!(getContext().getUIOptions().FILTER_RESOLVED_ITEMS && myResolvedElements.contains(refElement)) && refElement.isValid() && getFilter().accepts(refElement)) { if (skipEntryPoints(refElement)) return; registerContentEntry(refEntity, RefJavaUtil.getInstance().getPackageName(refEntity)); } @@ -538,11 +547,6 @@ public class UnusedDeclarationPresentation extends DefaultInspectionToolPresenta return !myContents.isEmpty() || super.hasReportedProblems(); } - @Override - public void ignoreCurrentElement(@NotNull RefEntity refEntity) { - myIgnoreElements.add(refEntity); - } - @Override public void ignoreElement(@NotNull RefEntity refEntity) { if (refEntity instanceof RefElement) { @@ -563,18 +567,7 @@ public class UnusedDeclarationPresentation extends DefaultInspectionToolPresenta @Override public void cleanup() { super.cleanup(); - myIgnoreElements.clear(); - } - - @Override - public boolean isElementIgnored(@NotNull RefEntity element) { - return myIgnoreElements.contains(element); - } - - @Override - @NotNull - public Set getIgnoredRefElements() { - return myIgnoreElements; + myResolvedElements.clear(); } @Override diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java b/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java index ae8e621c1b65..5a28d8170f11 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java @@ -292,13 +292,17 @@ public class GlobalInspectionContextImpl extends GlobalInspectionContextBase imp } } - public void ignoreElement(@NotNull InspectionProfileEntry tool, @NotNull PsiElement element) { + public void resolveElement(@NotNull InspectionProfileEntry tool, @NotNull PsiElement element) { final RefElement refElement = getRefManager().getReference(element); + if (refElement == null) return; final Tools tools = getTools().get(tool.getShortName()); if (tools != null){ for (ScopeToolState state : tools.getTools()) { InspectionToolWrapper toolWrapper = state.getTool(); - ignoreElementRecursively(toolWrapper, refElement); + InspectionToolPresentation presentation = getPresentationOrNull(toolWrapper); + if (presentation != null) { + resolveElementRecursively(presentation, refElement); + } } } } @@ -311,14 +315,11 @@ public class GlobalInspectionContextImpl extends GlobalInspectionContextBase imp return myOutputPath; } - private void ignoreElementRecursively(@NotNull InspectionToolWrapper toolWrapper, final RefEntity refElement) { - if (refElement != null) { - InspectionToolPresentation presentation = getPresentation(toolWrapper); - presentation.ignoreCurrentElement(refElement); - final List children = refElement.getChildren(); - for (RefEntity child : children) { - ignoreElementRecursively(toolWrapper, child); - } + private static void resolveElementRecursively(@NotNull InspectionToolPresentation presentation, @NotNull RefEntity refElement) { + presentation.suppressProblem(refElement); + final List children = refElement.getChildren(); + for (RefEntity child : children) { + resolveElementRecursively(presentation, child); } } @@ -864,12 +865,7 @@ public class GlobalInspectionContextImpl extends GlobalInspectionContextBase imp myViewClosed = true; myView = null; ((InspectionManagerEx)InspectionManager.getInstance(getProject())).closeRunningContext(this); - for (Tools tools : getTools().values()) { - for (ScopeToolState state : tools.getTools()) { - InspectionToolWrapper toolWrapper = state.getTool(); - getPresentation(toolWrapper).cleanup(); - } - } + myPresentationMap.clear(); super.close(noSuspiciousCodeFound); } @@ -890,6 +886,11 @@ public class GlobalInspectionContextImpl extends GlobalInspectionContextBase imp } private final ConcurrentMap myPresentationMap = ContainerUtil.newConcurrentMap(); + + @Nullable + public InspectionToolPresentation getPresentationOrNull(@NotNull InspectionToolWrapper toolWrapper) { + return myPresentationMap.get(toolWrapper); + } @NotNull public InspectionToolPresentation getPresentation(@NotNull InspectionToolWrapper toolWrapper) { InspectionToolPresentation presentation = myPresentationMap.get(toolWrapper); diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ex/InspectionRVContentProvider.java b/platform/lang-impl/src/com/intellij/codeInspection/ex/InspectionRVContentProvider.java index ecf170786dd5..adfd43a8aafc 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ex/InspectionRVContentProvider.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ex/InspectionRVContentProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * 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. @@ -24,6 +24,7 @@ import com.intellij.codeInspection.reference.RefElement; import com.intellij.codeInspection.reference.RefEntity; import com.intellij.codeInspection.reference.RefModule; import com.intellij.codeInspection.ui.*; +import com.intellij.codeInspection.ui.util.SynchronizedBidiMultiMap; import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; @@ -31,7 +32,6 @@ import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Ref; -import com.intellij.util.Function; import com.intellij.util.containers.MultiMap; import com.intellij.util.ui.tree.TreeUtil; import org.jetbrains.annotations.NotNull; @@ -40,6 +40,7 @@ import org.jetbrains.annotations.Nullable; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; import java.util.*; +import java.util.function.Function; import java.util.function.UnaryOperator; public abstract class InspectionRVContentProvider { @@ -140,8 +141,8 @@ public abstract class InspectionRVContentProvider { InspectionToolWrapper wrapper = toolNode.getToolWrapper(); InspectionToolPresentation presentation = context.getPresentation(wrapper); Map> content = presentation.getContent(); - Map problems = presentation.getProblemElements(); - return appendToolNodeContent(context, toolNode, parentNode, showStructure, groupBySeverity, content, problems); + SynchronizedBidiMultiMap problems = presentation.getProblemElements(); + return appendToolNodeContent(context, toolNode, parentNode, showStructure, groupBySeverity, content, problems::get); } public abstract InspectionNode appendToolNodeContent(@NotNull GlobalInspectionContextImpl context, @@ -150,7 +151,7 @@ public abstract class InspectionRVContentProvider { final boolean showStructure, boolean groupBySeverity, @NotNull Map> contents, - @NotNull Map problems); + @NotNull Function problems); protected abstract void appendDescriptor(@NotNull GlobalInspectionContextImpl context, @NotNull InspectionToolWrapper toolWrapper, @@ -175,7 +176,7 @@ public abstract class InspectionRVContentProvider { for (String packageName : packageContents.keySet()) { final Set elements = packageContents.get(packageName); for (T userObject : elements) { - final RefEntityContainer container = computeContainer.fun(userObject); + final RefEntityContainer container = computeContainer.apply(userObject); supportStructure &= container.supportStructure(); final String moduleName = showStructure ? container.getModule() : null; Map packageNodes = module2PackageMap.get(moduleName); diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ex/InspectionRVContentProviderImpl.java b/platform/lang-impl/src/com/intellij/codeInspection/ex/InspectionRVContentProviderImpl.java index 645990fc5d01..8c565e3a18f9 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ex/InspectionRVContentProviderImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ex/InspectionRVContentProviderImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * 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. @@ -22,6 +22,7 @@ import com.intellij.codeInspection.reference.RefEntity; import com.intellij.codeInspection.reference.RefModule; import com.intellij.codeInspection.reference.RefUtil; import com.intellij.codeInspection.ui.*; +import com.intellij.codeInspection.ui.util.SynchronizedBidiMultiMap; import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; @@ -34,6 +35,7 @@ import org.jetbrains.annotations.NotNull; import java.util.Iterator; import java.util.Map; import java.util.Set; +import java.util.function.Function; public class InspectionRVContentProviderImpl extends InspectionRVContentProvider { public InspectionRVContentProviderImpl(final Project project) { @@ -49,7 +51,7 @@ public class InspectionRVContentProviderImpl extends InspectionRVContentProvider final SearchScope searchScope = context.getCurrentScope().toSearchScope(); if (searchScope instanceof LocalSearchScope) { final Map> contents = presentation.getContent(); - final Map problemElements = presentation.getProblemElements(); + final SynchronizedBidiMultiMap problemElements = presentation.getProblemElements(); for (Set entities : contents.values()) { for (Iterator iterator = entities.iterator(); iterator.hasNext(); ) { RefEntity entity = iterator.next(); @@ -87,7 +89,7 @@ public class InspectionRVContentProviderImpl extends InspectionRVContentProvider final boolean showStructure, boolean groupBySeverity, @NotNull final Map> contents, - @NotNull final Map problems) { + @NotNull final Function problems) { final InspectionToolWrapper toolWrapper = toolNode.getToolWrapper(); InspectionNode mergedToolNode = (InspectionNode)merge(toolNode, parentNode, !groupBySeverity); @@ -105,7 +107,7 @@ public class InspectionRVContentProviderImpl extends InspectionRVContentProvider contents, false, toolWrapper, - refElement -> new RefEntityContainer<>(refElement, problems.get(refElement)), + refElement -> new RefEntityContainer<>(refElement, problems.apply(refElement)), showStructure, node -> merge(node, mergedToolNode, true)); return mergedToolNode; diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ex/LocalQuickFixWrapper.java b/platform/lang-impl/src/com/intellij/codeInspection/ex/LocalQuickFixWrapper.java index 947fbbc109de..7a8aabb9cbef 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ex/LocalQuickFixWrapper.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ex/LocalQuickFixWrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * 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. @@ -87,7 +87,7 @@ public class LocalQuickFixWrapper extends QuickFixAction { final Runnable refreshViews = () -> { DaemonCodeAnalyzer.getInstance(project).restart(); for (CommonProblemDescriptor descriptor : descriptors) { - ignore(ignoredElements, descriptor, getWorkingQuickFix(descriptor.getFixes()), context); + ignore(ignoredElements, descriptor, getWorkingQuickFix(descriptor.getFixes()) != null, context); } final RefManager refManager = context.getRefManager(); @@ -113,7 +113,7 @@ public class LocalQuickFixWrapper extends QuickFixAction { //CCE here means QuickFix was incorrectly inherited, is there a way to signal (plugin) it is wrong? fix.applyFix(project, descriptor); restart = true; - ignore(ignoredElements, descriptor, fix, context); + ignore(ignoredElements, descriptor, true, context); } } } @@ -137,11 +137,11 @@ public class LocalQuickFixWrapper extends QuickFixAction { private void ignore(@NotNull Collection ignoredElements, @NotNull CommonProblemDescriptor descriptor, - @Nullable QuickFix fix, + boolean hasFix, @NotNull GlobalInspectionContextImpl context) { - if (fix != null) { + if (hasFix) { InspectionToolPresentation presentation = context.getPresentation(myToolWrapper); - presentation.ignoreProblem(descriptor, fix); + presentation.resolveProblem(descriptor); } if (descriptor instanceof ProblemDescriptor) { PsiElement element = ((ProblemDescriptor)descriptor).getPsiElement(); diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ex/QuickFixAction.java b/platform/lang-impl/src/com/intellij/codeInspection/ex/QuickFixAction.java index b854d5727b22..d7635e3530b0 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ex/QuickFixAction.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ex/QuickFixAction.java @@ -160,10 +160,10 @@ public class QuickFixAction extends AnAction implements CustomComponentAction { refManager.inspectionReadActionFinished(); try { - final Set ignoredElements = new HashSet<>(); - performFixesInBatch(project, descriptors, context, ignoredElements); + final Set resolvedElements = new HashSet<>(); + performFixesInBatch(project, descriptors, context, resolvedElements); - refreshViews(project, ignoredElements, myToolWrapper); + refreshViews(project, resolvedElements, myToolWrapper); } finally { //to make offline view lazy if (initial) refManager.inspectionReadActionStarted(); @@ -270,20 +270,20 @@ public class QuickFixAction extends AnAction implements CustomComponentAction { return selection.toArray(new RefEntity[selection.size()]); } - private static void refreshViews(@NotNull Project project, @NotNull Set selectedElements, @NotNull InspectionToolWrapper toolWrapper) { + private static void refreshViews(@NotNull Project project, @NotNull Set resolvedElements, @NotNull InspectionToolWrapper toolWrapper) { InspectionManagerEx managerEx = (InspectionManagerEx)InspectionManager.getInstance(project); final Set runningContexts = managerEx.getRunningContexts(); for (GlobalInspectionContextImpl context : runningContexts) { - for (PsiElement element : selectedElements) { - context.ignoreElement(toolWrapper.getTool(), element); + for (PsiElement element : resolvedElements) { + context.resolveElement(toolWrapper.getTool(), element); } context.refreshViews(); } } - protected static void refreshViews(@NotNull Project project, @NotNull RefEntity[] refElements, @NotNull InspectionToolWrapper toolWrapper) { + protected static void refreshViews(@NotNull Project project, @NotNull RefEntity[] resolvedElements, @NotNull InspectionToolWrapper toolWrapper) { final Set ignoredElements = new HashSet<>(); - for (RefEntity element : refElements) { + for (RefEntity element : resolvedElements) { final PsiElement psiElement = element instanceof RefElement ? ((RefElement)element).getElement() : null; if (psiElement != null && psiElement.isValid()) { ignoredElements.add(psiElement); diff --git a/platform/lang-impl/src/com/intellij/codeInspection/offlineViewer/OfflineInspectionRVContentProvider.java b/platform/lang-impl/src/com/intellij/codeInspection/offlineViewer/OfflineInspectionRVContentProvider.java index ee0c715fb53e..6fd3cbf621d4 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/offlineViewer/OfflineInspectionRVContentProvider.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/offlineViewer/OfflineInspectionRVContentProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * 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. @@ -35,6 +35,7 @@ import org.jetbrains.annotations.Nullable; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; import java.util.*; +import java.util.function.Function; public class OfflineInspectionRVContentProvider extends InspectionRVContentProvider { private final Map>> myContent; @@ -95,7 +96,7 @@ public class OfflineInspectionRVContentProvider extends InspectionRVContentProvi GlobalInspectionContextImpl context = tree.getContext(); InspectionToolPresentation presentation = context.getPresentation(toolWrapper); - return presentation.extractActiveFixes(selectedRefElements, actions, tree.getSelectedDescriptors()); + return presentation.extractActiveFixes(selectedRefElements, actions::get, tree.getSelectedDescriptors()); } @Override @@ -105,11 +106,11 @@ public class OfflineInspectionRVContentProvider extends InspectionRVContentProvi @Override public InspectionNode appendToolNodeContent(@NotNull GlobalInspectionContextImpl context, - @NotNull final InspectionNode toolNode, - @NotNull final InspectionTreeNode parentNode, - final boolean showStructure, + @NotNull InspectionNode toolNode, + @NotNull InspectionTreeNode parentNode, + boolean showStructure, boolean groupBySeverity, @NotNull final Map> contents, - @NotNull final Map problems) { + @NotNull Function problems) { InspectionToolWrapper toolWrapper = toolNode.getToolWrapper(); final Map> filteredContent = getFilteredContent(context, toolWrapper); if (filteredContent != null && !filteredContent.values().isEmpty()) { @@ -143,7 +144,7 @@ public class OfflineInspectionRVContentProvider extends InspectionRVContentProvi final Map> current = new HashMap<>(content); content = null; //GC it InspectionToolPresentation presentation = context.getPresentation(toolWrapper); - for (RefEntity refEntity : presentation.getIgnoredRefElements()) { + for (RefEntity refEntity : presentation.getResolvedElements()) { if (refEntity instanceof RefElement) { excludeProblem(refEntity.getExternalName(), current); } diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/DefaultInspectionToolPresentation.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/DefaultInspectionToolPresentation.java index 5e89058a1564..9fa4b0d2cae9 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ui/DefaultInspectionToolPresentation.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/DefaultInspectionToolPresentation.java @@ -23,7 +23,7 @@ import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInspection.*; import com.intellij.codeInspection.ex.*; import com.intellij.codeInspection.reference.*; -import com.intellij.concurrency.ConcurrentCollectionFactory; +import com.intellij.codeInspection.ui.util.SynchronizedBidiMultiMap; import com.intellij.lang.annotation.HighlightSeverity; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.PathMacroManager; @@ -39,6 +39,7 @@ import com.intellij.profile.codeInspection.InspectionProjectProfileManager; import com.intellij.profile.codeInspection.ProjectInspectionProfileManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; +import com.intellij.util.ArrayFactory; import com.intellij.util.ArrayUtil; import com.intellij.util.IncorrectOperationException; import com.intellij.util.containers.ContainerUtil; @@ -52,9 +53,10 @@ import org.jetbrains.annotations.Nullable; import java.io.*; import java.util.*; +import java.util.function.Function; import java.util.function.Predicate; -public class DefaultInspectionToolPresentation implements ProblemDescriptionsProcessor, InspectionToolPresentation { +public class DefaultInspectionToolPresentation implements InspectionToolPresentation { protected static final Logger LOG = Logger.getInstance(DefaultInspectionToolPresentation.class); @NotNull private final InspectionToolWrapper myToolWrapper; @@ -62,15 +64,15 @@ public class DefaultInspectionToolPresentation implements ProblemDescriptionsPro protected InspectionNode myToolNode; private final Object myLock = new Object(); - private final Map myProblemElements = - ConcurrentCollectionFactory.createMap(ContainerUtil.identityStrategy()); + + protected final SynchronizedBidiMultiMap myProblemElements = createBidiMap(); + protected final SynchronizedBidiMultiMap mySuppressedElements = createBidiMap(); + private final SynchronizedBidiMultiMap myResolvedElements = createBidiMap(); + protected final Map> myContents = Collections.synchronizedMap(new HashMap>(1)); // keys can be null private final Set myModulesProblems = Collections.synchronizedSet(ContainerUtil.newIdentityTroveSet()); - private final Map myProblemToElements = Collections.synchronizedMap(ContainerUtil.newIdentityTroveMap()); - private DescriptorComposer myComposer; - private final Map> myQuickFixActions = Collections.synchronizedMap(ContainerUtil.newIdentityTroveMap()); - private final Map myIgnoredElements = Collections.synchronizedMap(ContainerUtil.newIdentityTroveMap()); + private DescriptorComposer myComposer; private volatile boolean isDisposed; public DefaultInspectionToolPresentation(@NotNull InspectionToolWrapper toolWrapper, @NotNull GlobalInspectionContextImpl context) { @@ -78,6 +80,53 @@ public class DefaultInspectionToolPresentation implements ProblemDescriptionsPro myContext = context; } + public void resolveElement(@NotNull RefEntity entity) { + CommonProblemDescriptor[] removed = myProblemElements.remove(entity); + if (removed != null) { + myResolvedElements.put(entity, removed); + } + } + + public void resolveProblem(@NotNull CommonProblemDescriptor descriptor) { + myResolvedElements.put(myProblemElements.removeValue(descriptor), descriptor); + } + + public boolean isProblemResolved(@Nullable CommonProblemDescriptor descriptor) { + return myResolvedElements.containsValue(descriptor); + } + + public boolean isProblemResolved(@Nullable RefEntity entity) { + return myResolvedElements.containsKey(entity); + } + + @NotNull + @Override + public Collection getResolvedElements() { + return myResolvedElements.keys(); + } + + public void suppressProblem(@NotNull CommonProblemDescriptor descriptor) { + mySuppressedElements.put(myProblemElements.removeValue(descriptor), descriptor); + } + + @Override + public void suppressProblem(@NotNull RefEntity entity) { + CommonProblemDescriptor[] removed = myProblemElements.remove(entity); + if (removed != null) { + mySuppressedElements.put(entity, removed); + } + } + + @Override + public boolean isSuppressed(RefEntity element) { + return mySuppressedElements.containsKey(element); + } + + @Override + public boolean isSuppressed(CommonProblemDescriptor descriptor) { + return mySuppressedElements.containsValue(descriptor); + } + @Nullable @Override public HighlightSeverity getSeverity(@NotNull RefElement element) { @@ -164,25 +213,14 @@ public class DefaultInspectionToolPresentation implements ProblemDescriptionsPro if (descriptors.length == 0) return; if (filterSuppressed) { if (myContext.getOutputPath() == null || !(myToolWrapper instanceof LocalInspectionToolWrapper)) { - synchronized (myLock) { - Map problemElements = getProblemElements(); - CommonProblemDescriptor[] problems = problemElements.get(refElement); - problems = problems == null ? descriptors : mergeDescriptors(problems, descriptors); - problemElements.put(refElement, problems); - } - for (CommonProblemDescriptor description : descriptors) { - getProblemToElements().put(description, refElement); - collectQuickFixes(description.getFixes(), refElement); - } + myProblemElements.put(refElement, descriptors); } else { writeOutput(descriptors, refElement); } } - else { //just need to collect problems - for (CommonProblemDescriptor descriptor : descriptors) { - getProblemToElements().put(descriptor, refElement); - } + else { + myProblemElements.put(refElement, descriptors); } final GlobalInspectionContextImpl context = getContext(); @@ -194,9 +232,10 @@ public class DefaultInspectionToolPresentation implements ProblemDescriptionsPro } } - @NotNull - public static CommonProblemDescriptor[] mergeDescriptors(@NotNull CommonProblemDescriptor[] problems1, - @NotNull CommonProblemDescriptor[] problems2) { + public static CommonProblemDescriptor[] mergeDescriptors(CommonProblemDescriptor[] problems1, + CommonProblemDescriptor[] problems2) { + if (problems1 == null) return problems2; + if (problems2 == null) return problems1; CommonProblemDescriptor[] out = new CommonProblemDescriptor[problems1.length + problems2.length]; int o = problems1.length; Equality equality = (o1, o2) -> { @@ -272,131 +311,23 @@ public class DefaultInspectionToolPresentation implements ProblemDescriptionsPro @Override @NotNull public Collection getProblemDescriptors() { - return getProblemToElements().keySet(); - } - - private void collectQuickFixes(final QuickFix[] fixes, @NotNull RefEntity refEntity) { - if (fixes != null && fixes.length != 0) { - Set localQuickFixes = getQuickFixActions().computeIfAbsent(refEntity, k -> new HashSet<>()); - ContainerUtil.addAll(localQuickFixes, fixes); - } + return myProblemElements.getValues(); } @Override public void ignoreElement(@NotNull final RefEntity refEntity) { - final CommonProblemDescriptor[] removedDescriptors = getProblemElements().remove(refEntity); - if (removedDescriptors != null) { - for (CommonProblemDescriptor descriptor : removedDescriptors) { - getProblemToElements().remove(descriptor); - } - } - - getQuickFixActions().remove(refEntity); - } - - @Override - public void ignoreCurrentElement(@NotNull RefEntity refEntity) { - getIgnoredElements().put(refEntity, mergeDescriptors(getIgnoredElements().getOrDefault(refEntity, CommonProblemDescriptor.EMPTY_ARRAY), - getProblemElements().getOrDefault(refEntity, CommonProblemDescriptor.EMPTY_ARRAY))); - } - - @Override - public void amnesty(@NotNull RefEntity refEntity, @NotNull CommonProblemDescriptor descriptor) { - final CommonProblemDescriptor[] ignoredDescriptors = getIgnoredElements().get(refEntity); - if (ignoredDescriptors != null) { - final CommonProblemDescriptor[] remainElements = ArrayUtil.remove(ignoredDescriptors, descriptor); - if (remainElements.length != 0) { - getIgnoredElements().put(refEntity, remainElements); - } else { - getIgnoredElements().remove(refEntity); - } - } - } - - private void ignoreProblem(@Nullable RefEntity refEntity, CommonProblemDescriptor problem, int idx) { - if (refEntity == null) return; - final Set localQuickFixes = getQuickFixActions().get(refEntity); - final QuickFix[] fixes = problem.getFixes(); - if (isIgnoreProblem(fixes, localQuickFixes, idx)){ - getProblemToElements().remove(problem); - Map problemElements = getProblemElements(); - synchronized (myLock) { - CommonProblemDescriptor[] descriptors = problemElements.get(refEntity); - if (descriptors != null) { - ArrayList newDescriptors = new ArrayList<>(Arrays.asList(descriptors)); - newDescriptors.remove(problem); - CommonProblemDescriptor[] newDescriptorsAsArray = newDescriptors.toArray(new CommonProblemDescriptor[newDescriptors.size()]); - getQuickFixActions().put(refEntity, null); - if (!newDescriptors.isEmpty()) { - problemElements.put(refEntity, newDescriptorsAsArray); - for (CommonProblemDescriptor descriptor : newDescriptors) { - collectQuickFixes(descriptor.getFixes(), refEntity); - } - } - ignoreProblemElement(refEntity, newDescriptorsAsArray, problem); - } - } - } - } - - private void ignoreProblemElement(RefEntity refEntity, CommonProblemDescriptor[] newDescriptors, CommonProblemDescriptor toIgnore){ - if (newDescriptors != null && newDescriptors.length == 0) { - newDescriptors = null; - } - if (newDescriptors == null) { - getProblemElements().remove(refEntity); - } else { - getProblemElements().put(refEntity, newDescriptors); - } - CommonProblemDescriptor[] oldIgnored = getIgnoredElements().getOrDefault(refEntity, CommonProblemDescriptor.EMPTY_ARRAY); - CommonProblemDescriptor[] update = new CommonProblemDescriptor[oldIgnored.length + 1]; - System.arraycopy(oldIgnored, 0, update, 0, oldIgnored.length); - update[update.length - 1] = toIgnore; - getIgnoredElements().put(refEntity, update); - } - - @Override - public void ignoreCurrentElementProblem(@Nullable RefEntity refEntity, @Nullable CommonProblemDescriptor descriptor) { - CommonProblemDescriptor[] descriptors = getIgnoredElements().get(refEntity); - if (descriptors == null) { - descriptors = CommonProblemDescriptor.EMPTY_ARRAY; - } - getIgnoredElements().put(refEntity, ArrayUtil.append(descriptors, descriptor)); - } - - private static boolean isIgnoreProblem(QuickFix[] problemFixes, Set fixes, int idx){ - if (problemFixes == null || fixes == null) { - return true; - } - if (problemFixes.length <= idx){ - return true; - } - for (QuickFix fix : problemFixes) { - if (fix != problemFixes[idx] && !fixes.contains(fix)){ - return false; - } - } - return true; + myProblemElements.remove(refEntity); } @Override public void cleanup() { - synchronized (myLock) { - myProblemElements.clear(); - myProblemToElements.clear(); - myQuickFixActions.clear(); - myIgnoredElements.clear(); - myContents.clear(); - myModulesProblems.clear(); - } - isDisposed = true; } @Override @Nullable public CommonProblemDescriptor[] getDescriptions(@NotNull RefEntity refEntity) { - final CommonProblemDescriptor[] problems = getProblemElements().get(refEntity); + final CommonProblemDescriptor[] problems = getProblemElements().getOrDefault(refEntity, null); if (problems == null) return null; if (!refEntity.isValid()) { @@ -498,7 +429,7 @@ public class DefaultInspectionToolPresentation implements ProblemDescriptionsPro @Override public boolean hasReportedProblems() { - return !myProblemToElements.isEmpty(); + return !myProblemElements.isEmpty(); } @Override @@ -509,9 +440,9 @@ public class DefaultInspectionToolPresentation implements ProblemDescriptionsPro } protected void updateProblemElements() { - final Set elements = getProblemElements().keySet(); + final Collection elements = getProblemElements().keys(); for (RefEntity element : elements) { - if (getContext().getUIOptions().FILTER_RESOLVED_ITEMS && getIgnoredElements().containsKey(element)) continue; + if (getContext().getUIOptions().FILTER_RESOLVED_ITEMS && (isProblemResolved(element) || isSuppressed(element))) continue; if (element instanceof RefModule) { myModulesProblems.add((RefModule)element); } @@ -542,18 +473,18 @@ public class DefaultInspectionToolPresentation implements ProblemDescriptionsPro @Override @NotNull public QuickFixAction[] getQuickFixes(@NotNull final RefEntity[] refElements, InspectionTree tree) { - return extractActiveFixes(refElements, getProblemElements(), tree != null ? tree.getSelectedDescriptors() : null); + return extractActiveFixes(refElements, getProblemElements()::get, tree != null ? tree.getSelectedDescriptors() : null); } @Override @NotNull public QuickFixAction[] extractActiveFixes(@NotNull RefEntity[] refElements, - @NotNull Map descriptorMap, + @NotNull Function descriptorMap, @Nullable CommonProblemDescriptor[] allowedDescriptors) { final Set allowedDescriptorSet = allowedDescriptors == null ? null : ContainerUtil.newHashSet(allowedDescriptors); Map result = null; for (RefEntity refElement : refElements) { - final CommonProblemDescriptor[] descriptors = descriptorMap.get(refElement); + final CommonProblemDescriptor[] descriptors = descriptorMap.apply(refElement); if (descriptors == null) continue; for (CommonProblemDescriptor d : descriptors) { if (allowedDescriptorSet != null && !allowedDescriptorSet.contains(d)) { @@ -608,64 +539,15 @@ public class DefaultInspectionToolPresentation implements ProblemDescriptionsPro @Override public RefEntity getElement(@NotNull CommonProblemDescriptor descriptor) { - return getProblemToElements().get(descriptor); - } - - @Override - public void ignoreProblem(@NotNull CommonProblemDescriptor descriptor, @NotNull QuickFix fix) { - RefEntity refElement = getProblemToElements().get(descriptor); - if (refElement != null) { - final QuickFix[] fixes = descriptor.getFixes(); - for (int i = 0; i < fixes.length; i++) { - if (fixes[i] == fix){ - ignoreProblem(refElement, descriptor, i); - return; - } - } - } - } - - - @Override - public boolean isElementIgnored(@NotNull RefEntity element) { - return getIgnoredElements().containsKey(element); - } - - @Override - public boolean isProblemResolved(@Nullable RefEntity refEntity, @Nullable CommonProblemDescriptor descriptor) { - if (descriptor == null) return true; - CommonProblemDescriptor[] descriptors = getIgnoredElements().get(refEntity); - return descriptors != null && ArrayUtil.contains(descriptor, descriptors); - } - - @NotNull - @Override - public Set getIgnoredRefElements() { - return getIgnoredElements().keySet(); + return myProblemElements.getKeyFor(descriptor); } @Override @NotNull - public Map getProblemElements() { + public SynchronizedBidiMultiMap getProblemElements() { return myProblemElements; } - @NotNull - private Map getProblemToElements() { - return myProblemToElements; - } - - @NotNull - private Map> getQuickFixActions() { - return myQuickFixActions; - } - - @NotNull - @Override - public Map getIgnoredElements() { - return myIgnoredElements; - } - @Override public void createToolNode(@NotNull GlobalInspectionContextImpl globalInspectionContext, @NotNull InspectionNode node, @NotNull InspectionRVContentProvider provider, @@ -675,7 +557,6 @@ public class DefaultInspectionToolPresentation implements ProblemDescriptionsPro myToolNode = node; } - @Override @Nullable public IntentionAction findQuickFixes(@NotNull final CommonProblemDescriptor problemDescriptor, final String hint) { @@ -722,4 +603,18 @@ public class DefaultInspectionToolPresentation implements ProblemDescriptionsPro } }; } + + public static SynchronizedBidiMultiMap createBidiMap() { + return new SynchronizedBidiMultiMap() { + @Override + public CommonProblemDescriptor[] merge(CommonProblemDescriptor[] values1, CommonProblemDescriptor[] values2) { + return mergeDescriptors(values1, values2); + } + + @Override + public ArrayFactory arrayFactory() { + return CommonProblemDescriptor[]::new; + } + }; + } } diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionResultsView.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionResultsView.java index 4cf9f7691313..f0b611c7e209 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionResultsView.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionResultsView.java @@ -67,12 +67,10 @@ import com.intellij.util.ObjectUtils; import com.intellij.util.OpenSourceUtil; import com.intellij.util.concurrency.AppExecutorUtil; import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.containers.FactoryMap; import com.intellij.util.containers.HashSet; import com.intellij.util.ui.JBUI; import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.tree.TreeUtil; -import gnu.trove.THashSet; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -124,7 +122,6 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro private InspectionTreeLoadingProgressAware myLoadingProgressPreview; private final ExcludedInspectionTreeNodesManager myExcludedInspectionTreeNodesManager; @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") - private final Map> mySuppressedNodes = FactoryMap.createMap(key -> new THashSet<>()); private final InspectionViewSuppressActionHolder mySuppressActionHolder = new InspectionViewSuppressActionHolder(); private final Object myTreeStructureUpdateLock = new Object(); @@ -664,10 +661,6 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro return mySuppressActionHolder; } - public Set getSuppressedNodes(String toolId) { - return mySuppressedNodes.get(toolId); - } - @NotNull public ExcludedInspectionTreeNodesManager getExcludedManager() { return myExcludedInspectionTreeNodesManager; @@ -697,7 +690,7 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro } final InspectionNode toolNode = presentation.getToolNode(); LOG.assertTrue(toolNode != null); - final Map problems = new HashMap<>(); + final Map problems = new HashMap<>(1); problems.put(refElement, descriptors); final Map> contents = new HashMap<>(); final String groupName = refElement.getRefManager().getGroupName((RefElement)refElement); @@ -710,7 +703,7 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro uiOptions.SHOW_STRUCTURE, true, contents, - problems); + problems::get); } } })); diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionToolPresentation.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionToolPresentation.java index 0b9623b2597c..d3b4df163653 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionToolPresentation.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/InspectionToolPresentation.java @@ -18,11 +18,11 @@ package com.intellij.codeInspection.ui; import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInspection.CommonProblemDescriptor; import com.intellij.codeInspection.ProblemDescriptionsProcessor; -import com.intellij.codeInspection.QuickFix; import com.intellij.codeInspection.ex.*; import com.intellij.codeInspection.reference.RefElement; import com.intellij.codeInspection.reference.RefEntity; import com.intellij.codeInspection.reference.RefModule; +import com.intellij.codeInspection.ui.util.SynchronizedBidiMultiMap; import com.intellij.lang.annotation.HighlightSeverity; import org.jdom.Element; import org.jetbrains.annotations.NotNull; @@ -32,6 +32,7 @@ import javax.swing.*; import java.util.Collection; import java.util.Map; import java.util.Set; +import java.util.function.Function; import java.util.function.Predicate; public interface InspectionToolPresentation extends ProblemDescriptionsProcessor { @@ -39,9 +40,6 @@ public interface InspectionToolPresentation extends ProblemDescriptionsProcessor @NotNull InspectionToolWrapper getToolWrapper(); - @NotNull - Map getIgnoredElements(); - void createToolNode(@NotNull GlobalInspectionContextImpl globalInspectionContext, @NotNull InspectionNode node, @NotNull InspectionRVContentProvider provider, @@ -64,12 +62,26 @@ public interface InspectionToolPresentation extends ProblemDescriptionsProcessor @NotNull Map> getContent(); - void ignoreCurrentElement(@NotNull RefEntity refEntity); - void amnesty(@NotNull RefEntity refEntity, @NotNull CommonProblemDescriptor descriptor); - void cleanup(); - boolean isElementIgnored(@NotNull RefEntity element); + void resolveElement(@NotNull RefEntity entity); + + void resolveProblem(@NotNull CommonProblemDescriptor descriptor); + + boolean isProblemResolved(@Nullable CommonProblemDescriptor descriptor); + + boolean isProblemResolved(@Nullable RefEntity entity); + @NotNull - Set getIgnoredRefElements(); + Collection getResolvedElements(); + + void suppressProblem(@NotNull CommonProblemDescriptor descriptor); + + void suppressProblem(@NotNull RefEntity entity); + + boolean isSuppressed(RefEntity element); + + boolean isSuppressed(CommonProblemDescriptor descriptor); + + void cleanup(); @Nullable IntentionAction findQuickFixes(@NotNull CommonProblemDescriptor descriptor, final String hint); @NotNull @@ -80,19 +92,16 @@ public interface InspectionToolPresentation extends ProblemDescriptionsProcessor @NotNull QuickFixAction[] getQuickFixes(@NotNull final RefEntity[] refElements, @Nullable InspectionTree tree); @NotNull - Map getProblemElements(); + SynchronizedBidiMultiMap getProblemElements(); @NotNull Collection getProblemDescriptors(); - boolean isProblemResolved(@Nullable RefEntity refEntity, @Nullable CommonProblemDescriptor descriptor); - void ignoreCurrentElementProblem(@Nullable RefEntity refEntity, @Nullable CommonProblemDescriptor descriptor); void addProblemElement(@Nullable RefEntity refElement, boolean filterSuppressed, @NotNull CommonProblemDescriptor... descriptions); - void ignoreProblem(@NotNull CommonProblemDescriptor descriptor, @NotNull QuickFix fix); @NotNull GlobalInspectionContextImpl getContext(); @NotNull QuickFixAction[] extractActiveFixes(@NotNull RefEntity[] refElements, - @NotNull Map descriptorMap, + @NotNull Function descriptorMap, @Nullable CommonProblemDescriptor[] allowedDescriptors); void exportResults(@NotNull Element parentNode, @NotNull Predicate isEntityExcluded, @@ -117,4 +126,5 @@ public interface InspectionToolPresentation extends ProblemDescriptionsProcessor @Nullable HighlightSeverity getSeverity(@NotNull RefElement element); + } diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/ProblemDescriptionNode.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/ProblemDescriptionNode.java index c4061b2f5396..a1f9674d575b 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ui/ProblemDescriptionNode.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/ProblemDescriptionNode.java @@ -69,6 +69,11 @@ public class ProblemDescriptionNode extends SuppressableInspectionTreeNode { myLineNumber = myDescriptor instanceof ProblemDescriptor ? ((ProblemDescriptor)myDescriptor).getLineNumber() : (lineNumberCounter == null ? -1 : lineNumberCounter.getAsInt()); } + @Override + public final boolean isAlreadySuppressedFromView() { + return myDescriptor != null && getPresentation().isSuppressed(myDescriptor); + } + public int getLineNumber() { return myLineNumber; } @@ -93,14 +98,9 @@ public class ProblemDescriptionNode extends SuppressableInspectionTreeNode { return myDescriptor; } - @Override - public int getProblemCount(boolean allowSuppressed) { - return getPresentation().isProblemResolved(getElement(), myDescriptor) && !(allowSuppressed && isAlreadySuppressedFromView() && isValid())? 0 : 1; - } - @Override public void visitProblemSeverities(TObjectIntHashMap counter) { - if (!getPresentation().isProblemResolved(getElement(), myDescriptor)) { + if (!isExcluded() && !isQuickFixAppliedFromView() && !isAlreadySuppressedFromView()) { counter.put(myLevel, counter.get(myLevel) + 1); } } @@ -116,22 +116,6 @@ public class ProblemDescriptionNode extends SuppressableInspectionTreeNode { return true; } - @Override - public void excludeElement(ExcludedInspectionTreeNodesManager manager) { - InspectionToolPresentation presentation = getPresentation(); - presentation.ignoreCurrentElementProblem(getElement(), getDescriptor()); - super.excludeElement(manager); - } - - @Override - public void amnestyElement(ExcludedInspectionTreeNodesManager manager) { - if (!isAlreadySuppressedFromView()) { - InspectionToolPresentation presentation = getPresentation(); - presentation.amnesty(getElement(), getDescriptor()); - } - super.amnestyElement(manager); - } - @Override protected void dropCache(Project project) { if (!isQuickFixAppliedFromView()) { @@ -151,7 +135,7 @@ public class ProblemDescriptionNode extends SuppressableInspectionTreeNode { @Override public boolean isQuickFixAppliedFromView() { - return (myDescriptor != null && getPresentation().isProblemResolved(getElement(), myDescriptor)) && !isAlreadySuppressedFromView(); + return (myDescriptor != null && getPresentation().isProblemResolved(myDescriptor)) && !isAlreadySuppressedFromView(); } @Nullable diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/RefElementNode.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/RefElementNode.java index 195f37395c12..4cc893b1f1e4 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ui/RefElementNode.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/RefElementNode.java @@ -22,7 +22,6 @@ import com.intellij.codeInspection.InspectionsBundle; import com.intellij.codeInspection.reference.RefDirectory; import com.intellij.codeInspection.reference.RefElement; import com.intellij.codeInspection.reference.RefEntity; -import com.intellij.openapi.vcs.FileStatus; import gnu.trove.TObjectIntHashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -45,6 +44,11 @@ public class RefElementNode extends SuppressableInspectionTreeNode { myIcon = refEntity == null ? null : refEntity.getIcon(false); } + @Override + public final boolean isAlreadySuppressedFromView() { + return getElement() != null && getPresentation().isSuppressed(getElement()); + } + public boolean hasDescriptorsUnder() { return myHasDescriptorsUnder; } @@ -75,16 +79,6 @@ public class RefElementNode extends SuppressableInspectionTreeNode { return refEntity != null && refEntity.isValid(); } - @Override - public void excludeElement(ExcludedInspectionTreeNodesManager excludedManager) { - super.excludeElement(excludedManager); - } - - @Override - public void amnestyElement(ExcludedInspectionTreeNodesManager excludedManager) { - super.amnestyElement(excludedManager); - } - @Override public void add(MutableTreeNode newChild) { checkHasDescriptorUnder(newChild); @@ -115,14 +109,9 @@ public class RefElementNode extends SuppressableInspectionTreeNode { : super.getContainingFileLocalEntity(); } - @Override - public int getProblemCount(boolean allowSuppressed) { - return isLeaf() ? getPresentation().getIgnoredRefElements().contains(getElement()) && !(allowSuppressed && isAlreadySuppressedFromView() && isValid()) ? 0 : 1 : super.getProblemCount(allowSuppressed); - } - @Override public void visitProblemSeverities(TObjectIntHashMap counter) { - if (isLeaf() && !getPresentation().isElementIgnored(getElement())) { + if (!isExcluded() && isLeaf() && !getPresentation().isProblemResolved(getElement()) && !getPresentation().isSuppressed(getElement())) { counter.put(HighlightDisplayLevel.WARNING, counter.get(HighlightDisplayLevel.WARNING) + 1); return; } @@ -131,7 +120,7 @@ public class RefElementNode extends SuppressableInspectionTreeNode { @Override public boolean isQuickFixAppliedFromView() { - return false; + return isLeaf() && getPresentation().isProblemResolved(getElement()); } @Nullable diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/SuppressableInspectionTreeNode.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/SuppressableInspectionTreeNode.java index 65dc760b244d..8b37095a0ff6 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ui/SuppressableInspectionTreeNode.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/SuppressableInspectionTreeNode.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * 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. @@ -55,19 +55,15 @@ public abstract class SuppressableInspectionTreeNode extends InspectionTreeNode return isLeaf(); } - public final boolean isAlreadySuppressedFromView() { - final Object usrObj = getUserObject(); - if (usrObj != null) { - InspectionResultsView view = myPresentation.getContext().getView(); - if (view != null && view.getSuppressedNodes(myPresentation.getToolWrapper().getShortName()).contains(usrObj)) { - return true; - } - } - return false; - } + public abstract boolean isAlreadySuppressedFromView(); public abstract boolean isQuickFixAppliedFromView(); + @Override + public int getProblemCount(boolean allowSuppressed) { + return !isExcluded() && isValid() && !isQuickFixAppliedFromView() && (allowSuppressed || !isAlreadySuppressedFromView()) ? 1 : 0; + } + @NotNull public Set getAvailableSuppressActions() { return myAvailableSuppressActions; @@ -115,10 +111,13 @@ public abstract class SuppressableInspectionTreeNode extends InspectionTreeNode @Nullable @Override public String getTailText() { - if (!isValid()) { - return "No longer valid"; + if (isQuickFixAppliedFromView()) { + return null; } - return isAlreadySuppressedFromView() ? "Suppressed" : null; + if (isAlreadySuppressedFromView()) { + return "Suppressed"; + } + return !isValid() ? "No longer valid" : null; } @NotNull @@ -163,4 +162,8 @@ public abstract class SuppressableInspectionTreeNode extends InspectionTreeNode } } } + + protected boolean isExcluded() { + return getPresentation().getContext().getView().getExcludedManager().isExcluded(this); + } } diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/actions/suppress/SuppressActionSequentialTask.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/actions/suppress/SuppressActionSequentialTask.java index 09d48b33596d..ee0b908edeca 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ui/actions/suppress/SuppressActionSequentialTask.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/actions/suppress/SuppressActionSequentialTask.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * 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. @@ -19,9 +19,7 @@ import com.intellij.codeInspection.*; import com.intellij.codeInspection.ex.GlobalInspectionContextImpl; import com.intellij.codeInspection.ex.InspectionManagerEx; import com.intellij.codeInspection.ex.InspectionToolWrapper; -import com.intellij.codeInspection.reference.RefElement; import com.intellij.codeInspection.reference.RefEntity; -import com.intellij.codeInspection.ui.ProblemDescriptionNode; import com.intellij.codeInspection.ui.SuppressableInspectionTreeNode; import com.intellij.openapi.application.WriteAction; import com.intellij.openapi.diagnostic.Logger; @@ -33,12 +31,9 @@ import com.intellij.psi.PsiElement; import com.intellij.util.IncorrectOperationException; import com.intellij.util.SequentialTask; import com.intellij.util.ThrowableRunnable; -import com.intellij.util.containers.Queue; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Collections; -import java.util.List; import java.util.Set; /** @@ -50,17 +45,14 @@ public class SuppressActionSequentialTask implements SequentialTask { private SuppressableInspectionTreeNode[] myNodesToSuppress; @NotNull private final SuppressIntentionAction mySuppressAction; @NotNull private final InspectionToolWrapper myWrapper; - @NotNull private final GlobalInspectionContextImpl myContext; private int myCount = 0; public SuppressActionSequentialTask(@NotNull SuppressableInspectionTreeNode[] nodesToSuppress, @NotNull SuppressIntentionAction suppressAction, - @NotNull InspectionToolWrapper wrapper, - @NotNull GlobalInspectionContextImpl context) { + @NotNull InspectionToolWrapper wrapper) { myNodesToSuppress = nodesToSuppress; mySuppressAction = suppressAction; myWrapper = wrapper; - myContext = context; } @@ -132,36 +124,11 @@ public class SuppressActionSequentialTask implements SequentialTask { final Set globalInspectionContexts = ((InspectionManagerEx)InspectionManager.getInstance(element.getProject())).getRunningContexts(); for (GlobalInspectionContextImpl context : globalInspectionContexts) { - context.ignoreElement(wrapper.getTool(), container); + context.resolveElement(wrapper.getTool(), container); if (descriptor != null) { - context.getPresentation(wrapper).ignoreCurrentElementProblem(refEntity, descriptor); + context.getPresentation(wrapper).suppressProblem(descriptor); } } - - final RefElement containerRef = refEntity.getRefManager().getReference(container); - final Set suppressedNodes = myContext.getView().getSuppressedNodes(wrapper.getShortName()); - if (containerRef != null) { - Queue toIgnoreInView = new Queue<>(1); - toIgnoreInView.addLast(containerRef); - while (!toIgnoreInView.isEmpty()) { - final RefEntity entity = toIgnoreInView.pullFirst(); - if (node instanceof ProblemDescriptionNode) { - final CommonProblemDescriptor[] descriptors = myContext.getPresentation(wrapper).getIgnoredElements().get(entity); - if (descriptors != null) { - Collections.addAll(suppressedNodes, descriptors); - } - } else { - suppressedNodes.add(entity); - } - final List children = entity.getChildren(); - for (RefEntity child : children) { - toIgnoreInView.addLast(child); - } - } - } - if (node instanceof ProblemDescriptionNode) { - suppressedNodes.add(descriptor); - } } catch (IncorrectOperationException e1) { LOG.error(e1); diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/actions/suppress/SuppressActionWrapper.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/actions/suppress/SuppressActionWrapper.java index b7dc0071c7b5..f6b46818a45c 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ui/actions/suppress/SuppressActionWrapper.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/actions/suppress/SuppressActionWrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * 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. @@ -102,7 +102,7 @@ public class SuppressActionWrapper extends ActionGroup implements CompactActionG final SequentialModalProgressTask progressTask = new SequentialModalProgressTask(project, templatePresentationText, true); progressTask.setMinIterationTime(200); - progressTask.setTask(new SuppressActionSequentialTask(nodes, mySuppressAction, wrapper, view.getGlobalInspectionContext())); + progressTask.setTask(new SuppressActionSequentialTask(nodes, mySuppressAction, wrapper)); ProgressManager.getInstance().run(progressTask); }, templatePresentationText, null); diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ui/util/SynchronizedBidiMultiMap.java b/platform/lang-impl/src/com/intellij/codeInspection/ui/util/SynchronizedBidiMultiMap.java new file mode 100644 index 000000000000..dc18e47260a5 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInspection/ui/util/SynchronizedBidiMultiMap.java @@ -0,0 +1,103 @@ +/* + * 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.codeInspection.ui.util; + +import com.intellij.util.ArrayFactory; +import com.intellij.util.ArrayUtil; +import gnu.trove.THashMap; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Map; + +public abstract class SynchronizedBidiMultiMap { + private final Map myKey2Values = new THashMap<>(); + private final Map myValue2Keys = new THashMap<>(); + + public synchronized Collection keys() { + return new ArrayList<>(myKey2Values.keySet()); + } + + public synchronized boolean containsKey(K key) { + return myKey2Values.containsKey(key); + } + + public synchronized boolean containsValue(V value) { + return myValue2Keys.containsKey(value); + } + + public synchronized K getKeyFor(V value) { + return myValue2Keys.get(value); + } + + public synchronized V[] get(K key) { + return myKey2Values.get(key); + } + public synchronized V[] getOrDefault(K key, V[] defaultValue) { + V[] values = get(key); + return values == null ? defaultValue : values; + } + + public synchronized void put(K key, V... values) { + myKey2Values.merge(key, values, this::merge); + for (V value : values) { + myValue2Keys.put(value, key); + } + } + + /** + * @return new elements or null! + */ + public synchronized V[] remove(K key, V value) { + V[] newValues = myKey2Values.computeIfPresent(key, (k, vs) -> { + V[] removed = ArrayUtil.remove(vs, value, arrayFactory()); + if (removed.length == 0) return null; + return removed; + }); + myValue2Keys.remove(value); + return newValues; + } + + public synchronized K removeValue(V value) { + K key = myValue2Keys.get(value); + if (key != null) { + remove(key, value); + } + return key; + } + + public synchronized V[] remove(K key) { + V[] removed = myKey2Values.remove(key); + if (removed != null) { + for (V v : removed) { + myValue2Keys.remove(v); + } + } + return removed; + } + + public synchronized Collection getValues() { + return myValue2Keys.keySet(); + } + + public synchronized boolean isEmpty() { + return myValue2Keys.isEmpty(); + } + + public abstract V[] merge(V[] values1, V[] values2); + + public abstract ArrayFactory arrayFactory(); +}