diff --git a/platform/usageView/src/com/intellij/usages/UsageInfo2UsageAdapter.java b/platform/usageView/src/com/intellij/usages/UsageInfo2UsageAdapter.java index 72a5becca822..c8079d894dbd 100644 --- a/platform/usageView/src/com/intellij/usages/UsageInfo2UsageAdapter.java +++ b/platform/usageView/src/com/intellij/usages/UsageInfo2UsageAdapter.java @@ -432,6 +432,22 @@ public class UsageInfo2UsageAdapter implements UsageInModule, @Override @NotNull public TextChunk[] getText() { + return doUpdateCachedText(); + } + + @Nullable + @Override + public TextChunk[] getCachedText() { + return SoftReference.dereference(myTextChunks); + } + + @Override + public void updateCachedText() { + doUpdateCachedText(); + } + + @NotNull + private TextChunk[] doUpdateCachedText() { TextChunk[] chunks = SoftReference.dereference(myTextChunks); final long currentModificationStamp = getCurrentModificationStamp(); boolean isModified = currentModificationStamp != myModificationStamp; diff --git a/platform/usageView/src/com/intellij/usages/UsagePresentation.java b/platform/usageView/src/com/intellij/usages/UsagePresentation.java index 5cf992d43d49..026806292f68 100644 --- a/platform/usageView/src/com/intellij/usages/UsagePresentation.java +++ b/platform/usageView/src/com/intellij/usages/UsagePresentation.java @@ -16,6 +16,7 @@ package com.intellij.usages; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; @@ -26,6 +27,20 @@ public interface UsagePresentation { @NotNull TextChunk[] getText(); + /** + * If the implementation caches or lazy-loades the text chunks internally, this method gives it a chance to avoid + * re-calculating it synchronously on EDT and return the possibly obsolete data. + * + * The component using this presentation might call {@link UsagePresentation#updateCachedText()} in a background + * thread and then use {@link UsagePresentation#getCachedText()} to draw the text. + */ + @Nullable + default TextChunk[] getCachedText() { + return getText(); + } + + default void updateCachedText() {} + @NotNull String getPlainText(); diff --git a/platform/usageView/src/com/intellij/usages/impl/Node.java b/platform/usageView/src/com/intellij/usages/impl/Node.java index 7ee5a1c5bfb5..5bf2fc9a61f5 100644 --- a/platform/usageView/src/com/intellij/usages/impl/Node.java +++ b/platform/usageView/src/com/intellij/usages/impl/Node.java @@ -67,6 +67,8 @@ public abstract class Node extends DefaultMutableTreeNode { protected abstract boolean isDataReadOnly(); protected abstract boolean isDataExcluded(); + protected void updateCachedPresentation() {} + @NotNull protected abstract String getText(@NotNull UsageView view); @@ -98,6 +100,7 @@ public abstract class Node extends DefaultMutableTreeNode { boolean isDataValid = isDataValid(); boolean isReadOnly = isDataReadOnly(); String text = getText(view); + updateCachedPresentation(); doUpdate(edtNodeChangedQueue, isDataValid, isReadOnly, text); } diff --git a/platform/usageView/src/com/intellij/usages/impl/UsageNode.java b/platform/usageView/src/com/intellij/usages/impl/UsageNode.java index fb1da704d410..2af7dc81ece2 100644 --- a/platform/usageView/src/com/intellij/usages/impl/UsageNode.java +++ b/platform/usageView/src/com/intellij/usages/impl/UsageNode.java @@ -93,4 +93,9 @@ public class UsageNode extends Node implements Comparable, Navigatabl protected String getText(@NotNull final UsageView view) { return getUsage().getPresentation().getPlainText(); } + + @Override + protected void updateCachedPresentation() { + getUsage().getPresentation().updateCachedText(); + } } diff --git a/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java b/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java index 561742c3ce35..d5d7b9fbfd08 100644 --- a/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java +++ b/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java @@ -1419,7 +1419,7 @@ public class UsageViewImpl implements UsageViewEx { } } - private void updateLater() { + void updateLater() { myUpdateAlarm.cancelAllRequests(); myUpdateAlarm.addRequest(() -> { if (myProject.isDisposed()) return; diff --git a/platform/usageView/src/com/intellij/usages/impl/UsageViewTreeCellRenderer.java b/platform/usageView/src/com/intellij/usages/impl/UsageViewTreeCellRenderer.java index f20c081de2c8..610672ff656a 100644 --- a/platform/usageView/src/com/intellij/usages/impl/UsageViewTreeCellRenderer.java +++ b/platform/usageView/src/com/intellij/usages/impl/UsageViewTreeCellRenderer.java @@ -49,14 +49,15 @@ class UsageViewTreeCellRenderer extends ColoredTreeCellRenderer { private static final SimpleTextAttributes ourNumberOfUsagesAttribute = SimpleTextAttributes.fromTextAttributes(ourColorsScheme.getAttributes(UsageTreeColors.NUMBER_OF_USAGES)); private static final SimpleTextAttributes ourInvalidAttributesDarcula = new SimpleTextAttributes(null, DarculaColors.RED, null, ourInvalidAttributes.getStyle()); private static final Insets STANDARD_IPAD_NOWIFI = new Insets(1, 2, 1, 2); + private static final TextChunk[] ourLoadingText = {new TextChunk(SimpleTextAttributes.GRAY_ATTRIBUTES.toTextAttributes(), "loading...")}; private boolean myRowBoundsCalled; private final UsageViewPresentation myPresentation; - private final UsageView myView; + private final UsageViewImpl myView; private boolean myCalculated; private int myRowHeight = AllIcons.Nodes.AbstractClass.getIconHeight()+2; - UsageViewTreeCellRenderer(@NotNull UsageView view) { + UsageViewTreeCellRenderer(@NotNull UsageViewImpl view) { myView = view; myPresentation = view.getPresentation(); } @@ -150,7 +151,16 @@ class UsageViewTreeCellRenderer extends ColoredTreeCellRenderer { } if (node.isValid()) { - TextChunk[] text = node.getUsage().getPresentation().getText(); + TextChunk[] text = node.getUsage().getPresentation().getCachedText(); + if (text == null) { + // either: + // 1. the node was never updated yet + // 2. the usage presentation have dropped the cached text by itself + // (i.e. it was stored by a soft reference and was gc-ed). + // In either case, `myView.updateLater()` will eventually re-update the visible nodes. + text = ourLoadingText; + myView.updateLater(); + } for (int i = 0; i < text.length; i++) { TextChunk textChunk = text[i]; SimpleTextAttributes simples = textChunk.getSimpleAttributesIgnoreBackground();