Extract PsiImplementationViewSession class for implementation view search logic

This commit is contained in:
Dmitry Jemerov
2018-12-28 16:42:12 +01:00
parent f2f79946b1
commit 398aa0a0c6
4 changed files with 365 additions and 273 deletions
@@ -3,7 +3,7 @@ package com.intellij.codeInsight.hint.actions;
import com.intellij.codeInsight.TargetElementUtil;
import com.intellij.codeInsight.daemon.impl.PsiElementListNavigator;
import com.intellij.codeInsight.hint.PsiImplementationViewElement;
import com.intellij.codeInsight.hint.PsiImplementationViewSession;
import com.intellij.codeInsight.navigation.BackgroundUpdaterTask;
import com.intellij.ide.util.MethodCellRenderer;
import com.intellij.ide.util.PsiClassListCellRenderer;
@@ -30,9 +30,9 @@ public class ShowSiblingsAction extends ShowImplementationsAction {
if (project == null) return;
PsiDocumentManager.getInstance(project).commitAllDocuments();
final Editor editor = getEditor(dataContext);
final Editor editor = PsiImplementationViewSession.getEditor(dataContext);
PsiElement element = getElement(project, file, editor, CommonDataKeys.PSI_ELEMENT.getData(dataContext));
PsiElement element = PsiImplementationViewSession.getElement(project, file, editor, CommonDataKeys.PSI_ELEMENT.getData(dataContext));
if (element == null && file == null) return;
PsiFile containingFile = element != null ? element.getContainingFile() : file;
@@ -77,9 +77,10 @@ public class ShowSiblingsAction extends ShowImplementationsAction {
PsiFile file,
boolean invokedFromEditor,
@NotNull PsiElement element) {
final PsiElement[] impls = getSelfAndImplementations(editor, element, createImplementationsSearcher(), false);
final PsiElement[] impls = PsiImplementationViewSession
.getSelfAndImplementations(editor, element, PsiImplementationViewSession.createImplementationsSearcher(true), false);
final String text = SymbolPresentationUtil.getSymbolPresentableText(element);
showImplementations(ContainerUtil.map(impls, PsiImplementationViewElement::new), project, text, editor, file, element, invokedFromEditor, invokedByShortcut);
showImplementations(new PsiImplementationViewSession(project, element, impls, text, editor, file), invokedFromEditor, invokedByShortcut);
}
@Override
@@ -3,14 +3,12 @@ package com.intellij.codeInsight;
import com.intellij.codeInsight.hint.ImplementationViewElement;
import com.intellij.codeInsight.hint.PsiImplementationViewElement;
import com.intellij.codeInsight.hint.PsiImplementationViewSession;
import com.intellij.codeInsight.hint.actions.ShowImplementationsAction;
import com.intellij.ide.DataManager;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
@@ -28,15 +26,10 @@ public class ShowImplementationsTestUtil {
final Ref<List<ImplementationViewElement>> ref = new Ref<>();
new ShowImplementationsAction() {
@Override
protected void showImplementations(@NotNull List<ImplementationViewElement> impls,
@NotNull Project project,
String text,
Editor editor,
PsiFile file,
PsiElement element,
protected void showImplementations(@NotNull PsiImplementationViewSession session,
boolean invokedFromEditor,
boolean invokedByShortcut) {
ref.set(impls);
ref.set(session.getImplementationElements());
}
}.performForContext(context);
return ContainerUtil.map2Array(ref.get(), PsiElement.class, (element) -> ((PsiImplementationViewElement) element).getPsiElement());
@@ -0,0 +1,315 @@
// Copyright 2000-2018 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.hint;
import com.intellij.codeInsight.TargetElementUtil;
import com.intellij.codeInsight.documentation.DocumentationManager;
import com.intellij.codeInsight.navigation.ImplementationSearcher;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.TextEditor;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.PomTargetPsiElement;
import com.intellij.psi.*;
import com.intellij.psi.presentation.java.SymbolPresentationUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.util.Processor;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* @author yole
*/
public class PsiImplementationViewSession {
private static final Logger LOG = Logger.getInstance(PsiImplementationViewSession.class);
@NotNull private final Project myProject;
@Nullable private final PsiElement myElement;
private final PsiElement[] myImpls;
private final String myText;
private final Editor myEditor;
@Nullable private final PsiFile myFile;
public PsiImplementationViewSession(@NotNull Project project, @Nullable PsiElement element, PsiElement[] impls, String text, Editor editor,
@Nullable PsiFile file) {
myProject = project;
myElement = element;
myImpls = impls;
myText = text;
myEditor = editor;
myFile = file;
}
@NotNull
public Project getProject() {
return myProject;
}
@Nullable
public PsiElement getElement() {
return myElement;
}
public List<ImplementationViewElement> getImplementationElements() {
return ContainerUtil.map(myImpls, PsiImplementationViewElement::new);
}
public String getText() {
return myText;
}
public Editor getEditor() {
return myEditor;
}
@Nullable
public PsiFile getFile() {
return myFile;
}
public PsiImplementationViewSession createSessionForLookupElement(Object lookupItemObject, boolean isSearchDeep) {
final PsiElement element = lookupItemObject instanceof PsiElement
? (PsiElement)lookupItemObject
: DocumentationManager.getInstance(myProject).getElementFromLookup(myEditor, myFile);
PsiElement[] impls = {};
String text = "";
if (element != null) {
// if (element instanceof PsiPackage) return;
PsiFile containingFile = element.getContainingFile();
if (containingFile == null || !containingFile.getViewProvider().isPhysical()) return null;
impls = getSelfAndImplementations(myEditor, element, PsiImplementationViewSession.createImplementationsSearcher(isSearchDeep));
text = SymbolPresentationUtil.getSymbolPresentableText(element);
}
return new PsiImplementationViewSession(myProject, element, impls, text, myEditor, myFile);
}
public boolean elementRequiresIncludeSelf() {
return !(myElement instanceof PomTargetPsiElement);
}
public boolean needUpdateInBackground() {
return myElement != null;
}
@NotNull
static PsiElement[] filterElements(@NotNull final PsiElement[] targetElements) {
final Set<PsiElement> unique = new LinkedHashSet<>(Arrays.asList(targetElements));
for (final PsiElement elt : targetElements) {
ApplicationManager.getApplication().runReadAction(() -> {
final PsiFile containingFile = elt.getContainingFile();
LOG.assertTrue(containingFile != null, elt);
PsiFile psiFile = containingFile.getOriginalFile();
if (psiFile.getVirtualFile() == null) unique.remove(elt);
});
}
// special case for Python (PY-237)
// if the definition is the tree parent of the target element, filter out the target element
for (int i = 1; i < targetElements.length; i++) {
final PsiElement targetElement = targetElements[i];
if (ReadAction.compute(() -> PsiTreeUtil.isAncestor(targetElement, targetElements[0], true))) {
unique.remove(targetElements[0]);
break;
}
}
return PsiUtilCore.toPsiElementArray(unique);
}
@NotNull
public static ImplementationSearcher createImplementationsSearcher(final boolean searchDeep) {
if (ApplicationManager.getApplication().isUnitTestMode()) {
return new ImplementationSearcher() {
@Override
protected PsiElement[] filterElements(PsiElement element, PsiElement[] targetElements) {
return PsiImplementationViewSession.filterElements(targetElements);
}
};
}
return new ImplementationSearcher.FirstImplementationsSearcher() {
@Override
protected PsiElement[] filterElements(PsiElement element, PsiElement[] targetElements) {
return PsiImplementationViewSession.filterElements(targetElements);
}
@Override
protected boolean isSearchDeep() {
return searchDeep;
}
};
}
@NotNull
static PsiElement[] getSelfAndImplementations(Editor editor,
@NotNull PsiElement element,
@NotNull ImplementationSearcher handler) {
return getSelfAndImplementations(editor, element, handler, !(element instanceof PomTargetPsiElement));
}
@NotNull
public static PsiElement[] getSelfAndImplementations(Editor editor,
@NotNull PsiElement element,
@NotNull ImplementationSearcher handler,
final boolean includeSelfAlways) {
final PsiElement[] handlerImplementations = handler.searchImplementations(element, editor, includeSelfAlways, true);
if (handlerImplementations.length > 0) return handlerImplementations;
return ReadAction.compute(() -> {
PsiElement psiElement = element;
PsiFile psiFile = psiElement.getContainingFile();
if (psiFile == null) {
// Magically, it's null for ant property declarations.
psiElement = psiElement.getNavigationElement();
psiFile = psiElement.getContainingFile();
if (psiFile == null) {
return PsiElement.EMPTY_ARRAY;
}
}
if (psiFile.getVirtualFile() != null && (psiElement.getTextRange() != null || psiElement instanceof PsiFile)) {
return new PsiElement[]{psiElement};
}
return PsiElement.EMPTY_ARRAY;
});
}
public List<ImplementationViewElement> searchImplementationsInBackground(@NotNull ProgressIndicator indicator,
boolean isSearchDeep,
boolean includeSelf,
final Processor<PsiElement> processor) {
final ImplementationSearcher.BackgroundableImplementationSearcher implementationSearcher =
new ImplementationSearcher.BackgroundableImplementationSearcher() {
@Override
protected boolean isSearchDeep() {
return isSearchDeep;
}
@Override
protected void processElement(PsiElement element) {
if (!processor.process(element)) {
indicator.cancel();
}
indicator.checkCanceled();
}
@Override
protected PsiElement[] filterElements(PsiElement element, PsiElement[] targetElements) {
return PsiImplementationViewSession.filterElements(targetElements);
}
};
PsiElement[] psiElements;
if (!includeSelf) {
psiElements = getSelfAndImplementations(myEditor, myElement, implementationSearcher, false);
}
else {
psiElements = getSelfAndImplementations(myEditor, myElement, implementationSearcher);
}
return ContainerUtil.map(psiElements, PsiImplementationViewElement::new);
}
public static Editor getEditor(@NotNull DataContext dataContext) {
Editor editor = CommonDataKeys.EDITOR.getData(dataContext);
if (editor == null) {
final PsiFile file = CommonDataKeys.PSI_FILE.getData(dataContext);
if (file != null) {
final VirtualFile virtualFile = file.getVirtualFile();
if (virtualFile != null) {
final FileEditor fileEditor = FileEditorManager.getInstance(file.getProject()).getSelectedEditor(virtualFile);
if (fileEditor instanceof TextEditor) {
editor = ((TextEditor)fileEditor).getEditor();
}
}
}
}
return editor;
}
@Nullable
public static PsiImplementationViewSession create(@NotNull DataContext dataContext,
Project project,
boolean searchDeep) {
PsiFile file = CommonDataKeys.PSI_FILE.getData(dataContext);
Editor editor = getEditor(dataContext);
PsiElement element = CommonDataKeys.PSI_ELEMENT.getData(dataContext);
element = getElement(project, file, editor, element);
if (element == null && file == null) return null;
PsiFile containingFile = element != null ? element.getContainingFile() : file;
if (containingFile == null || !containingFile.getViewProvider().isPhysical()) return null;
PsiReference ref = null;
if (editor != null) {
ref = TargetElementUtil.findReference(editor, editor.getCaretModel().getOffset());
if (element == null && ref != null) {
element = TargetElementUtil.getInstance().adjustReference(ref);
}
}
//check attached sources if any
if (element instanceof PsiCompiledElement) {
element = element.getNavigationElement();
}
String text = "";
PsiElement[] impls = PsiElement.EMPTY_ARRAY;
if (element != null) {
impls = getSelfAndImplementations(editor, element, createImplementationsSearcher(searchDeep));
text = SymbolPresentationUtil.getSymbolPresentableText(element);
}
if (impls.length == 0 && ref instanceof PsiPolyVariantReference) {
final PsiPolyVariantReference polyReference = (PsiPolyVariantReference)ref;
PsiElement refElement = polyReference.getElement();
TextRange rangeInElement = polyReference.getRangeInElement();
String refElementText = refElement.getText();
LOG.assertTrue(rangeInElement.getEndOffset() <= refElementText.length(),
"Ref:" + polyReference + "; refElement: " + refElement + "; refText:" + refElementText);
text = rangeInElement.substring(refElementText);
final ResolveResult[] results = polyReference.multiResolve(false);
final List<PsiElement> implsList = new ArrayList<>(results.length);
for (ResolveResult result : results) {
final PsiElement resolvedElement = result.getElement();
if (resolvedElement != null && resolvedElement.isPhysical()) {
implsList.add(resolvedElement);
}
}
if (!implsList.isEmpty()) {
impls = implsList.toArray(PsiElement.EMPTY_ARRAY);
}
}
return new PsiImplementationViewSession(project, element, impls, text, editor, file);
}
public static PsiElement getElement(@NotNull Project project, PsiFile file, Editor editor, PsiElement element) {
if (element == null && editor != null) {
element = TargetElementUtil.findTargetElement(editor, TargetElementUtil.getInstance().getAllAccepted());
final PsiElement adjustedElement =
TargetElementUtil.getInstance().adjustElement(editor, TargetElementUtil.getInstance().getAllAccepted(), element, null);
if (adjustedElement != null) {
element = adjustedElement;
}
else if (file != null) {
element = DocumentationManager.getInstance(project).getElementFromLookup(editor, file);
}
}
return element;
}
}
@@ -2,24 +2,17 @@
package com.intellij.codeInsight.hint.actions;
import com.intellij.codeInsight.CodeInsightBundle;
import com.intellij.codeInsight.TargetElementUtil;
import com.intellij.codeInsight.documentation.DocumentationManager;
import com.intellij.codeInsight.hint.ImplementationViewComponent;
import com.intellij.codeInsight.hint.ImplementationViewElement;
import com.intellij.codeInsight.hint.PsiImplementationViewElement;
import com.intellij.codeInsight.hint.PsiImplementationViewSession;
import com.intellij.codeInsight.lookup.LookupManager;
import com.intellij.codeInsight.navigation.BackgroundUpdaterTask;
import com.intellij.codeInsight.navigation.ImplementationSearcher;
import com.intellij.featureStatistics.FeatureUsageTracker;
import com.intellij.ide.DataManager;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.TextEditor;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.impl.BackgroundableProcessIndicator;
@@ -28,34 +21,29 @@ import com.intellij.openapi.ui.ListComponentUpdater;
import com.intellij.openapi.ui.popup.JBPopup;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.PomTargetPsiElement;
import com.intellij.psi.*;
import com.intellij.psi.presentation.java.SymbolPresentationUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
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.UsageView;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ShowImplementationsAction extends AnAction implements PopupAction {
@NonNls public static final String CODEASSISTS_QUICKDEFINITION_LOOKUP_FEATURE = "codeassists.quickdefinition.lookup";
@NonNls public static final String CODEASSISTS_QUICKDEFINITION_FEATURE = "codeassists.quickdefinition";
private static final Logger LOG = Logger.getInstance(ShowImplementationsAction.class);
private Reference<JBPopup> myPopupRef;
private Reference<ImplementationsUpdaterTask> myTaskRef;
@@ -86,157 +74,39 @@ public class ShowImplementationsAction extends AnAction implements PopupAction {
}
protected static Editor getEditor(@NotNull DataContext dataContext) {
Editor editor = CommonDataKeys.EDITOR.getData(dataContext);
if (editor == null) {
final PsiFile file = CommonDataKeys.PSI_FILE.getData(dataContext);
if (file != null) {
final VirtualFile virtualFile = file.getVirtualFile();
if (virtualFile != null) {
final FileEditor fileEditor = FileEditorManager.getInstance(file.getProject()).getSelectedEditor(virtualFile);
if (fileEditor instanceof TextEditor) {
editor = ((TextEditor)fileEditor).getEditor();
}
}
}
}
return editor;
}
public void performForContext(@NotNull DataContext dataContext, boolean invokedByShortcut) {
final Project project = CommonDataKeys.PROJECT.getData(dataContext);
if (project == null) return;
PsiDocumentManager.getInstance(project).commitAllDocuments();
PsiFile file = CommonDataKeys.PSI_FILE.getData(dataContext);
Editor editor = getEditor(dataContext);
PsiElement element = CommonDataKeys.PSI_ELEMENT.getData(dataContext);
boolean isInvokedFromEditor = CommonDataKeys.EDITOR.getData(dataContext) != null;
element = getElement(project, file, editor, element);
if (element == null && file == null) return;
PsiFile containingFile = element != null ? element.getContainingFile() : file;
if (containingFile == null || !containingFile.getViewProvider().isPhysical()) return;
PsiReference ref = null;
if (editor != null) {
ref = TargetElementUtil.findReference(editor, editor.getCaretModel().getOffset());
if (element == null && ref != null) {
element = TargetElementUtil.getInstance().adjustReference(ref);
}
}
//check attached sources if any
if (element instanceof PsiCompiledElement) {
element = element.getNavigationElement();
}
String text = "";
PsiElement[] impls = PsiElement.EMPTY_ARRAY;
if (element != null) {
impls = getSelfAndImplementations(editor, element, createImplementationsSearcher());
text = SymbolPresentationUtil.getSymbolPresentableText(element);
}
if (impls.length == 0 && ref instanceof PsiPolyVariantReference) {
final PsiPolyVariantReference polyReference = (PsiPolyVariantReference)ref;
PsiElement refElement = polyReference.getElement();
TextRange rangeInElement = polyReference.getRangeInElement();
String refElementText = refElement.getText();
LOG.assertTrue(rangeInElement.getEndOffset() <= refElementText.length(),
"Ref:" + polyReference + "; refElement: " + refElement + "; refText:" + refElementText);
text = rangeInElement.substring(refElementText);
final ResolveResult[] results = polyReference.multiResolve(false);
final List<PsiElement> implsList = new ArrayList<>(results.length);
for (ResolveResult result : results) {
final PsiElement resolvedElement = result.getElement();
if (resolvedElement != null && resolvedElement.isPhysical()) {
implsList.add(resolvedElement);
}
}
if (!implsList.isEmpty()) {
impls = implsList.toArray(PsiElement.EMPTY_ARRAY);
}
}
showImplementations(ContainerUtil.map(impls, PsiImplementationViewElement::new), project, text, editor, file, element, isInvokedFromEditor, invokedByShortcut);
PsiImplementationViewSession psiImplementationViewSession = PsiImplementationViewSession.create(dataContext, project, isSearchDeep());
if (psiImplementationViewSession == null) return;
showImplementations(psiImplementationViewSession, isInvokedFromEditor, invokedByShortcut);
}
protected static PsiElement getElement(@NotNull Project project, PsiFile file, Editor editor, PsiElement element) {
if (element == null && editor != null) {
element = TargetElementUtil.findTargetElement(editor, TargetElementUtil.getInstance().getAllAccepted());
final PsiElement adjustedElement =
TargetElementUtil.getInstance().adjustElement(editor, TargetElementUtil.getInstance().getAllAccepted(), element, null);
if (adjustedElement != null) {
element = adjustedElement;
}
else if (file != null) {
element = DocumentationManager.getInstance(project).getElementFromLookup(editor, file);
}
private void updateElementImplementations(final Object lookupItemObject, PsiImplementationViewSession session) {
PsiImplementationViewSession newSession = session.createSessionForLookupElement(lookupItemObject, isSearchDeep());
if (newSession != null) {
showImplementations(newSession, false, false);
}
return element;
}
@NotNull
protected ImplementationSearcher createImplementationsSearcher() {
if (ApplicationManager.getApplication().isUnitTestMode()) {
return new ImplementationSearcher() {
@Override
protected PsiElement[] filterElements(PsiElement element, PsiElement[] targetElements) {
return ShowImplementationsAction.filterElements(targetElements);
}
};
}
return new ImplementationSearcher.FirstImplementationsSearcher() {
@Override
protected PsiElement[] filterElements(PsiElement element, PsiElement[] targetElements) {
return ShowImplementationsAction.filterElements(targetElements);
}
@Override
protected boolean isSearchDeep() {
return ShowImplementationsAction.this.isSearchDeep();
}
};
}
private void updateElementImplementations(final PsiElement element, final Editor editor, @NotNull Project project, final PsiFile file) {
PsiElement[] impls = {};
String text = "";
if (element != null) {
// if (element instanceof PsiPackage) return;
PsiFile containingFile = element.getContainingFile();
if (containingFile == null || !containingFile.getViewProvider().isPhysical()) return;
impls = getSelfAndImplementations(editor, element, createImplementationsSearcher());
text = SymbolPresentationUtil.getSymbolPresentableText(element);
}
showImplementations(ContainerUtil.map(impls, PsiImplementationViewElement::new), project, text, editor, file, element, false, false);
}
protected void showImplementations(@NotNull List<ImplementationViewElement> impls,
@NotNull final Project project,
final String text,
final Editor editor,
final PsiFile file,
final PsiElement element,
protected void showImplementations(@NotNull PsiImplementationViewSession session,
boolean invokedFromEditor,
boolean invokedByShortcut) {
List<ImplementationViewElement> impls = session.getImplementationElements();
if (impls.size() == 0) return;
Project project = session.getProject();
FeatureUsageTracker.getInstance().triggerFeatureUsed(CODEASSISTS_QUICKDEFINITION_FEATURE);
if (LookupManager.getInstance(project).getActiveLookup() != null) {
FeatureUsageTracker.getInstance().triggerFeatureUsed(CODEASSISTS_QUICKDEFINITION_LOOKUP_FEATURE);
}
PsiFile file = session.getFile();
int index = 0;
if (invokedFromEditor && file != null && impls.size() > 1) {
final VirtualFile virtualFile = file.getVirtualFile();
@@ -250,13 +120,13 @@ public class ShowImplementationsAction extends AnAction implements PopupAction {
}
final Ref<UsageView> usageView = new Ref<>();
final String title = CodeInsightBundle.message("implementation.view.title", text);
final String title = CodeInsightBundle.message("implementation.view.title", session.getText());
JBPopup popup = SoftReference.dereference(myPopupRef);
if (popup != null && popup.isVisible() && popup instanceof AbstractPopup) {
final ImplementationViewComponent component = (ImplementationViewComponent) ((AbstractPopup)popup).getComponent();
((AbstractPopup)popup).setCaption(title);
component.update(impls, index);
updateInBackground(editor, element, component, title, (AbstractPopup)popup, usageView);
updateInBackground(session, component, title, (AbstractPopup)popup, usageView);
if (invokedByShortcut) {
((AbstractPopup)popup).focusPreferredComponent();
}
@@ -268,8 +138,7 @@ public class ShowImplementationsAction extends AnAction implements PopupAction {
final PopupUpdateProcessor updateProcessor = new PopupUpdateProcessor(project) {
@Override
public void updatePopup(Object lookupItemObject) {
final PsiElement element = lookupItemObject instanceof PsiElement ? (PsiElement)lookupItemObject : DocumentationManager.getInstance(project).getElementFromLookup(editor, file);
updateElementImplementations(element, editor, project, file);
updateElementImplementations(lookupItemObject, session);
}
};
@@ -280,7 +149,7 @@ public class ShowImplementationsAction extends AnAction implements PopupAction {
.setDimensionServiceKey(project, DocumentationManager.JAVADOC_LOCATION_AND_SIZE, false)
.setResizable(true)
.setMovable(true)
.setRequestFocus(invokedFromEditor && LookupManager.getActiveLookup(editor) == null)
.setRequestFocus(invokedFromEditor && LookupManager.getActiveLookup(session.getEditor()) == null)
.setTitle(title)
.setCouldPin(popup1 -> {
usageView.set(component.showInUsageView());
@@ -297,17 +166,16 @@ public class ShowImplementationsAction extends AnAction implements PopupAction {
})
.createPopup();
updateInBackground(editor, element, component, title, (AbstractPopup)popup, usageView);
updateInBackground(session, component, title, (AbstractPopup)popup, usageView);
PopupPositionManager.positionPopupInBestPosition(popup, editor, DataManager.getInstance().getDataContext());
PopupPositionManager.positionPopupInBestPosition(popup, session.getEditor(), DataManager.getInstance().getDataContext());
component.setHint(popup, title);
myPopupRef = new WeakReference<>(popup);
}
}
private void updateInBackground(Editor editor,
@Nullable PsiElement element,
private void updateInBackground(@NotNull PsiImplementationViewSession session,
@NotNull ImplementationViewComponent component,
String title,
@NotNull AbstractPopup popup,
@@ -317,9 +185,9 @@ public class ShowImplementationsAction extends AnAction implements PopupAction {
updaterTask.cancelTask();
}
if (element == null) return; //already found
final ImplementationsUpdaterTask task = new ImplementationsUpdaterTask(element, editor, title, isIncludeAlwaysSelf(), component);
task.init(popup, new ImplementationViewComponentUpdater(component, element), usageView);
if (!session.needUpdateInBackground()) return; // already found
final ImplementationsUpdaterTask task = new ImplementationsUpdaterTask(session, title, isIncludeAlwaysSelf(), component);
task.init(popup, new ImplementationViewComponentUpdater(component, session.elementRequiresIncludeSelf() ? 1 : 0), usageView);
myTaskRef = new WeakReference<>(task);
ProgressManager.getInstance().runProcessWithProgressAsynchronously(task, new BackgroundableProcessIndicator(task));
@@ -329,73 +197,17 @@ public class ShowImplementationsAction extends AnAction implements PopupAction {
return true;
}
@NotNull
private static PsiElement[] getSelfAndImplementations(Editor editor,
@NotNull PsiElement element,
@NotNull ImplementationSearcher handler) {
return getSelfAndImplementations(editor, element, handler, !(element instanceof PomTargetPsiElement));
}
@NotNull
protected static PsiElement[] getSelfAndImplementations(Editor editor,
@NotNull PsiElement element,
@NotNull ImplementationSearcher handler,
final boolean includeSelfAlways) {
final PsiElement[] handlerImplementations = handler.searchImplementations(element, editor, includeSelfAlways, true);
if (handlerImplementations.length > 0) return handlerImplementations;
return ReadAction.compute(() -> {
PsiElement psiElement = element;
PsiFile psiFile = psiElement.getContainingFile();
if (psiFile == null) {
// Magically, it's null for ant property declarations.
psiElement = psiElement.getNavigationElement();
psiFile = psiElement.getContainingFile();
if (psiFile == null) {
return PsiElement.EMPTY_ARRAY;
}
}
if (psiFile.getVirtualFile() != null && (psiElement.getTextRange() != null || psiElement instanceof PsiFile)) {
return new PsiElement[]{psiElement};
}
return PsiElement.EMPTY_ARRAY;
});
}
@NotNull
private static PsiElement[] filterElements(@NotNull final PsiElement[] targetElements) {
final Set<PsiElement> unique = new LinkedHashSet<>(Arrays.asList(targetElements));
for (final PsiElement elt : targetElements) {
ApplicationManager.getApplication().runReadAction(() -> {
final PsiFile containingFile = elt.getContainingFile();
LOG.assertTrue(containingFile != null, elt);
PsiFile psiFile = containingFile.getOriginalFile();
if (psiFile.getVirtualFile() == null) unique.remove(elt);
});
}
// special case for Python (PY-237)
// if the definition is the tree parent of the target element, filter out the target element
for (int i = 1; i < targetElements.length; i++) {
final PsiElement targetElement = targetElements[i];
if (ReadAction.compute(() -> PsiTreeUtil.isAncestor(targetElement, targetElements[0], true))) {
unique.remove(targetElements[0]);
break;
}
}
return PsiUtilCore.toPsiElementArray(unique);
}
protected boolean isSearchDeep() {
return false;
}
private static class ImplementationViewComponentUpdater implements ListComponentUpdater {
private final ImplementationViewComponent myComponent;
private final PsiElement myElement;
private int myIncludeSelfIdx;
ImplementationViewComponentUpdater(ImplementationViewComponent component, PsiElement element) {
ImplementationViewComponentUpdater(ImplementationViewComponent component, int includeSelfIdx) {
myComponent = component;
myElement = element;
myIncludeSelfIdx = includeSelfIdx;
}
@Override
@@ -406,7 +218,7 @@ public class ShowImplementationsAction extends AnAction implements PopupAction {
@Override
public void replaceModel(@NotNull List<? extends PsiElement> data) {
final ImplementationViewElement[] elements = myComponent.getElements();
final int includeSelfIdx = myElement instanceof PomTargetPsiElement ? 0 : 1;
final int includeSelfIdx = myIncludeSelfIdx;
final int startIdx = elements.length - includeSelfIdx;
List<ImplementationViewElement> result = new ArrayList<>();
Collections.addAll(result, elements);
@@ -419,22 +231,18 @@ public class ShowImplementationsAction extends AnAction implements PopupAction {
private class ImplementationsUpdaterTask extends BackgroundUpdaterTask {
private final String myCaption;
private final Editor myEditor;
@NotNull
private final PsiElement myElement;
private final PsiImplementationViewSession mySession;
private final boolean myIncludeSelf;
private final ImplementationViewComponent myComponent;
private PsiElement[] myElements;
private List<ImplementationViewElement> myElements;
private ImplementationsUpdaterTask(@NotNull PsiElement element,
final Editor editor,
private ImplementationsUpdaterTask(PsiImplementationViewSession session,
final String caption,
boolean includeSelf,
ImplementationViewComponent component) {
super(element.getProject(), ImplementationSearcher.SEARCHING_FOR_IMPLEMENTATIONS, null);
super(session.getProject(), ImplementationSearcher.SEARCHING_FOR_IMPLEMENTATIONS, null);
myCaption = caption;
myEditor = editor;
myElement = element;
mySession = session;
myIncludeSelf = includeSelf;
myComponent = component;
}
@@ -448,44 +256,19 @@ public class ShowImplementationsAction extends AnAction implements PopupAction {
@Override
public void run(@NotNull final ProgressIndicator indicator) {
super.run(indicator);
final ImplementationSearcher.BackgroundableImplementationSearcher implementationSearcher =
new ImplementationSearcher.BackgroundableImplementationSearcher() {
@Override
protected boolean isSearchDeep() {
return ShowImplementationsAction.this.isSearchDeep();
}
@Override
protected void processElement(PsiElement element) {
if (!updateComponent(element)) {
indicator.cancel();
}
indicator.checkCanceled();
}
@Override
protected PsiElement[] filterElements(PsiElement element, PsiElement[] targetElements) {
return ShowImplementationsAction.filterElements(targetElements);
}
};
if (!myIncludeSelf) {
myElements = getSelfAndImplementations(myEditor, myElement, implementationSearcher, false);
}
else {
myElements = getSelfAndImplementations(myEditor, myElement, implementationSearcher);
}
myElements = mySession.searchImplementationsInBackground(indicator, isSearchDeep(), myIncludeSelf, this::updateComponent);
}
@Override
public int getCurrentSize() {
if (myElements != null) return myElements.length;
if (myElements != null) return myElements.size();
return super.getCurrentSize();
}
@Override
public void onSuccess() {
if (!cancelTask()) {
myComponent.update(ContainerUtil.map(myElements, PsiImplementationViewElement::new), myComponent.getIndex());
myComponent.update(myElements, myComponent.getIndex());
}
super.onSuccess();
}