mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Extract PsiImplementationViewSession class for implementation view search logic
This commit is contained in:
@@ -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());
|
||||
|
||||
+315
@@ -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;
|
||||
}
|
||||
}
|
||||
+41
-258
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user