Separate tree structures for local and non-local root declarations
This commit is contained in:
@@ -1030,7 +1030,7 @@ public class JetPsiUtil {
|
||||
return header != null ? header.getQualifiedName() : null;
|
||||
}
|
||||
|
||||
public static JetElement getLocalizingCodeBlock(@NotNull JetNamedDeclaration declaration) {
|
||||
public static JetElement getEnclosingBlockForLocalDeclaration(@NotNull JetNamedDeclaration declaration) {
|
||||
//noinspection unchecked
|
||||
JetDeclaration container =
|
||||
PsiTreeUtil.getParentOfType(declaration, JetNamedFunction.class, JetPropertyAccessor.class, JetClassInitializer.class);
|
||||
@@ -1041,4 +1041,8 @@ public class JetPsiUtil {
|
||||
? ((JetClassInitializer) container).getBody()
|
||||
: ((JetDeclarationWithBody) container).getBodyExpression();
|
||||
}
|
||||
|
||||
public static boolean isLocal(@NotNull JetNamedDeclaration declaration) {
|
||||
return getEnclosingBlockForLocalDeclaration(declaration) != null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@ import com.intellij.psi.PsiMethod;
|
||||
import com.intellij.ui.PopupHandler;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.lang.psi.JetNamedDeclaration;
|
||||
import org.jetbrains.jet.plugin.hierarchy.HierarchyUtils;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.Comparator;
|
||||
@@ -87,7 +86,7 @@ public class KotlinCallHierarchyBrowser extends CallHierarchyBrowserBase {
|
||||
@Override
|
||||
protected HierarchyTreeStructure createHierarchyTreeStructure(@NotNull String typeName, @NotNull PsiElement psiElement) {
|
||||
if (typeName.equals(CALLER_TYPE)) {
|
||||
return new KotlinCallerMethodsTreeStructure(myProject, psiElement, getCurrentScopeType());
|
||||
return KotlinCallerMethodsTreeStructure.newInstance(myProject, psiElement, getCurrentScopeType());
|
||||
}
|
||||
|
||||
if (typeName.equals(CALLEE_TYPE)) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.jetbrains.jet.plugin.hierarchy.calls;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.intellij.ide.hierarchy.HierarchyNodeDescriptor;
|
||||
import com.intellij.ide.hierarchy.HierarchyTreeStructure;
|
||||
import com.intellij.ide.hierarchy.call.CallHierarchyNodeDescriptor;
|
||||
@@ -7,7 +8,6 @@ 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.util.PsiTreeUtil;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -19,30 +19,16 @@ import java.util.Map;
|
||||
|
||||
public abstract class KotlinCallTreeStructure extends HierarchyTreeStructure {
|
||||
protected final String scopeType;
|
||||
protected final JetElement localizingCodeBlock;
|
||||
protected final PsiMethod basePsiMethod;
|
||||
protected final PsiClass basePsiClass;
|
||||
|
||||
public KotlinCallTreeStructure(@NotNull Project project, PsiElement element, String scopeType) {
|
||||
super(project, createNodeDescriptor(project, element, null));
|
||||
|
||||
this.scopeType = scopeType;
|
||||
}
|
||||
|
||||
localizingCodeBlock = element instanceof JetNamedDeclaration
|
||||
? JetPsiUtil.getLocalizingCodeBlock((JetNamedDeclaration) element)
|
||||
protected static JetElement getEnclosingBlockForLocalDeclaration(PsiElement element) {
|
||||
return element instanceof JetNamedDeclaration
|
||||
? JetPsiUtil.getEnclosingBlockForLocalDeclaration((JetNamedDeclaration) element)
|
||||
: null;
|
||||
|
||||
if (localizingCodeBlock == null) {
|
||||
basePsiMethod = getPsiMethod(element);
|
||||
assert basePsiMethod != null;
|
||||
|
||||
basePsiClass = basePsiMethod.getContainingClass();
|
||||
assert basePsiClass != null;
|
||||
}
|
||||
else {
|
||||
basePsiMethod = null;
|
||||
basePsiClass = null;
|
||||
}
|
||||
}
|
||||
|
||||
protected static HierarchyNodeDescriptor createNodeDescriptor(Project project, PsiElement element, HierarchyNodeDescriptor parent) {
|
||||
@@ -58,15 +44,46 @@ public abstract class KotlinCallTreeStructure extends HierarchyTreeStructure {
|
||||
: ((KotlinCallHierarchyNodeDescriptor)descriptor).getTargetElement();
|
||||
}
|
||||
|
||||
private static PsiMethod getClosestContainingClassConstructor(PsiElement element) {
|
||||
while (element != null) {
|
||||
JetClassOrObject classOrObject = PsiTreeUtil.getParentOfType(element, JetClassOrObject.class, false);
|
||||
if (classOrObject == null) return null;
|
||||
private static final Predicate<PsiElement> IS_NON_LOCAL_DECLARATION = new Predicate<PsiElement>() {
|
||||
@Override
|
||||
public boolean apply(@javax.annotation.Nullable PsiElement input) {
|
||||
return input instanceof PsiMethod
|
||||
|| ((input instanceof JetNamedFunction || input instanceof JetClassOrObject || input instanceof JetProperty)
|
||||
&& !JetPsiUtil.isLocal((JetNamedDeclaration) input));
|
||||
}
|
||||
};
|
||||
|
||||
element = classOrObject.getParent();
|
||||
@Nullable
|
||||
protected static PsiMethod getRepresentativePsiMethod(PsiElement element) {
|
||||
while (true) {
|
||||
element = JetPsiUtil.getParentByTypeAndPredicate(element, PsiElement.class, IS_NON_LOCAL_DECLARATION, false);
|
||||
if (element == null) return null;
|
||||
|
||||
PsiClass psiClass = LightClassUtil.getPsiClass(classOrObject);
|
||||
if (psiClass == null) continue;
|
||||
PsiMethod method = getRepresentativePsiMethodForNonLocalDeclaration(element);
|
||||
if (method != null) return method;
|
||||
|
||||
element = element.getParent();
|
||||
}
|
||||
}
|
||||
|
||||
private static PsiMethod getRepresentativePsiMethodForNonLocalDeclaration(PsiElement element) {
|
||||
if (element instanceof PsiMethod) {
|
||||
return (PsiMethod) element;
|
||||
}
|
||||
|
||||
if (element instanceof JetNamedFunction) {
|
||||
return LightClassUtil.getLightClassMethod((JetNamedFunction) element);
|
||||
}
|
||||
|
||||
if (element instanceof JetProperty) {
|
||||
LightClassUtil.PropertyAccessorsPsiMethods propertyMethods =
|
||||
LightClassUtil.getLightClassPropertyMethods((JetProperty) element);
|
||||
return (propertyMethods.getGetter() != null) ? propertyMethods.getGetter() : propertyMethods.getSetter();
|
||||
}
|
||||
|
||||
if (element instanceof JetClassOrObject) {
|
||||
PsiClass psiClass = LightClassUtil.getPsiClass((JetClassOrObject) element);
|
||||
if (psiClass == null) return null;
|
||||
|
||||
PsiMethod[] constructors = psiClass.getConstructors();
|
||||
if (constructors.length > 0) return constructors[0];
|
||||
@@ -75,33 +92,6 @@ public abstract class KotlinCallTreeStructure extends HierarchyTreeStructure {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected final PsiMethod getPsiMethod(PsiElement element) {
|
||||
assert localizingCodeBlock == null : "Can't build light method for local declaration";
|
||||
|
||||
if (element instanceof PsiMethod) {
|
||||
return (PsiMethod) element;
|
||||
}
|
||||
|
||||
PsiMethod method = null;
|
||||
if (element instanceof JetNamedFunction) {
|
||||
method = LightClassUtil.getLightClassMethod((JetNamedFunction) element);
|
||||
}
|
||||
else if (element instanceof JetProperty) {
|
||||
LightClassUtil.PropertyAccessorsPsiMethods propertyMethods =
|
||||
LightClassUtil.getLightClassPropertyMethods((JetProperty) element);
|
||||
|
||||
if (propertyMethods.getGetter() != null) {
|
||||
method = propertyMethods.getGetter();
|
||||
}
|
||||
else {
|
||||
method = propertyMethods.getSetter();
|
||||
}
|
||||
}
|
||||
|
||||
return method != null ? method : getClosestContainingClassConstructor(element);
|
||||
}
|
||||
|
||||
protected static CallHierarchyNodeDescriptor getJavaNodeDescriptor(HierarchyNodeDescriptor originalDescriptor) {
|
||||
if (originalDescriptor instanceof CallHierarchyNodeDescriptor) return (CallHierarchyNodeDescriptor) originalDescriptor;
|
||||
|
||||
@@ -109,7 +99,9 @@ public abstract class KotlinCallTreeStructure extends HierarchyTreeStructure {
|
||||
return ((KotlinCallHierarchyNodeDescriptor) originalDescriptor).getJavaDelegate();
|
||||
}
|
||||
|
||||
protected final Object[] collectNodeDescriptors(HierarchyNodeDescriptor descriptor, List<? extends PsiElement> calleeElements) {
|
||||
protected Object[] collectNodeDescriptors(
|
||||
HierarchyNodeDescriptor descriptor, List<? extends PsiElement> calleeElements, PsiClass basePsiClass
|
||||
) {
|
||||
HashMap<PsiElement, HierarchyNodeDescriptor> declarationToDescriptorMap = new HashMap<PsiElement, HierarchyNodeDescriptor>();
|
||||
for (PsiElement callee : calleeElements) {
|
||||
if (basePsiClass != null && !isInScope(basePsiClass, callee, scopeType)) continue;
|
||||
|
||||
+8
-2
@@ -35,10 +35,16 @@ import java.util.List;
|
||||
|
||||
public class KotlinCalleeMethodsTreeStructure extends KotlinCallTreeStructure {
|
||||
private final CalleeMethodsTreeStructure javaTreeStructure;
|
||||
private final PsiClass representativePsiClass;
|
||||
|
||||
public KotlinCalleeMethodsTreeStructure(@NotNull Project project, @NotNull PsiElement element, String scopeType) {
|
||||
super(project, element, scopeType);
|
||||
this.javaTreeStructure = basePsiMethod != null ? new CalleeMethodsTreeStructure(project, basePsiMethod, scopeType) : null;
|
||||
|
||||
PsiMethod representativePsiMethod = getRepresentativePsiMethod(element);
|
||||
assert representativePsiMethod != null;
|
||||
|
||||
this.representativePsiClass = representativePsiMethod.getContainingClass();
|
||||
this.javaTreeStructure = new CalleeMethodsTreeStructure(project, representativePsiMethod, scopeType);
|
||||
}
|
||||
|
||||
private static List<? extends PsiElement> getCalleeElements(@NotNull JetElement rootElement, BindingContext bindingContext) {
|
||||
@@ -126,6 +132,6 @@ public class KotlinCalleeMethodsTreeStructure extends KotlinCallTreeStructure {
|
||||
BindingContext bindingContext =
|
||||
AnalyzerFacadeWithCache.analyzeFileWithCache((JetFile) targetElement.getContainingFile()).getBindingContext();
|
||||
List<? extends PsiElement> calleeDescriptors = getCalleeElements((JetElement) targetElement, bindingContext);
|
||||
return collectNodeDescriptors(descriptor, calleeDescriptors);
|
||||
return collectNodeDescriptors(descriptor, calleeDescriptors, representativePsiClass);
|
||||
}
|
||||
}
|
||||
|
||||
+173
-146
@@ -43,156 +43,183 @@ import org.jetbrains.jet.plugin.references.JetPsiReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
|
||||
public class KotlinCallerMethodsTreeStructure extends KotlinCallTreeStructure {
|
||||
private final CallerMethodsTreeStructure javaTreeStructure;
|
||||
public abstract class KotlinCallerMethodsTreeStructure extends KotlinCallTreeStructure {
|
||||
private static class WithLocalRoot extends KotlinCallerMethodsTreeStructure {
|
||||
private final JetElement codeBlockForLocalDeclaration;
|
||||
|
||||
public KotlinCallerMethodsTreeStructure(@NotNull Project project, @NotNull PsiElement element, String scopeType) {
|
||||
private WithLocalRoot(
|
||||
@NotNull Project project,
|
||||
@NotNull PsiElement element,
|
||||
String scopeType,
|
||||
JetElement codeBlockForLocalDeclaration
|
||||
) {
|
||||
super(project, element, scopeType);
|
||||
this.codeBlockForLocalDeclaration = codeBlockForLocalDeclaration;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object[] buildChildren(HierarchyNodeDescriptor descriptor) {
|
||||
final PsiElement element = getTargetElement(descriptor);
|
||||
|
||||
BindingContext bindingContext =
|
||||
AnalyzerFacadeWithCache.analyzeFileWithCache((JetFile) element.getContainingFile()).getBindingContext();
|
||||
|
||||
final ArrayList<PsiElement> result = new ArrayList<PsiElement>();
|
||||
codeBlockForLocalDeclaration.accept(new CalleeReferenceVisitorBase(bindingContext, true) {
|
||||
@Override
|
||||
protected void processDeclaration(JetReferenceExpression reference, PsiElement declaration) {
|
||||
if (!declaration.equals(element)) return;
|
||||
|
||||
//noinspection unchecked
|
||||
PsiElement container =
|
||||
PsiTreeUtil.getParentOfType(reference, JetNamedFunction.class, JetPropertyAccessor.class, JetClassOrObject.class);
|
||||
if (container instanceof JetPropertyAccessor) {
|
||||
container = PsiTreeUtil.getParentOfType(container, JetProperty.class);
|
||||
}
|
||||
|
||||
if (container != null) {
|
||||
result.add(container);
|
||||
}
|
||||
}
|
||||
});
|
||||
return collectNodeDescriptors(descriptor, result, null);
|
||||
}
|
||||
}
|
||||
|
||||
private static class WithNonLocalRoot extends KotlinCallerMethodsTreeStructure {
|
||||
private final CallerMethodsTreeStructure javaTreeStructure;
|
||||
private final PsiClass basePsiClass;
|
||||
|
||||
private WithNonLocalRoot(@NotNull Project project, @NotNull PsiElement element, String scopeType, PsiMethod basePsiMethod) {
|
||||
super(project, element, scopeType);
|
||||
|
||||
this.basePsiClass = basePsiMethod.getContainingClass();
|
||||
this.javaTreeStructure = new CallerMethodsTreeStructure(project, basePsiMethod, scopeType);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object[] buildChildren(HierarchyNodeDescriptor descriptor) {
|
||||
PsiElement element = getTargetElement(descriptor);
|
||||
|
||||
SearchScope searchScope = getSearchScope(scopeType, basePsiClass);
|
||||
Map<PsiElement, HierarchyNodeDescriptor> methodToDescriptorMap =
|
||||
new HashMap<PsiElement, HierarchyNodeDescriptor>();
|
||||
|
||||
Object[] javaCallers = null;
|
||||
if (element instanceof PsiMethod && javaTreeStructure != null) {
|
||||
javaCallers = javaTreeStructure.getChildElements(getJavaNodeDescriptor(descriptor));
|
||||
processPsiMethodCallers((PsiMethod) element, descriptor, methodToDescriptorMap, searchScope, true);
|
||||
}
|
||||
if (element instanceof JetNamedFunction) {
|
||||
PsiMethod lightMethod = LightClassUtil.getLightClassMethod((JetNamedFunction) element);
|
||||
processPsiMethodCallers(lightMethod, descriptor, methodToDescriptorMap, searchScope, false);
|
||||
}
|
||||
if (element instanceof JetProperty) {
|
||||
LightClassUtil.PropertyAccessorsPsiMethods propertyMethods =
|
||||
LightClassUtil.getLightClassPropertyMethods((JetProperty) element);
|
||||
processPsiMethodCallers(propertyMethods.getGetter(), descriptor, methodToDescriptorMap, searchScope, false);
|
||||
processPsiMethodCallers(propertyMethods.getSetter(), descriptor, methodToDescriptorMap, searchScope, false);
|
||||
}
|
||||
if (element instanceof JetClassOrObject) {
|
||||
processJetClassOrObjectCallers((JetClassOrObject) element, descriptor, methodToDescriptorMap, searchScope);
|
||||
}
|
||||
|
||||
Object[] callers = methodToDescriptorMap.values().toArray(new Object[methodToDescriptorMap.size()]);
|
||||
return (javaCallers != null) ? ArrayUtil.mergeArrays(javaCallers, callers) : callers;
|
||||
}
|
||||
|
||||
private void processPsiMethodCallers(
|
||||
@Nullable PsiMethod lightMethod,
|
||||
HierarchyNodeDescriptor descriptor,
|
||||
Map<PsiElement, HierarchyNodeDescriptor> methodToDescriptorMap,
|
||||
SearchScope searchScope,
|
||||
boolean kotlinOnly
|
||||
) {
|
||||
if (lightMethod == null) return;
|
||||
MethodReferencesSearch.search(lightMethod, searchScope, true)
|
||||
.forEach(defaultQueryProcessor(descriptor, methodToDescriptorMap, kotlinOnly));
|
||||
}
|
||||
|
||||
private void processJetClassOrObjectCallers(
|
||||
final JetClassOrObject classOrObject,
|
||||
HierarchyNodeDescriptor descriptor,
|
||||
Map<PsiElement, HierarchyNodeDescriptor> methodToDescriptorMap,
|
||||
SearchScope searchScope
|
||||
) {
|
||||
Processor<PsiReference> processor = new FilteringProcessor<PsiReference>(
|
||||
new Condition<PsiReference>() {
|
||||
@Override
|
||||
public boolean value(PsiReference reference) {
|
||||
return FindUsagesUtils.isConstructorUsage(reference.getElement(), classOrObject);
|
||||
}
|
||||
},
|
||||
defaultQueryProcessor(descriptor, methodToDescriptorMap, false)
|
||||
);
|
||||
ReferencesSearch.search(classOrObject, searchScope, false).forEach(processor);
|
||||
}
|
||||
|
||||
private Processor<PsiReference> defaultQueryProcessor(
|
||||
final HierarchyNodeDescriptor descriptor,
|
||||
final Map<PsiElement, HierarchyNodeDescriptor> methodToDescriptorMap,
|
||||
final boolean kotlinOnly
|
||||
) {
|
||||
return new ReadActionProcessor<PsiReference>() {
|
||||
@Override
|
||||
public boolean processInReadAction(PsiReference ref) {
|
||||
// copied from Java
|
||||
if (!(ref instanceof PsiReferenceExpression || ref instanceof JetPsiReference)) {
|
||||
if (!(ref instanceof PsiElement)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
PsiElement parent = ((PsiElement) ref).getParent();
|
||||
if (parent instanceof PsiNewExpression) {
|
||||
if (((PsiNewExpression) parent).getClassReference() != ref) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (parent instanceof PsiAnonymousClass) {
|
||||
if (((PsiAnonymousClass) parent).getBaseClassReference() != ref) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
PsiElement element = HierarchyUtils.getCallHierarchyElement(ref.getElement());
|
||||
|
||||
if (kotlinOnly && !(element instanceof JetNamedDeclaration)) return true;
|
||||
|
||||
// If reference belongs to property initializer, show enclosing declaration instead
|
||||
if (element instanceof JetProperty) {
|
||||
JetProperty property = (JetProperty) element;
|
||||
if (PsiTreeUtil.isAncestor(property.getInitializer(), ref.getElement(), false)) {
|
||||
element = HierarchyUtils.getCallHierarchyElement(element.getParent());
|
||||
}
|
||||
}
|
||||
|
||||
if (element != null) {
|
||||
addNodeDescriptorForElement(element, methodToDescriptorMap, descriptor);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private KotlinCallerMethodsTreeStructure(@NotNull Project project, @NotNull PsiElement element, String scopeType) {
|
||||
super(project, element, scopeType);
|
||||
this.javaTreeStructure = basePsiMethod != null ? new CallerMethodsTreeStructure(project, basePsiMethod, scopeType) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object[] buildChildren(HierarchyNodeDescriptor descriptor) {
|
||||
PsiElement targetElement = getTargetElement(descriptor);
|
||||
public static KotlinCallerMethodsTreeStructure newInstance(@NotNull Project project, @NotNull PsiElement element, String scopeType) {
|
||||
JetElement codeBlockForLocalDeclaration = getEnclosingBlockForLocalDeclaration(element);
|
||||
if (codeBlockForLocalDeclaration != null) return new WithLocalRoot(project, element, scopeType, codeBlockForLocalDeclaration);
|
||||
|
||||
if (localizingCodeBlock != null) {
|
||||
return buildLocalizedChildren(targetElement, (KotlinCallHierarchyNodeDescriptor) descriptor);
|
||||
}
|
||||
|
||||
return processCallers(targetElement, descriptor);
|
||||
}
|
||||
|
||||
private Object[] buildLocalizedChildren(final PsiElement element, KotlinCallHierarchyNodeDescriptor descriptor) {
|
||||
BindingContext bindingContext =
|
||||
AnalyzerFacadeWithCache.analyzeFileWithCache((JetFile) element.getContainingFile()).getBindingContext();
|
||||
|
||||
final ArrayList<PsiElement> result = new ArrayList<PsiElement>();
|
||||
localizingCodeBlock.accept(new CalleeReferenceVisitorBase(bindingContext, true) {
|
||||
@Override
|
||||
protected void processDeclaration(JetReferenceExpression reference, PsiElement declaration) {
|
||||
if (!declaration.equals(element)) return;
|
||||
|
||||
//noinspection unchecked
|
||||
PsiElement container =
|
||||
PsiTreeUtil.getParentOfType(reference, JetNamedFunction.class, JetPropertyAccessor.class, JetClassOrObject.class);
|
||||
if (container instanceof JetPropertyAccessor) {
|
||||
container = PsiTreeUtil.getParentOfType(container, JetProperty.class);
|
||||
}
|
||||
|
||||
if (container != null) {
|
||||
result.add(container);
|
||||
}
|
||||
}
|
||||
});
|
||||
return collectNodeDescriptors(descriptor, result);
|
||||
}
|
||||
|
||||
private Object[] processCallers(PsiElement element, HierarchyNodeDescriptor descriptor) {
|
||||
SearchScope searchScope = getSearchScope(scopeType, basePsiClass);
|
||||
Map<PsiElement, HierarchyNodeDescriptor> methodToDescriptorMap =
|
||||
new HashMap<PsiElement, HierarchyNodeDescriptor>();
|
||||
|
||||
Object[] javaCallers = null;
|
||||
if (element instanceof PsiMethod && javaTreeStructure != null) {
|
||||
javaCallers = javaTreeStructure.getChildElements(getJavaNodeDescriptor(descriptor));
|
||||
processPsiMethodCallers((PsiMethod) element, descriptor, methodToDescriptorMap, searchScope, true);
|
||||
}
|
||||
if (element instanceof JetNamedFunction) {
|
||||
PsiMethod lightMethod = LightClassUtil.getLightClassMethod((JetNamedFunction) element);
|
||||
processPsiMethodCallers(lightMethod, descriptor, methodToDescriptorMap, searchScope, false);
|
||||
}
|
||||
if (element instanceof JetProperty) {
|
||||
LightClassUtil.PropertyAccessorsPsiMethods propertyMethods =
|
||||
LightClassUtil.getLightClassPropertyMethods((JetProperty) element);
|
||||
processPsiMethodCallers(propertyMethods.getGetter(), descriptor, methodToDescriptorMap, searchScope, false);
|
||||
processPsiMethodCallers(propertyMethods.getSetter(), descriptor, methodToDescriptorMap, searchScope, false);
|
||||
}
|
||||
if (element instanceof JetClassOrObject) {
|
||||
processJetClassOrObjectCallers((JetClassOrObject) element, descriptor, methodToDescriptorMap, searchScope);
|
||||
}
|
||||
|
||||
Object[] callers = methodToDescriptorMap.values().toArray(new Object[methodToDescriptorMap.size()]);
|
||||
return (javaCallers != null) ? ArrayUtil.mergeArrays(javaCallers, callers) : callers;
|
||||
}
|
||||
|
||||
private void processPsiMethodCallers(
|
||||
@Nullable PsiMethod lightMethod,
|
||||
HierarchyNodeDescriptor descriptor,
|
||||
Map<PsiElement, HierarchyNodeDescriptor> methodToDescriptorMap,
|
||||
SearchScope searchScope,
|
||||
boolean kotlinOnly
|
||||
) {
|
||||
if (lightMethod == null) return;
|
||||
MethodReferencesSearch.search(lightMethod, searchScope, true)
|
||||
.forEach(defaultQueryProcessor(descriptor, methodToDescriptorMap, kotlinOnly));
|
||||
}
|
||||
|
||||
private void processJetClassOrObjectCallers(
|
||||
final JetClassOrObject classOrObject,
|
||||
HierarchyNodeDescriptor descriptor,
|
||||
Map<PsiElement, HierarchyNodeDescriptor> methodToDescriptorMap,
|
||||
SearchScope searchScope
|
||||
) {
|
||||
Processor<PsiReference> processor = new FilteringProcessor<PsiReference>(
|
||||
new Condition<PsiReference>() {
|
||||
@Override
|
||||
public boolean value(PsiReference reference) {
|
||||
return FindUsagesUtils.isConstructorUsage(reference.getElement(), classOrObject);
|
||||
}
|
||||
},
|
||||
defaultQueryProcessor(descriptor, methodToDescriptorMap, false)
|
||||
);
|
||||
ReferencesSearch.search(classOrObject, searchScope, false).forEach(processor);
|
||||
}
|
||||
|
||||
private Processor<PsiReference> defaultQueryProcessor(
|
||||
final HierarchyNodeDescriptor descriptor,
|
||||
final Map<PsiElement, HierarchyNodeDescriptor> methodToDescriptorMap,
|
||||
final boolean kotlinOnly
|
||||
) {
|
||||
return new ReadActionProcessor<PsiReference>() {
|
||||
@Override
|
||||
public boolean processInReadAction(PsiReference ref) {
|
||||
// copied from Java
|
||||
if (!(ref instanceof PsiReferenceExpression || ref instanceof JetPsiReference)) {
|
||||
if (!(ref instanceof PsiElement)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
PsiElement parent = ((PsiElement) ref).getParent();
|
||||
if (parent instanceof PsiNewExpression) {
|
||||
if (((PsiNewExpression) parent).getClassReference() != ref) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (parent instanceof PsiAnonymousClass) {
|
||||
if (((PsiAnonymousClass) parent).getBaseClassReference() != ref) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
PsiElement element = HierarchyUtils.getCallHierarchyElement(ref.getElement());
|
||||
|
||||
if (kotlinOnly && !(element instanceof JetNamedDeclaration)) return true;
|
||||
|
||||
// If reference belongs to property initializer, show enclosing declaration instead
|
||||
if (element instanceof JetProperty) {
|
||||
JetProperty property = (JetProperty) element;
|
||||
if (PsiTreeUtil.isAncestor(property.getInitializer(), ref.getElement(), false)) {
|
||||
element = HierarchyUtils.getCallHierarchyElement(element.getParent());
|
||||
}
|
||||
}
|
||||
|
||||
if (element != null) {
|
||||
addNodeDescriptorForElement(element, methodToDescriptorMap, descriptor);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
PsiMethod representativeMethod = getRepresentativePsiMethod(element);
|
||||
assert representativeMethod != null;
|
||||
return new WithNonLocalRoot(project, element, scopeType, representativeMethod);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ public abstract class AbstractHierarchyTest extends HierarchyViewTestBase {
|
||||
return new Computable<HierarchyTreeStructure>() {
|
||||
@Override
|
||||
public HierarchyTreeStructure compute() {
|
||||
return new KotlinCallerMethodsTreeStructure(
|
||||
return KotlinCallerMethodsTreeStructure.newInstance(
|
||||
getProject(),
|
||||
getElementAtCaret(LanguageCallHierarchy.INSTANCE.forLanguage(getLanguage())),
|
||||
HierarchyBrowserBaseEx.SCOPE_PROJECT
|
||||
|
||||
Reference in New Issue
Block a user