diff --git a/java/java-impl/src/com/intellij/codeInsight/hint/actions/ShowSiblingsAction.java b/java/java-impl/src/com/intellij/codeInsight/hint/actions/ShowSiblingsAction.java index 24fdedb8dc35..34dfe233d902 100644 --- a/java/java-impl/src/com/intellij/codeInsight/hint/actions/ShowSiblingsAction.java +++ b/java/java-impl/src/com/intellij/codeInsight/hint/actions/ShowSiblingsAction.java @@ -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 diff --git a/java/testFramework/src/com/intellij/codeInsight/ShowImplementationsTestUtil.java b/java/testFramework/src/com/intellij/codeInsight/ShowImplementationsTestUtil.java index 97aa03e75946..70b3efe52cc3 100644 --- a/java/testFramework/src/com/intellij/codeInsight/ShowImplementationsTestUtil.java +++ b/java/testFramework/src/com/intellij/codeInsight/ShowImplementationsTestUtil.java @@ -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> ref = new Ref<>(); new ShowImplementationsAction() { @Override - protected void showImplementations(@NotNull List 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()); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/hint/PsiImplementationViewSession.java b/platform/lang-impl/src/com/intellij/codeInsight/hint/PsiImplementationViewSession.java new file mode 100644 index 000000000000..9f73837ed6c2 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/hint/PsiImplementationViewSession.java @@ -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 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 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 searchImplementationsInBackground(@NotNull ProgressIndicator indicator, + boolean isSearchDeep, + boolean includeSelf, + final Processor 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 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; + } +} 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 cfa38922f976..bf1a7aa20bcb 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 @@ -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 myPopupRef; private Reference 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 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 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 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 = 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 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 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 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 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(); }