UsageViewEx (@NotNull ProgressIndicator indicator, long startSearchStamp) { UsageViewEx existingUsageView = myUsageViewRef.get(); if (existingUsageView != null) { return existingUsageView; } int usageCount = myUsageCountWithoutDefinition.get(); if (usageCount == 0 || usageCount == 1 && !myProcessPresentation.isShowPanelIfOnlyOneUsage() && System.currentTimeMillis() < startSearchStamp + 500) { return null; } UsageViewEx usageView = myUsageViewManager.createUsageView(mySearchFor, Usage.EMPTY_ARRAY, myPresentation, mySearcherFactory); if (myUsageViewRef.compareAndSet(null, usageView)) { // associate progress only if created successfully, otherwise Dispose will cancel the actual progress, see IDEA-195542 PsiElement element = getPsiElement(mySearchFor); ReadAction.run(() -> UsageViewStatisticsCollector.logSearchStarted(myProject, usageView, CodeNavigateSource.FindToolWindow, element, mySearchFor.length)); usageView.associateProgress(indicator); if (myProcessPresentation.isShowFindOptionsPrompt()) { openView(usageView); } else if (myListener != null) { SwingUtilities.invokeLater(() -> { if (!myProject.isDisposed()) { UsageViewEx uv = myUsageViewRef.get(); if (uv != null) { myListener.usageViewCreated(uv); } } }); } Usage firstUsage = myFirstUsage.get(); if (firstUsage != null) { ApplicationManager.getApplication().runReadAction(() -> usageView.appendUsage(firstUsage)); } return usageView; } // dispose duplicate UsageView created in a race Disposer.register(myProject, usageView); // UI thread because dispose does some sort of swing magic e.g., AnAction.unregisterCustomShortcutSet() ApplicationManager.getApplication().invokeLater(() -> Disposer.dispose(usageView), ModalityState.any(), myProject.getDisposed()); return myUsageViewRef.get(); }