Implement navigation to references from Call Hierarchy view

#KT-4518 Fixed
This commit is contained in:
Alexey Sedunov
2014-02-04 15:51:31 +04:00
committed by Aleksei Sedunov
parent c6c1389144
commit 7e1c9194f1
4 changed files with 50 additions and 34 deletions
@@ -14,6 +14,7 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.pom.Navigatable;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.ui.LayeredIcon;
import com.intellij.util.Function;
@@ -30,9 +31,12 @@ import org.jetbrains.jet.renderer.DescriptorRenderer;
import javax.swing.*;
import java.awt.*;
import java.util.HashSet;
import java.util.Set;
public class KotlinCallHierarchyNodeDescriptor extends HierarchyNodeDescriptor implements Navigatable {
private int usageCount = 1;
private int usageCount = 0;
private final Set<PsiReference> references = new HashSet<PsiReference>();
private final CallHierarchyNodeDescriptor javaDelegate;
public KotlinCallHierarchyNodeDescriptor(@NotNull Project project,
@@ -48,8 +52,11 @@ public class KotlinCallHierarchyNodeDescriptor extends HierarchyNodeDescriptor i
return javaDelegate;
}
public final void incrementUsageCount(){
usageCount++;
public final void addReference(PsiReference reference) {
if (references.add(reference)) {
usageCount++;
}
javaDelegate.addReference(reference);
}
public final PsiElement getTargetElement(){
@@ -210,18 +217,16 @@ public class KotlinCallHierarchyNodeDescriptor extends HierarchyNodeDescriptor i
@Override
public void navigate(boolean requestFocus) {
if (myElement instanceof Navigatable) {
((Navigatable)myElement).navigate(requestFocus);
}
javaDelegate.navigate(requestFocus);
}
@Override
public boolean canNavigate() {
return myElement instanceof Navigatable && ((Navigatable) myElement).canNavigate();
return javaDelegate.canNavigate();
}
@Override
public boolean canNavigateToSource() {
return canNavigate();
return javaDelegate.canNavigateToSource();
}
}
@@ -7,6 +7,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiReference;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.HashMap;
import jet.Function1;
@@ -16,14 +17,13 @@ import org.jetbrains.jet.asJava.LightClassUtil;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.psi.psiUtil.PsiUtilPackage;
import java.util.List;
import java.util.Map;
public abstract class KotlinCallTreeStructure extends HierarchyTreeStructure {
protected final String scopeType;
public KotlinCallTreeStructure(@NotNull Project project, PsiElement element, String scopeType) {
super(project, createNodeDescriptor(project, element, null));
super(project, createNodeDescriptor(project, element, null, false));
this.scopeType = scopeType;
}
@@ -33,11 +33,13 @@ public abstract class KotlinCallTreeStructure extends HierarchyTreeStructure {
: null;
}
protected static HierarchyNodeDescriptor createNodeDescriptor(Project project, PsiElement element, HierarchyNodeDescriptor parent) {
protected static HierarchyNodeDescriptor createNodeDescriptor(
Project project, PsiElement element, HierarchyNodeDescriptor parent, boolean navigateToReference
) {
boolean root = (parent == null);
return element instanceof JetElement
? new KotlinCallHierarchyNodeDescriptor(project, parent, element, root, false)
: new CallHierarchyNodeDescriptor(project, parent, element, root, false);
? new KotlinCallHierarchyNodeDescriptor(project, parent, element, root, navigateToReference)
: new CallHierarchyNodeDescriptor(project, parent, element, root, navigateToReference);
}
protected static PsiElement getTargetElement(HierarchyNodeDescriptor descriptor) {
@@ -102,34 +104,40 @@ public abstract class KotlinCallTreeStructure extends HierarchyTreeStructure {
}
protected Object[] collectNodeDescriptors(
HierarchyNodeDescriptor descriptor, List<? extends PsiElement> calleeElements, PsiClass basePsiClass
HierarchyNodeDescriptor descriptor, Map<PsiReference, PsiElement> referencesToCalleeElements, PsiClass basePsiClass
) {
HashMap<PsiElement, HierarchyNodeDescriptor> declarationToDescriptorMap = new HashMap<PsiElement, HierarchyNodeDescriptor>();
for (PsiElement callee : calleeElements) {
for (Map.Entry<PsiReference, PsiElement> refToCallee : referencesToCalleeElements.entrySet()) {
PsiReference ref = refToCallee.getKey();
PsiElement callee = refToCallee.getValue();
if (basePsiClass != null && !isInScope(basePsiClass, callee, scopeType)) continue;
addNodeDescriptorForElement(callee, declarationToDescriptorMap, descriptor);
addNodeDescriptorForElement(ref, callee, declarationToDescriptorMap, descriptor);
}
return declarationToDescriptorMap.values().toArray(new Object[declarationToDescriptorMap.size()]);
}
protected final void addNodeDescriptorForElement(
PsiReference reference,
PsiElement element,
Map<PsiElement, HierarchyNodeDescriptor> declarationToDescriptorMap,
HierarchyNodeDescriptor descriptor
) {
HierarchyNodeDescriptor d = declarationToDescriptorMap.get(element);
if (d == null) {
d = createNodeDescriptor(myProject, element, descriptor);
d = createNodeDescriptor(myProject, element, descriptor, true);
declarationToDescriptorMap.put(element, d);
}
else {
if (d instanceof CallHierarchyNodeDescriptor) {
((CallHierarchyNodeDescriptor) d).incrementUsageCount();
}
else if (d instanceof KotlinCallHierarchyNodeDescriptor) {
((KotlinCallHierarchyNodeDescriptor) d).incrementUsageCount();
}
else if (d instanceof CallHierarchyNodeDescriptor) {
((CallHierarchyNodeDescriptor) d).incrementUsageCount();
}
if (d instanceof CallHierarchyNodeDescriptor) {
((CallHierarchyNodeDescriptor) d).addReference(reference);
}
else if (d instanceof KotlinCallHierarchyNodeDescriptor) {
((KotlinCallHierarchyNodeDescriptor) d).addReference(reference);
}
}
@@ -23,13 +23,16 @@ import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiReference;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.java.jetAsJava.KotlinLightMethod;
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class KotlinCalleeMethodsTreeStructure extends KotlinCallTreeStructure {
private final CalleeMethodsTreeStructure javaTreeStructure;
@@ -45,7 +48,7 @@ public class KotlinCalleeMethodsTreeStructure extends KotlinCallTreeStructure {
this.javaTreeStructure = new CalleeMethodsTreeStructure(project, representativePsiMethod, scopeType);
}
private static List<? extends PsiElement> getCalleeElements(@NotNull JetElement rootElement) {
private static Map<PsiReference, PsiElement> getReferencesToCalleeElements(@NotNull JetElement rootElement) {
List<JetElement> elementsToAnalyze = new ArrayList<JetElement>();
if (rootElement instanceof JetNamedFunction) {
elementsToAnalyze.add(((JetNamedFunction) rootElement).getBodyExpression());
@@ -78,19 +81,19 @@ public class KotlinCalleeMethodsTreeStructure extends KotlinCallTreeStructure {
}
}
final ArrayList<PsiElement> result = new ArrayList<PsiElement>();
final Map<PsiReference, PsiElement> referencesToCalleeElements = new HashMap<PsiReference, PsiElement>();
for (JetElement element : elementsToAnalyze) {
element.accept(
new CalleeReferenceVisitorBase(AnalyzerFacadeWithCache.getContextForElement(element), false) {
@Override
protected void processDeclaration(JetReferenceExpression reference, PsiElement declaration) {
result.add(declaration);
referencesToCalleeElements.put(reference.getReference(), declaration);
}
}
);
}
return result;
return referencesToCalleeElements;
}
@NotNull
@@ -126,7 +129,7 @@ public class KotlinCalleeMethodsTreeStructure extends KotlinCallTreeStructure {
}
private Object[] buildChildrenByKotlinTarget(HierarchyNodeDescriptor descriptor, JetElement targetElement) {
List<? extends PsiElement> calleeDescriptors = getCalleeElements(targetElement);
return collectNodeDescriptors(descriptor, calleeDescriptors, representativePsiClass);
Map<PsiReference, PsiElement> referencesToCalleeElements = getReferencesToCalleeElements(targetElement);
return collectNodeDescriptors(descriptor, referencesToCalleeElements, representativePsiClass);
}
}
@@ -66,7 +66,7 @@ public abstract class KotlinCallerMethodsTreeStructure extends KotlinCallTreeStr
BindingContext bindingContext = AnalyzerFacadeWithCache.getContextForElement((JetElement) element);
final ArrayList<PsiElement> result = new ArrayList<PsiElement>();
final Map<PsiReference, PsiElement> referencesToElements = new HashMap<PsiReference, PsiElement>();
codeBlockForLocalDeclaration.accept(new CalleeReferenceVisitorBase(bindingContext, true) {
@Override
protected void processDeclaration(JetReferenceExpression reference, PsiElement declaration) {
@@ -80,11 +80,11 @@ public abstract class KotlinCallerMethodsTreeStructure extends KotlinCallTreeStr
}
if (container != null) {
result.add(container);
referencesToElements.put(reference.getReference(), container);
}
}
});
return collectNodeDescriptors(descriptor, result, null);
return collectNodeDescriptors(descriptor, referencesToElements, null);
}
}
@@ -230,7 +230,7 @@ public abstract class KotlinCallerMethodsTreeStructure extends KotlinCallTreeStr
}
if (element != null) {
addNodeDescriptorForElement(element, methodToDescriptorMap, descriptor);
addNodeDescriptorForElement(ref, element, methodToDescriptorMap, descriptor);
}
return true;