IDEA-CR-50999: Added ability to pass ImplementationViewElement in asynchronous implementation view search.

GitOrigin-RevId: 3796f503b014f8268f9e992508df23c8cf17856b
This commit is contained in:
epeshk
2019-08-02 20:04:09 +03:00
committed by intellij-monorepo-bot
parent a7bc072e78
commit f136cc3284
8 changed files with 235 additions and 180 deletions
@@ -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() {
@@ -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<in PsiElement>): List<ImplementationViewElement>
processor: Processor<in ImplementationViewElement>): List<ImplementationViewElement>
fun elementRequiresIncludeSelf(): Boolean
fun needUpdateInBackground(): Boolean
}
@@ -195,7 +195,7 @@ public class PsiImplementationViewSession implements ImplementationViewSession {
@NotNull
@Override
public List<ImplementationViewElement> searchImplementationsInBackground(@NotNull ProgressIndicator indicator,
@NotNull final Processor<? super PsiElement> processor) {
@NotNull final Processor<? super ImplementationViewElement> 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();
@@ -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<ImplementationViewElement> {
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<? extends PsiElement> data) {
public void replaceModel(@NotNull List<? extends ImplementationViewElement> data) {
final ImplementationViewElement[] elements = myComponent.getElements();
final int includeSelfIdx = myIncludeSelfIdx;
final int startIdx = elements.length - includeSelfIdx;
List<ImplementationViewElement> 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<ImplementationViewElement> {
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) {
@@ -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<? extends UsageView> myUsageView;
private final Collection<PsiElement> 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<PsiElement> {
public BackgroundUpdaterTask(@Nullable Project project, @NotNull String title, @Nullable Comparator<PsiElement> 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<? extends UsageView> usageView) {
myPopup = popup;
myUpdater = updater;
myUsageView = usageView;
}
public abstract String getCaption(int size);
protected void replaceModel(@NotNull List<? extends PsiElement> data) {
myUpdater.replaceModel(data);
}
protected void paintBusy(boolean paintBusy) {
myUpdater.paintBusy(paintBusy);
super(project, title, comparator);
}
protected static Comparator<PsiElement> createComparatorWrapper(@NotNull Comparator<? super PsiElement> 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<PsiElement> 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));
}
}
@@ -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<T> extends Task.Backgroundable {
protected JBPopup myPopup;
private GenericListComponentUpdater<T> myUpdater;
private Ref<? extends UsageView> myUsageView;
private final Collection<T> 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<T> comparator) {
super(project, title);
myData = comparator == null ? ContainerUtil.newSmartList() : new TreeSet<>(comparator);
}
@TestOnly
public GenericListComponentUpdater<T> getUpdater() {
return myUpdater;
}
public void init(@NotNull JBPopup popup, @NotNull GenericListComponentUpdater<T> updater, @NotNull Ref<? extends UsageView> 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<? extends T> 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<T> 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();
}
}
@@ -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<T> {
void replaceModel(@NotNull List<? extends T> data);
void paintBusy(boolean paintBusy);
}
@@ -6,7 +6,6 @@ import org.jetbrains.annotations.NotNull;
import java.util.List;
public interface ListComponentUpdater {
void replaceModel(@NotNull List<? extends PsiElement> data);
void paintBusy(boolean paintBusy);
public interface ListComponentUpdater extends GenericListComponentUpdater<PsiElement> {
}