From b88d1e3042e603dfee030d46ba545e92e277adce Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Wed, 24 Jan 2018 19:00:29 +0300 Subject: [PATCH] execute node.update() in background because it could call smartPointer.isValid() which is ridiculously expensive in CLion --- .../usages/impl/UsageNodeTreeBuilderTest.java | 14 +- .../intellij/usages/impl/UsageViewTest.java | 48 ++++--- .../src/com/intellij/usages/impl/Node.java | 11 +- .../usages/impl/SearchForUsagesRunnable.java | 6 +- .../intellij/usages/impl/UsageViewImpl.java | 125 +++++++++++++----- .../usages/impl/UsageViewManagerImpl.java | 18 +-- 6 files changed, 158 insertions(+), 64 deletions(-) diff --git a/platform/platform-tests/testSrc/com/intellij/usages/impl/UsageNodeTreeBuilderTest.java b/platform/platform-tests/testSrc/com/intellij/usages/impl/UsageNodeTreeBuilderTest.java index 5db5276992d5..71a2d1149e4b 100644 --- a/platform/platform-tests/testSrc/com/intellij/usages/impl/UsageNodeTreeBuilderTest.java +++ b/platform/platform-tests/testSrc/com/intellij/usages/impl/UsageNodeTreeBuilderTest.java @@ -21,6 +21,9 @@ import com.intellij.openapi.extensions.ExtensionPoint; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.extensions.ExtensionsArea; import com.intellij.openapi.fileEditor.FileEditorLocation; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.io.FileUtil; @@ -112,13 +115,13 @@ public class UsageNodeTreeBuilderTest extends LightPlatformTestCase { UsageGroupingRuleProvider provider = new UsageGroupingRuleProvider() { @NotNull @Override - public UsageGroupingRule[] getActiveRules(Project project) { + public UsageGroupingRule[] getActiveRules(@NotNull Project project) { return rules; } @NotNull @Override - public AnAction[] createGroupingActions(UsageView view) { + public AnAction[] createGroupingActions(@NotNull UsageView view) { return AnAction.EMPTY_ARRAY; } }; @@ -130,6 +133,13 @@ public class UsageNodeTreeBuilderTest extends LightPlatformTestCase { usageView.appendUsage(usage); } UIUtil.dispatchAllInvocationEvents(); + ProgressManager.getInstance().run(new Task.Modal(getProject(), "waiting", false) { + @Override + public void run(@NotNull ProgressIndicator indicator) { + usageView.waitForUpdateRequestsCompletion(); + } + }); + UIUtil.dispatchAllInvocationEvents(); return usageView.getRoot(); } diff --git a/platform/platform-tests/testSrc/com/intellij/usages/impl/UsageViewTest.java b/platform/platform-tests/testSrc/com/intellij/usages/impl/UsageViewTest.java index 9106a7eb0ddb..255542f0c079 100644 --- a/platform/platform-tests/testSrc/com/intellij/usages/impl/UsageViewTest.java +++ b/platform/platform-tests/testSrc/com/intellij/usages/impl/UsageViewTest.java @@ -6,7 +6,6 @@ package com.intellij.usages.impl; import com.intellij.find.FindManager; import com.intellij.find.findUsages.FindUsagesHandler; import com.intellij.find.findUsages.FindUsagesManager; -import com.intellij.find.findUsages.PsiElement2UsageTargetAdapter; import com.intellij.find.impl.FindManagerImpl; import com.intellij.ide.actions.exclusion.ExclusionHandler; import com.intellij.ide.impl.TypeSafeDataProviderAdapter; @@ -16,6 +15,9 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.editor.Document; import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.encoding.EncodingManager; @@ -31,6 +33,7 @@ import com.intellij.usageView.UsageInfo; import com.intellij.usages.*; import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.tree.TreeUtil; +import org.jetbrains.annotations.NotNull; import javax.swing.*; import javax.swing.tree.TreeNode; @@ -57,9 +60,7 @@ public class UsageViewTest extends LightPlatformCodeInsightFixtureTestCase { usages[i] = createUsage(psiFile,i); } - UsageView usageView = UsageViewManager.getInstance(getProject()).createUsageView(UsageTarget.EMPTY_ARRAY, usages, new UsageViewPresentation(), null); - - Disposer.register(myFixture.getTestRootDisposable(), usageView); + UsageView usageView = createUsageView(usages); ((EncodingManagerImpl)EncodingManager.getInstance()).clearDocumentQueue(); FileDocumentManager.getInstance().saveAllDocuments(); @@ -73,8 +74,7 @@ public class UsageViewTest extends LightPlatformCodeInsightFixtureTestCase { PsiFile psiFile = myFixture.addFileToProject("X.java", "public class X{ int xxx; } //comment"); Usage usage = createUsage(psiFile, psiFile.getText().indexOf("xxx")); - UsageView usageView = UsageViewManager.getInstance(getProject()).createUsageView(UsageTarget.EMPTY_ARRAY, new Usage[]{usage}, new UsageViewPresentation(), null); - Disposer.register(myFixture.getTestRootDisposable(), usageView); + UsageView usageView = createUsageView(new Usage[]{usage}); PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject()); Document document = documentManager.getDocument(psiFile); @@ -87,8 +87,7 @@ public class UsageViewTest extends LightPlatformCodeInsightFixtureTestCase { PsiFile psiFile = myFixture.addFileToProject("X.java", "public class X{ int xxx; } //comment"); Usage usage = new UsageInfo2UsageAdapter(new UsageInfo(psiFile, psiFile.getText().indexOf("xxx"), StringUtil.indexOfSubstringEnd(psiFile.getText(),"xxx"))); - UsageView usageView = UsageViewManager.getInstance(getProject()).createUsageView(UsageTarget.EMPTY_ARRAY, new Usage[]{usage}, new UsageViewPresentation(), null); - Disposer.register(myFixture.getTestRootDisposable(), usageView); + UsageView usageView = createUsageView(new Usage[]{usage}); PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject()); Document document = documentManager.getDocument(psiFile); @@ -118,11 +117,17 @@ public class UsageViewTest extends LightPlatformCodeInsightFixtureTestCase { PsiNamedElement bar = (PsiNamedElement)members[members.length - 3]; assertEquals("bar", bar.getName()); - UsageTarget target = new PsiElement2UsageTargetAdapter(bar); FindUsagesManager usagesManager = ((FindManagerImpl)FindManager.getInstance(getProject())).getFindUsagesManager(); FindUsagesHandler handler = usagesManager.getNewFindUsagesHandler(bar, false); UsageViewImpl usageView = (UsageViewImpl)usagesManager.doFindUsages(new PsiElement[]{bar}, PsiElement.EMPTY_ARRAY, handler, handler.getFindUsagesOptions(), false); + ProgressManager.getInstance().run(new Task.Modal(getProject(), "waiting", false) { + @Override + public void run(@NotNull ProgressIndicator indicator) { + usageView.waitForUpdateRequestsCompletion(); + usageView.drainQueuedUsageNodes(); + } + }); Disposer.register(myFixture.getTestRootDisposable(), usageView); @@ -156,9 +161,7 @@ public class UsageViewTest extends LightPlatformCodeInsightFixtureTestCase { PsiFile psiFile = myFixture.addFileToProject("X.java", "public class X{ int xxx; } //comment"); Usage usage = new UsageInfo2UsageAdapter(new UsageInfo(psiFile, psiFile.getText().indexOf("xxx"), StringUtil.indexOfSubstringEnd(psiFile.getText(),"xxx"))); - UsageViewImpl usageView = - (UsageViewImpl)UsageViewManager.getInstance(getProject()).createUsageView(UsageTarget.EMPTY_ARRAY, new Usage[]{usage}, new UsageViewPresentation(), null); - Disposer.register(myFixture.getTestRootDisposable(), usageView); + UsageViewImpl usageView = createUsageView(new Usage[]{usage}); usageView.excludeUsages(new Usage[]{usage}); UIUtil.dispatchAllInvocationEvents(); @@ -204,14 +207,27 @@ public class UsageViewTest extends LightPlatformCodeInsightFixtureTestCase { " 1 public class X{ int xxx; } //comment\n", StringUtil.convertLineSeparators(text)); } + @NotNull + private UsageViewImpl createUsageView(@NotNull Usage[] usages) { + UsageViewImpl usageView = + (UsageViewImpl)UsageViewManager.getInstance(getProject()).createUsageView(UsageTarget.EMPTY_ARRAY, usages, new UsageViewPresentation(), null); + Disposer.register(myFixture.getTestRootDisposable(), usageView); + ProgressManager.getInstance().run(new Task.Modal(getProject(), "Waiting For Usages", false) { + @Override + public void run(@NotNull ProgressIndicator indicator) { + usageView.drainQueuedUsageNodes(); + } + }); + UIUtil.dispatchAllInvocationEvents(); + + return usageView; + } + public void testExcludeNodeMustExcludeChildrenAndParents() { PsiFile psiFile = myFixture.addFileToProject("X.java", "public class X{ int xxx; } //comment"); Usage usage = new UsageInfo2UsageAdapter(new UsageInfo(psiFile, psiFile.getText().indexOf("xxx"), StringUtil.indexOfSubstringEnd(psiFile.getText(),"xxx"))); - UsageViewImpl usageView = - (UsageViewImpl)UsageViewManager.getInstance(getProject()).createUsageView(UsageTarget.EMPTY_ARRAY, new Usage[]{usage}, new UsageViewPresentation(), null); - Disposer.register(myFixture.getTestRootDisposable(), usageView); - UIUtil.dispatchAllInvocationEvents(); + UsageViewImpl usageView = createUsageView(new Usage[]{usage}); Node[] usageNode = new Node[1]; TreeUtil.traverse(usageView.getRoot(), node -> { diff --git a/platform/usageView/src/com/intellij/usages/impl/Node.java b/platform/usageView/src/com/intellij/usages/impl/Node.java index 3d11fb5814f5..e9f01ca2b26e 100644 --- a/platform/usageView/src/com/intellij/usages/impl/Node.java +++ b/platform/usageView/src/com/intellij/usages/impl/Node.java @@ -15,6 +15,7 @@ */ package com.intellij.usages.impl; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.usages.UsageView; import com.intellij.util.BitUtil; import com.intellij.util.Consumer; @@ -30,7 +31,7 @@ import java.util.Vector; public abstract class Node extends DefaultMutableTreeNode { private int myCachedTextHash; - private byte myCachedFlags; // bit packed flags below: + private byte myCachedFlags; // guarded by this; bit packed flags below: static final byte EXCLUDED_MASK = 1<<3; private static final byte UPDATED_MASK = 1<<4; @@ -41,11 +42,11 @@ public abstract class Node extends DefaultMutableTreeNode { @MagicConstant(intValues = {CACHED_INVALID_MASK, CACHED_READ_ONLY_MASK, READ_ONLY_COMPUTED_MASK, EXCLUDED_MASK, UPDATED_MASK}) private @interface FlagConstant {} - private boolean isFlagSet(@FlagConstant byte mask) { + private synchronized boolean isFlagSet(@FlagConstant byte mask) { return BitUtil.isSet(myCachedFlags, mask); } - private void setFlag(@FlagConstant byte mask, boolean value) { + private synchronized void setFlag(@FlagConstant byte mask, boolean value) { myCachedFlags = BitUtil.set(myCachedFlags, mask, value); } @@ -92,6 +93,8 @@ public abstract class Node extends DefaultMutableTreeNode { } final synchronized void update(@NotNull UsageView view, @NotNull Consumer edtNodeChangedQueue) { + // performance: always update in background because smart pointer' isValid() can cause PSI chameleons expansion which is ridiculously expensive in cpp + assert !ApplicationManager.getApplication().isDispatchThread(); boolean isDataValid = isDataValid(); boolean isReadOnly = isDataReadOnly(); String text = getText(view); @@ -127,9 +130,11 @@ public abstract class Node extends DefaultMutableTreeNode { // same as DefaultMutableTreeNode.insert() except it doesn't try to remove the newChild from its parent since we know it's new void insertNewNode(@NotNull Node newChild, int childIndex) { + ApplicationManager.getApplication().assertIsDispatchThread(); if (children == null) { children = new Vector(); } + //noinspection unchecked children.insertElementAt(newChild, childIndex); } diff --git a/platform/usageView/src/com/intellij/usages/impl/SearchForUsagesRunnable.java b/platform/usageView/src/com/intellij/usages/impl/SearchForUsagesRunnable.java index e46bea1b1bda..6b913dbe974b 100644 --- a/platform/usageView/src/com/intellij/usages/impl/SearchForUsagesRunnable.java +++ b/platform/usageView/src/com/intellij/usages/impl/SearchForUsagesRunnable.java @@ -498,8 +498,12 @@ class SearchForUsagesRunnable implements Runnable { } } + UsageViewImpl usageView = myUsageViewRef.get(); + if (usageView != null) { + usageView.waitForUpdateRequestsCompletion(); + } if (myListener != null) { - myListener.findingUsagesFinished(myUsageViewRef.get()); + myListener.findingUsagesFinished(usageView); } } diff --git a/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java b/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java index 1aa0f6e6d4ff..b9aca29ebc0b 100644 --- a/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java +++ b/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java @@ -12,6 +12,7 @@ import com.intellij.navigation.NavigationItem; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.progress.ProgressIndicator; @@ -22,10 +23,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.ui.SimpleToolWindowPanel; import com.intellij.openapi.ui.Splitter; -import com.intellij.openapi.util.Comparing; -import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.util.Factory; -import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.*; import com.intellij.openapi.vfs.ReadonlyStatusHandler; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; @@ -53,6 +51,7 @@ import com.intellij.util.concurrency.EdtExecutorService; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.LinkedMultiMap; import com.intellij.util.containers.MultiMap; +import com.intellij.util.containers.Queue; import com.intellij.util.enumeration.EmptyEnumeration; import com.intellij.util.messages.MessageBusConnection; import com.intellij.util.ui.DialogUtil; @@ -77,9 +76,7 @@ import java.awt.*; import java.awt.event.*; import java.util.*; import java.util.List; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.*; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -158,6 +155,7 @@ public class UsageViewImpl implements UsageView { private Usage myOriginUsage; @Nullable private Runnable myRerunActivity; private boolean myDisposeSmartPointersOnClose = true; + private final Queue> updateRequests = new Queue<>(10); // guarded by insertionRequests public UsageViewImpl(@NotNull final Project project, @NotNull UsageViewPresentation presentation, @@ -469,6 +467,7 @@ public class UsageViewImpl implements UsageView { Rectangle visibleRect = myTree.getVisibleRect(); int rowForLocation = myTree.getClosestRowForLocation(0, visibleRect.y); int visibleRowCount = getVisibleRowCount(); + List toUpdate = new ArrayList<>(); for (int i = rowForLocation + visibleRowCount + 1; i >= rowForLocation; i--) { final TreePath eachPath = myTree.getPathForRow(i); if (eachPath == null) continue; @@ -476,10 +475,14 @@ public class UsageViewImpl implements UsageView { treeState.invalidatePathBounds(eachPath); Object node = eachPath.getLastPathComponent(); if (node instanceof UsageNode) { - ((UsageNode)node).update(this, edtNodeChangedQueue); + toUpdate.add((Node)node); } } - myTree.repaint(visibleRect); + queueUpdateBulk(toUpdate, ()->{ + if (!isDisposed()) { + myTree.repaint(visibleRect); + } + }); } else { myTree.setCellRenderer(myUsageViewTreeCellRenderer); @@ -637,7 +640,9 @@ public class UsageViewImpl implements UsageView { if (component instanceof Node) { Node node = (Node)component; if (!expandingAll && node.needsUpdate()) { - checkNodeValidity(node, path); + List toUpdate = new ArrayList<>(); + checkNodeValidity(node, path, toUpdate); + queueUpdateBulk(toUpdate, EmptyRunnable.getInstance()); } } } @@ -886,17 +891,17 @@ public class UsageViewImpl implements UsageView { reset(); myBuilder.setGroupingRules(getActiveGroupingRules(myProject, getUsageViewSettings())); myBuilder.setFilteringRules(getActiveFilteringRules(myProject)); - ApplicationManager.getApplication().runReadAction(() -> { - for (Usage usage : allUsages) { - if (!usage.isValid()) { - continue; - } - if (usage instanceof MergeableUsage) { - ((MergeableUsage)usage).reset(); - } - appendUsage(usage); + for (int i = allUsages.size() - 1; i >= 0; i--) { + Usage usage = allUsages.get(i); + if (!usage.isValid()) { + allUsages.remove(i); + continue; } - }); + if (usage instanceof MergeableUsage) { + ((MergeableUsage)usage).reset(); + } + } + appendUsagesInBulk(allUsages); if (myTree != null) { excludeUsages(excludedUsages.toArray(Usage.EMPTY_ARRAY)); } @@ -1081,10 +1086,50 @@ public class UsageViewImpl implements UsageView { @Override public void appendUsage(@NotNull Usage usage) { - doAppendUsage(usage); + if (ApplicationManager.getApplication().isDispatchThread()) { + addUpdateRequest(ApplicationManager.getApplication().executeOnPooledThread(() -> ReadAction.run(() -> doAppendUsage(usage)))); + } + else { + doAppendUsage(usage); + } + } + + private void addUpdateRequest(@NotNull Future request) { + synchronized (updateRequests) { + while (!updateRequests.isEmpty() && updateRequests.peekFirst().isDone()) { + updateRequests.pullFirst(); + } + updateRequests.addLast(request); + } + } + + void waitForUpdateRequestsCompletion() { + assert !ApplicationManager.getApplication().isDispatchThread(); + while (true) { + Future request; + synchronized (updateRequests) { + request = updateRequests.isEmpty() ? null : updateRequests.pullFirst(); + } + if (request == null) break; + try { + request.get(); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + void appendUsagesInBulk(@NotNull Collection usages) { + addUpdateRequest(ApplicationManager.getApplication().executeOnPooledThread(() -> ReadAction.run(() -> { + for (Usage usage : usages) { + doAppendUsage(usage); + } + }))); } public UsageNode doAppendUsage(@NotNull Usage usage) { + assert !ApplicationManager.getApplication().isDispatchThread(); // invoke in ReadAction to be be sure that usages are not invalidated while the tree is being built ApplicationManager.getApplication().assertReadAccessAllowed(); if (!usage.isValid()) { @@ -1175,28 +1220,46 @@ public class UsageViewImpl implements UsageView { ApplicationManager.getApplication().assertIsDispatchThread(); if (myProject.isDisposed()) return; TreeNode root = (TreeNode)myTree.getModel().getRoot(); - checkNodeValidity(root, new TreePath(root)); + List toUpdate = new ArrayList<>(); + checkNodeValidity(root, new TreePath(root), toUpdate); + queueUpdateBulk(toUpdate, EmptyRunnable.getInstance()); updateOnSelectionChanged(); } + private void queueUpdateBulk(@NotNull List toUpdate, @NotNull Runnable onCompletedInEdt) { + if (toUpdate.isEmpty()) return; + addUpdateRequest(ApplicationManager.getApplication().executeOnPooledThread(() -> { + for (Node node : toUpdate) { + try { + if (isDisposed()) break; + ReadAction.run(() -> node.update(this, edtNodeChangedQueue)); + } + catch (IndexNotReadyException ignore) { + } + } + ApplicationManager.getApplication().invokeLater(onCompletedInEdt); + })); + } + private void updateImmediatelyNodesUpToRoot(@NotNull Collection nodes) { ApplicationManager.getApplication().assertIsDispatchThread(); if (myProject.isDisposed()) return; TreeNode root = (TreeNode)myTree.getModel().getRoot(); - Set updated = new HashSet<>(); + Set queued = new HashSet<>(); + List toUpdate = new ArrayList<>(); while (true) { Set parents = new HashSet<>(); for (Node node : nodes) { - node.update(this, edtNodeChangedQueue); + toUpdate.add(node); TreeNode parent = node.getParent(); - if (parent != root && parent instanceof Node && updated.add((Node)parent)) { + if (parent != root && parent instanceof Node && queued.add((Node)parent)) { parents.add((Node)parent); } } if (parents.isEmpty()) break; nodes = parents; } - + queueUpdateBulk(toUpdate, EmptyRunnable.getInstance()); updateImmediately(); } @@ -1212,7 +1275,7 @@ public class UsageViewImpl implements UsageView { } } - private void checkNodeValidity(@NotNull TreeNode node, @NotNull TreePath path) { + private void checkNodeValidity(@NotNull TreeNode node, @NotNull TreePath path, @NotNull List result) { ApplicationManager.getApplication().assertIsDispatchThread(); boolean shouldCheckChildren = true; if (myTree.isCollapsed(path)) { @@ -1230,17 +1293,13 @@ public class UsageViewImpl implements UsageView { if (shouldCheckChildren && isVisible != UsageViewTreeCellRenderer.RowLocation.AFTER_VISIBLE_RECT) { for (int i=0; i < node.getChildCount(); i++) { TreeNode child = node.getChildAt(i); - checkNodeValidity(child, path.pathByAddingChild(child)); + checkNodeValidity(child, path.pathByAddingChild(child), result); } } // call update last, to let children a chance to update their cache first if (node instanceof Node && node != getModelRoot() && isVisible == UsageViewTreeCellRenderer.RowLocation.INSIDE_VISIBLE_RECT) { - try { - ((Node)node).update(this, edtNodeChangedQueue); - } - catch (IndexNotReadyException ignore) { - } + result.add((Node)node); } } diff --git a/platform/usageView/src/com/intellij/usages/impl/UsageViewManagerImpl.java b/platform/usageView/src/com/intellij/usages/impl/UsageViewManagerImpl.java index 5661d05c0ffa..6174d30b4d7c 100644 --- a/platform/usageView/src/com/intellij/usages/impl/UsageViewManagerImpl.java +++ b/platform/usageView/src/com/intellij/usages/impl/UsageViewManagerImpl.java @@ -46,6 +46,7 @@ import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Arrays; import java.util.concurrent.atomic.AtomicReference; /** @@ -67,8 +68,15 @@ public class UsageViewManagerImpl extends UsageViewManager { @NotNull UsageViewPresentation presentation, Factory usageSearcherFactory) { UsageViewImpl usageView = new UsageViewImpl(myProject, presentation, targets, usageSearcherFactory); - appendUsages(usages, usageView); + usageView.appendUsagesInBulk(Arrays.asList(usages)); + ProgressManager.getInstance().run(new Task.Modal(myProject, "Waiting For Usages", false) { + @Override + public void run(@NotNull ProgressIndicator indicator) { + usageView.waitForUpdateRequestsCompletion(); + } + }); usageView.setSearchInProgress(false); + return usageView; } @@ -205,14 +213,6 @@ public class UsageViewManagerImpl extends UsageViewManager { } } - protected static void appendUsages(@NotNull final Usage[] foundUsages, @NotNull final UsageViewImpl usageView) { - ApplicationManager.getApplication().runReadAction(() -> { - for (Usage foundUsage : foundUsages) { - usageView.appendUsage(foundUsage); - } - }); - } - public static void showTooManyUsagesWarningLater(@NotNull final Project project, @NotNull final TooManyUsagesStatus tooManyUsagesStatus,