Implement navigation to references from Call Hierarchy view
#KT-4518 Fixed
This commit is contained in:
committed by
Aleksei Sedunov
parent
c6c1389144
commit
7e1c9194f1
+13
-8
@@ -14,6 +14,7 @@ import com.intellij.openapi.util.text.StringUtil;
|
|||||||
import com.intellij.pom.Navigatable;
|
import com.intellij.pom.Navigatable;
|
||||||
import com.intellij.psi.PsiClass;
|
import com.intellij.psi.PsiClass;
|
||||||
import com.intellij.psi.PsiElement;
|
import com.intellij.psi.PsiElement;
|
||||||
|
import com.intellij.psi.PsiReference;
|
||||||
import com.intellij.psi.util.PsiTreeUtil;
|
import com.intellij.psi.util.PsiTreeUtil;
|
||||||
import com.intellij.ui.LayeredIcon;
|
import com.intellij.ui.LayeredIcon;
|
||||||
import com.intellij.util.Function;
|
import com.intellij.util.Function;
|
||||||
@@ -30,9 +31,12 @@ import org.jetbrains.jet.renderer.DescriptorRenderer;
|
|||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public class KotlinCallHierarchyNodeDescriptor extends HierarchyNodeDescriptor implements Navigatable {
|
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;
|
private final CallHierarchyNodeDescriptor javaDelegate;
|
||||||
|
|
||||||
public KotlinCallHierarchyNodeDescriptor(@NotNull Project project,
|
public KotlinCallHierarchyNodeDescriptor(@NotNull Project project,
|
||||||
@@ -48,8 +52,11 @@ public class KotlinCallHierarchyNodeDescriptor extends HierarchyNodeDescriptor i
|
|||||||
return javaDelegate;
|
return javaDelegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void incrementUsageCount(){
|
public final void addReference(PsiReference reference) {
|
||||||
usageCount++;
|
if (references.add(reference)) {
|
||||||
|
usageCount++;
|
||||||
|
}
|
||||||
|
javaDelegate.addReference(reference);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final PsiElement getTargetElement(){
|
public final PsiElement getTargetElement(){
|
||||||
@@ -210,18 +217,16 @@ public class KotlinCallHierarchyNodeDescriptor extends HierarchyNodeDescriptor i
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void navigate(boolean requestFocus) {
|
public void navigate(boolean requestFocus) {
|
||||||
if (myElement instanceof Navigatable) {
|
javaDelegate.navigate(requestFocus);
|
||||||
((Navigatable)myElement).navigate(requestFocus);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canNavigate() {
|
public boolean canNavigate() {
|
||||||
return myElement instanceof Navigatable && ((Navigatable) myElement).canNavigate();
|
return javaDelegate.canNavigate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canNavigateToSource() {
|
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.PsiClass;
|
||||||
import com.intellij.psi.PsiElement;
|
import com.intellij.psi.PsiElement;
|
||||||
import com.intellij.psi.PsiMethod;
|
import com.intellij.psi.PsiMethod;
|
||||||
|
import com.intellij.psi.PsiReference;
|
||||||
import com.intellij.util.ArrayUtil;
|
import com.intellij.util.ArrayUtil;
|
||||||
import com.intellij.util.containers.HashMap;
|
import com.intellij.util.containers.HashMap;
|
||||||
import jet.Function1;
|
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.*;
|
||||||
import org.jetbrains.jet.lang.psi.psiUtil.PsiUtilPackage;
|
import org.jetbrains.jet.lang.psi.psiUtil.PsiUtilPackage;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public abstract class KotlinCallTreeStructure extends HierarchyTreeStructure {
|
public abstract class KotlinCallTreeStructure extends HierarchyTreeStructure {
|
||||||
protected final String scopeType;
|
protected final String scopeType;
|
||||||
|
|
||||||
public KotlinCallTreeStructure(@NotNull Project project, PsiElement element, 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;
|
this.scopeType = scopeType;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,11 +33,13 @@ public abstract class KotlinCallTreeStructure extends HierarchyTreeStructure {
|
|||||||
: null;
|
: 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);
|
boolean root = (parent == null);
|
||||||
return element instanceof JetElement
|
return element instanceof JetElement
|
||||||
? new KotlinCallHierarchyNodeDescriptor(project, parent, element, root, false)
|
? new KotlinCallHierarchyNodeDescriptor(project, parent, element, root, navigateToReference)
|
||||||
: new CallHierarchyNodeDescriptor(project, parent, element, root, false);
|
: new CallHierarchyNodeDescriptor(project, parent, element, root, navigateToReference);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static PsiElement getTargetElement(HierarchyNodeDescriptor descriptor) {
|
protected static PsiElement getTargetElement(HierarchyNodeDescriptor descriptor) {
|
||||||
@@ -102,34 +104,40 @@ public abstract class KotlinCallTreeStructure extends HierarchyTreeStructure {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected Object[] collectNodeDescriptors(
|
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>();
|
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;
|
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()]);
|
return declarationToDescriptorMap.values().toArray(new Object[declarationToDescriptorMap.size()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final void addNodeDescriptorForElement(
|
protected final void addNodeDescriptorForElement(
|
||||||
|
PsiReference reference,
|
||||||
PsiElement element,
|
PsiElement element,
|
||||||
Map<PsiElement, HierarchyNodeDescriptor> declarationToDescriptorMap,
|
Map<PsiElement, HierarchyNodeDescriptor> declarationToDescriptorMap,
|
||||||
HierarchyNodeDescriptor descriptor
|
HierarchyNodeDescriptor descriptor
|
||||||
) {
|
) {
|
||||||
HierarchyNodeDescriptor d = declarationToDescriptorMap.get(element);
|
HierarchyNodeDescriptor d = declarationToDescriptorMap.get(element);
|
||||||
if (d == null) {
|
if (d == null) {
|
||||||
d = createNodeDescriptor(myProject, element, descriptor);
|
d = createNodeDescriptor(myProject, element, descriptor, true);
|
||||||
declarationToDescriptorMap.put(element, d);
|
declarationToDescriptorMap.put(element, d);
|
||||||
}
|
}
|
||||||
else {
|
else if (d instanceof CallHierarchyNodeDescriptor) {
|
||||||
if (d instanceof CallHierarchyNodeDescriptor) {
|
((CallHierarchyNodeDescriptor) d).incrementUsageCount();
|
||||||
((CallHierarchyNodeDescriptor) d).incrementUsageCount();
|
}
|
||||||
}
|
|
||||||
else if (d instanceof KotlinCallHierarchyNodeDescriptor) {
|
if (d instanceof CallHierarchyNodeDescriptor) {
|
||||||
((KotlinCallHierarchyNodeDescriptor) d).incrementUsageCount();
|
((CallHierarchyNodeDescriptor) d).addReference(reference);
|
||||||
}
|
}
|
||||||
|
else if (d instanceof KotlinCallHierarchyNodeDescriptor) {
|
||||||
|
((KotlinCallHierarchyNodeDescriptor) d).addReference(reference);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+9
-6
@@ -23,13 +23,16 @@ import com.intellij.openapi.project.Project;
|
|||||||
import com.intellij.psi.PsiClass;
|
import com.intellij.psi.PsiClass;
|
||||||
import com.intellij.psi.PsiElement;
|
import com.intellij.psi.PsiElement;
|
||||||
import com.intellij.psi.PsiMethod;
|
import com.intellij.psi.PsiMethod;
|
||||||
|
import com.intellij.psi.PsiReference;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.jet.lang.psi.*;
|
import org.jetbrains.jet.lang.psi.*;
|
||||||
import org.jetbrains.jet.lang.resolve.java.jetAsJava.KotlinLightMethod;
|
import org.jetbrains.jet.lang.resolve.java.jetAsJava.KotlinLightMethod;
|
||||||
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache;
|
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class KotlinCalleeMethodsTreeStructure extends KotlinCallTreeStructure {
|
public class KotlinCalleeMethodsTreeStructure extends KotlinCallTreeStructure {
|
||||||
private final CalleeMethodsTreeStructure javaTreeStructure;
|
private final CalleeMethodsTreeStructure javaTreeStructure;
|
||||||
@@ -45,7 +48,7 @@ public class KotlinCalleeMethodsTreeStructure extends KotlinCallTreeStructure {
|
|||||||
this.javaTreeStructure = new CalleeMethodsTreeStructure(project, representativePsiMethod, scopeType);
|
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>();
|
List<JetElement> elementsToAnalyze = new ArrayList<JetElement>();
|
||||||
if (rootElement instanceof JetNamedFunction) {
|
if (rootElement instanceof JetNamedFunction) {
|
||||||
elementsToAnalyze.add(((JetNamedFunction) rootElement).getBodyExpression());
|
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) {
|
for (JetElement element : elementsToAnalyze) {
|
||||||
element.accept(
|
element.accept(
|
||||||
new CalleeReferenceVisitorBase(AnalyzerFacadeWithCache.getContextForElement(element), false) {
|
new CalleeReferenceVisitorBase(AnalyzerFacadeWithCache.getContextForElement(element), false) {
|
||||||
@Override
|
@Override
|
||||||
protected void processDeclaration(JetReferenceExpression reference, PsiElement declaration) {
|
protected void processDeclaration(JetReferenceExpression reference, PsiElement declaration) {
|
||||||
result.add(declaration);
|
referencesToCalleeElements.put(reference.getReference(), declaration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return referencesToCalleeElements;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -126,7 +129,7 @@ public class KotlinCalleeMethodsTreeStructure extends KotlinCallTreeStructure {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Object[] buildChildrenByKotlinTarget(HierarchyNodeDescriptor descriptor, JetElement targetElement) {
|
private Object[] buildChildrenByKotlinTarget(HierarchyNodeDescriptor descriptor, JetElement targetElement) {
|
||||||
List<? extends PsiElement> calleeDescriptors = getCalleeElements(targetElement);
|
Map<PsiReference, PsiElement> referencesToCalleeElements = getReferencesToCalleeElements(targetElement);
|
||||||
return collectNodeDescriptors(descriptor, calleeDescriptors, representativePsiClass);
|
return collectNodeDescriptors(descriptor, referencesToCalleeElements, representativePsiClass);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -66,7 +66,7 @@ public abstract class KotlinCallerMethodsTreeStructure extends KotlinCallTreeStr
|
|||||||
|
|
||||||
BindingContext bindingContext = AnalyzerFacadeWithCache.getContextForElement((JetElement) element);
|
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) {
|
codeBlockForLocalDeclaration.accept(new CalleeReferenceVisitorBase(bindingContext, true) {
|
||||||
@Override
|
@Override
|
||||||
protected void processDeclaration(JetReferenceExpression reference, PsiElement declaration) {
|
protected void processDeclaration(JetReferenceExpression reference, PsiElement declaration) {
|
||||||
@@ -80,11 +80,11 @@ public abstract class KotlinCallerMethodsTreeStructure extends KotlinCallTreeStr
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (container != null) {
|
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) {
|
if (element != null) {
|
||||||
addNodeDescriptorForElement(element, methodToDescriptorMap, descriptor);
|
addNodeDescriptorForElement(ref, element, methodToDescriptorMap, descriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
Reference in New Issue
Block a user