special case of show implementations for Python: show initializer for target expressi...
[fedora-idea.git] / platform / lang-impl / src / com / intellij / codeInsight / hint / actions / ShowImplementationsAction.java
blob7583c57b8b223675e8a5f92bb31eb8dd5be8fd03
1 /*
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;
43 import java.util.*;
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;
68 PsiElement element;
69 if (editor != null) {
70 element = TargetElementUtilBase.findTargetElement(editor, TargetElementUtilBase.getInstance().getAllAccepted());
72 else {
73 element = LangDataKeys.PSI_ELEMENT.getData(dataContext);
74 if (file != null) {
75 final FileEditor fileEditor = FileEditorManager.getInstance(project).getSelectedEditor(file.getVirtualFile());
76 if (fileEditor instanceof TextEditor) {
77 editor = ((TextEditor)fileEditor).getEditor();
82 String text = "";
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);
94 if (editor != null) {
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;
133 String text = "";
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);
153 int index = 0;
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())) {
158 index = 1;
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)
172 .setProject(project)
173 .addListener(updateProcessor)
174 .addUserData(updateProcessor)
175 .setDimensionServiceKey(project, "ShowImplementationPopup", false)
176 .setResizable(true)
177 .setMovable(true)
178 .setTitle(title)
179 .createPopup();
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]);
199 break;
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};
220 else {
221 return PsiElement.EMPTY_ARRAY;
225 @Override
226 public void update(final AnActionEvent e) {
227 final Project project = e.getData(PlatformDataKeys.PROJECT);
228 e.getPresentation().setEnabled(project != null);