Implement call hierarchy

This commit is contained in:
Alexey Sedunov
2013-09-11 15:23:22 +04:00
parent e4936549ee
commit 41ea7da1e3
61 changed files with 1987 additions and 20 deletions
@@ -102,17 +102,17 @@ public class JetObjectDeclaration extends JetNamedDeclarationStub<PsiJetObjectSt
@Override
@Nullable
public JetModifierList getModifierList() {
PsiElement parent = getParent();
if (isClassObject(parent)) {
if (isClassObject()) {
PsiElement parent = getParent();
assert parent instanceof JetDeclaration;
return ((JetDeclaration)parent).getModifierList();
}
return (JetModifierList) findChildByType(JetNodeTypes.MODIFIER_LIST);
}
private static boolean isClassObject(@NotNull PsiElement parent) {
return parent.getNode().getElementType().equals(JetNodeTypes.CLASS_OBJECT);
public boolean isClassObject() {
PsiElement parent = getParent();
return parent != null && parent.getNode().getElementType().equals(JetNodeTypes.CLASS_OBJECT);
}
@Override
@@ -1021,4 +1021,12 @@ public class JetPsiUtil {
}
}
}
@Nullable
public static String getPackageName(@NotNull JetElement element) {
JetFile file = (JetFile) element.getContainingFile();
JetNamespaceHeader header = PsiTreeUtil.findChildOfType(file, JetNamespaceHeader.class);
return header != null ? header.getQualifiedName() : null;
}
}
@@ -395,7 +395,9 @@ public class GenerateTests {
AbstractHierarchyTest.class,
testModelWithDirectories("idea/testData/hierarchy/class/type", "doTypeClassHierarchyTest"),
testModelWithDirectories("idea/testData/hierarchy/class/super", "doSuperClassHierarchyTest"),
testModelWithDirectories("idea/testData/hierarchy/class/sub", "doSubClassHierarchyTest")
testModelWithDirectories("idea/testData/hierarchy/class/sub", "doSubClassHierarchyTest"),
testModelWithDirectories("idea/testData/hierarchy/calls/callers", "doCallerHierarchyTest"),
testModelWithDirectories("idea/testData/hierarchy/calls/callees", "doCalleeHierarchyTest")
);
generateTest(
+7
View File
@@ -263,6 +263,13 @@
<library.presentationProvider implementation="org.jetbrains.jet.plugin.framework.JsHeaderLibraryPresentationProvider"/>
<typeHierarchyProvider language="jet" implementationClass="org.jetbrains.jet.plugin.hierarchy.KotlinTypeHierarchyProvider"/>
<callHierarchyProvider
language="jet"
implementationClass="org.jetbrains.jet.plugin.hierarchy.calls.KotlinCallHierarchyProvider" />
<callHierarchyProvider
language="JAVA"
implementationClass="org.jetbrains.jet.plugin.hierarchy.calls.KotlinCallHierarchyProvider"
order="first" />
<java.elementFinder implementation="org.jetbrains.jet.asJava.JavaElementFinder"/>
<java.shortNamesCache implementation="org.jetbrains.jet.plugin.caches.JetShortNamesCache"/>
@@ -0,0 +1,27 @@
package org.jetbrains.jet.plugin.hierarchy;
import com.google.common.base.Predicate;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import org.jetbrains.jet.lang.psi.*;
import javax.annotation.Nullable;
public class HierarchyUtils {
public static final Predicate<PsiElement> IS_CALL_HIERARCHY_ELEMENT = new Predicate<PsiElement>() {
@Override
public boolean apply(@Nullable PsiElement input) {
return input instanceof PsiMethod ||
input instanceof PsiClass ||
input instanceof JetFile ||
(input instanceof JetNamedFunction && !((JetNamedFunction) input).isLocal()) ||
input instanceof JetClassOrObject ||
(input instanceof JetProperty && !((JetProperty) input).isLocal());
}
};
public static PsiElement getCallHierarchyElement(PsiElement element) {
return JetPsiUtil.getParentByTypeAndPredicate(element, PsiElement.class, IS_CALL_HIERARCHY_ELEMENT, false);
}
}
@@ -0,0 +1,104 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.plugin.hierarchy.calls;
import com.intellij.ide.hierarchy.CallHierarchyBrowserBase;
import com.intellij.ide.hierarchy.HierarchyNodeDescriptor;
import com.intellij.ide.hierarchy.HierarchyTreeStructure;
import com.intellij.ide.hierarchy.JavaHierarchyUtil;
import com.intellij.ide.util.treeView.NodeDescriptor;
import com.intellij.openapi.actionSystem.ActionGroup;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.ActionPlaces;
import com.intellij.openapi.actionSystem.IdeActions;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
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;
import java.util.Map;
public class KotlinCallHierarchyBrowser extends CallHierarchyBrowserBase {
public KotlinCallHierarchyBrowser(@NotNull Project project, @NotNull PsiElement element) {
super(project, element);
}
@Override
protected void createTrees(@NotNull Map<String, JTree> type2TreeMap) {
ActionGroup group = (ActionGroup) ActionManager.getInstance().getAction(IdeActions.GROUP_CALL_HIERARCHY_POPUP);
JTree tree1 = createTree(false);
PopupHandler.installPopupHandler(tree1, group, ActionPlaces.CALL_HIERARCHY_VIEW_POPUP, ActionManager.getInstance());
BaseOnThisMethodAction baseOnThisMethodAction = new BaseOnThisMethodAction();
baseOnThisMethodAction.registerCustomShortcutSet(
ActionManager.getInstance().getAction(IdeActions.ACTION_CALL_HIERARCHY).getShortcutSet(), tree1
);
type2TreeMap.put(CALLEE_TYPE, tree1);
JTree tree2 = createTree(false);
PopupHandler.installPopupHandler(tree2, group, ActionPlaces.CALL_HIERARCHY_VIEW_POPUP, ActionManager.getInstance());
baseOnThisMethodAction.registerCustomShortcutSet(
ActionManager.getInstance().getAction(IdeActions.ACTION_CALL_HIERARCHY).getShortcutSet(), tree2
);
type2TreeMap.put(CALLER_TYPE, tree2);
}
private static PsiElement getTargetElement(@NotNull HierarchyNodeDescriptor descriptor) {
if (descriptor instanceof KotlinCallHierarchyNodeDescriptor) {
return ((KotlinCallHierarchyNodeDescriptor) descriptor).getTargetElement();
}
return null;
}
@Override
protected PsiElement getElementFromDescriptor(@NotNull HierarchyNodeDescriptor descriptor) {
return getTargetElement(descriptor);
}
@Override
protected PsiElement getOpenFileElementFromDescriptor(@NotNull HierarchyNodeDescriptor descriptor) {
return getTargetElement(descriptor);
}
@Override
protected boolean isApplicableElement(@NotNull PsiElement element) {
return element instanceof JetNamedDeclaration || element instanceof PsiMethod;
}
@Override
protected HierarchyTreeStructure createHierarchyTreeStructure(@NotNull String typeName, @NotNull PsiElement psiElement) {
if (typeName.equals(CALLER_TYPE)) {
return new KotlinCallerMethodsTreeStructure(myProject, psiElement, getCurrentScopeType());
}
if (typeName.equals(CALLEE_TYPE)) {
return new KotlinCalleeMethodsTreeStructure(myProject, psiElement, getCurrentScopeType());
}
return null;
}
@Override
protected Comparator<NodeDescriptor> getComparator() {
return JavaHierarchyUtil.getComparator(myProject);
}
}
@@ -0,0 +1,258 @@
package org.jetbrains.jet.plugin.hierarchy.calls;
import com.intellij.icons.AllIcons;
import com.intellij.ide.IdeBundle;
import com.intellij.ide.hierarchy.HierarchyNodeDescriptor;
import com.intellij.ide.hierarchy.JavaHierarchyUtil;
import com.intellij.ide.hierarchy.call.CallHierarchyNodeDescriptor;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ui.util.CompositeAppearance;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Iconable;
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.util.PsiTreeUtil;
import com.intellij.ui.LayeredIcon;
import com.intellij.util.Function;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache;
import org.jetbrains.jet.renderer.DescriptorRenderer;
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
public class KotlinCallHierarchyNodeDescriptor extends HierarchyNodeDescriptor implements Navigatable {
private int usageCount = 1;
private final List<PsiReference> references = new ArrayList<PsiReference>();
private final CallHierarchyNodeDescriptor javaDelegate;
public KotlinCallHierarchyNodeDescriptor(@NotNull Project project,
HierarchyNodeDescriptor parentDescriptor,
@NotNull PsiElement element,
boolean isBase,
boolean navigateToReference) {
super(project, parentDescriptor, element, isBase);
this.javaDelegate = new CallHierarchyNodeDescriptor(myProject, null, element, isBase, navigateToReference);
}
public final CallHierarchyNodeDescriptor getJavaDelegate() {
return javaDelegate;
}
public final void incrementUsageCount(){
usageCount++;
}
public void addReference(PsiReference reference) {
references.add(reference);
}
public boolean hasReference(PsiReference reference) {
return references.contains(reference);
}
public final PsiElement getTargetElement(){
return myElement;
}
@Override
public final boolean isValid(){
return myElement != null && myElement.isValid();
}
@Override
public final boolean update(){
CompositeAppearance oldText = myHighlightedText;
Icon oldIcon = getIcon();
int flags = Iconable.ICON_FLAG_VISIBILITY;
if (isMarkReadOnly()) {
flags |= Iconable.ICON_FLAG_READ_STATUS;
}
boolean changes = super.update();
PsiElement targetElement = getTargetElement();
String elementText = renderElement(targetElement);
if (elementText == null) {
String invalidPrefix = IdeBundle.message("node.hierarchy.invalid");
if (!myHighlightedText.getText().startsWith(invalidPrefix)) {
myHighlightedText.getBeginning().addText(invalidPrefix, HierarchyNodeDescriptor.getInvalidPrefixAttributes());
}
return true;
}
Icon newIcon = targetElement.getIcon(flags);
if (changes && myIsBase) {
LayeredIcon icon = new LayeredIcon(2);
icon.setIcon(newIcon, 0);
icon.setIcon(AllIcons.Hierarchy.Base, 1, -AllIcons.Hierarchy.Base.getIconWidth() / 2, 0);
newIcon = icon;
}
setIcon(newIcon);
myHighlightedText = new CompositeAppearance();
TextAttributes mainTextAttributes = null;
if (myColor != null) {
mainTextAttributes = new TextAttributes(myColor, null, null, null, Font.PLAIN);
}
String packageName = null;
if (targetElement instanceof JetElement) {
packageName = JetPsiUtil.getPackageName((JetElement) targetElement);
}
else {
PsiClass enclosingClass = PsiTreeUtil.getParentOfType(targetElement, PsiClass.class, false);
if (enclosingClass != null) {
packageName = JavaHierarchyUtil.getPackageName(enclosingClass);
}
}
myHighlightedText.getEnding().addText(elementText, mainTextAttributes);
if (usageCount > 1) {
myHighlightedText.getEnding().addText(
IdeBundle.message("node.call.hierarchy.N.usages", usageCount),
HierarchyNodeDescriptor.getUsageCountPrefixAttributes()
);
}
if (packageName != null && packageName.length() > 0) {
myHighlightedText.getEnding().addText(" (" + packageName + ")", HierarchyNodeDescriptor.getPackageNameAttributes());
}
myName = myHighlightedText.getText();
if (!(Comparing.equal(myHighlightedText, oldText) && Comparing.equal(getIcon(), oldIcon))) {
changes = true;
}
return changes;
}
@Nullable
private static String renderElement(@Nullable PsiElement element) {
String elementText;
String containerText = null;
if (element instanceof JetFile) {
elementText = ((JetFile) element).getName();
}
else if (element instanceof JetNamedDeclaration) {
BindingContext bindingContext =
AnalyzerFacadeWithCache.analyzeFileWithCache((JetFile) element.getContainingFile()).getBindingContext();
DeclarationDescriptor descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, element);
if (descriptor == null) return null;
if (element instanceof JetClassOrObject) {
if (element instanceof JetObjectDeclaration && ((JetObjectDeclaration) element).isClassObject()) {
descriptor = descriptor.getContainingDeclaration();
if (!(descriptor instanceof ClassDescriptor)) return null;
elementText = renderClassOrObject((ClassDescriptor) descriptor);
}
else if (element instanceof JetEnumEntry) {
elementText = ((JetEnumEntry) element).getName();
}
else {
if (((JetClassOrObject) element).getName() != null) {
elementText = renderClassOrObject((ClassDescriptor) descriptor);
}
else {
elementText = "[anonymous]";
}
}
}
else if (element instanceof JetNamedFunction) {
elementText = renderNamedFunction((FunctionDescriptor) descriptor);
}
else if (element instanceof JetProperty) {
elementText = ((JetProperty) element).getName();
}
else return null;
DeclarationDescriptor containerDescriptor = descriptor.getContainingDeclaration();
while (containerDescriptor != null) {
String name = containerDescriptor.getName().asString();
if (!name.startsWith("<")) {
containerText = name;
break;
}
containerDescriptor = containerDescriptor.getContainingDeclaration();
}
}
else if (element instanceof PsiMember) {
PsiMember member = (PsiMember) element;
if (member instanceof PsiMethod) {
elementText = renderPsiMethod((PsiMethod) member);
} else if (member instanceof PsiClass) {
elementText = ClassPresentationUtil.getNameForClass((PsiClass) member, false);
} else return null;
PsiClass containingClass = member.getContainingClass();
if (containingClass != null) {
containerText = ClassPresentationUtil.getNameForClass(containingClass, false);
}
}
else return null;
if (elementText == null) return null;
return containerText != null ? containerText + "." + elementText : elementText;
}
private static String renderPsiMethod(PsiMethod member) {
return PsiFormatUtil.formatMethod(
member, PsiSubstitutor.EMPTY, PsiFormatUtilBase.SHOW_NAME | PsiFormatUtilBase.SHOW_PARAMETERS, PsiFormatUtilBase.SHOW_TYPE
);
}
private static String renderNamedFunction(FunctionDescriptor descriptor) {
String name = descriptor.getName().asString();
String paramTypes = StringUtil.join(
descriptor.getValueParameters(),
new Function<ValueParameterDescriptor, String>() {
@Override
public String fun(ValueParameterDescriptor descriptor) {
return DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(descriptor.getType());
}
},
", "
);
return name + "(" + paramTypes + ")";
}
private static String renderClassOrObject(ClassDescriptor descriptor) {
return descriptor.getName().asString();
}
@Override
public void navigate(boolean requestFocus) {
if (myElement instanceof Navigatable) {
((Navigatable)myElement).navigate(requestFocus);
}
}
@Override
public boolean canNavigate() {
return myElement instanceof Navigatable && ((Navigatable) myElement).canNavigate();
}
@Override
public boolean canNavigateToSource() {
return canNavigate();
}
}
@@ -0,0 +1,68 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.plugin.hierarchy.calls;
import com.intellij.codeInsight.TargetElementUtilBase;
import com.intellij.ide.hierarchy.CallHierarchyBrowserBase;
import com.intellij.ide.hierarchy.HierarchyBrowser;
import com.intellij.ide.hierarchy.HierarchyProvider;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.plugin.JetPluginUtil;
import org.jetbrains.jet.plugin.hierarchy.HierarchyUtils;
public class KotlinCallHierarchyProvider implements HierarchyProvider {
@Override
public PsiElement getTarget(@NotNull DataContext dataContext) {
Project project = CommonDataKeys.PROJECT.getData(dataContext);
if (project == null) return null;
return HierarchyUtils.getCallHierarchyElement(getCurrentElement(dataContext, project));
}
private static PsiElement getCurrentElement(DataContext dataContext, Project project) {
Editor editor = CommonDataKeys.EDITOR.getData(dataContext);
if (editor != null) {
PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
if (file == null) return null;
if (!JetPluginUtil.isInSource(file)) return null;
if (JetPluginUtil.isKtFileInGradleProjectInWrongFolder(file)) return null;
return TargetElementUtilBase.findTargetElement(editor, TargetElementUtilBase.getInstance().getAllAccepted());
}
return CommonDataKeys.PSI_ELEMENT.getData(dataContext);
}
@NotNull
@Override
public HierarchyBrowser createHierarchyBrowser(PsiElement target) {
return new KotlinCallHierarchyBrowser(target.getProject(), target);
}
@Override
public void browserActivated(@NotNull HierarchyBrowser hierarchyBrowser) {
((KotlinCallHierarchyBrowser) hierarchyBrowser).changeView(CallHierarchyBrowserBase.CALLER_TYPE);
}
}
@@ -0,0 +1,97 @@
package org.jetbrains.jet.plugin.hierarchy.calls;
import com.intellij.ide.hierarchy.HierarchyNodeDescriptor;
import com.intellij.ide.hierarchy.HierarchyTreeStructure;
import com.intellij.ide.hierarchy.call.CallHierarchyNodeDescriptor;
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 org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.asJava.LightClassUtil;
import org.jetbrains.jet.lang.psi.*;
public abstract class KotlinCallTreeStructure extends HierarchyTreeStructure {
public KotlinCallTreeStructure(@NotNull Project project, HierarchyNodeDescriptor baseDescriptor) {
super(project, baseDescriptor);
}
protected static PsiElement getTargetElement(HierarchyNodeDescriptor descriptor) {
return descriptor instanceof CallHierarchyNodeDescriptor
? ((CallHierarchyNodeDescriptor) descriptor).getEnclosingElement()
: ((KotlinCallHierarchyNodeDescriptor)descriptor).getTargetElement();
}
@Nullable
protected PsiMethod getBasePsiMethod() {
return getPsiMethod(((KotlinCallHierarchyNodeDescriptor) getBaseDescriptor()).getTargetElement());
}
@Nullable
protected PsiClass getBasePsiClass() {
PsiMethod method = getBasePsiMethod();
return method != null ? method.getContainingClass() : null;
}
private static PsiMethod getClosestContainingClassConstructor(PsiElement element) {
while (element != null) {
JetClassOrObject classOrObject = PsiTreeUtil.getParentOfType(element, JetClassOrObject.class, false);
if (classOrObject == null) return null;
element = classOrObject.getParent();
PsiClass psiClass = LightClassUtil.getPsiClass(classOrObject);
if (psiClass == null) continue;
PsiMethod[] constructors = psiClass.getConstructors();
if (constructors.length > 0) return constructors[0];
}
return null;
}
@Nullable
protected static PsiMethod getPsiMethod(PsiElement element) {
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);
}
@Nullable
protected static PsiClass getPsiClass(PsiElement element) {
PsiMethod method = getPsiMethod(element);
return method != null ? method.getContainingClass() : null;
}
protected static CallHierarchyNodeDescriptor getJavaNodeDescriptor(HierarchyNodeDescriptor originalDescriptor) {
if (originalDescriptor instanceof CallHierarchyNodeDescriptor) return (CallHierarchyNodeDescriptor) originalDescriptor;
assert originalDescriptor instanceof KotlinCallHierarchyNodeDescriptor;
return ((KotlinCallHierarchyNodeDescriptor) originalDescriptor).getJavaDelegate();
}
@Override
public boolean isAlwaysShowPlus() {
return true;
}
}
@@ -0,0 +1,232 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.plugin.hierarchy.calls;
import com.intellij.ide.hierarchy.HierarchyNodeDescriptor;
import com.intellij.ide.hierarchy.call.CallHierarchyNodeDescriptor;
import com.intellij.ide.hierarchy.call.CalleeMethodsTreeStructure;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.HashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.PropertyDescriptor;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.BindingContextUtils;
import org.jetbrains.jet.lang.resolve.java.jetAsJava.JetClsMethod;
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache;
import java.util.ArrayList;
import java.util.List;
public class KotlinCalleeMethodsTreeStructure extends KotlinCallTreeStructure {
private final String scopeType;
private final CalleeMethodsTreeStructure javaTreeStucture;
public KotlinCalleeMethodsTreeStructure(@NotNull Project project, @NotNull PsiElement element, String scopeType) {
super(project, new KotlinCallHierarchyNodeDescriptor(project, null, element, true, false));
this.scopeType = scopeType;
PsiMethod psiMethod = getPsiMethod(element);
assert psiMethod != null;
this.javaTreeStucture = new CalleeMethodsTreeStructure(project, psiMethod, scopeType);
}
private static class VisitorBase extends JetTreeVisitorVoid {
final BindingContext bindingContext;
final ArrayList<PsiElement> result;
private VisitorBase(BindingContext bindingContext, ArrayList<PsiElement> result) {
this.bindingContext = bindingContext;
this.result = result;
}
@Override
public void visitJetElement(JetElement element) {
if (!(element instanceof JetClassOrObject || element instanceof JetNamedFunction)) {
super.visitJetElement(element);
}
}
@Override
public void visitCallExpression(JetCallExpression expression) {
super.visitCallExpression(expression);
processCallElement(expression);
}
@Override
public void visitSimpleNameExpression(JetSimpleNameExpression expression) {
DeclarationDescriptor descriptor = bindingContext.get(BindingContext.REFERENCE_TARGET, expression);
if (descriptor instanceof PropertyDescriptor) {
PsiElement declaration = BindingContextUtils.descriptorToDeclaration(bindingContext, descriptor);
if (declaration instanceof JetProperty) {
result.add(declaration);
}
}
}
@Override
public void visitDelegationSpecifier(JetDelegationSpecifier specifier) {
if (specifier instanceof JetCallElement) {
processCallElement((JetCallElement) specifier);
}
}
private void processCallElement(JetCallElement element) {
JetExpression callee = element.getCalleeExpression();
JetReferenceExpression referenceExpression = null;
if (callee instanceof JetReferenceExpression) {
referenceExpression = (JetReferenceExpression) callee;
}
else if (callee instanceof JetConstructorCalleeExpression) {
referenceExpression = ((JetConstructorCalleeExpression) callee).getConstructorReferenceExpression();
}
if (referenceExpression != null) {
DeclarationDescriptor descriptor = bindingContext.get(BindingContext.REFERENCE_TARGET, referenceExpression);
if (descriptor == null) return;
PsiElement declaration = BindingContextUtils.descriptorToDeclaration(bindingContext, descriptor);
if (declaration instanceof JetClassOrObject
|| (declaration instanceof JetNamedFunction && !((JetNamedFunction) declaration).isLocal())
|| declaration instanceof PsiMethod ) {
result.add(declaration);
}
}
}
}
private static List<? extends PsiElement> getCalleeElements(@NotNull JetElement rootElement, BindingContext bindingContext) {
ArrayList<PsiElement> result = new ArrayList<PsiElement>();
JetVisitorVoid visitor = new VisitorBase(bindingContext, result);
if (rootElement instanceof JetNamedFunction) {
JetExpression body = ((JetNamedFunction) rootElement).getBodyExpression();
if (body != null) {
body.accept(visitor);
}
} else if (rootElement instanceof JetProperty) {
for (JetPropertyAccessor accessor : ((JetProperty) rootElement).getAccessors()) {
JetExpression body = accessor.getBodyExpression();
if (body != null) {
body.accept(visitor);
}
}
} else {
JetClassOrObject classOrObject = (JetClassOrObject) rootElement;
for (JetDelegationSpecifier specifier : classOrObject.getDelegationSpecifiers()) {
if (specifier instanceof JetCallElement) {
specifier.accept(visitor);
}
}
JetClassBody body = classOrObject.getBody();
if (body != null) {
for (JetClassInitializer initializer : body.getAnonymousInitializers()) {
initializer.getBody().accept(visitor);
}
for (JetProperty property : body.getProperties()) {
JetExpression initializer = property.getInitializer();
if (initializer != null) {
initializer.accept(visitor);
}
}
}
}
return result;
}
// copied from Java
private Object[] collectNodeDescriptors(
HierarchyNodeDescriptor descriptor,
List<? extends PsiElement> calleeElements,
PsiElement baseElement
) {
HashMap<PsiElement, KotlinCallHierarchyNodeDescriptor> declarationToDescriptorMap =
new HashMap<PsiElement, KotlinCallHierarchyNodeDescriptor>();
ArrayList<KotlinCallHierarchyNodeDescriptor> result = new ArrayList<KotlinCallHierarchyNodeDescriptor>();
for (PsiElement callee : calleeElements) {
if (!isInScope(baseElement, callee, scopeType)) continue;
KotlinCallHierarchyNodeDescriptor d = declarationToDescriptorMap.get(callee);
if (d == null) {
d = new KotlinCallHierarchyNodeDescriptor(myProject, descriptor, callee, false, false);
declarationToDescriptorMap.put(callee, d);
result.add(d);
}
else {
d.incrementUsageCount();
}
}
return ArrayUtil.toObjectArray(result);
}
@Override
protected Object[] buildChildren(HierarchyNodeDescriptor descriptor) {
PsiElement targetElement = getTargetElement(descriptor);
// Kotlin class constructor invoked from Java code
if (targetElement instanceof PsiMethod) {
PsiMethod psiMethod = (PsiMethod) targetElement;
if (psiMethod.isConstructor()) {
PsiClass psiClass = psiMethod.getContainingClass();
PsiElement navigationElement = psiClass != null ? psiClass.getNavigationElement() : null;
if (navigationElement instanceof JetClass) {
return buildChildrenByKotlinTarget(descriptor, (JetElement) navigationElement);
}
}
}
// Kotlin function or property invoked from Java code
if (targetElement instanceof JetClsMethod) {
return buildChildrenByKotlinTarget(descriptor, ((JetClsMethod) targetElement).getOrigin());
}
if (targetElement instanceof JetElement) {
return buildChildrenByKotlinTarget(descriptor, (JetElement) targetElement);
}
if (javaTreeStucture != null) {
CallHierarchyNodeDescriptor javaDescriptor = descriptor instanceof CallHierarchyNodeDescriptor
? (CallHierarchyNodeDescriptor) descriptor
: ((KotlinCallHierarchyNodeDescriptor)descriptor).getJavaDelegate();
return javaTreeStucture.getChildElements(javaDescriptor);
}
return ArrayUtil.EMPTY_OBJECT_ARRAY;
}
private Object[] buildChildrenByKotlinTarget(HierarchyNodeDescriptor descriptor, JetElement targetElement) {
PsiClass baseClass = getBasePsiClass();
if (baseClass == null) return ArrayUtil.EMPTY_OBJECT_ARRAY;
BindingContext bindingContext =
AnalyzerFacadeWithCache.analyzeFileWithCache((JetFile) targetElement.getContainingFile()).getBindingContext();
List<? extends PsiElement> calleeDescriptors = getCalleeElements((JetElement) targetElement, bindingContext);
return collectNodeDescriptors(descriptor, calleeDescriptors, baseClass);
}
}
@@ -0,0 +1,183 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.plugin.hierarchy.calls;
import com.intellij.ide.hierarchy.HierarchyNodeDescriptor;
import com.intellij.ide.hierarchy.call.CallerMethodsTreeStructure;
import com.intellij.openapi.application.ReadActionProcessor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.psi.*;
import com.intellij.psi.search.SearchScope;
import com.intellij.psi.search.searches.MethodReferencesSearch;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.FilteringProcessor;
import com.intellij.util.Processor;
import com.intellij.util.containers.HashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.asJava.LightClassUtil;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.plugin.findUsages.FindUsagesUtils;
import org.jetbrains.jet.plugin.hierarchy.HierarchyUtils;
import org.jetbrains.jet.plugin.references.JetPsiReference;
import java.util.Map;
public class KotlinCallerMethodsTreeStructure extends KotlinCallTreeStructure {
private final String scopeType;
private final CallerMethodsTreeStructure javaTreeStructure;
public KotlinCallerMethodsTreeStructure(@NotNull Project project, @NotNull PsiElement element, String scopeType) {
super(project, new KotlinCallHierarchyNodeDescriptor(project, null, element, true, false));
this.scopeType = scopeType;
PsiMethod psiMethod = getPsiMethod(element);
assert psiMethod != null;
this.javaTreeStructure = new CallerMethodsTreeStructure(project, psiMethod, scopeType);
}
@Override
protected Object[] buildChildren(HierarchyNodeDescriptor descriptor) {
PsiElement targetElement = getTargetElement(descriptor);
PsiClass baseClass = getBasePsiClass();
if (baseClass == null) return ArrayUtil.EMPTY_OBJECT_ARRAY;
return processCallers(targetElement, descriptor, baseClass);
}
private Object[] processCallers(PsiElement element, HierarchyNodeDescriptor descriptor, PsiClass baseClass) {
SearchScope searchScope = getSearchScope(scopeType, baseClass);
Map<PsiElement, KotlinCallHierarchyNodeDescriptor> methodToDescriptorMap =
new HashMap<PsiElement, KotlinCallHierarchyNodeDescriptor>();
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, KotlinCallHierarchyNodeDescriptor> 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, KotlinCallHierarchyNodeDescriptor> 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, KotlinCallHierarchyNodeDescriptor> 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) {
KotlinCallHierarchyNodeDescriptor d = methodToDescriptorMap.get(element);
if (d == null) {
d = new KotlinCallHierarchyNodeDescriptor(myProject, descriptor, element, false, true);
methodToDescriptorMap.put(element, d);
}
else if (!d.hasReference(ref)) {
d.incrementUsageCount();
}
d.addReference(ref);
}
return true;
}
};
}
}
@@ -0,0 +1,9 @@
<node text="KClient.KClient()" base="true">
<node text="KA.KA()(2 usages) ()"/>
<node text="KA.foo(String) ()"/>
<node text="JA.JA()(2 usages) ()"/>
<node text="JA.getName() ()"/>
<node text="JA.foo(String) ()">
<node text="PrintStream.println(String) (java.io)"/>
</node>
</node>
@@ -0,0 +1,31 @@
class KA {
KA() {
}
public final String name = "A";
public final String foo(String s) {
return "A " + s;
}
}
class KClientBase {
}
class KClient extends KClientBase {
public <caret>KClient() {
super();
new KA().foo(new KA().name);
new JA().foo(new JA().getName());
}
{
new KA().foo(new KA().name);
new JA().foo(new JA().getName());
}
public final void bar() {
new KA().foo(new KA().name);
new JA().foo(new JA().getName());
}
}
@@ -0,0 +1,8 @@
open class JA() {
public var name: String = "A"
public open fun foo(s: String): String {
System.out.println(s)
return "A " + s
}
}
@@ -0,0 +1,18 @@
<node text="KClient.bar()" base="true">
<node text="KA.KA()(2 usages) ()"/>
<node text="KA.foo(String)(2 usages) ()"/>
<node text="JA.JA()(4 usages) ()"/>
<node text="JA.getName()(2 usages) ()"/>
<node text="JA.foo(String)(2 usages) ()">
<node text="PrintStream.println(String) (java.io)"/>
</node>
<node text="Anonymous in bar() in KClient.run() ()">
<node text="KA.KA() ()"/>
<node text="KA.foo(String) ()"/>
<node text="JA.JA()(2 usages) ()"/>
<node text="JA.getName() ()"/>
<node text="JA.foo(String) ()">
<node text="PrintStream.println(String) (java.io)"/>
</node>
</node>
</node>
@@ -0,0 +1,25 @@
class KA {
public KA() {
}
public final String name = "A";
public final String foo(String s) {
return "A " + s;
}
}
class KClient {
public final void <caret>bar() {
new KA().foo("");
new JA().foo(new JA().getName());
new Runnable() {
public void run() {
new KA().foo("");
new JA().foo(new JA().getName());
}
}.run();
}
}
@@ -0,0 +1,8 @@
open class JA() {
public var name: String = "A"
public open fun foo(s: String): String {
System.out.println(s)
return "A " + s
}
}
@@ -0,0 +1,12 @@
<node text="KClient" base="true">
<node text="KClientBase"/>
<node text="KA(4 usages)"/>
<node text="KA.name(2 usages)"/>
<node text="KA.foo(String)(2 usages)"/>
<node text="JA.JA()(4 usages)"/>
<node text="JA.foo(String)(2 usages)">
<node text="PrintStream.println(String) (java.io)"/>
</node>
<node text="packageVal"/>
<node text="packageFun(String)(2 usages)"/>
</node>
@@ -0,0 +1,36 @@
class KA {
val name = "A"
fun foo(s: String): String = "A: $s"
}
fun packageFun(s: String): String = s
val packageVal = ""
open class KClientBase {
}
class <caret>KClient(): KClientBase() {
{
fun localFun(s: String): String = packageFun(s)
KA().foo(KA().name)
JA().foo(JA().name)
localFun(packageVal)
}
val bar = run {
val localVal = packageFun("")
KA().foo(KA().name)
JA().foo(JA().name)
packageFun(localVal)
}
fun bar() {
KA().foo(KA().name)
JA().foo(JA().name)
}
}
@@ -0,0 +1,12 @@
class JA {
public String name = "A";
public JA() {
}
public String foo(String s) {
System.out.println(s);
return "A " + s;
}
}
@@ -0,0 +1,10 @@
<node text="KClient" base="true">
<node text="packageFun(String)(2 usages)"/>
<node text="KA(2 usages)"/>
<node text="KA.name"/>
<node text="KA.foo(String)"/>
<node text="JA.JA()(2 usages)"/>
<node text="JA.foo(String)">
<node text="PrintStream.println(String) (java.io)"/>
</node>
</node>
@@ -0,0 +1,38 @@
class KA {
val name = "A"
fun foo(s: String): String = "A: $s"
}
fun packageFun(s: String): String = s
val packageVal = ""
open class KClientBase {
}
class KClient: KClientBase() {
class <caret>object {
val bar = run {
val localVal = packageFun("")
KA().foo(KA().name)
JA().foo(JA().name)
packageFun(localVal)
}
fun bar() {
KA().foo(KA().name)
JA().foo(JA().name)
}
}
{
fun localFun(s: String): String = packageFun(s)
KA().foo(KA().name)
JA().foo(JA().name)
localFun(packageVal)
}
}
@@ -0,0 +1,12 @@
class JA {
public JA() {
}
public String name = "A";
public String foo(String s) {
System.out.println(s);
return "A " + s;
}
}
@@ -0,0 +1,10 @@
<node text="MyEnum.FIRST" base="true">
<node text="packageFun(String)(2 usages)"/>
<node text="KA(2 usages)"/>
<node text="KA.name"/>
<node text="KA.foo(String)"/>
<node text="JA.JA()(2 usages)"/>
<node text="JA.foo(String)">
<node text="PrintStream.println(String) (java.io)"/>
</node>
</node>
@@ -0,0 +1,45 @@
class KA {
val name = "A"
fun foo(s: String): String = "A: $s"
}
fun packageFun(s: String): String = s
val packageVal = ""
open class KClientBase {
}
enum class MyEnum {
<caret>FIRST {
val bar = run {
val localVal = packageFun("")
KA().foo(KA().name)
JA().foo(JA().name)
packageFun(localVal)
}
}
SECOND {
{
KA().foo(KA().name)
JA().foo(JA().name)
}
}
fun bar() {
KA().foo(KA().name)
JA().foo(JA().name)
}
{
fun localFun(s: String): String = packageFun(s)
KA().foo(KA().name)
JA().foo(JA().name)
localFun(packageVal)
}
}
@@ -0,0 +1,12 @@
class JA {
public JA() {
}
public String name = "A";
public String foo(String s) {
System.out.println(s);
return "A " + s;
}
}
@@ -0,0 +1,11 @@
<node text="KClient.bar()" base="true">
<node text="KA(4 usages)"/>
<node text="KA.name(2 usages)"/>
<node text="KA.foo(String)(2 usages)"/>
<node text="JA.JA()(4 usages)"/>
<node text="JA.foo(String)(2 usages)">
<node text="PrintStream.println(String) (java.io)"/>
</node>
<node text="packageVal"/>
<node text="packageFun(String)"/>
</node>
@@ -0,0 +1,25 @@
class KA {
val name = "A"
fun foo(s: String): String = "A: $s"
}
fun packageFun(s: String): String = s
val packageVal = ""
class KClient() {
fun <caret>bar() {
fun localFun(s: String): String = packageFun(s)
val localVal = localFun("")
KA().foo(KA().name)
JA().foo(JA().name)
localFun(packageVal)
run {
KA().foo(KA().name)
JA().foo(JA().name)
packageFun(localVal)
}
}
}
@@ -0,0 +1,12 @@
class JA {
public JA() {
}
public String name = "A";
public String foo(String s) {
System.out.println(s);
return "A " + s;
}
}
@@ -0,0 +1,10 @@
<node text="KClient.T ()" base="true">
<node text="KA.name ()"/>
<node text="JA.JA()(2 usages) ()"/>
<node text="JA.foo(String) ()">
<node text="PrintStream.println(String) (java.io)"/>
</node>
<node text="packageFun(String)(2 usages) ()"/>
<node text="KA.foo(String) ()"/>
<node text="KA(2 usages) ()"/>
</node>
@@ -0,0 +1,38 @@
class KA {
val name = "A"
fun foo(s: String): String = "A: $s"
}
fun packageFun(s: String): String = s
val packageVal = ""
open class KClientBase {
}
class KClient: KClientBase() {
class <caret>T {
val bar = run {
val localVal = packageFun("")
KA().foo(KA().name)
JA().foo(JA().name)
packageFun(localVal)
}
fun bar() {
KA().foo(KA().name)
JA().foo(JA().name)
}
}
{
fun localFun(s: String): String = packageFun(s)
KA().foo(KA().name)
JA().foo(JA().name)
localFun(packageVal)
}
}
@@ -0,0 +1,12 @@
class JA {
public JA() {
}
public String name = "A";
public String foo(String s) {
System.out.println(s);
return "A " + s;
}
}
@@ -0,0 +1,12 @@
<node text="KClient" base="true">
<node text="KClientBase"/>
<node text="KA(4 usages)"/>
<node text="KA.name(2 usages)"/>
<node text="KA.foo(String)(2 usages)"/>
<node text="JA.JA()(4 usages)"/>
<node text="JA.foo(String)(2 usages)">
<node text="PrintStream.println(String) (java.io)"/>
</node>
<node text="packageVal"/>
<node text="packageFun(String)(2 usages)"/>
</node>
@@ -0,0 +1,36 @@
class KA {
val name = "A"
fun foo(s: String): String = "A: $s"
}
fun packageFun(s: String): String = s
val packageVal = ""
open class KClientBase {
}
object <caret>KClient: KClientBase() {
{
fun localFun(s: String): String = packageFun(s)
KA().foo(KA().name)
JA().foo(JA().name)
localFun(packageVal)
}
val bar = run {
val localVal = packageFun("")
KA().foo(KA().name)
JA().foo(JA().name)
packageFun(localVal)
}
fun bar() {
KA().foo(KA().name)
JA().foo(JA().name)
}
}
@@ -0,0 +1,12 @@
class JA {
public JA() {
}
public String name = "A";
public String foo(String s) {
System.out.println(s);
return "A " + s;
}
}
@@ -0,0 +1,11 @@
<node text="KClient.bar" base="true">
<node text="KA(4 usages)"/>
<node text="KA.name(2 usages)"/>
<node text="KA.foo(String)(2 usages)"/>
<node text="JA.JA()(4 usages)"/>
<node text="JA.foo(String)(2 usages)">
<node text="PrintStream.println(String) (java.io)"/>
</node>
<node text="packageVal"/>
<node text="packageFun(String)"/>
</node>
@@ -0,0 +1,28 @@
class KA {
val name = "A"
fun foo(s: String): String = "A: $s"
}
fun packageFun(s: String): String = s
val packageVal = ""
class KClient() {
val <caret>bar: String
get() {
fun localFun(s: String): String = packageFun(s)
val localVal = localFun("")
KA().foo(KA().name)
JA().foo(JA().name)
localFun(packageVal)
run {
KA().foo(KA().name)
JA().foo(JA().name)
packageFun(localVal)
}
return ""
}
}
@@ -0,0 +1,12 @@
class JA {
public JA() {
}
public String name = "A";
public String foo(String s) {
System.out.println(s);
return "A " + s;
}
}
@@ -0,0 +1,7 @@
<node text="KA.KA()" base="true">
<node text="KClient.bar() ()"/>
<node text="KClient.getBar() ()"/>
<node text="KClient ()"/>
<node text="JA.newKA()"/>
<node text="JA"/>
</node>
@@ -0,0 +1,27 @@
class KA {
public <caret>KA() {
}
public final String name = "A";
public String foo(String s) {
return "A " + s;
}
}
class KClient {
{
new KA();
}
public static final a = new KA();
public final String getBar() {
return new KA().name;
}
public final KA bar() {
return new KA();
}
}
@@ -0,0 +1,7 @@
open class JA() {
public var name: String = KA().getName()
public open fun newKA(): KA? {
return KA()
}
}
@@ -0,0 +1,7 @@
<node text="KA.foo(String)" base="true">
<node text="KClient(2 usages) ()"/>
<node text="KClient.getBar() ()"/>
<node text="KClient.bar() ()"/>
<node text="JA.foo()"/>
<node text="JA"/>
</node>
@@ -0,0 +1,23 @@
class KA {
public final String name = "A"
public final String <caret>foo(String s) {
return "A " + s;
}
}
class KClient {
{
new KA().foo("");
}
public static final String a = new KA().foo("");
public final String getBar() {
return new KA().foo("");
}
public final String bar() {
return new KA().foo("");
}
}
@@ -0,0 +1,7 @@
open class JA() {
public var name: String = KA().foo("")
public open fun foo(): String {
return KA().foo("")
}
}
@@ -0,0 +1,11 @@
<node text="KA" base="true">
<node text="JA"/>
<node text="JA.newKA()"/>
<node text="KClient"/>
<node text="KClient"/>
<node text="KClient.bar"/>
<node text="KClient.bar()(2 usages)"/>
<node text="KClientObj"/>
<node text="main0.kt"/>
<node text="packageFun(String)"/>
</node>
@@ -0,0 +1,31 @@
class <caret>KA {
val name = "A"
fun foo(s: String): String = "A: $s"
}
fun packageFun(s: String): String = s + KA().name
val packageVal = KA().name
class KClient {
{
KA()
}
class object {
val a = KA()
}
val bar: String
get() = KA().name
fun bar() {
fun localFun() = KA()
KA()
}
}
object KClientObj {
val a = KA()
}
@@ -0,0 +1,7 @@
class JA {
public String name = new KA().getName();
public KA newKA() {
return new KA();
}
}
@@ -0,0 +1,11 @@
<node text="KA.foo(String)" base="true">
<node text="JA"/>
<node text="JA.foo()"/>
<node text="KClient"/>
<node text="KClient"/>
<node text="KClient.bar"/>
<node text="KClient.bar()(2 usages)"/>
<node text="KClientObj"/>
<node text="main0.kt"/>
<node text="packageFun(String)"/>
</node>
@@ -0,0 +1,31 @@
class KA {
val name = "A"
fun <caret>foo(s: String): String = "A: $s"
}
fun packageFun(s: String): String = KA().foo(s)
val packageVal = KA().foo("")
class KClient {
{
KA().foo("")
}
class object {
val a = KA().foo("")
}
val bar: String
get() = KA().foo("")
fun bar() {
fun localFun() = KA().foo("")
KA().foo("")
}
}
object KClientObj {
val a = KA().foo("")
}
@@ -0,0 +1,7 @@
class JA {
public String name = new KA().foo("");
public String foo() {
return new KA().foo("");
}
}
@@ -0,0 +1,12 @@
<node text="T.KA ()" base="true">
<node text="main0.kt ()"/>
<node text="JA.newKA() ()"/>
<node text="KClientObj ()"/>
<node text="KClient ()"/>
<node text="packageFun(String) ()"/>
<node text="KClient.bar ()"/>
<node text="bar.localFun() ()"/>
<node text="KClient ()"/>
<node text="JA ()"/>
<node text="KClient.bar() ()"/>
</node>
@@ -0,0 +1,33 @@
class T {
class <caret>KA {
val name = "A"
fun foo(s: String): String = "A: $s"
}
}
fun packageFun(s: String): String = s + T.KA().name
val packageVal = T.KA().name
class KClient {
{
T.KA()
}
class object {
val a = T.KA()
}
val bar: String
get() = T.KA().name
fun bar() {
fun localFun() = T.KA()
T.KA()
}
}
object KClientObj {
val a = T.KA()
}
@@ -0,0 +1,7 @@
class JA {
public String name = new T.KA().getName();
public KA newKA() {
return new T.KA();
}
}
@@ -0,0 +1,12 @@
<node text="T.KA ()" base="true">
<node text="KClient.bar ()"/>
<node text="JA.newKA() ()"/>
<node text="bar.localFun() ()"/>
<node text="JA ()"/>
<node text="KClient ()"/>
<node text="KClientObj ()"/>
<node text="KClient.bar() ()"/>
<node text="packageFun(String) ()"/>
<node text="KClient ()"/>
<node text="main0.kt ()"/>
</node>
@@ -0,0 +1,33 @@
class T {
inner class <caret>KA {
val name = "A"
fun foo(s: String): String = "A: $s"
}
}
fun packageFun(s: String): String = s + T().KA().name
val packageVal = T().KA().name
class KClient {
{
T().KA()
}
class object {
val a = T().KA()
}
val bar: String
get() = T().KA().name
fun bar() {
fun localFun() = T().KA()
T().KA()
}
}
object KClientObj {
val a = T().KA()
}
@@ -0,0 +1,7 @@
class JA {
public String name = new T().new KA().getName();
public KA newKA() {
return new T().new KA();
}
}
@@ -0,0 +1,11 @@
<node text="KA.name" base="true">
<node text="JA"/>
<node text="JA.getName()"/>
<node text="KClient(2 usages)"/>
<node text="KClient(2 usages)"/>
<node text="KClient.bar()(4 usages)"/>
<node text="KClient.bar(4 usages)"/>
<node text="KClientObj(4 usages)"/>
<node text="main0.kt(2 usages)"/>
<node text="packageFun(String)(2 usages)"/>
</node>
@@ -0,0 +1,35 @@
class KA {
var <caret>name = "A"
fun foo(s: String): String = "A: $s"
}
fun packageFun(s: String): String = KA().name
val packageVal = KA().name
class KClient {
{
KA().name = ""
}
class object {
val a = KA().name
}
val bar: String
get() = KA().name
set(value: String) {KA().name = value}
fun bar() {
fun localFun() = KA().name
val s = KA().name
}
}
object KClientObj {
val a = KA().name
{
KA().name = ""
}
}
@@ -0,0 +1,7 @@
class JA {
public String name = new KA().getName();
public String getName() {
return new KA().getName();
}
}
@@ -16,15 +16,13 @@
package org.jetbrains.jet.plugin.hierarchy;
import com.intellij.ide.hierarchy.HierarchyBrowserBaseEx;
import com.intellij.ide.hierarchy.HierarchyTreeStructure;
import com.intellij.ide.hierarchy.LanguageTypeHierarchy;
import com.intellij.ide.hierarchy.*;
import com.intellij.ide.hierarchy.type.SubtypesHierarchyTreeStructure;
import com.intellij.ide.hierarchy.type.SupertypesHierarchyTreeStructure;
import com.intellij.ide.hierarchy.type.TypeHierarchyTreeStructure;
import com.intellij.lang.Language;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.io.FileUtil;
@@ -38,6 +36,8 @@ import com.intellij.util.ArrayUtil;
import com.intellij.util.Processor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.plugin.PluginTestCaseBase;
import org.jetbrains.jet.plugin.hierarchy.calls.KotlinCalleeMethodsTreeStructure;
import org.jetbrains.jet.plugin.hierarchy.calls.KotlinCallerMethodsTreeStructure;
import java.io.File;
import java.util.ArrayList;
@@ -68,11 +68,24 @@ public abstract class AbstractHierarchyTest extends HierarchyViewTestBase {
doHierarchyTest(getSubTypesHierarchyStructure(), getFilesToConfigure());
}
protected void doCallerHierarchyTest(@NotNull String folderName) throws Exception {
this.folderName = folderName;
doHierarchyTest(getCallerHierarchyStructure(), getFilesToConfigure());
}
protected void doCalleeHierarchyTest(@NotNull String folderName) throws Exception {
this.folderName = folderName;
doHierarchyTest(getCalleeHierarchyStructure(), getFilesToConfigure());
}
private Computable<HierarchyTreeStructure> getSuperTypesHierarchyStructure() {
return new Computable<HierarchyTreeStructure>() {
@Override
public HierarchyTreeStructure compute() {
return new SupertypesHierarchyTreeStructure(getProject(), (PsiClass) getElementAtCaret());
return new SupertypesHierarchyTreeStructure(
getProject(),
(PsiClass) getElementAtCaret(LanguageTypeHierarchy.INSTANCE.forLanguage(getLanguage()))
);
}
};
}
@@ -81,8 +94,11 @@ public abstract class AbstractHierarchyTest extends HierarchyViewTestBase {
return new Computable<HierarchyTreeStructure>() {
@Override
public HierarchyTreeStructure compute() {
return new SubtypesHierarchyTreeStructure(getProject(), (PsiClass) getElementAtCaret(),
HierarchyBrowserBaseEx.SCOPE_PROJECT);
return new SubtypesHierarchyTreeStructure(
getProject(),
(PsiClass) getElementAtCaret(LanguageTypeHierarchy.INSTANCE.forLanguage(getLanguage())),
HierarchyBrowserBaseEx.SCOPE_PROJECT
);
}
};
}
@@ -91,14 +107,43 @@ public abstract class AbstractHierarchyTest extends HierarchyViewTestBase {
return new Computable<HierarchyTreeStructure>() {
@Override
public HierarchyTreeStructure compute() {
return new TypeHierarchyTreeStructure(getProject(), (PsiClass) getElementAtCaret(),
HierarchyBrowserBaseEx.SCOPE_PROJECT);
return new TypeHierarchyTreeStructure(
getProject(),
(PsiClass) getElementAtCaret(LanguageTypeHierarchy.INSTANCE.forLanguage(getLanguage())),
HierarchyBrowserBaseEx.SCOPE_PROJECT
);
}
};
}
private PsiElement getElementAtCaret() {
PsiElement target = LanguageTypeHierarchy.INSTANCE.forLanguage(getLanguage()).getTarget(getDataContext());
private Computable<HierarchyTreeStructure> getCallerHierarchyStructure() {
return new Computable<HierarchyTreeStructure>() {
@Override
public HierarchyTreeStructure compute() {
return new KotlinCallerMethodsTreeStructure(
getProject(),
getElementAtCaret(LanguageCallHierarchy.INSTANCE.forLanguage(getLanguage())),
HierarchyBrowserBaseEx.SCOPE_PROJECT
);
}
};
}
private Computable<HierarchyTreeStructure> getCalleeHierarchyStructure() {
return new Computable<HierarchyTreeStructure>() {
@Override
public HierarchyTreeStructure compute() {
return new KotlinCalleeMethodsTreeStructure(
getProject(),
getElementAtCaret(LanguageCallHierarchy.INSTANCE.forLanguage(getLanguage())),
HierarchyBrowserBaseEx.SCOPE_PROJECT
);
}
};
}
private PsiElement getElementAtCaret(HierarchyProvider provider) {
PsiElement target = provider.getTarget(getDataContext());
assert target != null : "Cannot apply action for element at caret";
return target;
}
@@ -111,8 +156,8 @@ public abstract class AbstractHierarchyTest extends HierarchyViewTestBase {
private DataContext getDataContext() {
MapDataContext context = new MapDataContext();
context.put(PlatformDataKeys.PROJECT, getProject());
context.put(PlatformDataKeys.EDITOR, getEditor());
context.put(CommonDataKeys.PROJECT, getProject());
context.put(CommonDataKeys.EDITOR, getEditor());
return context;
}
@@ -30,7 +30,7 @@ import org.jetbrains.jet.plugin.hierarchy.AbstractHierarchyTest;
/** This class is generated by {@link org.jetbrains.jet.generators.tests.GenerateTests}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@InnerTestClasses({HierarchyTestGenerated.Type.class, HierarchyTestGenerated.Super.class, HierarchyTestGenerated.Sub.class})
@InnerTestClasses({HierarchyTestGenerated.Type.class, HierarchyTestGenerated.Super.class, HierarchyTestGenerated.Sub.class, HierarchyTestGenerated.Callers.class, HierarchyTestGenerated.Callees.class})
public class HierarchyTestGenerated extends AbstractHierarchyTest {
@TestMetadata("idea/testData/hierarchy/class/type")
public static class Type extends AbstractHierarchyTest {
@@ -191,11 +191,109 @@ public class HierarchyTestGenerated extends AbstractHierarchyTest {
}
@TestMetadata("idea/testData/hierarchy/calls/callers")
public static class Callers extends AbstractHierarchyTest {
public void testAllFilesPresentInCallers() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("idea/testData/hierarchy/calls/callers"), Pattern.compile("^(.+)$"), false);
}
@TestMetadata("javaConstructor")
public void testJavaConstructor() throws Exception {
doCallerHierarchyTest("idea/testData/hierarchy/calls/callers/javaConstructor");
}
@TestMetadata("javaMethod")
public void testJavaMethod() throws Exception {
doCallerHierarchyTest("idea/testData/hierarchy/calls/callers/javaMethod");
}
@TestMetadata("kotlinClass")
public void testKotlinClass() throws Exception {
doCallerHierarchyTest("idea/testData/hierarchy/calls/callers/kotlinClass");
}
@TestMetadata("kotlinFunction")
public void testKotlinFunction() throws Exception {
doCallerHierarchyTest("idea/testData/hierarchy/calls/callers/kotlinFunction");
}
@TestMetadata("kotlinNestedClass")
public void testKotlinNestedClass() throws Exception {
doCallerHierarchyTest("idea/testData/hierarchy/calls/callers/kotlinNestedClass");
}
@TestMetadata("kotlinNestedInnerClass")
public void testKotlinNestedInnerClass() throws Exception {
doCallerHierarchyTest("idea/testData/hierarchy/calls/callers/kotlinNestedInnerClass");
}
@TestMetadata("kotlinProperty")
public void testKotlinProperty() throws Exception {
doCallerHierarchyTest("idea/testData/hierarchy/calls/callers/kotlinProperty");
}
}
@TestMetadata("idea/testData/hierarchy/calls/callees")
public static class Callees extends AbstractHierarchyTest {
public void testAllFilesPresentInCallees() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("idea/testData/hierarchy/calls/callees"), Pattern.compile("^(.+)$"), false);
}
@TestMetadata("javaConstructor")
public void testJavaConstructor() throws Exception {
doCalleeHierarchyTest("idea/testData/hierarchy/calls/callees/javaConstructor");
}
@TestMetadata("javaMethod")
public void testJavaMethod() throws Exception {
doCalleeHierarchyTest("idea/testData/hierarchy/calls/callees/javaMethod");
}
@TestMetadata("kotlinClass")
public void testKotlinClass() throws Exception {
doCalleeHierarchyTest("idea/testData/hierarchy/calls/callees/kotlinClass");
}
@TestMetadata("kotlinClassObject")
public void testKotlinClassObject() throws Exception {
doCalleeHierarchyTest("idea/testData/hierarchy/calls/callees/kotlinClassObject");
}
@TestMetadata("kotlinEnumClass")
public void testKotlinEnumClass() throws Exception {
doCalleeHierarchyTest("idea/testData/hierarchy/calls/callees/kotlinEnumClass");
}
@TestMetadata("kotlinFunction")
public void testKotlinFunction() throws Exception {
doCalleeHierarchyTest("idea/testData/hierarchy/calls/callees/kotlinFunction");
}
@TestMetadata("kotlinNestedClass")
public void testKotlinNestedClass() throws Exception {
doCalleeHierarchyTest("idea/testData/hierarchy/calls/callees/kotlinNestedClass");
}
@TestMetadata("kotlinObject")
public void testKotlinObject() throws Exception {
doCalleeHierarchyTest("idea/testData/hierarchy/calls/callees/kotlinObject");
}
@TestMetadata("kotlinProperty")
public void testKotlinProperty() throws Exception {
doCalleeHierarchyTest("idea/testData/hierarchy/calls/callees/kotlinProperty");
}
}
public static Test suite() {
TestSuite suite = new TestSuite("HierarchyTestGenerated");
suite.addTestSuite(Type.class);
suite.addTestSuite(Super.class);
suite.addTestSuite(Sub.class);
suite.addTestSuite(Callers.class);
suite.addTestSuite(Callees.class);
return suite;
}
}