[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
This commit is contained in:
Sergei Tachenov
2025-11-19 18:04:28 +00:00
committed by intellij-monorepo-bot
parent 8a585321a9
commit 4b442cc6c9
7 changed files with 36 additions and 11 deletions
+2 -2
View File
@@ -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
- <init>(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
@@ -342,7 +342,7 @@ public class GroupNode extends Node implements Navigatable, Comparable<GroupNode
}
@Override
public boolean canNavigate() {
protected boolean canDataNavigate(boolean isDataValid) {
return getGroup() != null && getGroup().canNavigate();
}
@@ -27,6 +27,7 @@ public abstract class Node extends DefaultMutableTreeNode {
static final byte EXCLUDED_MASK = 1 << 3;
private static final byte UPDATED_MASK = 1 << 4;
private static final byte FORCE_UPDATE_REQUESTED_MASK = 1 << 5;
private static final byte CACHED_CAN_NAVIGATE = 1 << 6;
@MagicConstant(intValues = {
CACHED_INVALID_MASK,
@@ -35,6 +36,7 @@ public abstract class Node extends DefaultMutableTreeNode {
EXCLUDED_MASK,
UPDATED_MASK,
FORCE_UPDATE_REQUESTED_MASK,
CACHED_CAN_NAVIGATE,
})
private @interface FlagConstant {
}
@@ -61,6 +63,8 @@ public abstract class Node extends DefaultMutableTreeNode {
protected abstract boolean isDataExcluded();
protected abstract boolean canDataNavigate(boolean isDataValid);
@ApiStatus.Internal
public @Nullable UsageNodePresentation getCachedPresentation() {
return null;
@@ -74,6 +78,10 @@ public abstract class Node extends DefaultMutableTreeNode {
return !isFlagSet(CACHED_INVALID_MASK);
}
public final boolean canNavigate() {
return isFlagSet(CACHED_CAN_NAVIGATE);
}
final boolean isReadOnly() {
boolean result;
boolean computed = isFlagSet(READ_ONLY_COMPUTED_MASK);
@@ -98,24 +106,30 @@ public abstract class Node extends DefaultMutableTreeNode {
ApplicationManager.getApplication().assertIsNonDispatchThread();
boolean isDataValid = isDataValid();
boolean isReadOnly = isDataReadOnly();
// pass isDataValid here to avoid recomputing it inside
boolean canNavigate = canDataNavigate(isDataValid);
String text = getNodeText();
updateCachedPresentation();
doUpdate(isDataValid, isReadOnly, text, edtFireTreeNodesChangedQueue);
doUpdate(isDataValid, isReadOnly, canNavigate, text, edtFireTreeNodesChangedQueue);
}
private synchronized void doUpdate(boolean isDataValid,
boolean isReadOnly,
boolean canNavigate,
@NotNull String text,
@NotNull Consumer<? super Node> 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();
@@ -39,8 +39,8 @@ public class UsageNode extends Node implements Comparable<UsageNode>, Navigatabl
}
@Override
public boolean canNavigate() {
return getUsage().isValid() && getUsage().canNavigate();
protected boolean canDataNavigate(boolean isDataValid) {
return isDataValid && getUsage().canNavigate();
}
@Override
@@ -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(), "");
@@ -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
@@ -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();