From f136cc32841b7d13b7bb0b0f0cc533acaef58c50 Mon Sep 17 00:00:00 2001 From: epeshk Date: Thu, 1 Aug 2019 18:49:07 +0300 Subject: [PATCH] IDEA-CR-50999: Added ability to pass `ImplementationViewElement` in asynchronous implementation view search. GitOrigin-RevId: 3796f503b014f8268f9e992508df23c8cf17856b --- .../hint/ImplementationViewElement.kt | 8 + .../hint/ImplementationViewSession.kt | 5 +- .../hint/PsiImplementationViewSession.java | 4 +- .../actions/ShowImplementationsAction.java | 21 +- .../navigation/BackgroundUpdaterTask.java | 172 +--------------- .../navigation/BackgroundUpdaterTaskBase.java | 189 ++++++++++++++++++ .../ui/GenericListComponentUpdater.java | 11 + .../openapi/ui/ListComponentUpdater.java | 5 +- 8 files changed, 235 insertions(+), 180 deletions(-) create mode 100644 platform/lang-impl/src/com/intellij/codeInsight/navigation/BackgroundUpdaterTaskBase.java create mode 100644 platform/platform-api/src/com/intellij/openapi/ui/GenericListComponentUpdater.java diff --git a/platform/lang-impl/src/com/intellij/codeInsight/hint/ImplementationViewElement.kt b/platform/lang-impl/src/com/intellij/codeInsight/hint/ImplementationViewElement.kt index 88e8861aa0a9..ad66d307a9d6 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/hint/ImplementationViewElement.kt +++ b/platform/lang-impl/src/com/intellij/codeInsight/hint/ImplementationViewElement.kt @@ -13,6 +13,9 @@ import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile import com.intellij.psi.PsiNamedElement import com.intellij.psi.util.PsiTreeUtil +import com.intellij.usageView.UsageInfo +import com.intellij.usages.Usage +import com.intellij.usages.UsageInfo2UsageAdapter import javax.swing.Icon /** @@ -33,6 +36,11 @@ abstract class ImplementationViewElement { abstract val elementForShowUsages: PsiElement? abstract fun navigate(focusEditor: Boolean) + + open val usage: Usage? + get() { + return UsageInfo2UsageAdapter(UsageInfo(elementForShowUsages ?: return null)) + } } class PsiImplementationViewElement(val psiElement: PsiElement) : ImplementationViewElement() { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/hint/ImplementationViewSession.kt b/platform/lang-impl/src/com/intellij/codeInsight/hint/ImplementationViewSession.kt index 38411ceca72d..6698dd61eaef 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/hint/ImplementationViewSession.kt +++ b/platform/lang-impl/src/com/intellij/codeInsight/hint/ImplementationViewSession.kt @@ -13,6 +13,9 @@ import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.PsiElement import com.intellij.psi.PsiManager import com.intellij.psi.presentation.java.SymbolPresentationUtil +import com.intellij.usageView.UsageInfo +import com.intellij.usages.Usage +import com.intellij.usages.UsageInfo2UsageAdapter import com.intellij.util.Processor interface ImplementationViewSession : Disposable { @@ -30,7 +33,7 @@ interface ImplementationViewSession : Disposable { val editor: Editor? fun searchImplementationsInBackground(indicator: ProgressIndicator, - processor: Processor): List + processor: Processor): List fun elementRequiresIncludeSelf(): Boolean fun needUpdateInBackground(): Boolean } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/hint/PsiImplementationViewSession.java b/platform/lang-impl/src/com/intellij/codeInsight/hint/PsiImplementationViewSession.java index 55b2d502443b..eafe2427fc85 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/hint/PsiImplementationViewSession.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/hint/PsiImplementationViewSession.java @@ -195,7 +195,7 @@ public class PsiImplementationViewSession implements ImplementationViewSession { @NotNull @Override public List searchImplementationsInBackground(@NotNull ProgressIndicator indicator, - @NotNull final Processor processor) { + @NotNull final Processor processor) { final ImplementationSearcher.BackgroundableImplementationSearcher implementationSearcher = new ImplementationSearcher.BackgroundableImplementationSearcher() { @Override @@ -205,7 +205,7 @@ public class PsiImplementationViewSession implements ImplementationViewSession { @Override protected void processElement(PsiElement element) { - if (!processor.process(element)) { + if (!processor.process(new PsiImplementationViewElement(element))) { indicator.cancel(); } indicator.checkCanceled(); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/hint/actions/ShowImplementationsAction.java b/platform/lang-impl/src/com/intellij/codeInsight/hint/actions/ShowImplementationsAction.java index 3e5792e10a32..cd37dddca600 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/hint/actions/ShowImplementationsAction.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/hint/actions/ShowImplementationsAction.java @@ -5,7 +5,7 @@ import com.intellij.codeInsight.CodeInsightBundle; import com.intellij.codeInsight.documentation.DocumentationManager; import com.intellij.codeInsight.hint.*; import com.intellij.codeInsight.lookup.LookupManager; -import com.intellij.codeInsight.navigation.BackgroundUpdaterTask; +import com.intellij.codeInsight.navigation.BackgroundUpdaterTaskBase; import com.intellij.codeInsight.navigation.ImplementationSearcher; import com.intellij.featureStatistics.FeatureUsageTracker; import com.intellij.ide.DataManager; @@ -20,18 +20,18 @@ import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.IndexNotReadyException; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.ListComponentUpdater; +import com.intellij.openapi.ui.GenericListComponentUpdater; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Ref; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDocumentManager; -import com.intellij.psi.PsiElement; import com.intellij.reference.SoftReference; import com.intellij.ui.popup.AbstractPopup; import com.intellij.ui.popup.PopupPositionManager; import com.intellij.ui.popup.PopupUpdateProcessor; +import com.intellij.usages.Usage; import com.intellij.usages.UsageView; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -229,7 +229,7 @@ public class ShowImplementationsAction extends DumbAwareAction implements PopupA return false; } - private static class ImplementationViewComponentUpdater implements ListComponentUpdater { + private static class ImplementationViewComponentUpdater implements GenericListComponentUpdater { private final ImplementationViewComponent myComponent; private final int myIncludeSelfIdx; @@ -244,20 +244,20 @@ public class ShowImplementationsAction extends DumbAwareAction implements PopupA } @Override - public void replaceModel(@NotNull List data) { + public void replaceModel(@NotNull List data) { final ImplementationViewElement[] elements = myComponent.getElements(); final int includeSelfIdx = myIncludeSelfIdx; final int startIdx = elements.length - includeSelfIdx; List result = new ArrayList<>(); Collections.addAll(result, elements); - for (PsiElement element : data.subList(startIdx, data.size())) { - result.add(new PsiImplementationViewElement(element)); + for (ImplementationViewElement element : data.subList(startIdx, data.size())) { + result.add(element); } myComponent.update(result, myComponent.getIndex()); } } - private static class ImplementationsUpdaterTask extends BackgroundUpdaterTask { + private static class ImplementationsUpdaterTask extends BackgroundUpdaterTaskBase { private final String myCaption; private final ImplementationViewSession mySession; private final ImplementationViewComponent myComponent; @@ -277,6 +277,11 @@ public class ShowImplementationsAction extends DumbAwareAction implements PopupA return myCaption; } + @Override + protected Usage createUsage(ImplementationViewElement element) { + return element.getUsage(); + } + @Override public void run(@NotNull final ProgressIndicator indicator) { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/navigation/BackgroundUpdaterTask.java b/platform/lang-impl/src/com/intellij/codeInsight/navigation/BackgroundUpdaterTask.java index 5905d9694282..5e391a4c69ad 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/navigation/BackgroundUpdaterTask.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/navigation/BackgroundUpdaterTask.java @@ -15,66 +15,22 @@ */ package com.intellij.codeInsight.navigation; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.application.ReadAction; -import com.intellij.openapi.progress.ProgressIndicator; -import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.ListComponentUpdater; -import com.intellij.openapi.ui.popup.JBPopup; -import com.intellij.openapi.util.Ref; import com.intellij.psi.PsiElement; import com.intellij.psi.util.PsiUtilCore; import com.intellij.usageView.UsageInfo; +import com.intellij.usages.Usage; import com.intellij.usages.UsageInfo2UsageAdapter; -import com.intellij.usages.UsageView; -import com.intellij.usages.impl.UsageViewImpl; -import com.intellij.util.Alarm; -import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.TestOnly; import java.util.*; -public abstract class BackgroundUpdaterTask extends Task.Backgroundable { - protected JBPopup myPopup; - private ListComponentUpdater myUpdater; - private Ref myUsageView; - private final Collection myData; - - private final Alarm myAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD); - private final Object lock = new Object(); - - private volatile boolean myCanceled; - private volatile boolean myFinished; - private volatile ProgressIndicator myIndicator; +public abstract class BackgroundUpdaterTask extends BackgroundUpdaterTaskBase { public BackgroundUpdaterTask(@Nullable Project project, @NotNull String title, @Nullable Comparator comparator) { - super(project, title); - myData = comparator == null ? ContainerUtil.newSmartList() : new TreeSet<>(comparator); - } - - @TestOnly - public ListComponentUpdater getUpdater() { - return myUpdater; - } - - public void init(@NotNull JBPopup popup, @NotNull ListComponentUpdater updater, @NotNull Ref usageView) { - myPopup = popup; - myUpdater = updater; - myUsageView = usageView; - } - - public abstract String getCaption(int size); - - protected void replaceModel(@NotNull List data) { - myUpdater.replaceModel(data); - } - - protected void paintBusy(boolean paintBusy) { - myUpdater.paintBusy(paintBusy); + super(project, title, comparator); } protected static Comparator createComparatorWrapper(@NotNull Comparator comparator) { @@ -87,125 +43,9 @@ public abstract class BackgroundUpdaterTask extends Task.Backgroundable { }; } - private boolean setCanceled() { - boolean canceled = myCanceled; - myCanceled = true; - return canceled; - } - - public boolean isCanceled() { - return myCanceled; - } - - /** - * @deprecated Use {@link #BackgroundUpdaterTask(Project, String, Comparator)} and {@link #updateComponent(PsiElement)} instead - */ - @Deprecated - public boolean updateComponent(@NotNull PsiElement element, @Nullable Comparator comparator) { - final UsageView view = myUsageView.get(); - if (view != null && !((UsageViewImpl)view).isDisposed()) { - ApplicationManager.getApplication().runReadAction(() -> view.appendUsage(new UsageInfo2UsageAdapter(new UsageInfo(element)))); - return true; - } - - if (myCanceled) return false; - - if (myPopup.isDisposed()) return false; - ModalityState modalityState = ModalityState.stateForComponent(myPopup.getContent()); - - synchronized (lock) { - if (myData.contains(element)) return true; - myData.add(element); - if (comparator != null && myData instanceof List) { - Collections.sort((List)myData, comparator); - } - } - - myAlarm.addRequest(() -> { - myAlarm.cancelAllRequests(); - refreshModelImmediately(); - }, 200, modalityState); - return true; - } - - public boolean updateComponent(@NotNull PsiElement element) { - final UsageView view = myUsageView.get(); - if (view != null && !((UsageViewImpl)view).isDisposed()) { - ApplicationManager.getApplication().runReadAction(() -> view.appendUsage(new UsageInfo2UsageAdapter(new UsageInfo(element)))); - return true; - } - - if (myCanceled) return false; - if (myPopup.isDisposed()) return false; - - synchronized (lock) { - if (!myData.add(element)) return true; - } - - myAlarm.addRequest(() -> { - myAlarm.cancelAllRequests(); - refreshModelImmediately(); - }, 200, ModalityState.stateForComponent(myPopup.getContent())); - return true; - } - - private void refreshModelImmediately() { - ApplicationManager.getApplication().assertIsDispatchThread(); - if (myCanceled) return; - if (myPopup.isDisposed()) return; - List data; - synchronized (lock) { - data = new ArrayList<>(myData); - } - replaceModel(data); - myPopup.setCaption(getCaption(getCurrentSize())); - myPopup.pack(true, true); - } - - public int getCurrentSize() { - synchronized (lock) { - return myData.size(); - } - } - @Override - public void run(@NotNull ProgressIndicator indicator) { - paintBusy(true); - myIndicator = indicator; - } - - @Override - public void onSuccess() { - myFinished = true; - refreshModelImmediately(); - paintBusy(false); - } - - @Override - public void onFinished() { - myAlarm.cancelAllRequests(); - myFinished = true; - } - - @Nullable - protected PsiElement getTheOnlyOneElement() { - synchronized (lock) { - if (myData.size() == 1) { - return myData.iterator().next(); - } - } - return null; - } - - public boolean isFinished() { - return myFinished; - } - - public boolean cancelTask() { - ProgressIndicator indicator = myIndicator; - if (indicator != null) { - indicator.cancel(); - } - return setCanceled(); + protected Usage createUsage(PsiElement element) { + return new UsageInfo2UsageAdapter(new UsageInfo(element)); } } + diff --git a/platform/lang-impl/src/com/intellij/codeInsight/navigation/BackgroundUpdaterTaskBase.java b/platform/lang-impl/src/com/intellij/codeInsight/navigation/BackgroundUpdaterTaskBase.java new file mode 100644 index 000000000000..aa169b310b78 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/navigation/BackgroundUpdaterTaskBase.java @@ -0,0 +1,189 @@ +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.codeInsight.navigation; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.Task; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.GenericListComponentUpdater; +import com.intellij.openapi.ui.popup.JBPopup; +import com.intellij.openapi.util.Ref; +import com.intellij.usages.Usage; +import com.intellij.usages.UsageView; +import com.intellij.usages.impl.UsageViewImpl; +import com.intellij.util.Alarm; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.TestOnly; + +import java.util.*; + +public abstract class BackgroundUpdaterTaskBase extends Task.Backgroundable { + protected JBPopup myPopup; + private GenericListComponentUpdater myUpdater; + private Ref myUsageView; + private final Collection myData; + + private final Alarm myAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD); + private final Object lock = new Object(); + + private volatile boolean myCanceled; + private volatile boolean myFinished; + private volatile ProgressIndicator myIndicator; + + public BackgroundUpdaterTaskBase(@Nullable Project project, @NotNull String title, @Nullable Comparator comparator) { + super(project, title); + myData = comparator == null ? ContainerUtil.newSmartList() : new TreeSet<>(comparator); + } + + @TestOnly + public GenericListComponentUpdater getUpdater() { + return myUpdater; + } + + public void init(@NotNull JBPopup popup, @NotNull GenericListComponentUpdater updater, @NotNull Ref usageView) { + myPopup = popup; + myUpdater = updater; + myUsageView = usageView; + } + + public abstract String getCaption(int size); + + @Nullable + protected abstract Usage createUsage(T element); + + protected void replaceModel(@NotNull List data) { + myUpdater.replaceModel(data); + } + + protected void paintBusy(boolean paintBusy) { + myUpdater.paintBusy(paintBusy); + } + + private boolean setCanceled() { + boolean canceled = myCanceled; + myCanceled = true; + return canceled; + } + + public boolean isCanceled() { + return myCanceled; + } + + /** + * @deprecated Use {@link #BackgroundUpdaterTaskBase(Project, String, Comparator)} and {@link #updateComponent(T)} instead + */ + @Deprecated + public boolean updateComponent(@NotNull T element, @Nullable Comparator comparator) { + if (tryAppendUsage(element)) return true; + if (myCanceled) return false; + + if (myPopup.isDisposed()) return false; + ModalityState modalityState = ModalityState.stateForComponent(myPopup.getContent()); + + synchronized (lock) { + if (myData.contains(element)) return true; + myData.add(element); + if (comparator != null && myData instanceof List) { + Collections.sort((List)myData, comparator); + } + } + + myAlarm.addRequest(() -> { + myAlarm.cancelAllRequests(); + refreshModelImmediately(); + }, 200, modalityState); + return true; + } + + private boolean tryAppendUsage(@NotNull T element) { + final UsageView view = myUsageView.get(); + if (view != null && !((UsageViewImpl)view).isDisposed()) { + Usage usage = createUsage(element); + if (usage == null) + return false; + ApplicationManager.getApplication().runReadAction(() -> view.appendUsage(usage)); + return true; + } + return false; + } + + public boolean updateComponent(@NotNull T element) { + if (tryAppendUsage(element)) return true; + + if (myCanceled) return false; + if (myPopup.isDisposed()) return false; + + synchronized (lock) { + if (!myData.add(element)) return true; + } + + myAlarm.addRequest(() -> { + myAlarm.cancelAllRequests(); + refreshModelImmediately(); + }, 200, ModalityState.stateForComponent(myPopup.getContent())); + return true; + } + + private void refreshModelImmediately() { + ApplicationManager.getApplication().assertIsDispatchThread(); + if (myCanceled) return; + if (myPopup.isDisposed()) return; + List data; + synchronized (lock) { + data = new ArrayList<>(myData); + } + replaceModel(data); + myPopup.setCaption(getCaption(getCurrentSize())); + myPopup.pack(true, true); + } + + public int getCurrentSize() { + synchronized (lock) { + return myData.size(); + } + } + + @Override + public void run(@NotNull ProgressIndicator indicator) { + paintBusy(true); + myIndicator = indicator; + } + + @Override + public void onSuccess() { + myFinished = true; + refreshModelImmediately(); + paintBusy(false); + } + + @Override + public void onFinished() { + myAlarm.cancelAllRequests(); + myFinished = true; + } + + @Nullable + protected T getTheOnlyOneElement() { + synchronized (lock) { + if (myData.size() == 1) { + return myData.iterator().next(); + } + } + return null; + } + + public boolean isFinished() { + return myFinished; + } + + public boolean cancelTask() { + ProgressIndicator indicator = myIndicator; + if (indicator != null) { + indicator.cancel(); + } + return setCanceled(); + } +} diff --git a/platform/platform-api/src/com/intellij/openapi/ui/GenericListComponentUpdater.java b/platform/platform-api/src/com/intellij/openapi/ui/GenericListComponentUpdater.java new file mode 100644 index 000000000000..233aeba5d4c8 --- /dev/null +++ b/platform/platform-api/src/com/intellij/openapi/ui/GenericListComponentUpdater.java @@ -0,0 +1,11 @@ +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.openapi.ui; + +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public interface GenericListComponentUpdater { + void replaceModel(@NotNull List data); + void paintBusy(boolean paintBusy); +} diff --git a/platform/platform-api/src/com/intellij/openapi/ui/ListComponentUpdater.java b/platform/platform-api/src/com/intellij/openapi/ui/ListComponentUpdater.java index 467832cc165c..39cf35d0c686 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/ListComponentUpdater.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/ListComponentUpdater.java @@ -6,7 +6,6 @@ import org.jetbrains.annotations.NotNull; import java.util.List; -public interface ListComponentUpdater { - void replaceModel(@NotNull List data); - void paintBusy(boolean paintBusy); +public interface ListComponentUpdater extends GenericListComponentUpdater { } +