From 4b442cc6c96d252884705751b97bde2236ae808d Mon Sep 17 00:00:00 2001 From: Sergei Tachenov Date: Wed, 19 Nov 2025 11:14:17 +0200 Subject: [PATCH] [UI, Find Usages] IJPL-162332 Cache canNavigate for usages Let's try to cache the values to implement canNavigate so it can be safely invoked on the EDT. This should fix the slow ops in next / prev occurrence. There's a risk of performance regressions, though, because now this value will be precomputed for all nodes, and there can easily be thousands of them. GitOrigin-RevId: 068f3f99c600024aea1aab9b5eba7e140ca951e3 --- platform/usageView-impl/api-dump.txt | 4 ++-- .../src/com/intellij/usages/impl/GroupNode.java | 2 +- .../src/com/intellij/usages/impl/Node.java | 16 +++++++++++++++- .../src/com/intellij/usages/impl/UsageNode.java | 4 ++-- .../intellij/usages/impl/UsageTargetNode.java | 5 +++++ .../com/intellij/usages/impl/UsageViewImpl.java | 11 ++++++----- .../usages/impl/UsageViewTreeModelBuilder.java | 5 +++++ 7 files changed, 36 insertions(+), 11 deletions(-) diff --git a/platform/usageView-impl/api-dump.txt b/platform/usageView-impl/api-dump.txt index 349051f6cc32..ab337dea33f8 100644 --- a/platform/usageView-impl/api-dump.txt +++ b/platform/usageView-impl/api-dump.txt @@ -5,7 +5,7 @@ c:com.intellij.usages.impl.GroupNode - com.intellij.pom.Navigatable - java.lang.Comparable - javax.swing.tree.DefaultMutableTreeNode -- canNavigate():Z +- p:canDataNavigate(Z):Z - canNavigateToSource():Z - compareTo(com.intellij.usages.impl.GroupNode):I - getGroup():com.intellij.usages.UsageGroup @@ -52,7 +52,7 @@ c:com.intellij.usages.impl.UsageNode - java.lang.Comparable - javax.swing.tree.DefaultMutableTreeNode - (com.intellij.usages.Usage):V -- canNavigate():Z +- p:canDataNavigate(Z):Z - canNavigateToSource():Z - compareTo(com.intellij.usages.impl.UsageNode):I - p:getNodeText():java.lang.String diff --git a/platform/usageView-impl/src/com/intellij/usages/impl/GroupNode.java b/platform/usageView-impl/src/com/intellij/usages/impl/GroupNode.java index 18023f056740..357bd31061a0 100644 --- a/platform/usageView-impl/src/com/intellij/usages/impl/GroupNode.java +++ b/platform/usageView-impl/src/com/intellij/usages/impl/GroupNode.java @@ -342,7 +342,7 @@ public class GroupNode extends Node implements Navigatable, Comparable edtFireTreeNodesChangedQueue) { boolean cachedValid = isValid(); boolean cachedReadOnly = isFlagSet(CACHED_READ_ONLY_MASK); + boolean cachedCanNavigate = canNavigate(); if (isDataValid != cachedValid || isReadOnly != cachedReadOnly || myCachedTextHash != text.hashCode() || + canNavigate != cachedCanNavigate || isFlagSet(FORCE_UPDATE_REQUESTED_MASK)) { setFlag(CACHED_INVALID_MASK, !isDataValid); setFlag(CACHED_READ_ONLY_MASK, isReadOnly); + setFlag(CACHED_CAN_NAVIGATE, canNavigate); setFlag(FORCE_UPDATE_REQUESTED_MASK, false); myCachedTextHash = text.hashCode(); diff --git a/platform/usageView-impl/src/com/intellij/usages/impl/UsageNode.java b/platform/usageView-impl/src/com/intellij/usages/impl/UsageNode.java index 01b314a171a9..7c0d17999b17 100644 --- a/platform/usageView-impl/src/com/intellij/usages/impl/UsageNode.java +++ b/platform/usageView-impl/src/com/intellij/usages/impl/UsageNode.java @@ -39,8 +39,8 @@ public class UsageNode extends Node implements Comparable, Navigatabl } @Override - public boolean canNavigate() { - return getUsage().isValid() && getUsage().canNavigate(); + protected boolean canDataNavigate(boolean isDataValid) { + return isDataValid && getUsage().canNavigate(); } @Override diff --git a/platform/usageView-impl/src/com/intellij/usages/impl/UsageTargetNode.java b/platform/usageView-impl/src/com/intellij/usages/impl/UsageTargetNode.java index 50fcc96206cb..0ac4510da9c3 100644 --- a/platform/usageView-impl/src/com/intellij/usages/impl/UsageTargetNode.java +++ b/platform/usageView-impl/src/com/intellij/usages/impl/UsageTargetNode.java @@ -25,6 +25,11 @@ class UsageTargetNode extends Node { return false; } + @Override + protected boolean canDataNavigate(boolean isDataValid) { + return false; + } + @Override protected @NotNull String getNodeText() { return ObjectUtils.notNull(getTarget().getPresentation().getPresentableText(), ""); diff --git a/platform/usageView-impl/src/com/intellij/usages/impl/UsageViewImpl.java b/platform/usageView-impl/src/com/intellij/usages/impl/UsageViewImpl.java index b9f5028781e3..f63aa751bc72 100644 --- a/platform/usageView-impl/src/com/intellij/usages/impl/UsageViewImpl.java +++ b/platform/usageView-impl/src/com/intellij/usages/impl/UsageViewImpl.java @@ -1902,8 +1902,11 @@ public class UsageViewImpl implements UsageViewEx { } private @Nullable Navigatable getNavigatableForNode(@NotNull DefaultMutableTreeNode node, boolean allowRequestFocus) { - Object userObject = node.getUserObject(); - if (userObject instanceof Navigatable navigatable) { + Object maybeNavigatable = node; + if (!(maybeNavigatable instanceof Navigatable)) { + maybeNavigatable = node.getUserObject(); + } + if (maybeNavigatable instanceof Navigatable navigatable) { return navigatable.canNavigate() ? new Navigatable() { @Override public void navigate(boolean requestFocus) { @@ -1939,9 +1942,7 @@ public class UsageViewImpl implements UsageViewEx { protected Navigatable createDescriptorForNode(@NotNull DefaultMutableTreeNode node) { if (node.getChildCount() > 0) return null; if (node instanceof Node n && n.isExcluded()) return null; - try (AccessToken ignore = SlowOperations.knownIssue("IJPL-162332")) { - return getNavigatableForNode(node, !myPresentation.isReplaceMode()); - } + return getNavigatableForNode(node, !myPresentation.isReplaceMode()); } @Override diff --git a/platform/usageView-impl/src/com/intellij/usages/impl/UsageViewTreeModelBuilder.java b/platform/usageView-impl/src/com/intellij/usages/impl/UsageViewTreeModelBuilder.java index 6a411bec9a53..8d6ffe1510d6 100644 --- a/platform/usageView-impl/src/com/intellij/usages/impl/UsageViewTreeModelBuilder.java +++ b/platform/usageView-impl/src/com/intellij/usages/impl/UsageViewTreeModelBuilder.java @@ -53,6 +53,11 @@ final class UsageViewTreeModelBuilder extends DefaultTreeModel { return false; } + @Override + protected boolean canDataNavigate(boolean isDataValid) { + return false; + } + @Override protected @NotNull String getNodeText() { return getUserObject().toString();