use AsyncTreeModel in inspection view

This commit is contained in:
Dmitry Batkovich
2018-11-30 16:09:54 +03:00
parent 98ead1d248
commit b5eef17b1f
42 changed files with 1169 additions and 1527 deletions
@@ -22,14 +22,11 @@ import com.intellij.codeInspection.ex.DescriptorComposer;
import com.intellij.codeInspection.ex.HTMLComposerImpl;
import com.intellij.codeInspection.reference.*;
import com.intellij.codeInspection.ui.InspectionToolPresentation;
import com.intellij.codeInspection.ui.InspectionTreeNode;
import com.intellij.codeInspection.ui.RefElementNode;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import javax.swing.tree.TreeNode;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class DeadHTMLComposer extends HTMLComposerImpl {
@@ -332,7 +329,7 @@ public class DeadHTMLComposer extends HTMLComposerImpl {
}
private void appendCallesList(RefElement element, StringBuffer buf, Set<? super RefElement> mentionedElements, boolean appendCallees){
final Set<RefElement> possibleChildren = getPossibleChildren(new RefElementNode(element, myToolPresentation), element);
final Set<RefElement> possibleChildren = getPossibleChildren(element);
if (!possibleChildren.isEmpty()) {
if (appendCallees){
appendHeading(buf, InspectionsBundle.message("inspection.export.results.callees"));
@@ -359,16 +356,13 @@ public class DeadHTMLComposer extends HTMLComposerImpl {
}
}
public static Set<RefElement> getPossibleChildren(final RefElementNode refElementNode, RefElement refElement) {
final TreeNode[] pathToRoot = refElementNode.getPath();
private static Set<RefElement> getPossibleChildren(RefElement refElement) {
if (!refElement.isValid()) return Collections.emptySet();
final HashSet<RefElement> newChildren = new HashSet<>();
if (!refElement.isValid()) return newChildren;
for (RefElement refCallee : refElement.getOutReferences()) {
if (((RefElementImpl)refCallee).isSuspicious()) {
if (notInPath(pathToRoot, refCallee)) newChildren.add(refCallee);
newChildren.add(refCallee);
}
}
@@ -379,7 +373,7 @@ public class DeadHTMLComposer extends HTMLComposerImpl {
if (!refMethod.isStatic() && !refMethod.isConstructor() && (aClass != null && !aClass.isAnonymous())) {
for (RefMethod refDerived : refMethod.getDerivedMethods()) {
if (((RefMethodImpl)refDerived).isSuspicious()) {
if (notInPath(pathToRoot, refDerived)) newChildren.add(refDerived);
newChildren.add(refDerived);
}
}
}
@@ -387,25 +381,16 @@ public class DeadHTMLComposer extends HTMLComposerImpl {
RefClass refClass = (RefClass) refElement;
for (RefClass subClass : refClass.getSubClasses()) {
if ((subClass.isInterface() || subClass.isAbstract()) && ((RefClassImpl)subClass).isSuspicious()) {
if (notInPath(pathToRoot, subClass)) newChildren.add(subClass);
newChildren.add(subClass);
}
}
if (refClass.getDefaultConstructor() instanceof RefImplicitConstructor) {
Set<RefElement> fromConstructor = getPossibleChildren(refElementNode, refClass.getDefaultConstructor());
Set<RefElement> fromConstructor = getPossibleChildren(refClass.getDefaultConstructor());
newChildren.addAll(fromConstructor);
}
}
return newChildren;
}
private static boolean notInPath(TreeNode[] pathToRoot, RefElement refChild) {
for (TreeNode aPathToRoot : pathToRoot) {
InspectionTreeNode node = (InspectionTreeNode)aPathToRoot;
if (node instanceof RefElementNode && ((RefElementNode)node).getElement() == refChild) return false;
}
return true;
}
}
@@ -9,7 +9,6 @@ import com.intellij.codeInspection.ex.*;
import com.intellij.codeInspection.reference.RefElement;
import com.intellij.codeInspection.reference.RefEntity;
import com.intellij.codeInspection.reference.RefJavaElement;
import com.intellij.codeInspection.ui.InspectionNode;
import com.intellij.codeInspection.ui.InspectionResultsView;
import com.intellij.codeInspection.ui.InspectionTreeNode;
import com.intellij.codeInspection.util.RefFilter;
@@ -40,7 +39,7 @@ public class DummyEntryPointsPresentation extends UnusedDeclarationPresentation
@Override
protected String getSeverityDelegateName() {
return UnusedDeclarationInspection.SHORT_NAME;
return UnusedDeclarationInspectionBase.SHORT_NAME;
}
private class MoveEntriesToSuspicious extends QuickFixAction {
@@ -84,12 +83,10 @@ public class DummyEntryPointsPresentation extends UnusedDeclarationPresentation
}
@Override
public void createToolNode(@NotNull GlobalInspectionContextImpl context, @NotNull InspectionNode node,
@NotNull InspectionRVContentProvider provider,
@NotNull InspectionTreeNode parentNode,
boolean showStructure,
boolean groupByStructure) {
myToolNode = node;
public void patchToolNode(@NotNull InspectionTreeNode node,
@NotNull InspectionRVContentProvider provider,
boolean showStructure,
boolean groupByStructure) {
}
@Override
@@ -23,6 +23,7 @@ import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.AtomicNotNullLazyValue;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VfsUtil;
@@ -73,6 +74,16 @@ public class UnusedDeclarationPresentation extends DefaultInspectionToolPresenta
private WeakUnreferencedFilter myFilter;
private DeadHTMLComposer myComposer;
private final AtomicNotNullLazyValue<InspectionToolWrapper> myDummyWrapper = new AtomicNotNullLazyValue<InspectionToolWrapper>() {
@NotNull
@Override
protected InspectionToolWrapper compute() {
InspectionToolWrapper toolWrapper = new GlobalInspectionToolWrapper(new DummyEntryPointsEP());
toolWrapper.initialize(myContext);
return toolWrapper;
}
};
@NonNls private static final String DELETE = "delete";
@NonNls private static final String COMMENT = "comment";
@@ -410,24 +421,21 @@ public class UnusedDeclarationPresentation extends DefaultInspectionToolPresenta
}
@Override
public void createToolNode(@NotNull GlobalInspectionContextImpl context,
@NotNull InspectionNode node,
@NotNull InspectionRVContentProvider provider,
@NotNull InspectionTreeNode parentNode,
boolean showStructure,
boolean groupByStructure) {
final EntryPointsNode entryPointsNode = new EntryPointsNode(context);
InspectionToolWrapper dummyToolWrapper = entryPointsNode.getToolWrapper();
InspectionToolPresentation presentation = context.getPresentation(dummyToolWrapper);
public void patchToolNode(@NotNull InspectionTreeNode node,
@NotNull InspectionRVContentProvider provider,
boolean showStructure,
boolean groupByStructure) {
InspectionTreeModel model = myContext.getView().getTree().getInspectionTreeModel();
model.createCustomNode(myDummyWrapper.getValue(), () -> new EntryPointsNode(myDummyWrapper.getValue(), myContext, model), node);
InspectionToolPresentation presentation = myContext.getPresentation(myDummyWrapper.getValue());
presentation.updateContent();
provider.appendToolNodeContent(context, entryPointsNode, node, showStructure, groupByStructure);
myToolNode = entryPointsNode;
provider.appendToolNodeContent(myContext, myDummyWrapper.getValue(), node, showStructure, groupByStructure);
}
@NotNull
@Override
public RefElementNode createRefNode(@Nullable RefEntity entity) {
return new RefElementNode(entity, this) {
public RefElementNode createRefNode(@Nullable RefEntity entity, @NotNull InspectionTreeModel model) {
return new RefElementNode(entity, this, model) {
@Nullable
@Override
public String getTailText() {
@@ -677,7 +685,7 @@ public class UnusedDeclarationPresentation extends DefaultInspectionToolPresenta
}
@Override
public int getProblemsCount(@NotNull InspectionTree tree) {
return 0;
public boolean showProblemCount() {
return false;
}
}
@@ -2,9 +2,7 @@
package com.intellij.codeInspection.ui;
import com.intellij.codeHighlighting.HighlightDisplayLevel;
import com.intellij.codeInspection.deadCode.DummyEntryPointsEP;
import com.intellij.codeInspection.ex.GlobalInspectionContextImpl;
import com.intellij.codeInspection.ex.GlobalInspectionToolWrapper;
import com.intellij.codeInspection.ex.InspectionToolWrapper;
import com.intellij.icons.AllIcons;
import gnu.trove.TObjectIntHashMap;
@@ -13,18 +11,13 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
/**
* @author max
*/
public class EntryPointsNode extends InspectionNode {
public EntryPointsNode(@NotNull GlobalInspectionContextImpl context) {
super(createDummyWrapper(context), context.getCurrentProfile());
}
private volatile boolean myExcluded;
private static InspectionToolWrapper createDummyWrapper(@NotNull GlobalInspectionContextImpl context) {
InspectionToolWrapper toolWrapper = new GlobalInspectionToolWrapper(new DummyEntryPointsEP());
toolWrapper.initialize(context);
return toolWrapper;
public EntryPointsNode(@NotNull InspectionToolWrapper dummyWrapper,
@NotNull GlobalInspectionContextImpl context,
@NotNull InspectionTreeModel model) {
super(dummyWrapper, context.getCurrentProfile(), model);
}
@Override
@@ -44,7 +37,17 @@ public class EntryPointsNode extends InspectionNode {
}
@Override
public int getProblemCount(boolean allowSuppressed) {
return 0;
public boolean isExcluded() {
return myExcluded;
}
@Override
public void excludeElement() {
myExcluded = true;
}
@Override
public void amnestyElement() {
myExcluded = false;
}
}
@@ -13,8 +13,8 @@ import com.intellij.codeInspection.ex.InspectionToolWrapper;
import com.intellij.codeInspection.offline.OfflineProblemDescriptor;
import com.intellij.codeInspection.offlineViewer.OfflineViewParseUtil;
import com.intellij.codeInspection.ui.InspectionResultsView;
import com.intellij.codeInspection.ui.InspectionRootNode;
import com.intellij.codeInspection.ui.InspectionTree;
import com.intellij.codeInspection.ui.InspectionTreeNode;
import com.intellij.codeInspection.ui.ProblemDescriptionNode;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ex.PathManagerEx;
@@ -129,11 +129,13 @@ public class OfflineInspectionResultViewTest extends TestSourceBasedTestCase {
InspectionTree tree = updateTree();
PlatformTestUtil.dispatchAllEventsInIdeEventQueue();
TreeUtil.expandAll(tree);
PlatformTestUtil.assertTreeEqual(tree, "-" + getProject() + "\n" +
PlatformTestUtil.dispatchAllEventsInIdeEventQueue();
PlatformTestUtil.assertTreeEqual(tree, "-InspectionViewTree\n" +
" -Java\n" +
" -Declaration redundancy\n" +
" -unused\n" +
" -Module: 'testOfflineWithInvalid'\n" +
" -Unused declaration\n" +
" Entry Points\n" +
" -testOfflineWithInvalid\n" +
" -<default>\n" +
" -Test\n" +
" -f()\n" +
@@ -141,26 +143,26 @@ public class OfflineInspectionResultViewTest extends TestSourceBasedTestCase {
" -b()\n" +
" -anonymous (Runnable)\n" +
" -run()\n" +
" Variable <code>i</code> is never used.\n" +
" Variable <code>r</code> is never used.\n" +
" Variable 'i' is never used.\n" +
" Variable 'r' is never used.\n" +
" -ff()\n" +
" Variable <code>a</code> is never used.\n" +
" Variable <code>d</code> is never used.\n" +
" Variable 'a' is never used.\n" +
" Variable 'd' is never used.\n" +
" -foo()\n" +
" Variable <code>j</code> is never used.\n" +
" Variable 'j' is never used.\n" +
" -main(String[])\n" +
" Variable <code>test</code> is never used.\n" +
" Variable 'test' is never used.\n" +
" -Probable bugs\n" +
" -EqualsWithItself\n" +
" -Module: 'testOfflineWithInvalid'\n" +
" -'equals()' called on itself\n" +
" -testOfflineWithInvalid\n" +
" -<default>\n" +
" -Test\n" +
" -m()\n" +
" 'equals()' called on itself\n" +
" -null\n" +
" -element no longer exists\n" +
" '()' called on itself\n"
);
tree.setSelectionRow(28);
tree.setSelectionRow(29);
final ProblemDescriptionNode node = (ProblemDescriptionNode)tree.getSelectionModel().getSelectionPath().getLastPathComponent();
assertFalse(node.isValid());
}
@@ -170,11 +172,13 @@ public class OfflineInspectionResultViewTest extends TestSourceBasedTestCase {
InspectionTree tree = updateTree();
PlatformTestUtil.dispatchAllEventsInIdeEventQueue();
TreeUtil.expandAll(tree);
PlatformTestUtil.assertTreeEqual(tree, "-" + getProject() + "\n" +
PlatformTestUtil.dispatchAllEventsInIdeEventQueue();
PlatformTestUtil.assertTreeEqual(tree, "-InspectionViewTree\n" +
" -Java\n" +
" -Declaration redundancy\n" +
" -unused\n" +
" -Module: 'testOfflineView'\n" +
" -Unused declaration\n" +
" Entry Points\n" +
" -testOfflineView\n" +
" -<default>\n" +
" -Test\n" +
" -f()\n" +
@@ -182,18 +186,18 @@ public class OfflineInspectionResultViewTest extends TestSourceBasedTestCase {
" -b()\n" +
" -anonymous (Runnable)\n" +
" -run()\n" +
" Variable <code>i</code> is never used.\n" +
" Variable <code>r</code> is never used.\n" +
" Variable 'i' is never used.\n" +
" Variable 'r' is never used.\n" +
" -ff()\n" +
" Variable <code>a</code> is never used.\n" +
" Variable <code>d</code> is never used.\n" +
" Variable 'a' is never used.\n" +
" Variable 'd' is never used.\n" +
" -foo()\n" +
" Variable <code>j</code> is never used.\n" +
" Variable 'j' is never used.\n" +
" -main(String[])\n" +
" Variable <code>test</code> is never used.\n" +
" Variable 'test' is never used.\n" +
" -Probable bugs\n" +
" -" + myDataFlowToolWrapper + "\n" +
" -Module: 'testOfflineView'\n" +
" -'equals()' called on itself\n" +
" -testOfflineView\n" +
" -<default>\n" +
" -Test\n" +
" -m()\n" +
@@ -206,62 +210,64 @@ public class OfflineInspectionResultViewTest extends TestSourceBasedTestCase {
tree = updateTree();
PlatformTestUtil.dispatchAllEventsInIdeEventQueue();
TreeUtil.expandAll(tree);
PlatformTestUtil.assertTreeEqual(tree, "-" + getProject() + "\n" +
PlatformTestUtil.dispatchAllEventsInIdeEventQueue();
PlatformTestUtil.assertTreeEqual(tree, "-InspectionViewTree\n" +
" -Java\n" +
" -Declaration redundancy\n" +
" -unused\n" +
" -Unused declaration\n" +
" Entry Points\n" +
" -Test\n" +
" -f()\n" +
" -D\n" +
" -b()\n" +
" -anonymous (Runnable)\n" +
" -run()\n" +
" Variable <code>i</code> is never used.\n" +
" Variable <code>r</code> is never used.\n" +
" Variable 'i' is never used.\n" +
" Variable 'r' is never used.\n" +
" -ff()\n" +
" Variable <code>a</code> is never used.\n" +
" Variable <code>d</code> is never used.\n" +
" Variable 'a' is never used.\n" +
" Variable 'd' is never used.\n" +
" -foo()\n" +
" Variable <code>j</code> is never used.\n" +
" Variable 'j' is never used.\n" +
" -main(String[])\n" +
" Variable <code>test</code> is never used.\n" +
" Variable 'test' is never used.\n" +
" -Probable bugs\n" +
" -EqualsWithItself\n" +
" -'equals()' called on itself\n" +
" -Test\n" +
" 'equals()' called on itself\n" +
" -Test2\n" +
" 'equals()' called on itself\n"
);
TreeUtil.selectNode(tree, tree.getRoot());
final InspectionTreeNode root = tree.getRoot();
InspectionRootNode root = tree.getInspectionTreeModel().getRoot();
root.excludeElement();
TreeUtil.treeNodeTraverser(root).traverse().processEach(node -> {
assertTrue(((InspectionTreeNode)node).isExcluded());
tree.getInspectionTreeModel().traverse(root, node -> {
assertTrue("node = " + node, node.isExcluded());
return true;
});
myView.getGlobalInspectionContext().getUIOptions().FILTER_RESOLVED_ITEMS = false;
tree = updateTree();
PlatformTestUtil.assertTreeEqual(tree, "-" + getProject() + "\n"
PlatformTestUtil.assertTreeEqual(tree, "-InspectionViewTree\n"
+ " -Java\n" +
" -Declaration redundancy\n" +
" -unused\n" +
" -Unused declaration\n" +
" Entry Points\n" +
" -Test\n" +
" -f()\n" +
" -D\n" +
" -b()\n" +
" -anonymous (Runnable)\n" +
" -run()\n" +
" Variable <code>i</code> is never used.\n" +
" Variable <code>r</code> is never used.\n" +
" Variable 'i' is never used.\n" +
" Variable 'r' is never used.\n" +
" -ff()\n" +
" Variable <code>a</code> is never used.\n" +
" Variable <code>d</code> is never used.\n" +
" Variable 'a' is never used.\n" +
" Variable 'd' is never used.\n" +
" -foo()\n" +
" Variable <code>j</code> is never used.\n" +
" Variable 'j' is never used.\n" +
" -main(String[])\n" +
" Variable <code>test</code> is never used.\n" +
" Variable 'test' is never used.\n" +
" -Probable bugs\n" +
" -EqualsWithItself\n" +
" -'equals()' called on itself\n" +
" -Test\n" +
" 'equals()' called on itself\n" +
" -Test2\n" +
@@ -269,10 +275,11 @@ public class OfflineInspectionResultViewTest extends TestSourceBasedTestCase {
);
}
private InspectionTree updateTree() {
private InspectionTree updateTree() throws InterruptedException {
myView.update();
final InspectionTree tree = myView.getTree();
TreeUtil.expandAll(tree);
PlatformTestUtil.dispatchAllEventsInIdeEventQueue();
return tree;
}
@@ -199,7 +199,7 @@ public class ViewOfflineResultsAction extends AnAction {
context.setCurrentScope(scope);
context.initializeTools(new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
final InspectionResultsView view = new InspectionResultsView(context,
new OfflineInspectionRVContentProvider(resMap, project));
new OfflineInspectionRVContentProvider(resMap));
((RefManagerImpl)context.getRefManager()).startOfflineView();
context.addView(view, title, true);
view.update();
@@ -19,7 +19,6 @@ import com.intellij.codeInspection.reference.RefVisitor;
import com.intellij.codeInspection.ui.DefaultInspectionToolPresentation;
import com.intellij.codeInspection.ui.InspectionResultsView;
import com.intellij.codeInspection.ui.InspectionToolPresentation;
import com.intellij.codeInspection.ui.InspectionTreeState;
import com.intellij.codeInspection.ui.actions.ExportHTMLAction;
import com.intellij.concurrency.JobLauncher;
import com.intellij.concurrency.JobLauncherImpl;
@@ -83,7 +82,7 @@ import java.util.function.Predicate;
public class GlobalInspectionContextImpl extends GlobalInspectionContextBase implements GlobalInspectionContext {
private static final int MAX_OPEN_GLOBAL_INSPECTION_XML_RESULT_FILES = SystemProperties.getIntProperty("max.open.global.inspection.xml.files", 50);
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.ex.GlobalInspectionContextImpl");
private static final Logger LOG = Logger.getInstance(GlobalInspectionContextImpl.class);
@SuppressWarnings("StaticNonFinalField")
public static volatile boolean CREATE_VIEW_FORCE;
public static final NotificationGroup NOTIFICATION_GROUP = NotificationGroup.toolWindowGroup("Inspection Results", ToolWindowId.INSPECTION);
@@ -97,7 +96,6 @@ public class GlobalInspectionContextImpl extends GlobalInspectionContextBase imp
@NotNull
private AnalysisUIOptions myUIOptions;
private InspectionTreeState myTreeState;
public GlobalInspectionContextImpl(@NotNull Project project, @NotNull NotNullLazyValue<? extends ContentManager> contentManager) {
super(project);
@@ -110,10 +108,6 @@ public class GlobalInspectionContextImpl extends GlobalInspectionContextBase imp
return myContentManager.getValue();
}
public void setTreeState(InspectionTreeState treeState) {
myTreeState = treeState;
}
public void addView(@NotNull InspectionResultsView view,
@NotNull String title,
boolean isOffline) {
@@ -134,9 +128,6 @@ public class GlobalInspectionContextImpl extends GlobalInspectionContextBase imp
if (!isOffline) {
myView.setUpdating(true);
}
if (myTreeState != null) {
myView.getTree().setTreeState(myTreeState);
}
myContent = ContentFactory.SERVICE.getInstance().createContent(view, title, false);
myContent.setHelpId(InspectionResultsView.HELP_ID);
myContent.setDisposer(myView);
@@ -387,7 +378,7 @@ public class GlobalInspectionContextImpl extends GlobalInspectionContextBase imp
LOG.info("Code inspection finished. Took " + elapsed + "ms");
if (getProject().isDisposed()) return;
InspectionResultsView newView = myView == null ? new InspectionResultsView(this, createContentProvider()) : null;
InspectionResultsView newView = myView == null ? new InspectionResultsView(this, new InspectionRVContentProviderImpl()) : null;
if (!(myView == null ? newView : myView).hasProblems()) {
NOTIFICATION_GROUP.createNotification(InspectionsBundle.message("inspection.no.problems.message",
scope.getFileCount(),
@@ -770,7 +761,7 @@ public class GlobalInspectionContextImpl extends GlobalInspectionContextBase imp
if (getCurrentScope() == null) return;
InspectionResultsView view = getView();
if (view == null) {
view = new InspectionResultsView(this, createContentProvider());
view = new InspectionResultsView(this, new InspectionRVContentProviderImpl());
addView(view);
}
}, x -> getCurrentScope() == null);
@@ -913,7 +904,7 @@ public class GlobalInspectionContextImpl extends GlobalInspectionContextBase imp
public void refreshViews() {
if (myView != null) {
myView.getTree().queueUpdate();
myView.getTree().getInspectionTreeModel().reload();
}
}
@@ -1092,15 +1083,11 @@ public class GlobalInspectionContextImpl extends GlobalInspectionContextBase imp
return myViewClosed;
}
private InspectionRVContentProvider createContentProvider() {
return new InspectionRVContentProviderImpl(getProject());
}
private void addProblemsToView(List<Tools> tools) {
if (ApplicationManager.getApplication().isHeadlessEnvironment() && !CREATE_VIEW_FORCE) {
return;
}
if (myView == null && !ReadAction.compute(() -> InspectionResultsView.hasProblems(tools, this, createContentProvider())).booleanValue()) {
if (myView == null && !ReadAction.compute(() -> InspectionResultsView.hasProblems(tools, this, new InspectionRVContentProviderImpl())).booleanValue()) {
return;
}
initializeViewIfNeed().doWhenDone(() -> myView.addTools(tools));
@@ -14,8 +14,6 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
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.openapi.util.text.StringUtil;
import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.MultiMap;
@@ -31,14 +29,11 @@ import javax.swing.tree.TreePath;
import java.text.MessageFormat;
import java.util.*;
import java.util.function.Function;
import java.util.function.UnaryOperator;
public abstract class InspectionRVContentProvider {
private static final Logger LOG = Logger.getInstance(InspectionRVContentProvider.class);
private final Project myProject;
public InspectionRVContentProvider(@NotNull Project project) {
myProject = project;
public InspectionRVContentProvider() {
}
protected static class RefEntityContainer<Descriptor> {
@@ -51,18 +46,22 @@ public abstract class InspectionRVContentProvider {
myDescriptors = descriptors;
}
@Nullable
public RefEntityContainer<Descriptor> getOwner() {
if (myEntity == null) return null;
final RefEntity entity = myEntity.getOwner();
return entity instanceof RefElement && !(entity instanceof RefDirectory)
? new RefEntityContainer<>(entity, myDescriptors)
: null;
}
@NotNull
public RefElementNode createNode(@NotNull InspectionToolPresentation presentation) {
return ReadAction.compute(() -> presentation.createRefNode(myEntity));
public RefElementNode createNode(@NotNull InspectionToolPresentation presentation,
InspectionTreeModel model,
InspectionTreeNode topParent,
boolean showStructure) {
RefEntityContainer<Descriptor> owner = getOwner();
InspectionTreeNode parent;
if (owner == null) {
parent = topParent;
} else {
parent = owner.createNode(presentation, model, topParent, showStructure);
if (!showStructure) {
return (RefElementNode)parent;
}
}
return model.createRefElementNode(myEntity, () -> presentation.createRefNode(myEntity, model), parent);
}
@Nullable
@@ -71,15 +70,18 @@ public abstract class InspectionRVContentProvider {
}
@Nullable
public String getModule() {
protected String getModuleName() {
final RefModule refModule = myEntity instanceof RefElement
? ((RefElement)myEntity).getModule()
: myEntity instanceof RefModule ? (RefModule)myEntity : null;
return refModule != null ? refModule.getName() : null;
}
boolean areEqual(final @NotNull RefEntity o1, final @NotNull RefEntity o2) {
return Comparing.equal(o1, o2);
@Nullable
public Module getModule(Project project) {
String name = getModuleName();
if (name == null) return null;
return ReadAction.compute(() -> ModuleManager.getInstance(project).findModuleByName(name));
}
boolean supportStructure() {
@@ -89,6 +91,15 @@ public abstract class InspectionRVContentProvider {
public Descriptor[] getDescriptors() {
return myDescriptors;
}
@Nullable
private RefEntityContainer<Descriptor> getOwner() {
if (myEntity == null) return null;
final RefEntity entity = myEntity.getOwner();
return entity instanceof RefElement && !(entity instanceof RefDirectory)
? new RefEntityContainer<>(entity, myDescriptors)
: null;
}
}
public abstract boolean checkReportedProblems(@NotNull GlobalInspectionContextImpl context, @NotNull InspectionToolWrapper toolWrapper);
@@ -197,15 +208,14 @@ public abstract class InspectionRVContentProvider {
.toArray(StackTraceElement.class);
}
public InspectionNode appendToolNodeContent(@NotNull GlobalInspectionContextImpl context,
@NotNull InspectionNode toolNode,
@NotNull InspectionTreeNode parentNode,
boolean showStructure,
boolean groupBySeverity) {
InspectionToolWrapper wrapper = toolNode.getToolWrapper();
public void appendToolNodeContent(@NotNull GlobalInspectionContextImpl context,
@NotNull InspectionToolWrapper wrapper,
@NotNull InspectionTreeNode parentNode,
boolean showStructure,
boolean groupBySeverity) {
InspectionToolPresentation presentation = context.getPresentation(wrapper);
Map<String, Set<RefEntity>> content = presentation.getContent();
return appendToolNodeContent(context, toolNode, parentNode, showStructure, groupBySeverity, content, entity -> {
appendToolNodeContent(context, wrapper, parentNode, showStructure, groupBySeverity, content, entity -> {
if (context.getUIOptions().FILTER_RESOLVED_ITEMS) {
return presentation.isExcluded(entity) ? null : presentation.getProblemElements().get(entity);
} else {
@@ -221,19 +231,18 @@ public abstract class InspectionRVContentProvider {
});
}
public abstract InspectionNode appendToolNodeContent(@NotNull GlobalInspectionContextImpl context,
@NotNull InspectionNode toolNode,
@NotNull InspectionTreeNode parentNode,
final boolean showStructure,
boolean groupBySeverity,
@NotNull Map<String, Set<RefEntity>> contents,
@NotNull Function<? super RefEntity, CommonProblemDescriptor[]> problems);
public abstract void appendToolNodeContent(@NotNull GlobalInspectionContextImpl context,
@NotNull InspectionToolWrapper wrapper,
@NotNull InspectionTreeNode parentNode,
final boolean showStructure,
boolean groupBySeverity,
@NotNull Map<String, Set<RefEntity>> contents,
@NotNull Function<? super RefEntity, CommonProblemDescriptor[]> problems);
protected abstract void appendDescriptor(@NotNull GlobalInspectionContextImpl context,
@NotNull InspectionToolWrapper toolWrapper,
@NotNull RefEntityContainer container,
@NotNull InspectionTreeNode pNode,
final boolean canPackageRepeat);
@NotNull InspectionTreeNode parent);
public boolean isContentLoaded() {
return true;
@@ -241,241 +250,37 @@ public abstract class InspectionRVContentProvider {
protected <T> void buildTree(@NotNull GlobalInspectionContextImpl context,
@NotNull Map<String, Set<T>> packageContents,
final boolean canPackageRepeat,
@NotNull InspectionToolWrapper toolWrapper,
@NotNull Function<? super T, ? extends RefEntityContainer<?>> computeContainer,
final boolean showStructure,
final UnaryOperator<? super InspectionTreeNode> createdNodesConsumer) {
final Map<String, Map<String, InspectionPackageNode>> module2PackageMap = new HashMap<>();
boolean supportStructure = showStructure;
final MultiMap<InspectionPackageNode, RefEntityContainer<?>> packageDescriptors = new MultiMap<>();
for (String packageName : packageContents.keySet()) {
final Set<T> elements = packageContents.get(packageName);
for (T userObject : elements) {
final RefEntityContainer container = computeContainer.apply(userObject);
supportStructure &= container.supportStructure();
final String moduleName = showStructure ? container.getModule() : null;
Map<String, InspectionPackageNode> packageNodes = module2PackageMap.get(moduleName);
if (packageNodes == null) {
packageNodes = new HashMap<>();
module2PackageMap.put(moduleName, packageNodes);
}
InspectionPackageNode pNode = packageNodes.get(packageName);
if (pNode == null) {
pNode = new InspectionPackageNode(packageName);
packageNodes.put(packageName, pNode);
}
packageDescriptors.putValue(pNode, container);
boolean showStructure,
final InspectionTreeNode parent,
InspectionTreeModel model) {
MultiMap<String, RefEntityContainer> evaluatedDescriptors = MultiMap.create();
for (Map.Entry<String, Set<T>> entry : packageContents.entrySet()) {
String packageName = entry.getKey();
for (T problemDescriptor : entry.getValue()) {
RefEntityContainer<?> container = computeContainer.apply(problemDescriptor);
evaluatedDescriptors.putValue(packageName, container);
showStructure &= container.supportStructure();
}
}
if (supportStructure) {
final HashMap<String, InspectionModuleNode> moduleNodes = new HashMap<>();
for (final String moduleName : module2PackageMap.keySet()) {
final Map<String, InspectionPackageNode> packageNodes = module2PackageMap.get(moduleName);
InspectionModuleNode moduleNode = moduleNodes.get(moduleName);
if (moduleNode == null) {
if (moduleName != null) {
final Module module = ReadAction.compute(() -> ModuleManager.getInstance(myProject).findModuleByName(moduleName));
if (module != null) {
moduleNode = new InspectionModuleNode(module);
moduleNodes.put(moduleName, moduleNode);
moduleNode = (InspectionModuleNode)createdNodesConsumer.apply(moduleNode);
}
else { //module content was removed ?
continue;
}
}
else {
for (InspectionPackageNode packageNode : packageNodes.values()) {
for (RefEntityContainer<?> container : packageDescriptors.get(packageNode)) {
appendDescriptor(context, toolWrapper, container, packageNode, canPackageRepeat);
}
createdNodesConsumer.apply(packageNode);
}
continue;
}
} else {
moduleNode = (InspectionModuleNode)createdNodesConsumer.apply(moduleNode);
}
for (InspectionPackageNode packageNode : packageNodes.values()) {
if (packageNode.getPackageName() != null) {
Collection<RefEntityContainer<?>> objectContainers = packageDescriptors.get(packageNode);
packageNode = (InspectionPackageNode)merge(packageNode, moduleNode, true);
for (RefEntityContainer<?> container : objectContainers) {
appendDescriptor(context, toolWrapper, container, packageNode, canPackageRepeat);
}
}
else {
for (RefEntityContainer<?> container : packageDescriptors.get(packageNode)) {
appendDescriptor(context, toolWrapper, container, moduleNode, canPackageRepeat);
}
}
for (Map.Entry<String, Collection<RefEntityContainer>> entry : evaluatedDescriptors.entrySet()) {
for (RefEntityContainer container : entry.getValue()) {
InspectionTreeNode currentParent = parent;
if (showStructure) {
String packageName = entry.getKey();
Module module = container.getModule(context.getProject());
InspectionTreeNode moduleNode = module != null ? model.createModuleNode(module, parent) : null;
currentParent = model.createPackageNode(packageName, moduleNode == null ? parent : moduleNode);
}
RefElementNode node = container.createNode(context.getPresentation(toolWrapper),
model,
currentParent,
showStructure || HighlightInfoType.UNUSED_SYMBOL_SHORT_NAME.equals(toolWrapper.getShortName()));
appendDescriptor(context, toolWrapper, container, node);
}
}
else {
for (Map<String, InspectionPackageNode> packageNodes : module2PackageMap.values()) {
for (InspectionPackageNode pNode : packageNodes.values()) {
for (RefEntityContainer<?> container : packageDescriptors.get(pNode)) {
appendDescriptor(context, toolWrapper, container, pNode, canPackageRepeat);
}
final int count = pNode.getChildCount();
final ArrayList<TreeNode> childNodes = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
childNodes.add(pNode.getChildAt(i));
}
for (TreeNode childNode: childNodes) {
if (childNode instanceof ProblemDescriptionNode) {
createdNodesConsumer.apply(pNode);
break;
}
LOG.assertTrue(childNode instanceof RefElementNode, childNode.getClass().getName());
final RefElementNode elementNode = (RefElementNode)childNode;
final Set<RefElementNode> parentNodes = new LinkedHashSet<>();
if (pNode.getPackageName() != null) {
parentNodes.add(elementNode);
} else {
boolean hasElementNodeUnder = true;
for(int e = 0; e < elementNode.getChildCount(); e++) {
final TreeNode grandChildNode = elementNode.getChildAt(e);
if (grandChildNode instanceof ProblemDescriptionNode) {
hasElementNodeUnder = false;
break;
}
LOG.assertTrue(grandChildNode instanceof RefElementNode);
parentNodes.add((RefElementNode)grandChildNode);
}
if (!hasElementNodeUnder) {
createdNodesConsumer.apply(elementNode);
continue;
}
}
//allow unused declaration to have structure at file level even when there are unused parameters
if (!HighlightInfoType.UNUSED_SYMBOL_SHORT_NAME.equals(toolWrapper.getShortName())) {
for (RefElementNode parentNode : parentNodes) {
final List<ProblemDescriptionNode> nodes = new ArrayList<>();
TreeUtil.traverse(parentNode, node -> {
if (node instanceof ProblemDescriptionNode) {
nodes.add((ProblemDescriptionNode)node);
}
return true;
});
if (nodes.isEmpty()) continue;
parentNode.removeAllChildren();
for (ProblemDescriptionNode node : nodes) {
parentNode.insertByOrder(node, false);
}
}
}
for (RefElementNode node : parentNodes) {
createdNodesConsumer.apply(node);
}
}
}
}
}
}
@NotNull
protected static RefElementNode addNodeToParent(@NotNull RefEntityContainer container,
@NotNull InspectionToolPresentation presentation,
final InspectionTreeNode parentNode) {
final RefElementNode nodeToBeAdded = container.createNode(presentation);
final Ref<Boolean> firstLevel = new Ref<>(true);
RefElementNode prevNode = null;
final Ref<RefElementNode> result = new Ref<>();
while (true) {
final RefElementNode currentNode = firstLevel.get() ? nodeToBeAdded : container.createNode(presentation);
final RefEntityContainer finalContainer = container;
final RefElementNode finalPrevNode = prevNode;
TreeUtil.treeNodeTraverser(parentNode).traverse(TreeTraversal.PRE_ORDER_DFS).processEach(node -> {
if (node instanceof RefElementNode) {
final RefElementNode refElementNode = (RefElementNode)node;
final RefEntity userObject = finalContainer.getRefEntity();
final RefEntity object = refElementNode.getElement();
if (userObject != null &&
object != null &&
(userObject.getClass().equals(object.getClass())) &&
finalContainer.areEqual(object, userObject)) {
if (firstLevel.get()) {
result.set(refElementNode);
}
else {
refElementNode.insertByOrder(finalPrevNode, false);
result.set(nodeToBeAdded);
}
return false;
}
}
return true;
});
if(!result.isNull()) return result.get();
if (!firstLevel.get()) {
currentNode.insertByOrder(prevNode, false);
}
final RefEntityContainer owner = container.getOwner();
if (owner == null) {
parentNode.insertByOrder(currentNode, false);
return nodeToBeAdded;
}
container = owner;
prevNode = currentNode;
firstLevel.set(false);
}
}
//class cast suppression
public static InspectionTreeNode merge(InspectionTreeNode child, InspectionTreeNode parent, boolean merge) {
return ReadAction.compute(() -> {
if (merge) {
for (int i = 0; i < parent.getChildCount(); i++) {
InspectionTreeNode current = (InspectionTreeNode)parent.getChildAt(i);
if (child.getClass() != current.getClass()) {
continue;
}
if (current instanceof InspectionPackageNode) {
if (((InspectionPackageNode)current).getPackageName().compareTo(((InspectionPackageNode)child).getPackageName()) == 0) {
processDepth(child, current);
return current;
}
}
else if (current instanceof RefElementNode) {
if (InspectionResultsViewComparator.getInstance().compare(current, child) == 0) {
processDepth(child, current);
return current;
}
}
else if (current instanceof InspectionNode) {
if (((InspectionNode)current).getToolWrapper().getShortName().compareTo(((InspectionNode)child).getToolWrapper().getShortName()) == 0) {
processDepth(child, current);
return current;
}
}
else if (current instanceof InspectionModuleNode) {
if (((InspectionModuleNode)current).getName().compareTo(((InspectionModuleNode)child).getName()) == 0) {
processDepth(child, current);
return current;
}
}
}
}
return parent.insertByOrder(child, false);
});
}
private static void processDepth(final InspectionTreeNode child, final InspectionTreeNode current) {
InspectionTreeNode[] children = new InspectionTreeNode[child.getChildCount()];
for (int i = 0; i < children.length; i++) {
children[i] = (InspectionTreeNode)child.getChildAt(i);
}
for (InspectionTreeNode node : children) {
merge(node, current, true);
}
}
@NotNull
@@ -11,8 +11,6 @@ import com.intellij.codeInspection.reference.RefEntity;
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;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
@@ -29,10 +27,6 @@ import java.util.Set;
import java.util.function.Function;
public class InspectionRVContentProviderImpl extends InspectionRVContentProvider {
public InspectionRVContentProviderImpl(final Project project) {
super(project);
}
@Override
public boolean checkReportedProblems(@NotNull GlobalInspectionContextImpl context,
@NotNull final InspectionToolWrapper toolWrapper) {
@@ -81,52 +75,50 @@ public class InspectionRVContentProviderImpl extends InspectionRVContentProvider
}
@Override
public InspectionNode appendToolNodeContent(@NotNull GlobalInspectionContextImpl context,
@NotNull final InspectionNode toolNode,
@NotNull final InspectionTreeNode parentNode,
final boolean showStructure,
boolean groupBySeverity,
@NotNull final Map<String, Set<RefEntity>> contents,
@NotNull final Function<? super RefEntity, CommonProblemDescriptor[]> problems) {
final InspectionToolWrapper toolWrapper = toolNode.getToolWrapper();
InspectionNode mergedToolNode = (InspectionNode)merge(toolNode, parentNode, !groupBySeverity);
public void appendToolNodeContent(@NotNull GlobalInspectionContextImpl context,
@NotNull final InspectionToolWrapper wrapper,
@NotNull final InspectionTreeNode toolNode,
final boolean showStructure,
boolean groupBySeverity,
@NotNull final Map<String, Set<RefEntity>> contents,
@NotNull final Function<? super RefEntity, CommonProblemDescriptor[]> problems) {
InspectionResultsView view = context.getView();
buildTree(context,
contents,
false,
toolWrapper,
wrapper,
refElement -> new RefEntityContainer<>(refElement, problems.apply(refElement)),
showStructure,
node -> merge(node, mergedToolNode, true));
return mergedToolNode;
toolNode,
view.getTree().getInspectionTreeModel());
}
@Override
protected void appendDescriptor(@NotNull GlobalInspectionContextImpl context,
@NotNull final InspectionToolWrapper toolWrapper,
@NotNull final RefEntityContainer container,
@NotNull final InspectionTreeNode pNode,
final boolean canPackageRepeat) {
@NotNull final InspectionTreeNode parent) {
final RefEntity refElement = container.getRefEntity();
InspectionTreeModel model = context.getView().getTree().getInspectionTreeModel();
InspectionToolPresentation presentation = context.getPresentation(toolWrapper);
final CommonProblemDescriptor[] problems = ((RefEntityContainer<CommonProblemDescriptor>)container).getDescriptors();
if (problems != null && problems.length != 0) {
final RefElementNode elemNode = addNodeToParent(container, presentation, pNode);
for (CommonProblemDescriptor problem : problems) {
assert problem != null;
elemNode.insertByOrder(ReadAction.compute(() -> new ProblemDescriptionNode(refElement, problem, presentation)), false);
elemNode.setProblem(elemNode.getChildCount() == 1 ? problems[0] : null);
model.createProblemDescriptorNode(refElement, problem, presentation, parent);
}
}
else {
if (canPackageRepeat && pNode instanceof InspectionPackageNode) {
final Set<RefEntity> currentElements = presentation.getContent().get(((InspectionPackageNode) pNode).getPackageName());
if (false) {
final Set<RefEntity> currentElements = presentation.getContent().get(((InspectionPackageNode) parent).getPackageName());
if (currentElements != null) {
final Set<RefEntity> currentEntities = new HashSet<>(currentElements);
if (RefUtil.contains(refElement, currentEntities)) return;
}
}
addNodeToParent(container, presentation, pNode);
//TODO don't understand
//addNodeToParent(container, presentation, parent, model);
}
}
}
@@ -42,6 +42,7 @@ import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.tree.TreePath;
import java.awt.event.MouseEvent;
import java.util.*;
@@ -110,10 +111,11 @@ public class QuickFixAction extends AnAction implements CustomComponentAction {
try {
Ref<List<CommonProblemDescriptor[]>> descriptors = Ref.create();
Set<VirtualFile> readOnlyFiles = new THashSet<>();
TreePath[] paths = tree.getSelectionPaths();
if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> ReadAction.run(() -> {
final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
indicator.setText("Checking problem descriptors...");
descriptors.set(tree.getSelectedDescriptorPacks(true, readOnlyFiles, false));
descriptors.set(tree.getSelectedDescriptorPacks(true, readOnlyFiles, false, paths));
}), InspectionsBundle.message("preparing.for.apply.fix"), true, e.getProject())) {
return;
}
@@ -41,19 +41,6 @@ public class InspectionTreeHtmlWriter {
serializeTreeToHtml();
}
private void traverseInspectionTree(final InspectionTreeNode node,
final Consumer<? super InspectionTreeNode> preAction,
final Consumer<? super InspectionTreeNode> postAction) {
if (node.isExcluded()) {
return;
}
preAction.accept(node);
for (int i = 0; i < node.getChildCount(); i++) {
traverseInspectionTree((InspectionTreeNode)node.getChildAt(i), preAction, postAction);
}
postAction.accept(node);
}
private void serializeTreeToHtml() {
appendHeader();
appendTree((builder) -> {
@@ -68,7 +55,8 @@ public class InspectionTreeHtmlWriter {
builder.append(escapeNonBreakingSymbols(text));
}
};
traverseInspectionTree(myTree.getRoot(),
InspectionTreeModel model = myTree.getInspectionTreeModel();
traverseInspectionTree(model.getRoot(),
(n) -> {
final int nodeId = System.identityHashCode(n);
builder
@@ -104,6 +92,19 @@ public class InspectionTreeHtmlWriter {
InspectionTreeHtmlExportResources.copyInspectionReportResources(myOutputDir);
}
private static void traverseInspectionTree(InspectionTreeNode node,
Consumer<? super InspectionTreeNode> preAction,
Consumer<? super InspectionTreeNode> postAction) {
if (node.isExcluded()) {
return;
}
preAction.accept(node);
for (InspectionTreeNode child : node.getChildren()) {
traverseInspectionTree(child, preAction, postAction);
}
postAction.accept(node);
}
private String convertNodeToHtml(InspectionTreeNode node) {
if (node instanceof InspectionRootNode) {
return "<b>'" + escapeNonBreakingSymbols(node) + "' project</b>";
@@ -139,7 +140,7 @@ public class InspectionTreeHtmlWriter {
return sb.toString();
}
else if (node instanceof RefElementNode) {
final String type = myManager.getType((RefEntity)node.getUserObject());
final String type = myManager.getType(((RefElementNode)node).getElement());
return type + "&nbsp;<b>" + node.toString() + "</b>";
}
else if (node instanceof InspectionNode) {
@@ -38,7 +38,7 @@ import java.util.stream.Collectors;
/**
* @author Dmitry Batkovich
*/
class OfflineDescriptorResolveResult {
public class OfflineDescriptorResolveResult {
private static final Logger LOG = Logger.getInstance(OfflineDescriptorResolveResult.class);
private final RefEntity myResolvedEntity;
private final CommonProblemDescriptor myResolvedDescriptor;
@@ -10,11 +10,8 @@ import com.intellij.codeInspection.offline.OfflineProblemDescriptor;
import com.intellij.codeInspection.reference.RefElement;
import com.intellij.codeInspection.reference.RefEntity;
import com.intellij.codeInspection.ui.*;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.util.containers.FactoryMap;
import java.util.HashSet;
import gnu.trove.THashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -27,9 +24,7 @@ public class OfflineInspectionRVContentProvider extends InspectionRVContentProvi
private final Map<String, Map<OfflineProblemDescriptor, OfflineDescriptorResolveResult>> myResolvedDescriptor =
FactoryMap.create(key -> new THashMap<>());
public OfflineInspectionRVContentProvider(@NotNull Map<String, Map<String, Set<OfflineProblemDescriptor>>> content,
@NotNull Project project) {
super(project);
public OfflineInspectionRVContentProvider(@NotNull Map<String, Map<String, Set<OfflineProblemDescriptor>>> content) {
myContent = content;
}
@@ -59,33 +54,28 @@ public class OfflineInspectionRVContentProvider extends InspectionRVContentProvi
}
@Override
public InspectionNode appendToolNodeContent(@NotNull GlobalInspectionContextImpl context,
@NotNull InspectionNode toolNode,
@NotNull InspectionTreeNode parentNode,
boolean showStructure,
boolean groupBySeverity, @NotNull final Map<String, Set<RefEntity>> contents,
@NotNull Function<? super RefEntity, CommonProblemDescriptor[]> problems) {
InspectionToolWrapper toolWrapper = toolNode.getToolWrapper();
final Map<String, Set<OfflineProblemDescriptor>> filteredContent = getFilteredContent(context, toolWrapper);
public void appendToolNodeContent(@NotNull GlobalInspectionContextImpl context,
@NotNull InspectionToolWrapper wrapper,
@NotNull InspectionTreeNode parentNode,
boolean showStructure,
boolean groupBySeverity, @NotNull final Map<String, Set<RefEntity>> contents,
@NotNull Function<? super RefEntity, CommonProblemDescriptor[]> problems) {
final Map<String, Set<OfflineProblemDescriptor>> filteredContent = getFilteredContent(context, wrapper);
InspectionResultsView view = context.getView();
if (filteredContent != null && !filteredContent.values().isEmpty()) {
parentNode.insertByOrder(toolNode, false);
buildTree(context, filteredContent, false, toolWrapper, descriptor -> {
buildTree(context, filteredContent, wrapper, descriptor -> {
final RefEntity element = descriptor.getRefElement(context.getRefManager());
return new RefEntityContainer<OfflineProblemDescriptor>(element, new OfflineProblemDescriptor[] {descriptor}) {
@Nullable
@Override
public String getModule() {
final String module = super.getModule();
protected String getModuleName() {
final String module = super.getModuleName();
return module == null ? descriptor.getModuleName() : module;
}
};
}, showStructure,
(newChild) -> {
toolNode.insertByOrder(newChild, false);
return newChild;
});
parentNode, view.getTree().getInspectionTreeModel());
}
return toolNode;
}
@Nullable
@@ -140,10 +130,9 @@ public class OfflineInspectionRVContentProvider extends InspectionRVContentProvi
protected void appendDescriptor(@NotNull GlobalInspectionContextImpl context,
@NotNull final InspectionToolWrapper toolWrapper,
@NotNull final RefEntityContainer container,
@NotNull final InspectionTreeNode packageNode,
final boolean canPackageRepeat) {
@NotNull final InspectionTreeNode parent) {
InspectionToolPresentation presentation = context.getPresentation(toolWrapper);
final RefElementNode elemNode = addNodeToParent(container, presentation, packageNode);
InspectionTreeModel model = context.getView().getTree().getInspectionTreeModel();
for (OfflineProblemDescriptor descriptor : ((RefEntityContainer<OfflineProblemDescriptor>)container).getDescriptors()) {
final OfflineDescriptorResolveResult resolveResult = myResolvedDescriptor.get(toolWrapper.getShortName())
.computeIfAbsent(descriptor, d -> OfflineDescriptorResolveResult.resolve(d, toolWrapper, presentation));
@@ -151,9 +140,9 @@ public class OfflineInspectionRVContentProvider extends InspectionRVContentProvi
CommonProblemDescriptor resolvedDescriptor = resolveResult.getResolvedDescriptor();
if (resolvedEntity != null && resolvedDescriptor != null) {
presentation.getProblemElements().put(resolvedEntity, resolvedDescriptor);
elemNode.insertByOrder(ReadAction.compute(() -> new ProblemDescriptionNode(resolvedEntity, resolvedDescriptor, presentation)), true);
model.createProblemDescriptorNode(resolvedEntity, resolvedDescriptor, presentation, parent);
} else {
elemNode.insertByOrder(ReadAction.compute(() -> OfflineProblemDescriptorNode.create(descriptor, resolveResult, presentation)), true);
model.createOfflineProblemDescriptorNode(descriptor, resolveResult, presentation, parent);
}
}
}
@@ -7,37 +7,39 @@ package com.intellij.codeInspection.offlineViewer;
import com.intellij.codeInspection.ProblemDescriptorUtil;
import com.intellij.codeInspection.offline.OfflineProblemDescriptor;
import com.intellij.codeInspection.ui.InspectionToolPresentation;
import com.intellij.codeInspection.ui.InspectionTreeModel;
import com.intellij.codeInspection.ui.ProblemDescriptionNode;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.NotNull;
public class OfflineProblemDescriptorNode extends ProblemDescriptionNode {
private final OfflineDescriptorResolveResult myDescriptorResolveResult;
private final OfflineProblemDescriptor myOfflineDescriptor;
private OfflineProblemDescriptorNode(OfflineDescriptorResolveResult descriptorResolveResult,
@NotNull InspectionToolPresentation presentation,
@NotNull OfflineProblemDescriptor offlineDescriptor) {
super(descriptorResolveResult.getResolvedEntity(), descriptorResolveResult.getResolvedDescriptor(), presentation, offlineDescriptor::getLine);
public OfflineProblemDescriptorNode(OfflineDescriptorResolveResult descriptorResolveResult,
@NotNull InspectionToolPresentation presentation,
@NotNull OfflineProblemDescriptor offlineDescriptor,
InspectionTreeModel model) {
super(descriptorResolveResult.getResolvedEntity(), descriptorResolveResult.getResolvedDescriptor(), presentation, offlineDescriptor::getLine, model);
myDescriptorResolveResult = descriptorResolveResult;
if (descriptorResolveResult.getResolvedDescriptor() == null) {
setUserObject(offlineDescriptor);
}
myOfflineDescriptor = offlineDescriptor;
}
static OfflineProblemDescriptorNode create(@NotNull OfflineProblemDescriptor offlineDescriptor,
@NotNull OfflineDescriptorResolveResult resolveResult,
@NotNull InspectionToolPresentation presentation) {
public static OfflineProblemDescriptorNode create(@NotNull OfflineProblemDescriptor offlineDescriptor,
@NotNull OfflineDescriptorResolveResult resolveResult,
@NotNull InspectionToolPresentation presentation,
InspectionTreeModel model) {
return new OfflineProblemDescriptorNode(resolveResult,
presentation,
offlineDescriptor);
offlineDescriptor, model);
}
@NotNull
@Override
protected String calculatePresentableName() {
String presentableName = super.calculatePresentableName();
return presentableName.isEmpty() && getUserObject() instanceof OfflineProblemDescriptor
? ProblemDescriptorUtil.unescapeTags(StringUtil.notNullize(((OfflineProblemDescriptor)getUserObject()).getDescription())).trim()
return presentableName.isEmpty() && getDescriptor() == null
? ProblemDescriptorUtil.unescapeTags(StringUtil.notNullize(myOfflineDescriptor.getDescription())).trim()
: presentableName;
}
@@ -56,8 +56,7 @@ public class DefaultInspectionToolPresentation implements InspectionToolPresenta
protected static final Logger LOG = Logger.getInstance(DefaultInspectionToolPresentation.class);
@NotNull private final InspectionToolWrapper myToolWrapper;
@NotNull private final GlobalInspectionContextImpl myContext;
protected InspectionNode myToolNode;
@NotNull protected final GlobalInspectionContextImpl myContext;
private final SynchronizedBidiMultiMap<RefEntity, CommonProblemDescriptor> myProblemElements = createBidiMap();
private final SynchronizedBidiMultiMap<RefEntity, CommonProblemDescriptor> mySuppressedElements = createBidiMap();
@@ -255,11 +254,6 @@ public class DefaultInspectionToolPresentation implements InspectionToolPresenta
}
}
@Override
public InspectionNode getToolNode() {
return myToolNode;
}
protected boolean isDisposed() {
return isDisposed;
}
@@ -464,15 +458,6 @@ public class DefaultInspectionToolPresentation implements InspectionToolPresenta
return myProblemElements;
}
@Override
public void createToolNode(@NotNull GlobalInspectionContextImpl globalInspectionContext, @NotNull InspectionNode node,
@NotNull InspectionRVContentProvider provider,
@NotNull InspectionTreeNode parentNode,
boolean showStructure,
boolean groupBySeverity) {
myToolNode = node;
}
@Override
@Nullable
public IntentionAction findQuickFixes(@NotNull final CommonProblemDescriptor problemDescriptor, final String hint) {
@@ -4,20 +4,25 @@ package com.intellij.codeInspection.ui;
import org.jetbrains.annotations.NotNull;
/**
* @author max
*/
public class InspectionGroupNode extends InspectionTreeNode {
InspectionGroupNode(@NotNull String subGroup) {
super(subGroup);
private final String myGroup;
InspectionGroupNode(@NotNull String group, @NotNull InspectionTreeModel model) {
super(model);
myGroup = group;
}
String getSubGroup() {
return (String) getUserObject();
return myGroup;
}
@Override
public boolean appearsBold() {
return true;
}
@Override
public String getPresentableText() {
return myGroup;
}
}
@@ -17,13 +17,16 @@ package com.intellij.codeInspection.ui;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleType;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.Icon;
public class InspectionModuleNode extends InspectionTreeNode{
public class InspectionModuleNode extends InspectionTreeNode {
@NotNull
private final Module myModule;
public InspectionModuleNode(final Module module) {
super(module);
public InspectionModuleNode(@NotNull Module module, InspectionTreeModel model) {
super(model);
myModule = module;
}
@@ -32,11 +35,12 @@ public class InspectionModuleNode extends InspectionTreeNode{
return myModule.isDisposed() ? null : ModuleType.get(myModule).getIcon();
}
public String getName(){
public String getName() {
return myModule.getName();
}
public String toString() {
@Override
public String getPresentableText() {
return getName();
}
}
@@ -21,24 +21,19 @@ import com.intellij.codeInspection.ex.InspectionToolWrapper;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author max
*/
public class InspectionNode extends InspectionTreeNode {
@NotNull private final InspectionToolWrapper myToolWrapper;
@NotNull private final InspectionProfileImpl myProfile;
public InspectionNode(@NotNull InspectionToolWrapper toolWrapper, @NotNull InspectionProfileImpl profile) {
super(toolWrapper);
public InspectionNode(@NotNull InspectionToolWrapper toolWrapper, @NotNull InspectionProfileImpl profile, @NotNull InspectionTreeModel model) {
super(model);
myToolWrapper = toolWrapper;
myProfile = profile;
}
public String toString() {
return getToolWrapper().getDisplayName();
}
@NotNull
public InspectionToolWrapper getToolWrapper() {
return (InspectionToolWrapper)getUserObject();
return myToolWrapper;
}
@Nullable
@@ -47,4 +42,9 @@ public class InspectionNode extends InspectionTreeNode {
final String shortName = getToolWrapper().getShortName();
return myProfile.getTools(shortName, null).isEnabled() ? null : "Disabled";
}
@Override
public String getPresentableText() {
return getToolWrapper().getDisplayName();
}
}
@@ -18,23 +18,27 @@ package com.intellij.codeInspection.ui;
import com.intellij.util.PlatformIcons;
import javax.swing.*;
import javax.swing.Icon;
/**
* @author max
*/
public class InspectionPackageNode extends InspectionTreeNode {
private final String myPackageName;
public InspectionPackageNode(String packageName) {
super(packageName);
public InspectionPackageNode(String packageName, InspectionTreeModel model) {
super(model);
myPackageName = packageName;
}
public String getPackageName() {
return (String) getUserObject();
return myPackageName;
}
@Override
public Icon getIcon(boolean expanded) {
return PlatformIcons.PACKAGE_ICON;
}
@Override
public String getPresentableText() {
return myPackageName;
}
}
@@ -14,7 +14,10 @@ import com.intellij.codeInspection.reference.RefEntity;
import com.intellij.codeInspection.ui.actions.ExportHTMLAction;
import com.intellij.codeInspection.ui.actions.InvokeQuickFixAction;
import com.intellij.diff.util.DiffUtil;
import com.intellij.ide.*;
import com.intellij.ide.CommonActionsManager;
import com.intellij.ide.DefaultTreeExpander;
import com.intellij.ide.OccurenceNavigator;
import com.intellij.ide.TreeExpander;
import com.intellij.ide.actions.exclusion.ExclusionHandler;
import com.intellij.ide.util.PsiNavigationSupport;
import com.intellij.injected.editor.VirtualFileWindow;
@@ -23,7 +26,6 @@ import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.*;
@@ -43,27 +45,25 @@ import com.intellij.profile.ProfileChangeAdapter;
import com.intellij.profile.codeInspection.ProjectInspectionProfileManager;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.ui.*;
import com.intellij.util.EditSourceOnDoubleClickHandler;
import com.intellij.ui.IdeBorderFactory;
import com.intellij.ui.OnePixelSplitter;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.ui.SideBorder;
import com.intellij.util.Alarm;
import com.intellij.util.ObjectUtils;
import com.intellij.util.OpenSourceUtil;
import com.intellij.util.concurrency.SequentialTaskExecutor;
import com.intellij.util.ui.EdtInvocationManager;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.UIUtil;
import com.intellij.util.ui.tree.TreeUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.awt.event.InputEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.util.List;
import java.util.*;
@@ -77,8 +77,6 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro
@NotNull
private final InspectionTree myTree;
@NotNull
private final OccurenceNavigator myOccurenceNavigator;
private volatile InspectionProfileImpl myInspectionProfile;
private final boolean mySettingsEnabled;
@NotNull
@@ -91,7 +89,6 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro
private final GlobalInspectionContextImpl myGlobalInspectionContext;
private boolean myRerun;
private volatile boolean myDisposed;
private int myUpdatingRequestors; //accessed only in edt
private boolean myApplyingFix; //accessed only in edt
@NotNull
@@ -100,11 +97,11 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro
private final ExclusionHandler<InspectionTreeNode> myExclusionHandler;
private EditorEx myPreviewEditor;
private InspectionTreeLoadingProgressAware myLoadingProgressPreview;
private final Alarm myLoadingProgressPreviewAlarm = new Alarm(this);
private final InspectionViewSuppressActionHolder mySuppressActionHolder = new InspectionViewSuppressActionHolder();
private final Object myTreeStructureUpdateLock = new Object();
private final ExecutorService myTreeUpdater = SequentialTaskExecutor
.createSequentialApplicationPoolExecutor("Inspection-View-Tree-Updater");
private final ExecutorService myTreeUpdater = SequentialTaskExecutor.createSequentialApplicationPoolExecutor("Inspection-View-Tree-Updater");
private volatile boolean myUpdating;
public InspectionResultsView(@NotNull GlobalInspectionContextImpl globalInspectionContext,
@NotNull InspectionRVContentProvider provider) {
@@ -114,9 +111,6 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro
myGlobalInspectionContext = globalInspectionContext;
myProvider = provider;
myTree = new InspectionTree(globalInspectionContext, this);
initTreeListeners();
myOccurenceNavigator = initOccurenceNavigator();
mySplitter = new OnePixelSplitter(false, AnalysisUIOptions.getInstance(globalInspectionContext.getProject()).SPLITTER_PROPORTION);
mySplitter.setFirstComponent(ScrollPaneFactory.createScrollPane(myTree, SideBorder.LEFT));
@@ -160,11 +154,9 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro
public void onDone(boolean isExcludeAction) {
if (isExcludeAction) {
myTree.removeSelectedProblems();
} else {
myTree.repaint();
}
else {
resetTree();
}
syncRightPanel();
}
};
@@ -201,98 +193,6 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro
});
}
private void initTreeListeners() {
if (ApplicationManager.getApplication().isUnitTestMode()) {
return;
}
myTree.getSelectionModel().addTreeSelectionListener(e -> {
if (myTree.isUnderQueueUpdate()) return;
syncRightPanel();
if (isAutoScrollMode()) {
OpenSourceUtil.openSourcesFrom(DataManager.getInstance().getDataContext(this), false);
}
});
EditSourceOnDoubleClickHandler.install(myTree);
myTree.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
OpenSourceUtil.openSourcesFrom(DataManager.getInstance().getDataContext(InspectionResultsView.this), false);
}
}
});
PopupHandler.installPopupHandler(myTree, IdeActions.INSPECTION_TOOL_WINDOW_TREE_POPUP, ActionPlaces.CODE_INSPECTION);
SmartExpander.installOn(myTree);
}
@NotNull
private OccurenceNavigatorSupport initOccurenceNavigator() {
return new OccurenceNavigatorSupport(myTree) {
@Override
protected boolean isOccurrenceNode(@NotNull DefaultMutableTreeNode node) {
if (node instanceof InspectionTreeNode && ((InspectionTreeNode)node).isExcluded()) {
return false;
}
if (node instanceof RefElementNode) {
final RefElementNode refNode = (RefElementNode)node;
if (refNode.hasDescriptorsUnder()) return false;
final RefEntity element = refNode.getElement();
return element != null && element.isValid();
}
return node instanceof ProblemDescriptionNode;
}
@Override
@Nullable
protected Navigatable createDescriptorForNode(@NotNull DefaultMutableTreeNode node) {
if (node instanceof InspectionTreeNode && ((InspectionTreeNode)node).isExcluded()) {
return null;
}
if (node instanceof RefElementNode) {
final RefElementNode refNode = (RefElementNode)node;
if (refNode.hasDescriptorsUnder()) return null;
final RefEntity element = refNode.getElement();
if (element == null || !element.isValid()) return null;
final CommonProblemDescriptor problem = refNode.getDescriptor();
if (problem != null) {
return navigate(problem);
}
if (element instanceof RefElement) {
return getOpenFileDescriptor((RefElement)element);
}
}
else if (node instanceof ProblemDescriptionNode) {
ProblemDescriptionNode problemNode = (ProblemDescriptionNode)node;
boolean isValid = problemNode.isValid() && (!problemNode.isQuickFixAppliedFromView() ||
problemNode.calculateIsValid());
return isValid
? navigate(problemNode.getDescriptor())
: InspectionResultsViewUtil.getNavigatableForInvalidNode(problemNode);
}
return null;
}
@Nullable
private Navigatable navigate(final CommonProblemDescriptor descriptor) {
return getSelectedNavigatable(descriptor);
}
@NotNull
@Override
public String getNextOccurenceActionName() {
return InspectionsBundle.message("inspection.action.go.next");
}
@NotNull
@Override
public String getPreviousOccurenceActionName() {
return InspectionsBundle.message("inspection.action.go.prev");
}
};
}
private void createActionsToolbar() {
JPanel westPanel = JBUI.Panels.simplePanel()
@@ -319,8 +219,8 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro
final TreeExpander treeExpander = new DefaultTreeExpander(myTree);
group.add(actionsManager.createExpandAllAction(treeExpander, myTree));
group.add(actionsManager.createCollapseAllAction(treeExpander, myTree));
group.add(actionsManager.createPrevOccurenceAction(myOccurenceNavigator));
group.add(actionsManager.createNextOccurenceAction(myOccurenceNavigator));
group.add(actionsManager.createPrevOccurenceAction(myTree.getOccurenceNavigator()));
group.add(actionsManager.createNextOccurenceAction(myTree.getOccurenceNavigator()));
group.add(ActionManager.getInstance().getAction("EditInspectionSettings"));
return createToolbar(group);
@@ -328,34 +228,34 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro
@Override
public boolean hasNextOccurence() {
return myOccurenceNavigator.hasNextOccurence();
return myTree.getOccurenceNavigator().hasNextOccurence();
}
@Override
public boolean hasPreviousOccurence() {
return myOccurenceNavigator.hasPreviousOccurence();
return myTree.getOccurenceNavigator().hasPreviousOccurence();
}
@Override
public OccurenceInfo goNextOccurence() {
return myOccurenceNavigator.goNextOccurence();
return myTree.getOccurenceNavigator().goNextOccurence();
}
@Override
public OccurenceInfo goPreviousOccurence() {
return myOccurenceNavigator.goPreviousOccurence();
return myTree.getOccurenceNavigator().goPreviousOccurence();
}
@NotNull
@Override
public String getNextOccurenceActionName() {
return myOccurenceNavigator.getNextOccurenceActionName();
return myTree.getOccurenceNavigator().getNextOccurenceActionName();
}
@NotNull
@Override
public String getPreviousOccurenceActionName() {
return myOccurenceNavigator.getPreviousOccurenceActionName();
return myTree.getOccurenceNavigator().getPreviousOccurenceActionName();
}
private static JComponent createToolbar(final DefaultActionGroup specialGroup) {
@@ -376,39 +276,17 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro
}
}
private boolean isAutoScrollMode() {
boolean isAutoScrollMode() {
String activeToolWindowId = ToolWindowManager.getInstance(getProject()).getActiveToolWindowId();
return myGlobalInspectionContext.getUIOptions().AUTOSCROLL_TO_SOURCE &&
(activeToolWindowId == null || activeToolWindowId.equals(ToolWindowId.INSPECTION));
}
Object getTreeStructureUpdateLock() {
return myTreeStructureUpdateLock;
}
@Nullable
private static Navigatable getOpenFileDescriptor(final RefElement refElement) {
PsiElement psiElement = refElement.getPsiElement();
if (psiElement == null) return null;
final PsiFile containingFile = psiElement.getContainingFile();
if (containingFile == null) return null;
VirtualFile file = containingFile.getVirtualFile();
if (file == null) return null;
return PsiNavigationSupport.getInstance().createNavigatable(refElement.getRefManager().getProject(), file,
psiElement.getTextOffset());
}
public void setApplyingFix(boolean applyingFix) {
myApplyingFix = applyingFix;
syncRightPanel();
}
void openRightPanelIfNeed() {
if (mySplitter.getSecondComponent() == null) {
syncRightPanel();
}
}
public void syncRightPanel() {
final Editor oldEditor = myPreviewEditor;
try {
@@ -482,7 +360,7 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro
editorPanel.setLayout(new BorderLayout());
final JPanel actionsPanel = new JPanel(new BorderLayout());
editorPanel.add(actionsPanel, BorderLayout.NORTH);
final int problemCount = myTree.getSelectedProblemCount(true);
final int problemCount = myTree.getSelectedProblemCount();
JComponent previewPanel = null;
final InspectionToolWrapper tool = myTree.getSelectedToolWrapper(true);
if (tool != null) {
@@ -597,12 +475,15 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro
HighlightDisplayLevel errorLevel,
boolean groupedBySeverity,
boolean isSingleInspectionRun) {
InspectionTreeNode parentNode = myTree.getToolParentNode(toolWrapper, errorLevel, groupedBySeverity, isSingleInspectionRun);
InspectionNode toolNode = new InspectionNode(toolWrapper, myInspectionProfile);
boolean showStructure = myGlobalInspectionContext.getUIOptions().SHOW_STRUCTURE;
toolNode = myProvider.appendToolNodeContent(myGlobalInspectionContext, toolNode, parentNode, showStructure, groupedBySeverity);
InspectionTreeNode toolNode = myTree.getToolProblemsRootNode(toolWrapper, errorLevel, groupedBySeverity, isSingleInspectionRun);
myProvider.appendToolNodeContent(myGlobalInspectionContext,
toolWrapper,
toolNode,
myGlobalInspectionContext.getUIOptions().SHOW_STRUCTURE,
groupedBySeverity);
InspectionToolPresentation presentation = myGlobalInspectionContext.getPresentation(toolWrapper);
presentation.createToolNode(myGlobalInspectionContext, toolNode, myProvider, parentNode, showStructure, groupedBySeverity);
presentation.patchToolNode(toolNode, myProvider,
myGlobalInspectionContext.getUIOptions().SHOW_STRUCTURE, groupedBySeverity);
registerActionShortcuts(presentation);
}
@@ -628,39 +509,38 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro
}
void addProblemDescriptors(InspectionToolWrapper wrapper, RefEntity refElement, CommonProblemDescriptor[] descriptors) {
updateTree(() -> {
synchronized (myTreeStructureUpdateLock) {
ReadAction.run(() -> {
if (!isDisposed()) {
ApplicationManager.getApplication().assertReadAccessAllowed();
final AnalysisUIOptions uiOptions = myGlobalInspectionContext.getUIOptions();
final InspectionToolPresentation presentation = myGlobalInspectionContext.getPresentation(wrapper);
if (presentation.getToolNode() == null) {
presentation.updateContent();
addTool(wrapper, HighlightDisplayLevel.find(presentation.getSeverity((RefElement)refElement)),
uiOptions.GROUP_BY_SEVERITY, isSingleInspectionRun());
return;
}
final InspectionNode toolNode = presentation.getToolNode();
LOG.assertTrue(toolNode != null);
final Map<RefEntity, CommonProblemDescriptor[]> problems = new HashMap<>(1);
problems.put(refElement, descriptors);
final Map<String, Set<RefEntity>> contents = new HashMap<>();
final String groupName = refElement.getRefManager().getGroupName((RefElement)refElement);
Set<RefEntity> content = contents.computeIfAbsent(groupName, __ -> new HashSet<>());
content.add(refElement);
updateTree(() -> ReadAction.run(() -> {
if (!isDisposed()) {
ApplicationManager.getApplication().assertReadAccessAllowed();
final AnalysisUIOptions uiOptions = myGlobalInspectionContext.getUIOptions();
final InspectionToolPresentation presentation = myGlobalInspectionContext.getPresentation(wrapper);
getProvider().appendToolNodeContent(myGlobalInspectionContext,
toolNode,
(InspectionTreeNode)toolNode.getParent(),
uiOptions.SHOW_STRUCTURE,
true,
contents,
problems::get);
final InspectionTreeNode toolNode =
myTree.getToolProblemsRootNode(wrapper, HighlightDisplayLevel.find(presentation.getSeverity((RefElement)refElement)),
uiOptions.GROUP_BY_SEVERITY, isSingleInspectionRun());
final Map<RefEntity, CommonProblemDescriptor[]> problems = new HashMap<>(1);
problems.put(refElement, descriptors);
final Map<String, Set<RefEntity>> contents = new HashMap<>();
final String groupName = refElement.getRefManager().getGroupName((RefElement)refElement);
Set<RefEntity> content = contents.computeIfAbsent(groupName, __ -> new HashSet<>());
content.add(refElement);
getProvider().appendToolNodeContent(myGlobalInspectionContext,
wrapper,
toolNode,
uiOptions.SHOW_STRUCTURE,
true,
contents,
problems::get);
myLoadingProgressPreviewAlarm.cancelAllRequests();
myLoadingProgressPreviewAlarm.addRequest(() -> {
if (myLoadingProgressPreview != null) {
myLoadingProgressPreview.updateLoadingProgress();
}
});
}, 200);
}
});
}));
}
public void update() {
@@ -670,14 +550,11 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro
final Runnable buildAction = () -> {
try {
setUpdating(true);
synchronized (myTreeStructureUpdateLock) {
myTree.removeAllNodes();
addToolsSynchronously(tools);
}
myTree.removeAllNodes();
addToolsSynchronously(tools);
}
finally {
setUpdating(false);
UIUtil.invokeLaterIfNeeded(() -> myTree.restoreExpansionAndSelection(false));
}
};
if (app.isUnitTestMode()) {
@@ -688,35 +565,16 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro
}
public void setUpdating(boolean isUpdating) {
final Runnable update = () -> {
if (isUpdating) {
myUpdatingRequestors++;
} else {
myUpdatingRequestors--;
}
boolean hasUpdatingRequestors = myUpdatingRequestors > 0;
myTree.setPaintBusy(hasUpdatingRequestors);
if (!hasUpdatingRequestors && myLoadingProgressPreview != null) {
myLoadingProgressPreview.treeLoaded();
}
//TODO Dmitrii Batkovich it's a hack (preview panel should be created during selection update)
if (!hasUpdatingRequestors && mySplitter.getSecondComponent() == null) {
final int count = myTree.getRoot().getChildCount();
if (count != 0) {
if (myTree.getSelectionCount() == 0) {
TreeUtil.selectFirstNode(myTree);
}
syncRightPanel();
myUpdating = isUpdating;
if (!isUpdating) {
myLoadingProgressPreviewAlarm.cancelAllRequests();
myLoadingProgressPreviewAlarm.addRequest(() -> {
if (myLoadingProgressPreview != null) {
myLoadingProgressPreview.treeLoaded();
}
}
};
final Application app = ApplicationManager.getApplication();
if (app.isDispatchThread()) {
update.run();
}
else {
app.invokeLater(update, ModalityState.any());
}, 200);
}
EdtInvocationManager.getInstance().invokeLater(() -> myTree.setPaintBusy(myUpdating));
}
public void addTools(Collection<? extends Tools> tools) {
@@ -725,30 +583,23 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro
private void addToolsSynchronously(Collection<? extends Tools> tools) {
if (isDisposed()) return;
synchronized (myTreeStructureUpdateLock) {
InspectionProfileImpl profile = myInspectionProfile;
boolean isGroupedBySeverity = myGlobalInspectionContext.getUIOptions().GROUP_BY_SEVERITY;
boolean singleInspectionRun = isSingleInspectionRun();
for (Tools currentTools : tools) {
InspectionToolWrapper defaultToolWrapper = currentTools.getDefaultState().getTool();
final HighlightDisplayKey key = HighlightDisplayKey.find(defaultToolWrapper.getShortName());
for (ScopeToolState state : myProvider.getTools(currentTools)) {
InspectionToolWrapper toolWrapper = state.getTool();
if (ReadAction.compute(() -> myProvider.checkReportedProblems(myGlobalInspectionContext, toolWrapper))) {
addTool(toolWrapper,
profile.getErrorLevel(key, state.getScope(getProject()), getProject()),
isGroupedBySeverity,
singleInspectionRun);
}
InspectionProfileImpl profile = myInspectionProfile;
boolean isGroupedBySeverity = myGlobalInspectionContext.getUIOptions().GROUP_BY_SEVERITY;
boolean singleInspectionRun = isSingleInspectionRun();
for (Tools currentTools : tools) {
InspectionToolWrapper defaultToolWrapper = currentTools.getDefaultState().getTool();
final HighlightDisplayKey key = HighlightDisplayKey.find(defaultToolWrapper.getShortName());
for (ScopeToolState state : myProvider.getTools(currentTools)) {
InspectionToolWrapper toolWrapper = state.getTool();
if (ReadAction.compute(() -> myProvider.checkReportedProblems(myGlobalInspectionContext, toolWrapper))) {
addTool(toolWrapper,
profile.getErrorLevel(key, state.getScope(getProject()), getProject()),
isGroupedBySeverity,
singleInspectionRun);
}
}
}
ApplicationManager.getApplication().invokeLater(() -> {
if (myTree.getSelectionCount() == 0) {
TreeUtil.selectFirstNode(myTree);
}
syncRightPanel();
});
}
@NotNull
@@ -761,12 +612,22 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro
if (PlatformDataKeys.HELP_ID.is(dataId)) return HELP_ID;
if (DATA_KEY.is(dataId)) return this;
if (ExclusionHandler.EXCLUSION_HANDLER.is(dataId)) return myExclusionHandler;
if (!ApplicationManager.getApplication().isDispatchThread()) return null;
TreePath[] paths = myTree.getSelectionPaths();
if (paths == null || paths.length == 0) return null;
if (paths.length > 1) {
if (LangDataKeys.PSI_ELEMENT_ARRAY.is(dataId)) {
return collectPsiElements();
RefEntity[] refElements = myTree.getSelectedElements();
List<PsiElement> psiElements = new ArrayList<>();
for (RefEntity refElement : refElements) {
PsiElement psiElement = refElement instanceof RefElement ? ((RefElement)refElement).getPsiElement() : null;
if (psiElement != null && psiElement.isValid()) {
psiElements.add(psiElement);
}
}
return PsiUtilCore.toPsiElementArray(psiElements);
}
return null;
}
@@ -789,25 +650,11 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro
PsiElement psiElement = item instanceof RefElement ? ((RefElement)item).getPsiElement() : null;
if (psiElement == null) return null;
final CommonProblemDescriptor problem = refElementNode.getDescriptor();
if (problem instanceof ProblemDescriptor) {
PsiElement elementFromDescriptor = ((ProblemDescriptor)problem).getPsiElement();
if (elementFromDescriptor == null && CommonDataKeys.NAVIGATABLE.is(dataId) && refElementNode.getChildCount() != 0) {
final InspectionTreeNode node = (InspectionTreeNode)refElementNode.getChildAt(0);
if (node.isValid()) {
return InspectionResultsViewUtil.getNavigatableForInvalidNode((ProblemDescriptionNode)node);
}
}
else {
psiElement = elementFromDescriptor;
}
}
if (CommonDataKeys.NAVIGATABLE.is(dataId)) {
return getSelectedNavigatable(problem, psiElement);
return getSelectedNavigatable(null, psiElement);
}
else if (CommonDataKeys.PSI_ELEMENT.is(dataId)) {
return psiElement != null && psiElement.isValid() ? psiElement : null;
return psiElement.isValid() ? psiElement : null;
}
}
else if (selectedNode instanceof ProblemDescriptionNode && CommonDataKeys.NAVIGATABLE.is(dataId)) {
@@ -818,28 +665,14 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro
return null;
}
void resetTree() {
try {
myTree.setQueueUpdate(true);
final TreePath[] selectionPath = myTree.getSelectionPaths();
final List<TreePath> expandedPaths = TreeUtil.collectExpandedPaths(myTree);
((DefaultTreeModel)myTree.getModel()).reload();
TreeUtil.restoreExpandedPaths(myTree, expandedPaths);
myTree.setSelectionPaths(selectionPath);
}
finally {
myTree.setQueueUpdate(false);
}
}
@Nullable
private Navigatable getSelectedNavigatable(final CommonProblemDescriptor descriptor) {
public static Navigatable getSelectedNavigatable(final CommonProblemDescriptor descriptor) {
return getSelectedNavigatable(descriptor,
descriptor instanceof ProblemDescriptor ? ((ProblemDescriptor)descriptor).getPsiElement() : null);
}
@Nullable
private Navigatable getSelectedNavigatable(final CommonProblemDescriptor descriptor, final PsiElement psiElement) {
private static Navigatable getSelectedNavigatable(CommonProblemDescriptor descriptor, PsiElement psiElement) {
if (descriptor instanceof ProblemDescriptorBase) {
Navigatable navigatable = ((ProblemDescriptorBase)descriptor).getNavigatable();
if (navigatable != null) {
@@ -861,25 +694,11 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro
startOffset = textRange.getStartOffset();
}
}
return PsiNavigationSupport.getInstance().createNavigatable(getProject(), virtualFile, startOffset);
return PsiNavigationSupport.getInstance().createNavigatable(psiElement.getProject(), virtualFile, startOffset);
}
return null;
}
@NotNull
private PsiElement[] collectPsiElements() {
RefEntity[] refElements = myTree.getSelectedElements();
List<PsiElement> psiElements = new ArrayList<>();
for (RefEntity refElement : refElements) {
PsiElement psiElement = refElement instanceof RefElement ? ((RefElement)refElement).getPsiElement() : null;
if (psiElement != null && psiElement.isValid()) {
psiElements.add(psiElement);
}
}
return PsiUtilCore.toPsiElementArray(psiElements);
}
@NotNull
public InspectionTree getTree() {
return myTree;
@@ -929,13 +748,7 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro
}
public boolean isUpdating() {
return myUpdatingRequestors > 0;
}
void updateRightPanelLoading() {
if (!myDisposed && isUpdating() && myLoadingProgressPreview != null) {
myLoadingProgressPreview.updateLoadingProgress();
}
return myUpdating;
}
public boolean hasProblems() {
@@ -968,7 +781,6 @@ public class InspectionResultsView extends JPanel implements Disposable, DataPro
myRerun = true;
if (myScope.isValid()) {
AnalysisUIOptions.getInstance(getProject()).save(myGlobalInspectionContext.getUIOptions());
myGlobalInspectionContext.setTreeState(getTree().getTreeState());
myGlobalInspectionContext.doInspections(myScope);
} else {
GlobalInspectionContextImpl.NOTIFICATION_GROUP.createNotification(InspectionsBundle.message("inspection.view.invalid.scope.message"), NotificationType.INFORMATION).notify(getProject());
@@ -2,7 +2,6 @@
package com.intellij.codeInspection.ui;
import com.intellij.codeInsight.daemon.impl.SeverityRegistrar;
import com.intellij.codeInspection.CommonProblemDescriptor;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.codeInspection.reference.RefElement;
@@ -26,6 +25,8 @@ import java.util.Comparator;
public class InspectionResultsViewComparator implements Comparator<InspectionTreeNode> {
private static final Logger LOG = Logger.getInstance(InspectionResultsViewComparator.class);
public static final InspectionResultsViewComparator INSTANCE = new InspectionResultsViewComparator();
public boolean areEqual(InspectionTreeNode o1, InspectionTreeNode o2) {
return o1.getClass().equals(o2.getClass()) && compare(o1, o2) == 0;
}
@@ -192,12 +193,4 @@ public class InspectionResultsViewComparator implements Comparator<InspectionTre
}
return entity1.getQualifiedName().compareToIgnoreCase(entity2.getQualifiedName());
}
private static class InspectionResultsViewComparatorHolder {
private static final InspectionResultsViewComparator ourInstance = new InspectionResultsViewComparator();
}
public static InspectionResultsViewComparator getInstance() {
return InspectionResultsViewComparatorHolder.ourInstance;
}
}
@@ -16,19 +16,13 @@
package com.intellij.codeInspection.ui;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
/**
* @author max
*/
public class InspectionRootNode extends InspectionTreeNode {
public InspectionRootNode(Project project, @NotNull InspectionTreeUpdater updater) {
super(project);
myUpdater = updater;
protected InspectionRootNode(InspectionTreeModel model) {
super(model);
}
public InspectionTreeUpdater getUpdater() {
return myUpdater;
@Override
public String getPresentableText() {
return "InspectionViewTree";
}
}
@@ -5,18 +5,20 @@ package com.intellij.codeInspection.ui;
import com.intellij.codeHighlighting.HighlightDisplayLevel;
import com.intellij.codeInsight.daemon.impl.SeverityRegistrar;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.Icon;
import java.util.Locale;
public class InspectionSeverityGroupNode extends InspectionTreeNode{
private final HighlightDisplayLevel myLevel;
private final SeverityRegistrar mySeverityRegistrar;
public InspectionSeverityGroupNode(final SeverityRegistrar severityRegistrar, final HighlightDisplayLevel level) {
super(level);
public InspectionSeverityGroupNode(@NotNull SeverityRegistrar severityRegistrar,
@NotNull HighlightDisplayLevel level,
@NotNull InspectionTreeModel model) {
super(model);
myLevel = level;
mySeverityRegistrar = severityRegistrar;
}
@@ -31,8 +33,9 @@ public class InspectionSeverityGroupNode extends InspectionTreeNode{
return true;
}
public String toString() {
return StringUtil.capitalize(myLevel.toString().toLowerCase());
@Override
public String getPresentableText() {
return StringUtil.capitalize(myLevel.toString().toLowerCase(Locale.ENGLISH));
}
public HighlightDisplayLevel getSeverityLevel() {
@@ -33,19 +33,16 @@ public interface InspectionToolPresentation extends ProblemDescriptionsProcessor
@NotNull
InspectionToolWrapper getToolWrapper();
void createToolNode(@NotNull GlobalInspectionContextImpl globalInspectionContext,
@NotNull InspectionNode node,
@NotNull InspectionRVContentProvider provider,
@NotNull InspectionTreeNode parentNode,
boolean showStructure,
boolean groupBySeverity);
default void patchToolNode(@NotNull InspectionTreeNode node,
@NotNull InspectionRVContentProvider provider,
boolean showStructure,
boolean groupBySeverity) {
@Nullable
InspectionNode getToolNode();
}
@NotNull
default RefElementNode createRefNode(@Nullable RefEntity entity) {
return new RefElementNode(entity, this);
default RefElementNode createRefNode(@Nullable RefEntity entity, @NotNull InspectionTreeModel model) {
return new RefElementNode(entity, this, model);
}
void updateContent();
@@ -127,8 +124,8 @@ public interface InspectionToolPresentation extends ProblemDescriptionsProcessor
return false;
}
default int getProblemsCount(@NotNull InspectionTree tree) {
return tree.getSelectedDescriptors().length;
default boolean showProblemCount() {
return true;
}
@Nullable
@@ -4,42 +4,54 @@ package com.intellij.codeInspection.ui;
import com.intellij.codeHighlighting.HighlightDisplayLevel;
import com.intellij.codeInspection.CommonProblemDescriptor;
import com.intellij.codeInspection.ex.GlobalInspectionContextImpl;
import com.intellij.codeInspection.ex.InspectionToolWrapper;
import com.intellij.codeInspection.InspectionsBundle;
import com.intellij.codeInspection.ex.BatchModeDescriptorsUtil;
import com.intellij.codeInspection.ex.GlobalInspectionContextImpl;
import com.intellij.codeInspection.ex.InspectionProfileImpl;
import com.intellij.codeInspection.ex.InspectionToolWrapper;
import com.intellij.codeInspection.reference.RefElement;
import com.intellij.codeInspection.reference.RefEntity;
import com.intellij.codeInspection.ui.util.SynchronizedBidiMultiMap;
import com.intellij.concurrency.ConcurrentCollectionFactory;
import com.intellij.ide.DataManager;
import com.intellij.ide.OccurenceNavigator;
import com.intellij.ide.util.PsiNavigationSupport;
import com.intellij.openapi.actionSystem.ActionPlaces;
import com.intellij.openapi.actionSystem.IdeActions;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.Navigatable;
import com.intellij.profile.codeInspection.ui.inspectionsTree.InspectionsConfigTreeComparator;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.SmartPsiElementPointer;
import com.intellij.ui.PopupHandler;
import com.intellij.ui.SmartExpander;
import com.intellij.ui.TreeSpeedSearch;
import com.intellij.ui.tree.AsyncTreeModel;
import com.intellij.ui.tree.TreePathUtil;
import com.intellij.ui.treeStructure.Tree;
import com.intellij.util.ArrayUtil;
import com.intellij.util.ConcurrencyUtil;
import com.intellij.util.EditSourceOnDoubleClickHandler;
import com.intellij.util.OpenSourceUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MultiMap;
import com.intellij.util.containers.Stack;
import com.intellij.util.containers.TreeTraversal;
import com.intellij.util.ui.EdtInvocationManager;
import com.intellij.util.ui.UIUtil;
import com.intellij.util.ui.tree.TreeModelAdapter;
import com.intellij.util.ui.tree.TreeUtil;
import gnu.trove.THashSet;
import gnu.trove.TObjectHashingStrategy;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeWillExpandListener;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.event.TreeModelEvent;
import javax.swing.tree.TreePath;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.util.*;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Predicate;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import static com.intellij.codeInspection.CommonProblemDescriptor.DESCRIPTOR_COMPARATOR;
@@ -48,37 +60,79 @@ public class InspectionTree extends Tree {
private static final Logger LOG = Logger.getInstance(InspectionTree.class);
@NotNull private final GlobalInspectionContextImpl myContext;
@NotNull private final ConcurrentMap<HighlightDisplayLevel, InspectionSeverityGroupNode> mySeverityGroupNodes = ContainerUtil.newConcurrentMap();
@NotNull private final ConcurrentMap<HighlightDisplayLevel, ConcurrentMap<String[], InspectionGroupNode>> myGroups = ContainerUtil.newConcurrentMap();
private final InspectionTreeModel myModel;
@NotNull private InspectionTreeState myState = new InspectionTreeState();
private boolean myQueueUpdate;
private final OccurenceNavigator myOccurenceNavigator = new MyOccurrenceNavigator();
public InspectionTree(@NotNull GlobalInspectionContextImpl context,
@NotNull InspectionResultsView view) {
Project project = context.getProject();
setModel(new DefaultTreeModel(new InspectionRootNode(project, new InspectionTreeUpdater(view))));
myModel = new InspectionTreeModel();
setModel(new AsyncTreeModel(myModel, view));
myContext = context;
setCellRenderer(new InspectionTreeCellRenderer(view));
setRootVisible(false);
setShowsRootHandles(true);
UIUtil.setLineStyleAngled(this);
addTreeWillExpandListener(new ExpandListener());
if (!ApplicationManager.getApplication().isUnitTestMode()) {
getSelectionModel().addTreeSelectionListener(e -> {
if (isUnderQueueUpdate()) return;
InspectionResultsView view1 = getContext().getView();
if (view1 != null && !view1.isDisposed()) {
view1.syncRightPanel();
if (view1.isAutoScrollMode()) {
OpenSourceUtil.openSourcesFrom(DataManager.getInstance().getDataContext(this), false);
}
}
});
myState.getExpandedUserObjects().add(project);
EditSourceOnDoubleClickHandler.install(this);
TreeUtil.installActions(this);
new TreeSpeedSearch(this, o -> InspectionsConfigTreeComparator.getDisplayTextToSort(o.getLastPathComponent().toString()));
addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
InspectionResultsView view1 = getContext().getView();
if (view1 != null && !view1.isDisposed()) {
OpenSourceUtil.openSourcesFrom(DataManager.getInstance().getDataContext(view1), false);
}
}
}
});
TreeUtil.installActions(this);
PopupHandler.installPopupHandler(this, IdeActions.INSPECTION_TOOL_WINDOW_TREE_POPUP, ActionPlaces.CODE_INSPECTION);
new TreeSpeedSearch(this, o -> InspectionsConfigTreeComparator.getDisplayTextToSort(o.getLastPathComponent().toString()));
}
addTreeSelectionListener(e -> {
TreePath newSelection = e.getNewLeadSelectionPath();
if (newSelection != null && !isUnderQueueUpdate()) {
myState.setSelectionPath(newSelection);
getModel().addTreeModelListener(new TreeModelAdapter() {
//TODO the same as DiscoveredTestTree (see setRootVisible)
boolean myAlreadyDone;
@Override
protected void process(@NotNull TreeModelEvent event, @NotNull EventType type) {
if (!myAlreadyDone && getSelectionCount() == 0) {
myAlreadyDone = true;
EdtInvocationManager.getInstance().invokeLater(() -> {
expandPath(new TreePath(myModel.getRoot()));
SmartExpander.installOn(InspectionTree.this);
InspectionResultsView v = getContext().getView();
if (v != null && !v.isDisposed()) {
v.syncRightPanel();
}
});
}
}
});
}
public InspectionTreeNode getRoot() {
return myModel.getRoot();
}
public InspectionTreeModel getInspectionTreeModel() {
return myModel;
}
public void setQueueUpdate(boolean queueUpdate) {
myQueueUpdate = queueUpdate;
}
@@ -88,19 +142,7 @@ public class InspectionTree extends Tree {
}
public void removeAllNodes() {
mySeverityGroupNodes.clear();
myGroups.clear();
getRoot().removeAllChildren();
ApplicationManager.getApplication().invokeLater(() -> {
InspectionResultsView view = myContext.getView();
if (view != null && !view.isDisposed()) {
nodeStructureChanged(getRoot());
}
});
}
public InspectionTreeNode getRoot() {
return (InspectionTreeNode)getModel().getRoot();
myModel.clearTree();
}
@Nullable
@@ -119,8 +161,17 @@ public class InspectionTree extends Tree {
@Nullable
public InspectionToolWrapper getSelectedToolWrapper(boolean allowDummy) {
final TreePath[] paths = getSelectionPaths();
if (paths == null) return null;
InspectionToolWrapper toolWrapper = null;
if (paths == null) {
InspectionProfileImpl profile = myContext.getView().getCurrentProfile();
String singleToolName = profile.getSingleTool();
if (singleToolName != null) {
InspectionToolWrapper tool = profile.getInspectionTool(singleToolName, myContext.getProject());
LOG.assertTrue(tool != null);
return tool;
}
return null;
}
InspectionToolWrapper resultWrapper = null;
for (TreePath path : paths) {
Object[] nodes = path.getPath();
for (int j = nodes.length - 1; j >= 0; j--) {
@@ -128,23 +179,25 @@ public class InspectionTree extends Tree {
if (node instanceof InspectionGroupNode) {
return null;
}
InspectionToolWrapper wrapper = null;
if (node instanceof InspectionNode) {
InspectionToolWrapper wrapper = ((InspectionNode)node).getToolWrapper();
if (!allowDummy && getContext().getPresentation(wrapper).isDummy()) {
continue;
}
if (toolWrapper == null) {
toolWrapper = wrapper;
}
else if (toolWrapper != wrapper) {
return null;
}
break;
wrapper = ((InspectionNode)node).getToolWrapper();
} else if (node instanceof SuppressableInspectionTreeNode) {
wrapper = ((SuppressableInspectionTreeNode)node).getPresentation().getToolWrapper();
}
if (wrapper == null || !allowDummy && getContext().getPresentation(wrapper).isDummy()) {
continue;
}
if (resultWrapper == null) {
resultWrapper = wrapper;
}
else if (resultWrapper != wrapper) {
return null;
}
break;
}
}
return toolWrapper;
return resultWrapper;
}
@Override
@@ -203,6 +256,15 @@ public class InspectionTree extends Tree {
return RefEntity.EMPTY_ELEMENTS_ARRAY;
}
public OccurenceNavigator getOccurenceNavigator() {
return myOccurenceNavigator;
}
public void selectNode(InspectionTreeNode node) {
TreePath path = getPathFor(node);
TreeUtil.selectPath(this, path);
}
private static void addElementsInNode(InspectionTreeNode node, Set<? super RefEntity> out) {
if (!node.isValid()) return;
if (node instanceof RefElementNode) {
@@ -213,45 +275,44 @@ public class InspectionTree extends Tree {
final RefEntity element = ((ProblemDescriptionNode)node).getElement();
out.add(element);
}
final Enumeration children = node.children();
while (children.hasMoreElements()) {
InspectionTreeNode child = (InspectionTreeNode)children.nextElement();
for (InspectionTreeNode child : node.getChildren()) {
addElementsInNode(child, out);
}
}
@NotNull
public CommonProblemDescriptor[] getAllValidSelectedDescriptors() {
return BatchModeDescriptorsUtil.flattenDescriptors(getSelectedDescriptorPacks(false, null, true));
return BatchModeDescriptorsUtil.flattenDescriptors(getSelectedDescriptorPacks(false, null, true, null));
}
@NotNull
public CommonProblemDescriptor[] getSelectedDescriptors() {
return BatchModeDescriptorsUtil.flattenDescriptors(getSelectedDescriptorPacks(false, null, false));
return BatchModeDescriptorsUtil.flattenDescriptors(getSelectedDescriptorPacks(false, null, false, null));
}
@NotNull
public List<CommonProblemDescriptor[]> getSelectedDescriptorPacks(boolean sortedByPosition,
@Nullable Set<? super VirtualFile> readOnlyFilesSink,
boolean allowResolved) {
final TreePath[] paths = getSelectionPaths();
boolean allowResolved,
TreePath[] paths) {
if (paths == null) {
ApplicationManager.getApplication().assertIsDispatchThread();
paths = getSelectionPaths();
}
if (paths == null) return Collections.emptyList();
// key can be node or VirtualFile (if problem descriptor node parent is a file/member RefElementNode).
MultiMap<Object, CommonProblemDescriptor> parentToChildNode = new MultiMap<>();
TreeUtil
.treePathTraverser(this)
.withRoots(Arrays.asList(paths))
.traverse()
.map(p -> p.getLastPathComponent())
//TODO expected thread
TreeTraversal.PLAIN_BFS.traversal(
ContainerUtil.map(paths, p -> (InspectionTreeNode)p.getLastPathComponent()),
(InspectionTreeNode n) -> myModel.getChildren(n))
.filter(ProblemDescriptionNode.class)
.filter(node -> node.getDescriptor() != null && isNodeValidAndIncluded(node, allowResolved))
.consumeEach(node -> {
Object key = getVirtualFileOrEntity(node.getElement());
parentToChildNode.putValue(key, node.getDescriptor());
});
final List<CommonProblemDescriptor[]> descriptors = new ArrayList<>();
for (Map.Entry<Object, Collection<CommonProblemDescriptor>> entry : parentToChildNode.entrySet()) {
Object key = entry.getKey();
@@ -268,68 +329,53 @@ public class InspectionTree extends Tree {
return descriptors;
}
@NotNull
InspectionTreeNode getToolParentNode(@NotNull InspectionToolWrapper toolWrapper,
HighlightDisplayLevel errorLevel,
boolean groupedBySeverity,
boolean isSingleInspectionRun) {
//synchronize
if (!groupedBySeverity && isSingleInspectionRun) {
return getRoot();
}
String[] groupPath = toolWrapper.getGroupPath();
if (groupPath.length == 0) {
LOG.error("groupPath is empty for tool: " + toolWrapper.getShortName() + ", class: " + toolWrapper.getTool().getClass());
return getRelativeRootNode(groupedBySeverity, errorLevel);
}
ConcurrentMap<String[], InspectionGroupNode> map = myGroups.get(errorLevel);
if (map == null) {
map = ConcurrencyUtil.cacheOrGet(myGroups, errorLevel, ConcurrentCollectionFactory.createMap(new TObjectHashingStrategy<String[]>() {
@Override
public int computeHashCode(String[] object) {
return Arrays.hashCode(object);
}
@Override
public boolean equals(String[] o1, String[] o2) {
return Arrays.equals(o1, o2);
}
}));
}
InspectionGroupNode group;
if (groupedBySeverity) {
group = map.get(groupPath);
}
else {
group = null;
for (Map<String[], InspectionGroupNode> groupMap : myGroups.values()) {
if ((group = groupMap.get(groupPath)) != null) break;
}
}
if (group == null) {
if (isSingleInspectionRun) {
return getRelativeRootNode(true, errorLevel);
}
group = map.computeIfAbsent(groupPath, p -> insertGroupNode(p, getRelativeRootNode(groupedBySeverity, errorLevel)));
}
return group;
@Nullable
@Override
public TreePath[] getSelectionPaths() {
ApplicationManager.getApplication().assertIsDispatchThread();
return super.getSelectionPaths();
}
@NotNull
private InspectionTreeNode getRelativeRootNode(boolean isGroupedBySeverity, HighlightDisplayLevel level) {
if (isGroupedBySeverity) {
InspectionSeverityGroupNode severityGroupNode = mySeverityGroupNodes.get(level);
if (severityGroupNode == null) {
InspectionSeverityGroupNode newNode = new InspectionSeverityGroupNode(myContext.getCurrentProfile().getProfileManager().getSeverityRegistrar(), level);
severityGroupNode = ConcurrencyUtil.cacheOrGet(mySeverityGroupNodes, level, newNode);
if (severityGroupNode == newNode) {
InspectionTreeNode root = getRoot();
root.insertByOrder(severityGroupNode, false);
}
}
return severityGroupNode;
InspectionTreeNode getToolProblemsRootNode(@NotNull InspectionToolWrapper toolWrapper,
@NotNull HighlightDisplayLevel errorLevel,
boolean groupedBySeverity,
boolean isSingleInspectionRun) {
InspectionTreeNode parent = getToolParentNode(toolWrapper, errorLevel, groupedBySeverity, isSingleInspectionRun);
if (isSingleInspectionRun) {
return parent;
}
return getRoot();
return myModel.createInspectionNode(toolWrapper, myContext.getCurrentProfile(), parent);
}
@NotNull
private InspectionTreeNode getToolParentNode(@NotNull InspectionToolWrapper toolWrapper,
@NotNull HighlightDisplayLevel errorLevel,
boolean groupedBySeverity,
boolean isSingleInspectionRun) {
//synchronize
if (!groupedBySeverity && isSingleInspectionRun) {
return myModel.getRoot();
}
InspectionTreeNode currentNode = groupedBySeverity
? myModel.createSeverityGroupNode(myContext.getCurrentProfile().getProfileManager().getSeverityRegistrar(),
errorLevel,
myModel.getRoot())
: myModel.getRoot();
if (isSingleInspectionRun) return currentNode;
String[] groupPath = toolWrapper.getGroupPath();
if (groupPath.length == 0) {
LOG.error("groupPath is empty for tool: " + toolWrapper.getShortName() + ", class: " + toolWrapper.getTool().getClass());
}
for (String subGroup : groupPath) {
currentNode = myModel.createGroupNode(subGroup, currentNode);
}
return currentNode;
}
public boolean areDescriptorNodesSelected() {
@@ -343,10 +389,13 @@ public class InspectionTree extends Tree {
return true;
}
public int getSelectedProblemCount(boolean allowSuppressed) {
public int getSelectedProblemCount() {
int count = 0;
for (TreePath path : TreeUtil.selectMaximals(getSelectionPaths())) {
count += ((InspectionTreeNode)path.getLastPathComponent()).getProblemCount(allowSuppressed);
LevelAndCount[] levels = ((InspectionTreeNode)path.getLastPathComponent()).getProblemLevels();
for (LevelAndCount level : levels) {
count += level.getCount();
}
}
return count;
}
@@ -358,58 +407,57 @@ public class InspectionTree extends Tree {
!node.isQuickFixAppliedFromView()));
}
private void nodeStructureChanged(InspectionTreeNode node) {
((DefaultTreeModel)getModel()).nodeStructureChanged(node);
}
public void queueUpdate() {
((InspectionRootNode) getRoot()).getUpdater().update(true);
}
public void restoreExpansionAndSelection(boolean treeNodesMightChange) {
myState.restoreExpansionAndSelection(this, treeNodesMightChange);
}
public void removeSelectedProblems() {
ApplicationManager.getApplication().assertIsDispatchThread();
TreePath[] selected = getSelectionPaths();
if (selected == null) return;
if (!getContext().getUIOptions().FILTER_RESOLVED_ITEMS) {
for (TreePath path : selected) {
InspectionTreeNode node = (InspectionTreeNode)path.getLastPathComponent();
myModel.traverse(node, n -> {
n.dropProblemCountCaches();
return true;
});
}
return;
}
synchronized (getContext().getView().getTreeStructureUpdateLock()) {
TreePath[] selected = getSelectionPaths();
if (selected == null) return;
Set<InspectionTreeNode> processedNodes = new THashSet<>();
List<InspectionTreeNode> toRemove = new ArrayList<>();
List<TreePath> pathsToSelect = new ArrayList<>();
for (TreePath path : selected) {
Object[] nodePath = path.getPath();
Set<InspectionTreeNode> processedNodes = new THashSet<>();
List<InspectionTreeNode> toRemove = new ArrayList<>();
List<TreePath> pathsToSelect = new ArrayList<>();
for (TreePath path : selected) {
Object[] nodePath = path.getPath();
// ignore root
for (int i = 1; i < nodePath.length; i++) {
InspectionTreeNode node = (InspectionTreeNode) nodePath[i];
if (!processedNodes.add(node)) break;
// ignore root
for (int i = 1; i < nodePath.length; i++) {
InspectionTreeNode node = (InspectionTreeNode) nodePath[i];
if (!processedNodes.add(node)) continue;
if (shouldDelete(node)) {
toRemove.add(node);
TreePath toSelect = getParentPath(path, nodePath.length - i);
if (toSelect != null) {
pathsToSelect.add(toSelect);
}
break;
if (shouldDelete(node)) {
toRemove.add(node);
TreePath toSelect = getParentPath(path, nodePath.length - i);
if (toSelect != null) {
pathsToSelect.add(toSelect);
}
break;
}
}
if (toRemove.isEmpty()) return;
DefaultTreeModel model = (DefaultTreeModel)getModel();
for (InspectionTreeNode node : toRemove) {
TreeNode parent = node.getParent();
if (parent != null) {
model.removeNodeFromParent(node);
}
}
TreeUtil.selectPath(this, TreeUtil.findCommonPath(pathsToSelect.toArray(new TreePath[0])));
}
if (toRemove.isEmpty()) return;
Set<InspectionTreeNode> parents = new THashSet<>();
for (InspectionTreeNode node : toRemove) {
InspectionTreeNode parent = node.getParent();
if (parent != null) {
myModel.remove(node);
parents.add(parent);
}
}
for (InspectionTreeNode parent : parents) {
parent.dropProblemCountCaches();
}
TreeUtil.selectPath(this, TreeUtil.findCommonPath(pathsToSelect.toArray(new TreePath[0])));
revalidate();
repaint();
}
@@ -425,10 +473,16 @@ public class InspectionTree extends Tree {
private boolean shouldDelete(InspectionTreeNode node) {
if (node instanceof RefElementNode) {
RefElementNode refElementNode = (RefElementNode)node;
RefEntity refEntity = refElementNode.getElement();
if (refEntity == null || isEntityExcludedOrResolvedRecursively(refEntity, refElementNode.getPresentation())) {
InspectionToolPresentation presentation = refElementNode.getPresentation();
RefEntity element = refElementNode.getElement();
if (element == null ||
presentation.isProblemResolved(element) ||
presentation.isExcluded(element) ||
presentation.isSuppressed(element)) {
return true;
}
List<? extends InspectionTreeNode> children = node.getChildren();
return !children.isEmpty() && children.stream().allMatch(this::shouldDelete);
}
else if (node instanceof ProblemDescriptionNode) {
ProblemDescriptionNode problemDescriptionNode = (ProblemDescriptionNode)node;
@@ -439,7 +493,7 @@ public class InspectionTree extends Tree {
}
}
else if (node instanceof InspectionGroupNode || node instanceof InspectionSeverityGroupNode || node instanceof InspectionModuleNode || node instanceof InspectionPackageNode) {
return IntStream.range(0, node.getChildCount()).mapToObj(i -> (InspectionTreeNode)node.getChildAt(i)).allMatch(this::shouldDelete);
return node.getChildren().stream().allMatch(this::shouldDelete);
}
else if (node instanceof InspectionNode) {
InspectionToolPresentation presentation = myContext.getPresentation(((InspectionNode)node).getToolWrapper());
@@ -452,80 +506,16 @@ public class InspectionTree extends Tree {
return false;
}
private static boolean isEntityExcludedOrResolvedRecursively(RefEntity key, InspectionToolPresentation presentation) {
if (presentation.isProblemResolved(key) ||
presentation.isExcluded(key) ||
presentation.isSuppressed(key)) {
return true;
}
List<RefEntity> children = key.getChildren();
return !children.isEmpty() && children.stream().allMatch(entity -> isEntityExcludedOrResolvedRecursively(entity, presentation));
}
public InspectionTreeState getTreeState() {
return myState;
}
public void setTreeState(@NotNull InspectionTreeState treeState) {
myState = treeState;
}
private class ExpandListener implements TreeWillExpandListener {
@Override
public void treeWillExpand(TreeExpansionEvent event) {
final InspectionTreeNode node = (InspectionTreeNode)event.getPath().getLastPathComponent();
myState.getExpandedUserObjects().add(node.getUserObject());
}
@Override
public void treeWillCollapse(TreeExpansionEvent event) {
InspectionTreeNode node = (InspectionTreeNode)event.getPath().getLastPathComponent();
myState.getExpandedUserObjects().remove(node.getUserObject());
}
}
@NotNull
public GlobalInspectionContextImpl getContext() {
return myContext;
}
private InspectionGroupNode insertGroupNode(@NotNull String[] groupPath, InspectionTreeNode parent) {
InspectionTreeNode currentNode = parent;
for (int groupIdx = 0; groupIdx < groupPath.length; groupIdx++) {
String subGroup = groupPath[groupIdx];
InspectionTreeNode next = null;
for (int i = 0; i < currentNode.getChildCount(); i++) {
TreeNode child = currentNode.getChildAt(i);
if (child instanceof InspectionGroupNode && ((InspectionGroupNode)child).getSubGroup().equals(subGroup)) {
next = (InspectionTreeNode)child;
break;
}
}
if (next == null) {
for (int i = groupIdx; i < groupPath.length; i++) {
InspectionResultsView view = getContext().getView();
if (view != null && !view.isDisposed()) {
currentNode = currentNode.insertByOrder(new InspectionGroupNode(groupPath[i]), false);
}
}
break;
}
else {
currentNode = next;
}
}
return (InspectionGroupNode)currentNode;
}
@NotNull
private static String[] getGroupPath(@NotNull InspectionGroupNode node) {
List<String> path = new ArrayList<>(2);
while (true) {
TreeNode parent = node.getParent();
InspectionTreeNode parent = node.getParent();
if (!(parent instanceof InspectionGroupNode)) break;
node = (InspectionGroupNode)parent;
path.add(node.getSubGroup());
@@ -546,4 +536,138 @@ public class InspectionTree extends Tree {
}
return entity;
}
private class MyOccurrenceNavigator implements OccurenceNavigator {
@Override
public boolean hasNextOccurence() {
return getNextNode(true) != null;
}
@Override
public boolean hasPreviousOccurence() {
return getNextNode(false) != null;
}
@Override
public OccurenceInfo goNextOccurence() {
InspectionTreeNode node = getNextNode(true);
if (node == null) return null;
selectNode(node);
return new OccurenceInfo(createDescriptorForNode(node), -1, -1);
}
@Override
public OccurenceInfo goPreviousOccurence() {
InspectionTreeNode node = getNextNode(false);
selectNode(node);
return node == null ? null : new OccurenceInfo(createDescriptorForNode(node), -1, -1);
}
@NotNull
@Override
public String getNextOccurenceActionName() {
return InspectionsBundle.message("inspection.action.go.next");
}
@NotNull
@Override
public String getPreviousOccurenceActionName() {
return InspectionsBundle.message("inspection.action.go.prev");
}
private InspectionTreeNode getNextNode(boolean next) {
InspectionTreeNode node = getSelectedNode();
if (node == null) {
return null;
}
return myModel.traverseFrom(node, next).filter(n -> n != node).filter(n -> isOccurrenceNode(n)).first();
}
private InspectionTreeNode getSelectedNode() {
TreePath path = getSelectionPath();
if (path == null) return null;
return (InspectionTreeNode)path.getLastPathComponent();
}
private boolean isOccurrenceNode(@NotNull InspectionTreeNode node) {
if (node.isExcluded()) {
return false;
}
if (node instanceof RefElementNode) {
final RefElementNode refNode = (RefElementNode)node;
if (hasDescriptorUnder(refNode)) return false;
final RefEntity element = refNode.getElement();
return element != null && element.isValid();
}
return node instanceof ProblemDescriptionNode;
}
@Nullable
private Navigatable createDescriptorForNode(@NotNull InspectionTreeNode node) {
if (node.isExcluded()) {
return null;
}
if (node instanceof RefElementNode) {
final RefElementNode refNode = (RefElementNode)node;
if (hasDescriptorUnder(refNode)) return null;
final RefEntity element = refNode.getElement();
if (element == null || !element.isValid()) return null;
if (element instanceof RefElement) {
return getOpenFileDescriptor((RefElement)element);
}
}
else if (node instanceof ProblemDescriptionNode) {
ProblemDescriptionNode problemNode = (ProblemDescriptionNode)node;
boolean isValid = problemNode.isValid() && (!problemNode.isQuickFixAppliedFromView() ||
problemNode.calculateIsValid());
return isValid
? navigate(problemNode.getDescriptor())
: InspectionResultsViewUtil.getNavigatableForInvalidNode(problemNode);
}
return null;
}
@Nullable
private Navigatable navigate(final CommonProblemDescriptor descriptor) {
return InspectionResultsView.getSelectedNavigatable(descriptor);
}
@Nullable
private Navigatable getOpenFileDescriptor(final RefElement refElement) {
PsiElement psiElement = refElement.getPsiElement();
if (psiElement == null) return null;
final PsiFile containingFile = psiElement.getContainingFile();
if (containingFile == null) return null;
VirtualFile file = containingFile.getVirtualFile();
if (file == null) return null;
return PsiNavigationSupport.getInstance().createNavigatable(refElement.getRefManager().getProject(), file,
psiElement.getTextOffset());
}
private boolean hasDescriptorUnder(RefElementNode node) {
InspectionTreeNode current = node;
while (current != null && current.getChildCount() != 0) {
current = current.getChildAt(0);
if (current instanceof ProblemDescriptionNode) return true;
}
return false;
}
}
private TreePath getPathFor(InspectionTreeNode node) {
TreePath result = TreePathUtil.pathToTreeNode(node);
Stack<TreePath> s = new Stack<>();
TreePath current = result;
while (current != null) {
s.add(current);
current = current.getParentPath();
}
while (!s.isEmpty()) {
TreePath p = s.pop();
expandPath(p);
}
return result;
}
}
@@ -55,7 +55,7 @@ class InspectionTreeCellRenderer extends ColoredTreeCellRenderer {
boolean hasFocus) {
InspectionTreeNode node = (InspectionTreeNode)value;
append(node.toString(),
append(node.getPresentableText(),
patchMainTextAttrs(node, node.appearsBold()
? SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES
: getMainForegroundAttributes(node)));
@@ -0,0 +1,220 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInspection.ui;
import com.intellij.codeHighlighting.HighlightDisplayLevel;
import com.intellij.codeInsight.daemon.impl.SeverityRegistrar;
import com.intellij.codeInspection.CommonProblemDescriptor;
import com.intellij.codeInspection.ex.InspectionProfileImpl;
import com.intellij.codeInspection.ex.InspectionToolWrapper;
import com.intellij.codeInspection.offline.OfflineProblemDescriptor;
import com.intellij.codeInspection.offlineViewer.OfflineDescriptorResolveResult;
import com.intellij.codeInspection.offlineViewer.OfflineProblemDescriptorNode;
import com.intellij.codeInspection.reference.RefEntity;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.ui.tree.BaseTreeModel;
import com.intellij.ui.tree.TreePathUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.ObjectUtils;
import com.intellij.util.Processor;
import com.intellij.util.containers.BidirectionalMap;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.JBIterable;
import com.intellij.util.containers.TreeTraversal;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.tree.TreePath;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
public class InspectionTreeModel extends BaseTreeModel<InspectionTreeNode> {
private static final Logger LOG = Logger.getInstance(InspectionTreeModel.class);
private final InspectionRootNode myRoot = new InspectionRootNode(this);
private final Map<InspectionTreeNode, Children> myChildren = new ConcurrentHashMap<>();
private final Map<InspectionTreeNode, InspectionTreeNode> myParents = new ConcurrentHashMap<>();
@Override
public int getIndexOfChild(Object object, Object child) {
return Collections.binarySearch(getChildren(object), (InspectionTreeNode)child, InspectionResultsViewComparator.INSTANCE);
}
public void reload() {
treeNodesChanged(null, null, null);
}
private static class Children {
private static final InspectionTreeNode[] EMPTY_ARRAY = new InspectionTreeNode[0];
private volatile InspectionTreeNode[] myChildren = EMPTY_ARRAY;
private final BidirectionalMap<Object, InspectionTreeNode> myUserObject2Node = new BidirectionalMap<>();
}
public InspectionTreeModel() {}
@Override
public List<? extends InspectionTreeNode> getChildren(Object parent) {
Children nodes = myChildren.get(((InspectionTreeNode)parent));
return nodes == null ? Collections.emptyList() : Collections.unmodifiableList(Arrays.asList(nodes.myChildren));
}
@Override
public InspectionRootNode getRoot() {
return myRoot;
}
@Nullable
public InspectionTreeNode getParent(InspectionTreeNode node) {
return myParents.get(node);
}
public void traverse(InspectionTreeNode node, Processor<? super InspectionTreeNode> processor) {
TreeTraversal.PRE_ORDER_DFS.traversal(node, n -> getChildren(n)).processEach(processor);
}
@NotNull
public JBIterable<InspectionTreeNode> traverseFrom(InspectionTreeNode node, boolean direction) {
return JBIterable.generate(node, n -> getParent(n)).filter(n -> getParent(n) != null).flatMap(n1 -> {
InspectionTreeNode p = getParent(n1);
@SuppressWarnings("ConstantConditions")
List<? extends InspectionTreeNode> children = p.getChildren();
int idx = getIndexOfChild(p, n1);
InspectionTreeNode[] arr = children.toArray(InspectionTreeNode.EMPTY_ARRAY);
List<? extends InspectionTreeNode> sublist = Arrays.asList(arr).subList(idx + ((n1 == node) ? 0 : 1), children.size());
return TreeTraversal.PRE_ORDER_DFS.traversal(sublist, (InspectionTreeNode n) -> direction ? getChildren(n) : ContainerUtil.reverse(getChildren(n)));
});
}
public void removeChild(@NotNull InspectionTreeNode node, int childIndex) {
InspectionTreeNode removed = myChildren.get(node).myChildren[childIndex];
remove(removed);
treeNodesChanged(null, null, null);
treeStructureChanged(null, null, null);
}
public void removeChildren(@NotNull InspectionTreeNode node) {
doRemove(node, node);
treeNodesChanged(null, null, null);
treeStructureChanged(null, null, null);
}
public void remove(@NotNull InspectionTreeNode node) {
doRemove(node, null);
treeNodesChanged(null, null, null);
treeStructureChanged(null, null, null);
}
private synchronized void doRemove(@NotNull InspectionTreeNode node, @Nullable InspectionTreeNode skip) {
for (InspectionTreeNode child : getChildren(node)) {
doRemove(child, skip);
}
if (node != skip) {
InspectionTreeNode parent = myParents.remove(node);
if (parent != null) {
Children parentChildren = myChildren.get(parent);
if (parentChildren != null) {
parentChildren.myChildren = ArrayUtil.remove(parentChildren.myChildren, node);
parentChildren.myUserObject2Node.removeValue(node);
}
}
}
}
public synchronized void clearTree() {
myChildren.clear();
myParents.clear();
}
@NotNull
public InspectionModuleNode createModuleNode(@NotNull Module module, @NotNull InspectionTreeNode parent) {
return getOrAdd(module, () -> new InspectionModuleNode(module, this), parent);
}
@NotNull
public InspectionPackageNode createPackageNode(String packageName, @NotNull InspectionTreeNode parent) {
return getOrAdd(packageName, () -> new InspectionPackageNode(packageName, this), parent);
}
@NotNull
public InspectionGroupNode createGroupNode(String group, @NotNull InspectionTreeNode parent) {
return getOrAdd(group, () -> new InspectionGroupNode(group, this), parent);
}
@NotNull
public InspectionSeverityGroupNode createSeverityGroupNode(SeverityRegistrar severityRegistrar, HighlightDisplayLevel level, @NotNull InspectionTreeNode parent) {
return getOrAdd(level, () -> new InspectionSeverityGroupNode(severityRegistrar, level, this), parent);
}
@NotNull
public RefElementNode createRefElementNode(@Nullable RefEntity entity,
@NotNull Supplier<? extends RefElementNode> supplier,
@NotNull InspectionTreeNode parent) {
return getOrAdd(entity, () -> ReadAction.compute(supplier::get), parent);
}
public <T extends InspectionTreeNode> void createCustomNode(@NotNull Object userObject, @NotNull Supplier<T> supplier, @NotNull InspectionTreeNode parent) {
getOrAdd(userObject, supplier, parent);
}
@NotNull
public InspectionNode createInspectionNode(@NotNull InspectionToolWrapper toolWrapper, InspectionProfileImpl profile, @NotNull InspectionTreeNode parent) {
return getOrAdd(toolWrapper, () -> new InspectionNode(toolWrapper, profile, this), parent);
}
public void createProblemDescriptorNode(RefEntity element,
@NotNull CommonProblemDescriptor descriptor,
@NotNull InspectionToolPresentation presentation,
@NotNull InspectionTreeNode parent) {
getOrAdd(descriptor, () -> ReadAction.compute(() -> new ProblemDescriptionNode(element, descriptor, presentation, this)), parent);
}
public void createOfflineProblemDescriptorNode(@NotNull OfflineProblemDescriptor offlineDescriptor,
@NotNull OfflineDescriptorResolveResult resolveResult,
@NotNull InspectionToolPresentation presentation,
@NotNull InspectionTreeNode parent) {
getOrAdd(offlineDescriptor,
() -> ReadAction.compute(() -> new OfflineProblemDescriptorNode(resolveResult, presentation, offlineDescriptor, this)),
parent);
}
private synchronized <T extends InspectionTreeNode> T getOrAdd(Object userObject, Supplier<T> supplier, InspectionTreeNode parent) {
LOG.assertTrue(!ApplicationManager.getApplication().isDispatchThread());
Children children = myChildren.computeIfAbsent(parent, __ -> new Children());
if (userObject == null) {
userObject = ObjectUtils.NULL;
}
InspectionTreeNode node = children.myUserObject2Node.get(userObject);
if (node == null) {
node = supplier.get();
InspectionTreeNode finalNode = node;
int idx = ReadAction.compute(() -> Arrays.binarySearch(children.myChildren, finalNode, InspectionResultsViewComparator.INSTANCE));
if (idx >= 0) {
InspectionTreeNode finalN = node;
LOG.error(ReadAction.compute(() -> "idx = " + idx + " node = " + finalN + " parent = " + parent + " children " + Arrays.toString(children.myChildren)));
}
int insertionPoint = -idx - 1;
children.myChildren = ArrayUtil.insert(children.myChildren, insertionPoint, node);
myParents.put(node, parent);
children.myUserObject2Node.put(userObject, node);
LOG.assertTrue(children.myChildren.length == children.myUserObject2Node.size());
if (node instanceof SuppressableInspectionTreeNode) {
((SuppressableInspectionTreeNode)node).nodeAdded();
}
TreePath path = TreePathUtil.pathToTreeNode(node);
TreePath parentPath = path.getParentPath();
treeNodesInserted(parentPath, null, null);
while (parentPath != null) {
treeStructureChanged(parentPath, null, null);
parentPath = parentPath.getParentPath();
}
}
//noinspection unchecked
return (T)node;
}
}
@@ -5,27 +5,23 @@ package com.intellij.codeInspection.ui;
import com.intellij.codeHighlighting.HighlightDisplayLevel;
import com.intellij.codeInspection.reference.RefEntity;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.util.AtomicClearableLazyValue;
import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.WeakInterner;
import com.intellij.util.ui.tree.TreeUtil;
import gnu.trove.TObjectHashingStrategy;
import gnu.trove.TObjectIntHashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeNode;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.*;
/**
* @author max
*/
public abstract class InspectionTreeNode extends DefaultMutableTreeNode {
public abstract class InspectionTreeNode implements TreeNode {
static final InspectionTreeNode[] EMPTY_ARRAY = new InspectionTreeNode[0];
private static final WeakInterner<LevelAndCount[]> LEVEL_AND_COUNT_INTERNER = new WeakInterner<>(new TObjectHashingStrategy<LevelAndCount[]>() {
@Override
public int computeHashCode(LevelAndCount[] object) {
@@ -55,10 +51,11 @@ public abstract class InspectionTreeNode extends DefaultMutableTreeNode {
return doesNeedInternProblemLevels() ? LEVEL_AND_COUNT_INTERNER.intern(arr) : arr;
}
};
protected volatile InspectionTreeUpdater myUpdater;
@NotNull
private final InspectionTreeModel myModel;
protected InspectionTreeNode(Object userObject) {
super(userObject);
protected InspectionTreeNode(@NotNull InspectionTreeModel model) {
myModel = model;
}
protected boolean doesNeedInternProblemLevels() {
@@ -82,7 +79,7 @@ public abstract class InspectionTreeNode extends DefaultMutableTreeNode {
InspectionTreeNode current = this;
while (current != null) {
current.myProblemLevels.drop();
current = (InspectionTreeNode)current.getParent();
current = current.getParent();
}
}
@@ -91,9 +88,7 @@ public abstract class InspectionTreeNode extends DefaultMutableTreeNode {
}
protected void visitProblemSeverities(@NotNull TObjectIntHashMap<HighlightDisplayLevel> counter) {
Enumeration enumeration = children();
while (enumeration.hasMoreElements()) {
InspectionTreeNode child = (InspectionTreeNode)enumeration.nextElement();
for (InspectionTreeNode child : getChildren()) {
for (LevelAndCount levelAndCount : child.getProblemLevels()) {
if (!counter.adjustValue(levelAndCount.getLevel(), levelAndCount.getCount())) {
counter.put(levelAndCount.getLevel(), levelAndCount.getCount());
@@ -102,29 +97,19 @@ public abstract class InspectionTreeNode extends DefaultMutableTreeNode {
}
}
public int getProblemCount(boolean allowSuppressed) {
int sum = 0;
Enumeration enumeration = children();
while (enumeration.hasMoreElements()) {
InspectionTreeNode child = (InspectionTreeNode)enumeration.nextElement();
sum += child.getProblemCount(allowSuppressed);
}
return sum;
}
public boolean isValid() {
return true;
}
public boolean isExcluded() {
Enumeration enumeration = children();
while (enumeration.hasMoreElements()) {
InspectionTreeNode child = (InspectionTreeNode)enumeration.nextElement();
List<? extends InspectionTreeNode> children = getChildren();
for (InspectionTreeNode child : children) {
if (!child.isExcluded()) {
return false;
}
}
return getChildCount() != 0;
return !children.isEmpty() ;
}
public boolean appearsBold() {
@@ -137,85 +122,27 @@ public abstract class InspectionTreeNode extends DefaultMutableTreeNode {
}
public void excludeElement() {
Enumeration enumeration = children();
while (enumeration.hasMoreElements()) {
InspectionTreeNode child = (InspectionTreeNode)enumeration.nextElement();
for (InspectionTreeNode child : getChildren()) {
child.excludeElement();
}
dropProblemCountCaches();
}
public void amnestyElement() {
Enumeration enumeration = children();
while (enumeration.hasMoreElements()) {
InspectionTreeNode child = (InspectionTreeNode)enumeration.nextElement();
for (InspectionTreeNode child : getChildren()) {
child.amnestyElement();
}
dropProblemCountCaches();
}
public InspectionTreeNode insertByOrder(InspectionTreeNode child, boolean allowDuplication) {
return ReadAction.compute(() -> {
if (!allowDuplication) {
int index = getIndex(child);
if (index != -1) {
return (InspectionTreeNode)getChildAt(index);
}
}
int index = TreeUtil.indexedBinarySearch(this, child, InspectionResultsViewComparator.getInstance());
if (!allowDuplication && index >= 0) {
return (InspectionTreeNode)getChildAt(index);
}
insert(child, Math.abs(index + 1));
return child;
});
}
@Override
public void add(MutableTreeNode newChild) {
super.add(newChild);
if (myUpdater != null) {
((InspectionTreeNode)newChild).propagateUpdater(myUpdater);
dropProblemCountCaches();
myUpdater.updateWithPreviewPanel();
}
}
@Override
public void insert(MutableTreeNode newChild, int childIndex) {
super.insert(newChild, childIndex);
if (myUpdater != null) {
((InspectionTreeNode)newChild).propagateUpdater(myUpdater);
dropProblemCountCaches();
myUpdater.updateWithPreviewPanel();
}
}
@Override
public void remove(int childIndex) {
super.remove(childIndex);
myModel.removeChild(this, childIndex);
dropProblemCountCaches();
}
protected void nodeAddedToTree() {
}
private void propagateUpdater(InspectionTreeUpdater updater) {
if (myUpdater != null) return;
myUpdater = updater;
Enumeration enumeration = children();
while (enumeration.hasMoreElements()) {
InspectionTreeNode child = (InspectionTreeNode)enumeration.nextElement();
child.propagateUpdater(updater);
child.nodeAddedToTree();
}
}
public RefEntity getContainingFileLocalEntity() {
final Enumeration children = children();
RefEntity current = null;
while (children.hasMoreElements()) {
InspectionTreeNode child = (InspectionTreeNode)children.nextElement();
for (InspectionTreeNode child : getChildren()) {
final RefEntity entity = child.getContainingFileLocalEntity();
if (entity == null || current != null) {
return null;
@@ -226,12 +153,53 @@ public abstract class InspectionTreeNode extends DefaultMutableTreeNode {
}
@Override
public synchronized TreeNode getParent() {
return super.getParent();
public boolean isLeaf() {
return getChildren().isEmpty();
}
public abstract String getPresentableText();
@NotNull
public List<? extends InspectionTreeNode> getChildren() {
return ObjectUtils.notNull(myModel.getChildren(this), Collections.emptyList());
}
@Override
public synchronized void setParent(MutableTreeNode newParent) {
super.setParent(newParent);
public InspectionTreeNode getParent() {
return myModel.getParent(this);
}
@Override
public int getChildCount() {
return getChildren().size();
}
@Override
public InspectionTreeNode getChildAt(int idx) {
return getChildren().get(idx);
}
public void removeAllChildren() {
myModel.removeChildren(this);
}
@Override
public int getIndex(TreeNode node) {
return myModel.getIndexOfChild(this, node);
}
@Override
public boolean getAllowsChildren() {
return true;
}
@Override
public Enumeration children() {
return Collections.enumeration(getChildren());
}
@Override
public String toString() {
return getPresentableText();
}
}
@@ -1,128 +0,0 @@
/*
* Copyright 2000-2016 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;
import com.intellij.util.ui.tree.TreeUtil;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
/**
* @author Dmitry Batkovich
*/
public class InspectionTreeState {
private final Set<Object> myExpandedUserObjects = new HashSet<>();
private InspectionTreeSelectionPath mySelectionPath;
public Set<Object> getExpandedUserObjects() {
return myExpandedUserObjects;
}
public void setSelectionPath(TreePath selectionPath) {
mySelectionPath = new InspectionTreeSelectionPath(selectionPath);
}
public void restoreExpansionAndSelection(InspectionTree tree, boolean treeNodeMightChange) {
restoreExpansionStatus((InspectionTreeNode)tree.getModel().getRoot(), tree);
if (mySelectionPath != null) {
mySelectionPath.restore(tree, treeNodeMightChange);
} else {
TreeUtil.selectFirstNode(tree);
}
}
private void restoreExpansionStatus(InspectionTreeNode node, InspectionTree tree) {
if (getExpandedUserObjects().contains(node.getUserObject())) {
//sortChildren(node);
TreeNode[] pathToNode = node.getPath();
tree.expandPath(new TreePath(pathToNode));
Enumeration children = node.children();
while (children.hasMoreElements()) {
InspectionTreeNode childNode = (InspectionTreeNode)children.nextElement();
restoreExpansionStatus(childNode, tree);
}
}
}
private static class InspectionTreeSelectionPath {
private final Object[] myPath;
private final int[] myIndices;
InspectionTreeSelectionPath(TreePath path) {
myPath = path.getPath();
myIndices = new int[myPath.length];
for (int i = 0; i < myPath.length - 1; i++) {
InspectionTreeNode node = (InspectionTreeNode)myPath[i];
myIndices[i + 1] = getChildIndex(node, (InspectionTreeNode)myPath[i + 1]);
}
}
private static int getChildIndex(InspectionTreeNode node, InspectionTreeNode child) {
int idx = 0;
Enumeration children = node.children();
while (children.hasMoreElements()) {
InspectionTreeNode ch = (InspectionTreeNode)children.nextElement();
if (ch == child) break;
idx++;
}
return idx;
}
public void restore(InspectionTree tree, boolean treeNodeMightChange) {
tree.getSelectionModel().removeSelectionPaths(tree.getSelectionModel().getSelectionPaths());
TreeUtil.selectPath(tree, restorePath(tree, treeNodeMightChange));
}
private TreePath restorePath(InspectionTree tree, boolean treeNodeMightChange) {
ArrayList<Object> newPath = new ArrayList<>();
newPath.add(tree.getModel().getRoot());
restorePath(newPath, 1, treeNodeMightChange);
return new TreePath(newPath.toArray());
}
private void restorePath(ArrayList<Object> newPath, int idx, boolean treeNodeMightChange) {
if (idx >= myPath.length) return;
InspectionTreeNode oldNode = (InspectionTreeNode)myPath[idx];
InspectionTreeNode newRoot = (InspectionTreeNode)newPath.get(idx - 1);
Enumeration children = newRoot.children();
while (children.hasMoreElements()) {
InspectionTreeNode child = (InspectionTreeNode)children.nextElement();
if (treeNodeMightChange ? InspectionResultsViewComparator.getInstance().areEqual(child, oldNode) : child == oldNode) {
newPath.add(child);
restorePath(newPath, idx + 1, treeNodeMightChange);
return;
}
}
// Exactly same element not found. Trying to select somewhat near.
int count = newRoot.getChildCount();
if (count > 0) {
if (myIndices[idx] < count) {
newPath.add(newRoot.getChildAt(myIndices[idx]));
}
else {
newPath.add(newRoot.getChildAt(count - 1));
}
}
}
}
}
@@ -1,64 +0,0 @@
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInspection.ui;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.util.ui.update.MergingUpdateQueue;
import com.intellij.util.ui.update.Update;
import javax.swing.tree.DefaultTreeModel;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* @author Dmitry Batkovich
*/
public class InspectionTreeUpdater {
private final MergingUpdateQueue myUpdateQueue;
private final InspectionResultsView myView;
private final AtomicBoolean myDoUpdatePreviewPanel = new AtomicBoolean(false);
public InspectionTreeUpdater(InspectionResultsView view) {
myView = view;
myUpdateQueue = new MergingUpdateQueue("InspectionView", 100, true, view, view);
myUpdateQueue.setPassThrough(false);
}
public void updateWithPreviewPanel() {
myDoUpdatePreviewPanel.set(true);
update(false);
}
public void update(boolean force) {
if (ApplicationManager.getApplication().isDispatchThread() && !force) {
return;
}
myUpdateQueue.queue(new MyTreeUpdate());
}
private class MyTreeUpdate extends Update {
MyTreeUpdate() {
super("inspection.view.update");
}
@Override
public void run() {
if (myView.isDisposed()) return;
final InspectionTree tree = myView.getTree();
try {
tree.setQueueUpdate(true);
((DefaultTreeModel)tree.getModel()).reload();
tree.restoreExpansionAndSelection(true);
myView.openRightPanelIfNeed();
if (myDoUpdatePreviewPanel.compareAndSet(true, false)) {
myView.updateRightPanelLoading();
}
} finally {
tree.setQueueUpdate(false);
}
}
@Override
public boolean canEat(Update update) {
return true;
}
}
}
@@ -5,7 +5,6 @@ package com.intellij.codeInspection.ui;
import com.intellij.codeInspection.reference.RefElement;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.util.Alarm;
@@ -17,7 +16,6 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -121,7 +119,7 @@ class InspectionViewChangeAdapter extends PsiTreeChangeAdapter {
if (pointer != null) {
VirtualFile vFile = pointer.getVirtualFile();
if (vFile == null || !vFile.isValid()) {
dropNodeCache((SuppressableInspectionTreeNode)node);
((SuppressableInspectionTreeNode)node).dropCache();
if (!needUpdateUI[0]) {
needUpdateUI[0] = true;
}
@@ -154,7 +152,7 @@ class InspectionViewChangeAdapter extends PsiTreeChangeAdapter {
VirtualFile vFile = pointer.getVirtualFile();
if (filesToCheck.contains(vFile)) {
unPresentFiles.remove(vFile);
dropNodeCache((SuppressableInspectionTreeNode)node);
((SuppressableInspectionTreeNode)node).dropCache();
if (!needUpdateUI[0]) {
needUpdateUI[0] = true;
}
@@ -168,9 +166,7 @@ class InspectionViewChangeAdapter extends PsiTreeChangeAdapter {
nodeProcessor = CompositeProcessor.combine(fileCheckProcessor, nodeProcessor);
}
synchronized (myView.getTreeStructureUpdateLock()) {
processNodesIfNeed(myView.getTree().getRoot(), Objects.requireNonNull(nodeProcessor));
}
myView.getTree().getInspectionTreeModel().traverse(myView.getTree().getInspectionTreeModel().getRoot(), nodeProcessor);
if (!unPresentFiles.isEmpty()) {
myUnPresentEditedFiles.addAll(unPresentFiles);
@@ -178,24 +174,12 @@ class InspectionViewChangeAdapter extends PsiTreeChangeAdapter {
if (needUpdateUI[0] && !myAlarm.isDisposed()) {
myAlarm.cancelAllRequests();
myAlarm.addRequest(() -> myView.resetTree(), 100, ModalityState.NON_MODAL);
//TODO replace with more accurate
myAlarm.addRequest(() -> myView.getTree().getInspectionTreeModel().reload(), 100, ModalityState.NON_MODAL);
}
}, 200);
}
private static void dropNodeCache(SuppressableInspectionTreeNode node) {
ReadAction.run(() -> node.dropCache());
}
private static void processNodesIfNeed(InspectionTreeNode node, Processor<? super InspectionTreeNode> processor) {
if (processor.process(node)) {
final int count = node.getChildCount();
for (int i = 0; i < count; i++) {
processNodesIfNeed((InspectionTreeNode)node.getChildAt(i), processor);
}
}
}
private static class CompositeProcessor<X> implements Processor<X> {
private final Processor<? super X> myFirstProcessor;
private boolean myFirstFinished;
@@ -207,7 +191,6 @@ class InspectionViewChangeAdapter extends PsiTreeChangeAdapter {
mySecondProcessor = secondProcessor;
}
@Override
public boolean process(X x) {
if (!myFirstFinished && !myFirstProcessor.process(x)) {
@@ -18,12 +18,9 @@ package com.intellij.codeInspection.ui;
import com.intellij.ui.components.JBLabel;
import com.intellij.ui.components.labels.LinkLabel;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.tree.TreeUtil;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeNode;
import java.awt.*;
/**
@@ -70,11 +67,11 @@ public class InspectionViewNavigationPanel extends JPanel implements InspectionT
myLinks.removeAll();
myLinks.add(Box.createVerticalStrut(JBUI.scale(10)));
for (int i = 0; i < currentChildrenCount; i++) {
final TreeNode child = myNode.getChildAt(i);
final LinkLabel link = new LinkLabel(child.toString(), null) {
final InspectionTreeNode child = myNode.getChildAt(i);
final LinkLabel link = new LinkLabel(child.getPresentableText(), null) {
@Override
public void doClick() {
TreeUtil.selectInTree((DefaultMutableTreeNode)child, true, myTree);
myTree.selectNode(child);
}
};
link.setBorder(JBUI.Borders.empty(1, 17, 3, 1));
@@ -34,6 +34,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -115,7 +116,7 @@ public class InspectionViewSuppressActionHolder {
if (!myElementLanguage.equals(that.myElementLanguage)) return false;
if (!myFileBaseLanguage.equals(that.myFileBaseLanguage)) return false;
if (!myFileLanguages.equals(that.myFileLanguages)) return false;
if (myInjectionDescriptor != null ? !myInjectionDescriptor.equals(that.myInjectionDescriptor) : that.myInjectionDescriptor != null) {
if (!Objects.equals(myInjectionDescriptor, that.myInjectionDescriptor)) {
return false;
}
@@ -13,6 +13,7 @@ import com.intellij.codeInspection.ex.InspectionToolWrapper;
import com.intellij.codeInspection.reference.RefElement;
import com.intellij.codeInspection.reference.RefEntity;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.profile.codeInspection.InspectionProfileManager;
import com.intellij.psi.PsiElement;
@@ -35,15 +36,17 @@ public class ProblemDescriptionNode extends SuppressableInspectionTreeNode {
public ProblemDescriptionNode(RefEntity element,
@NotNull CommonProblemDescriptor descriptor,
@NotNull InspectionToolPresentation presentation) {
this(element, descriptor, presentation, null);
@NotNull InspectionToolPresentation presentation,
@NotNull InspectionTreeModel model) {
this(element, descriptor, presentation, null, model);
}
protected ProblemDescriptionNode(@Nullable RefEntity element,
CommonProblemDescriptor descriptor,
@NotNull InspectionToolPresentation presentation,
@Nullable IntSupplier lineNumberCounter) {
super(descriptor, presentation);
@Nullable IntSupplier lineNumberCounter,
@NotNull InspectionTreeModel model) {
super(presentation, model);
myElement = element;
myDescriptor = descriptor;
myLevel = ObjectUtils.notNull(calculatePreciseLevel(element, descriptor, presentation), () -> {
@@ -111,7 +114,6 @@ public class ProblemDescriptionNode extends SuppressableInspectionTreeNode {
return myElement;
}
@Override
@Nullable
public CommonProblemDescriptor getDescriptor() {
return myDescriptor;
@@ -180,4 +182,17 @@ public class ProblemDescriptionNode extends SuppressableInspectionTreeNode {
final String text = super.getTailText();
return text == null ? "" : text;
}
@NotNull
@Override
public Pair<PsiElement, CommonProblemDescriptor> getSuppressContent() {
RefEntity refElement = getElement();
CommonProblemDescriptor descriptor = getDescriptor();
PsiElement element = descriptor instanceof ProblemDescriptor
? ((ProblemDescriptor)descriptor).getPsiElement()
: refElement instanceof RefElement
? ((RefElement)refElement).getPsiElement()
: null;
return Pair.create(element, descriptor);
}
}
@@ -70,10 +70,16 @@ public class QuickFixPreviewPanelFactory {
setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
boolean hasComponents = false;
final int actualProblemCount = myView.getTree().getContext().getPresentation(myWrapper).getProblemsCount(myView.getTree());
if (actualProblemCount > 1 || (actualProblemCount == 1 && multipleDescriptors)) {
add(getLabel(actualProblemCount));
hasComponents = true;
InspectionTree tree = myView.getTree();
InspectionToolPresentation presentation = tree.getContext().getPresentation(myWrapper);
final boolean showProblemCount = presentation.showProblemCount();
if (showProblemCount) {
final int actualProblemCount = tree.getSelectedProblemCount();
if (actualProblemCount > 1 || (actualProblemCount == 1 && multipleDescriptors)) {
add(getLabel(actualProblemCount));
hasComponents = true;
}
}
final DefaultActionGroup actions = new DefaultActionGroup();
@@ -197,7 +203,7 @@ public class QuickFixPreviewPanelFactory {
setBorder(JBUI.Borders.empty(16, 9, 13, 0));
AsyncProcessIcon waitingIcon = new AsyncProcessIcon("Inspection preview panel updating...");
Disposer.register(this, waitingIcon);
myWaitingLabel = getLabel(myView.getTree().getSelectedProblemCount(false));
myWaitingLabel = getLabel(myView.getTree() .getSelectedProblemCount());
add(myWaitingLabel);
add(waitingIcon);
}
@@ -207,7 +213,7 @@ public class QuickFixPreviewPanelFactory {
if (myWaitingLabel != null) {
myWaitingLabel.clear();
final InspectionTree tree = myView.getTree();
appendTextToLabel(myWaitingLabel, tree.getSelectedProblemCount(false));
appendTextToLabel(myWaitingLabel, tree.getSelectedProblemCount());
}
}
@@ -225,7 +231,7 @@ public class QuickFixPreviewPanelFactory {
private static SimpleColoredComponent getLabel(int problemsCount) {
SimpleColoredComponent label = new SimpleColoredComponent();
appendTextToLabel(label, problemsCount);
label.setBorder(JBUI.Borders.empty(0, 0, 0, 2));
label.setBorder(JBUI.Borders.emptyRight(2));
return label;
}
@@ -9,24 +9,24 @@ import com.intellij.codeInspection.reference.RefDirectory;
import com.intellij.codeInspection.reference.RefElement;
import com.intellij.codeInspection.reference.RefEntity;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiElement;
import gnu.trove.TObjectIntHashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeNode;
/**
* @author max
*/
public class RefElementNode extends SuppressableInspectionTreeNode {
private volatile boolean myHasDescriptorsUnder;
private volatile CommonProblemDescriptor mySingleDescriptor;
private final Icon myIcon;
public RefElementNode(@Nullable RefEntity userObject, @NotNull InspectionToolPresentation presentation) {
super(userObject, presentation);
final RefEntity refEntity = getElement();
@Nullable private final RefEntity myRefEntity;
public RefElementNode(@Nullable RefEntity refEntity, @NotNull InspectionToolPresentation presentation, InspectionTreeModel model) {
super(presentation, model);
myRefEntity = refEntity;
myIcon = refEntity == null ? null : refEntity.getIcon(false);
}
@@ -35,14 +35,10 @@ public class RefElementNode extends SuppressableInspectionTreeNode {
return getElement() != null && getPresentation().isSuppressed(getElement());
}
boolean hasDescriptorsUnder() {
return myHasDescriptorsUnder;
}
@Override
@Nullable
public RefEntity getElement() {
return (RefEntity)getUserObject();
return myRefEntity;
}
@Override
@@ -95,28 +91,6 @@ public class RefElementNode extends SuppressableInspectionTreeNode {
super.amnestyElement();
}
@Override
public void add(MutableTreeNode newChild) {
checkHasDescriptorUnder(newChild);
super.add(newChild);
}
@Override
public InspectionTreeNode insertByOrder(InspectionTreeNode child, boolean allowDuplication) {
checkHasDescriptorUnder(child);
return super.insertByOrder(child, allowDuplication);
}
public void setProblem(CommonProblemDescriptor descriptor) {
mySingleDescriptor = descriptor;
}
@Nullable
@Override
public CommonProblemDescriptor getDescriptor() {
return mySingleDescriptor;
}
@Override
public RefEntity getContainingFileLocalEntity() {
final RefEntity element = getElement();
@@ -156,16 +130,11 @@ public class RefElementNode extends SuppressableInspectionTreeNode {
return isLeaf() ? "" : null;
}
private void checkHasDescriptorUnder(MutableTreeNode newChild) {
if (myHasDescriptorsUnder) return;
if (newChild instanceof ProblemDescriptionNode ||
newChild instanceof RefElementNode && ((RefElementNode)newChild).hasDescriptorsUnder()) {
myHasDescriptorsUnder = true;
TreeNode parent = getParent();
while (parent instanceof RefElementNode) {
((RefElementNode)parent).myHasDescriptorsUnder = true;
parent = parent.getParent();
}
}
@NotNull
@Override
public Pair<PsiElement, CommonProblemDescriptor> getSuppressContent() {
RefEntity refElement = getElement();
PsiElement element = refElement instanceof RefElement ? ((RefElement)refElement).getPsiElement() : null;
return Pair.create(element, null);
}
}
@@ -4,11 +4,10 @@
package com.intellij.codeInspection.ui;
import com.intellij.codeInspection.CommonProblemDescriptor;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.codeInspection.SuppressIntentionAction;
import com.intellij.codeInspection.reference.RefElement;
import com.intellij.codeInspection.reference.RefEntity;
import com.intellij.concurrency.ConcurrentCollectionFactory;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiElement;
@@ -17,7 +16,6 @@ import com.intellij.util.containers.Interner;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.tree.TreeNode;
import java.util.Arrays;
import java.util.Collections;
import java.util.Set;
@@ -31,11 +29,20 @@ public abstract class SuppressableInspectionTreeNode extends InspectionTreeNode
private volatile Boolean myValid;
private volatile NodeState myPreviousState;
SuppressableInspectionTreeNode(Object userObject, @NotNull InspectionToolPresentation presentation) {
super(userObject);
SuppressableInspectionTreeNode(@NotNull InspectionToolPresentation presentation, @NotNull InspectionTreeModel model) {
super(model);
myPresentation = presentation;
}
void nodeAdded() {
dropProblemCountCaches();
ReadAction.run(() -> {
myPresentableName = calculatePresentableName();
myValid = calculateIsValid();
myAvailableSuppressActions = calculateAvailableSuppressActions();
});
}
@Override
protected boolean doesNeedInternProblemLevels() {
return true;
@@ -47,7 +54,7 @@ public abstract class SuppressableInspectionTreeNode extends InspectionTreeNode
}
public boolean canSuppress() {
return isLeaf();
return getChildren().isEmpty();
}
public abstract boolean isAlreadySuppressedFromView();
@@ -64,11 +71,6 @@ public abstract class SuppressableInspectionTreeNode extends InspectionTreeNode
return true;
}
@Override
public int getProblemCount(boolean allowSuppressed) {
return !isExcluded() && isValid() && !isQuickFixAppliedFromView() && (allowSuppressed || !isAlreadySuppressedFromView()) ? 1 : 0;
}
@NotNull
public synchronized Set<SuppressIntentionAction> getAvailableSuppressActions() {
Set<SuppressIntentionAction> actions = myAvailableSuppressActions;
@@ -86,9 +88,6 @@ public abstract class SuppressableInspectionTreeNode extends InspectionTreeNode
@Nullable
public abstract RefEntity getElement();
@Nullable
public abstract CommonProblemDescriptor getDescriptor();
@Override
public final synchronized boolean isValid() {
Boolean valid = myValid;
@@ -100,7 +99,7 @@ public abstract class SuppressableInspectionTreeNode extends InspectionTreeNode
}
@Override
public final synchronized String toString() {
public final synchronized String getPresentableText() {
String name = myPresentableName;
if (name == null) {
name = calculatePresentableName();
@@ -121,13 +120,6 @@ public abstract class SuppressableInspectionTreeNode extends InspectionTreeNode
return !isValid() ? "No longer valid" : null;
}
@Override
protected void nodeAddedToTree() {
myPresentableName = calculatePresentableName();
myValid = calculateIsValid();
myAvailableSuppressActions = calculateAvailableSuppressActions();
}
@NotNull
private Set<SuppressIntentionAction> calculateAvailableSuppressActions() {
return getElement() == null
@@ -136,16 +128,7 @@ public abstract class SuppressableInspectionTreeNode extends InspectionTreeNode
}
@NotNull
public final Pair<PsiElement, CommonProblemDescriptor> getSuppressContent() {
RefEntity refElement = getElement();
CommonProblemDescriptor descriptor = getDescriptor();
PsiElement element = descriptor instanceof ProblemDescriptor
? ((ProblemDescriptor)descriptor).getPsiElement()
: refElement instanceof RefElement
? ((RefElement)refElement).getPsiElement()
: null;
return Pair.create(element, descriptor);
}
public abstract Pair<PsiElement, CommonProblemDescriptor> getSuppressContent();
@NotNull
private Set<SuppressIntentionAction> calculateAvailableSuppressActions(@NotNull Project project) {
@@ -168,14 +151,19 @@ public abstract class SuppressableInspectionTreeNode extends InspectionTreeNode
protected abstract boolean calculateIsValid();
protected void dropCache() {
ReadAction.run(() -> doDropCache());
}
private void doDropCache() {
myProblemLevels.drop();
if (isQuickFixAppliedFromView() || isAlreadySuppressedFromView()) return;
// calculate all data on background thread
myValid = calculateIsValid();
myPresentableName = calculatePresentableName();
for (int i = 0; i < getChildCount(); i++) {
TreeNode child = getChildAt(i);
for (InspectionTreeNode child : getChildren()) {
if (child instanceof SuppressableInspectionTreeNode) {
((SuppressableInspectionTreeNode)child).dropCache();
((SuppressableInspectionTreeNode)child).doDropCache();
}
}
}
@@ -31,7 +31,6 @@ import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.JDOMUtil;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.CharsetToolkit;
import com.intellij.util.ui.tree.TreeUtil;
import gnu.trove.THashSet;
import org.jdom.Element;
import org.jdom.output.Format;
@@ -120,7 +119,7 @@ public class ExportHTMLAction extends AnAction implements DumbAware {
if (!outputDir.exists() && !outputDir.mkdirs()) {
throw new IOException("Cannot create \'" + outputDir + "\'");
}
final InspectionTreeNode root = myView.getTree().getRoot();
final InspectionTreeModel model = myView.getTree().getInspectionTreeModel();
final Exception[] ex = new Exception[1];
final Set<String> visitedTools = new THashSet<>();
@@ -128,7 +127,7 @@ public class ExportHTMLAction extends AnAction implements DumbAware {
Format format = JDOMUtil.createFormat("\n");
XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newInstance();
TreeUtil.treeNodeTraverser(root).traverse().processEach(node -> {
model.traverse(model.getRoot(), node -> {
if (node instanceof InspectionNode) {
InspectionNode toolNode = (InspectionNode)node;
if (toolNode.isExcluded()) return true;
@@ -66,6 +66,6 @@ public class InvokeQuickFixAction extends AnAction {
}
static boolean cantApplyFixes(InspectionResultsView view) {
return view.isUpdating() && !view.getTree().areDescriptorNodesSelected();
return !view.getTree().areDescriptorNodesSelected();
}
}
@@ -18,21 +18,16 @@ package com.intellij.ide.actions.exclusion;
import com.intellij.openapi.actionSystem.DataKey;
import org.jetbrains.annotations.NotNull;
import javax.swing.tree.MutableTreeNode;
/**
* @author Dmitry Batkovich
*/
public interface ExclusionHandler<T extends MutableTreeNode> {
public interface ExclusionHandler<Node> {
DataKey<ExclusionHandler> EXCLUSION_HANDLER = DataKey.create("tree.exclusion.handler");
boolean isNodeExclusionAvailable(@NotNull T node);
boolean isNodeExclusionAvailable(@NotNull Node node);
boolean isNodeExcluded(@NotNull T node);
boolean isNodeExcluded(@NotNull Node node);
void excludeNode(@NotNull T node);
void excludeNode(@NotNull Node node);
void includeNode(@NotNull T node);
void includeNode(@NotNull Node node);
boolean isActionEnabled(boolean isExcludeAction);
@@ -24,14 +24,11 @@ import com.intellij.util.ui.tree.TreeUtil;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import java.awt.*;
/**
* @author Dmitry Batkovich.
*/
abstract class TreeNodeExclusionAction<T extends MutableTreeNode> extends AnAction {
abstract class TreeNodeExclusionAction<T extends TreeNode> extends AnAction {
private final static Logger LOG = Logger.getInstance(TreeNodeExclusionAction.class);
private final boolean myIsExclude;
@@ -65,7 +62,7 @@ abstract class TreeNodeExclusionAction<T extends MutableTreeNode> extends AnActi
final T node = (T)path.getLastPathComponent();
TreeUtil.traverse(node, n -> {
if (!exclusionProcessor.isNodeExclusionAvailable((T)n)) return true;
final Boolean isNodeExcluded = exclusionProcessor.isNodeExcluded((T)n);
boolean isNodeExcluded = exclusionProcessor.isNodeExcluded((T)n);
if (myIsExclude != isNodeExcluded) {
isEnabled[0] = true;
return false;
@@ -95,7 +92,7 @@ abstract class TreeNodeExclusionAction<T extends MutableTreeNode> extends AnActi
final T node = (T)path.getLastPathComponent();
TreeUtil.traverse(node, n -> {
if (!exclusionProcessor.isNodeExclusionAvailable((T)n)) return true;
if (Boolean.valueOf(myIsExclude) != exclusionProcessor.isNodeExcluded((T)n)) {
if (myIsExclude != exclusionProcessor.isNodeExcluded((T)n)) {
if (myIsExclude) {
exclusionProcessor.excludeNode(node);
} else {
@@ -22,10 +22,10 @@ import com.intellij.openapi.wm.ToolWindowId;
import org.fest.swing.core.Robot;
import org.fest.swing.edt.GuiQuery;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import static org.fest.swing.edt.GuiActionRunner.execute;
@@ -47,10 +47,10 @@ public class InspectionsFixture extends ToolWindowFixture {
return execute(new GuiQuery<String>() {
@Override
@Nullable
protected String executeInEDT() throws Throwable {
@NotNull
protected String executeInEDT() {
StringBuilder sb = new StringBuilder();
InspectionsFixture.describe(myTree.getRoot(), sb, 0);
describe(myTree.getInspectionTreeModel().getRoot(), sb, 0);
return sb.toString();
}
});
@@ -65,11 +65,8 @@ public class InspectionsFixture extends ToolWindowFixture {
// The exact order of the results sometimes varies so sort the children alphabetically
// instead to ensure stable test output
List<InspectionTreeNode> children = new ArrayList<>(node.getChildCount());
for (int i = 0, n = node.getChildCount(); i < n; i++) {
children.add((InspectionTreeNode)node.getChildAt(i));
}
Collections.sort(children, (node1, node2) -> node1.toString().compareTo(node2.toString()));
List<InspectionTreeNode> children = new ArrayList<>(node.getChildren());
Collections.sort(children, Comparator.comparing(Object::toString));
for (InspectionTreeNode child : children) {
describe(child, sb, depth + 1);
}