2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com
.intellij
.codeInsight
.hint
.actions
;
18 import com
.intellij
.codeInsight
.CodeInsightBundle
;
19 import com
.intellij
.codeInsight
.TargetElementUtilBase
;
20 import com
.intellij
.codeInsight
.documentation
.DocumentationManager
;
21 import com
.intellij
.codeInsight
.hint
.ImplementationViewComponent
;
22 import com
.intellij
.codeInsight
.lookup
.LookupManager
;
23 import com
.intellij
.codeInsight
.navigation
.ImplementationSearcher
;
24 import com
.intellij
.featureStatistics
.FeatureUsageTracker
;
25 import com
.intellij
.ide
.DataManager
;
26 import com
.intellij
.openapi
.actionSystem
.*;
27 import com
.intellij
.openapi
.editor
.Editor
;
28 import com
.intellij
.openapi
.fileEditor
.FileEditor
;
29 import com
.intellij
.openapi
.fileEditor
.FileEditorManager
;
30 import com
.intellij
.openapi
.fileEditor
.TextEditor
;
31 import com
.intellij
.openapi
.project
.Project
;
32 import com
.intellij
.openapi
.ui
.popup
.JBPopup
;
33 import com
.intellij
.openapi
.ui
.popup
.JBPopupFactory
;
34 import com
.intellij
.openapi
.vfs
.VirtualFile
;
35 import com
.intellij
.pom
.PomTargetPsiElement
;
36 import com
.intellij
.psi
.*;
37 import com
.intellij
.psi
.presentation
.java
.SymbolPresentationUtil
;
38 import com
.intellij
.psi
.util
.PsiTreeUtil
;
39 import com
.intellij
.ui
.popup
.NotLookupOrSearchCondition
;
40 import com
.intellij
.ui
.popup
.PopupUpdateProcessor
;
41 import org
.jetbrains
.annotations
.NonNls
;
45 public class ShowImplementationsAction
extends AnAction
{
46 @NonNls public static final String CODEASSISTS_QUICKDEFINITION_LOOKUP_FEATURE
= "codeassists.quickdefinition.lookup";
47 @NonNls public static final String CODEASSISTS_QUICKDEFINITION_FEATURE
= "codeassists.quickdefinition";
49 public ShowImplementationsAction() {
50 setEnabledInModalContext(true);
51 setInjectedContext(true);
54 public void actionPerformed(AnActionEvent e
) {
55 performForContext(e
.getDataContext());
58 public void performForContext(DataContext dataContext
) {
59 final Project project
= PlatformDataKeys
.PROJECT
.getData(dataContext
);
60 Editor editor
= PlatformDataKeys
.EDITOR
.getData(dataContext
);
61 PsiFile file
= LangDataKeys
.PSI_FILE
.getData(dataContext
);
63 if (project
== null) return;
65 PsiDocumentManager
.getInstance(project
).commitAllDocuments();
67 boolean isInvokedFromEditor
= editor
!= null;
70 element
= TargetElementUtilBase
.findTargetElement(editor
, TargetElementUtilBase
.getInstance().getAllAccepted());
73 element
= LangDataKeys
.PSI_ELEMENT
.getData(dataContext
);
75 final FileEditor fileEditor
= FileEditorManager
.getInstance(project
).getSelectedEditor(file
.getVirtualFile());
76 if (fileEditor
instanceof TextEditor
) {
77 editor
= ((TextEditor
)fileEditor
).getEditor();
83 PsiElement
[] impls
= new PsiElement
[0];
84 PsiReference ref
= null;
86 final PsiElement adjustedElement
=
87 TargetElementUtilBase
.getInstance().adjustElement(editor
, TargetElementUtilBase
.getInstance().getAllAccepted(), element
, null);
88 if (adjustedElement
!= null) {
89 element
= adjustedElement
;
90 } else if (file
!= null && editor
!= null) {
91 element
= DocumentationManager
.getInstance(project
).getElementFromLookup(editor
, file
);
95 ref
= TargetElementUtilBase
.findReference(editor
, editor
.getCaretModel().getOffset());
96 if (element
== null && ref
!= null) {
97 element
= TargetElementUtilBase
.getInstance().adjustReference(ref
);
101 if (element
!= null) {
102 //if (element instanceof PsiPackage) return;
104 impls
= getSelfAndImplementations(editor
, element
);
105 text
= SymbolPresentationUtil
.getSymbolPresentableText(element
);
108 if (impls
.length
== 0 && ref
instanceof PsiPolyVariantReference
) {
109 final PsiPolyVariantReference polyReference
= (PsiPolyVariantReference
)ref
;
110 text
= polyReference
.getRangeInElement().substring(polyReference
.getElement().getText());
111 final ResolveResult
[] results
= polyReference
.multiResolve(false);
112 final List
<PsiElement
> implsList
= new ArrayList
<PsiElement
>(results
.length
);
114 for (ResolveResult result
: results
) {
115 final PsiElement resolvedElement
= result
.getElement();
117 if (resolvedElement
!= null && resolvedElement
.isPhysical()) {
118 implsList
.add(resolvedElement
);
122 if (!implsList
.isEmpty()) {
123 implsList
.toArray( impls
= new PsiElement
[implsList
.size()] );
128 showImplementations(impls
, project
, text
, editor
, file
, isInvokedFromEditor
);
131 protected void updateElementImplementations(final PsiElement element
, final Editor editor
, final Project project
, final PsiFile file
) {
132 PsiElement
[] impls
= null;
134 if (element
!= null) {
135 // if (element instanceof PsiPackage) return;
137 impls
= getSelfAndImplementations(editor
, element
);
138 text
= SymbolPresentationUtil
.getSymbolPresentableText(element
);
141 showImplementations(impls
, project
, text
, editor
, file
, false);
144 protected void showImplementations(final PsiElement
[] impls
, final Project project
, final String text
, final Editor editor
, final PsiFile file
,
145 boolean invokedFromEditor
) {
146 if (impls
== null || impls
.length
== 0) return;
148 FeatureUsageTracker
.getInstance().triggerFeatureUsed(CODEASSISTS_QUICKDEFINITION_FEATURE
);
149 if (LookupManager
.getInstance(project
).getActiveLookup() != null) {
150 FeatureUsageTracker
.getInstance().triggerFeatureUsed(CODEASSISTS_QUICKDEFINITION_LOOKUP_FEATURE
);
154 if (invokedFromEditor
&& file
!= null && impls
.length
> 1) {
155 final VirtualFile virtualFile
= file
.getVirtualFile();
156 final PsiFile containingFile
= impls
[0].getContainingFile();
157 if (virtualFile
!= null && containingFile
!= null && virtualFile
.equals(containingFile
.getVirtualFile())) {
161 final ImplementationViewComponent component
= new ImplementationViewComponent(impls
, index
);
162 if (component
.hasElementsToShow()) {
163 final PopupUpdateProcessor updateProcessor
= new PopupUpdateProcessor(project
) {
164 public void updatePopup(Object lookupItemObject
) {
165 final PsiElement element
= lookupItemObject
instanceof PsiElement ?
(PsiElement
)lookupItemObject
: DocumentationManager
.getInstance(project
).getElementFromLookup(editor
, file
);
166 updateElementImplementations(element
, editor
, project
, file
);
169 final String title
= CodeInsightBundle
.message("implementation.view.title", text
);
170 final JBPopup popup
= JBPopupFactory
.getInstance().createComponentPopupBuilder(component
, component
.getPrefferedFocusableComponent())
171 .setRequestFocusCondition(project
, NotLookupOrSearchCondition
.INSTANCE
)
173 .addListener(updateProcessor
)
174 .addUserData(updateProcessor
)
175 .setDimensionServiceKey(project
, "ShowImplementationPopup", false)
180 popup
.showInBestPositionFor(DataManager
.getInstance().getDataContext());
181 component
.setHint(popup
, title
);
186 private static PsiElement
[] getSelfAndImplementations(Editor editor
, PsiElement element
) {
187 ImplementationSearcher handler
= new ImplementationSearcher() {
188 protected PsiElement
[] filterElements(PsiElement element
, PsiElement
[] targetElements
, final int offset
) {
189 Set
<PsiElement
> unique
= new LinkedHashSet
<PsiElement
>(Arrays
.asList(targetElements
));
190 for (PsiElement elt
: targetElements
) {
191 PsiFile psiFile
= elt
.getContainingFile().getOriginalFile();
192 if (psiFile
.getVirtualFile() == null) unique
.remove(elt
);
194 // special case for Python (PY-237)
195 // if the definition is the tree parent of the target element, filter out the target element
196 for (int i
= 1; i
< targetElements
.length
; i
++) {
197 if (PsiTreeUtil
.isAncestor(targetElements
[i
], targetElements
[0], true)) {
198 unique
.remove(targetElements
[0]);
202 return unique
.toArray(new PsiElement
[unique
.size()]);
206 int offset
= editor
== null ?
0 : editor
.getCaretModel().getOffset();
207 final PsiElement
[] handlerImplementations
= handler
.searchImplementations(element
, offset
, !(element
instanceof PomTargetPsiElement
), true);
208 if (handlerImplementations
.length
> 0) return handlerImplementations
;
210 PsiFile psiFile
= element
.getContainingFile();
211 if (psiFile
== null) {
212 // Magically, it's null for ant property declarations.
213 element
= element
.getNavigationElement();
214 psiFile
= element
.getContainingFile();
215 if (psiFile
== null) return PsiElement
.EMPTY_ARRAY
;
217 if (psiFile
.getVirtualFile() != null && (element
.getTextRange() != null || element
instanceof PsiFile
)) {
218 return new PsiElement
[]{element
};
221 return PsiElement
.EMPTY_ARRAY
;
226 public void update(final AnActionEvent e
) {
227 final Project project
= e
.getData(PlatformDataKeys
.PROJECT
);
228 e
.getPresentation().setEnabled(project
!= null);