Call Hierarchy: Refactoring
Do not rely on light methods to search usages as some elements don't have them (e.g. local functions) #KT-9288 Fixed #KT-14428 Fixed
This commit is contained in:
@@ -36,3 +36,18 @@ fun KtDeclaration.processAllExactUsages(
|
||||
options()
|
||||
)
|
||||
}
|
||||
|
||||
fun KtDeclaration.processAllUsages(
|
||||
options: FindUsagesOptions,
|
||||
processor: (UsageInfo) -> Unit
|
||||
) {
|
||||
val findUsagesHandler = KotlinFindUsagesHandlerFactory(project).createFindUsagesHandler(this, false, false)
|
||||
findUsagesHandler.processElementUsages(
|
||||
this,
|
||||
{
|
||||
processor(it)
|
||||
true
|
||||
},
|
||||
options
|
||||
)
|
||||
}
|
||||
@@ -20,24 +20,9 @@ import com.intellij.codeInsight.TargetElementUtil
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiMethod
|
||||
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
||||
|
||||
fun isCallHierarchyElement(e: PsiElement): Boolean {
|
||||
return e is PsiMethod ||
|
||||
e is PsiClass ||
|
||||
e is KtFile ||
|
||||
e is KtNamedFunction ||
|
||||
e is KtSecondaryConstructor ||
|
||||
e is KtObjectDeclaration ||
|
||||
e is KtClass && !e.isInterface() ||
|
||||
e is KtProperty
|
||||
}
|
||||
|
||||
fun getCurrentElement(dataContext: DataContext, project: Project): PsiElement? {
|
||||
val editor = CommonDataKeys.EDITOR.getData(dataContext)
|
||||
@@ -49,5 +34,3 @@ fun getCurrentElement(dataContext: DataContext, project: Project): PsiElement? {
|
||||
|
||||
return CommonDataKeys.PSI_ELEMENT.getData(dataContext)
|
||||
}
|
||||
|
||||
fun getCallHierarchyElement(element: PsiElement) = element.parentsWithSelf.firstOrNull(::isCallHierarchyElement)
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2010-2017 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.kotlin.idea.hierarchy.calls;
|
||||
|
||||
import com.intellij.openapi.application.ReadActionProcessor;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.light.LightMemberReference;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.idea.references.KtReference;
|
||||
import org.jetbrains.kotlin.psi.KtElement;
|
||||
import org.jetbrains.kotlin.psi.KtImportDirective;
|
||||
import org.jetbrains.kotlin.psi.KtNamedDeclaration;
|
||||
import org.jetbrains.kotlin.psi.KtProperty;
|
||||
|
||||
public abstract class CalleeReferenceProcessor extends ReadActionProcessor<PsiReference> {
|
||||
private final boolean kotlinOnly;
|
||||
|
||||
public CalleeReferenceProcessor(boolean only) {
|
||||
kotlinOnly = only;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean processInReadAction(PsiReference ref) {
|
||||
// copied from Java
|
||||
if (!(ref instanceof PsiReferenceExpression || ref instanceof KtReference)) {
|
||||
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 if (ref instanceof LightMemberReference) {
|
||||
PsiElement refTarget = ref.resolve();
|
||||
// Accept implicit superclass constructor reference in Java code
|
||||
if (!(refTarget instanceof PsiMethod && ((PsiMethod) refTarget).isConstructor())) return true;
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
PsiElement refElement = ref.getElement();
|
||||
if (PsiTreeUtil.getParentOfType(refElement, KtImportDirective.class, true) != null) return true;
|
||||
|
||||
PsiElement element = (refElement instanceof KtElement)
|
||||
? CallHierarchyUtilsKt.getCallHierarchyElement(refElement)
|
||||
: PsiTreeUtil.getParentOfType(refElement, PsiMethod.class, false);
|
||||
|
||||
if (kotlinOnly && !(element instanceof KtNamedDeclaration)) return true;
|
||||
|
||||
// If reference belongs to property initializer, show enclosing declaration instead
|
||||
if (element instanceof KtProperty) {
|
||||
KtProperty property = (KtProperty) element;
|
||||
if (PsiTreeUtil.isAncestor(property.getInitializer(), refElement, false)) {
|
||||
element = CallHierarchyUtilsKt.getCallHierarchyElement(element.getParent());
|
||||
}
|
||||
}
|
||||
|
||||
if (element != null) {
|
||||
onAccept(ref, element);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected abstract void onAccept(@NotNull PsiReference ref, @NotNull PsiElement element);
|
||||
}
|
||||
@@ -36,7 +36,7 @@ public abstract class CalleeReferenceVisitorBase extends KtTreeVisitorVoid {
|
||||
this.deepTraversal = deepTraversal;
|
||||
}
|
||||
|
||||
protected abstract void processDeclaration(KtSimpleNameExpression reference, PsiElement declaration);
|
||||
protected abstract void processDeclaration(@NotNull KtSimpleNameExpression reference, @NotNull PsiElement declaration);
|
||||
|
||||
@Override
|
||||
public void visitKtElement(@NotNull KtElement element) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
* Copyright 2010-2017 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.
|
||||
@@ -25,20 +25,20 @@ 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.PsiClass;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.ui.PopupHandler;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.idea.hierarchy.HierarchyUtilsKt;
|
||||
import org.jetbrains.kotlin.psi.KtElement;
|
||||
|
||||
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);
|
||||
public KotlinCallHierarchyBrowser(@NotNull PsiElement element) {
|
||||
super(element.getProject(), element);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -63,7 +63,7 @@ public class KotlinCallHierarchyBrowser extends CallHierarchyBrowserBase {
|
||||
|
||||
private static PsiElement getTargetElement(@NotNull HierarchyNodeDescriptor descriptor) {
|
||||
if (descriptor instanceof KotlinCallHierarchyNodeDescriptor) {
|
||||
return ((KotlinCallHierarchyNodeDescriptor) descriptor).getTargetElement();
|
||||
return descriptor.getPsiElement();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -76,17 +76,19 @@ public class KotlinCallHierarchyBrowser extends CallHierarchyBrowserBase {
|
||||
@Override
|
||||
protected boolean isApplicableElement(@NotNull PsiElement element) {
|
||||
if (element instanceof PsiClass) return false; // PsiClass is not allowed at the hierarchy root
|
||||
return HierarchyUtilsKt.isCallHierarchyElement(element);
|
||||
return CallHierarchyUtilsKt.isCallHierarchyElement(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HierarchyTreeStructure createHierarchyTreeStructure(@NotNull String typeName, @NotNull PsiElement psiElement) {
|
||||
if (!(psiElement instanceof KtElement)) return null;
|
||||
|
||||
if (typeName.equals(CALLER_TYPE)) {
|
||||
return new KotlinCallerMethodsTreeStructure(myProject, psiElement, getCurrentScopeType());
|
||||
return new KotlinCallerTreeStructure((KtElement) psiElement, getCurrentScopeType());
|
||||
}
|
||||
|
||||
if (typeName.equals(CALLEE_TYPE)) {
|
||||
return new KotlinCalleeMethodsTreeStructure(myProject, psiElement, getCurrentScopeType());
|
||||
return new KotlinCalleeTreeStructure((KtElement) psiElement, getCurrentScopeType());
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
+19
-40
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
* Copyright 2010-2017 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.
|
||||
@@ -19,24 +19,22 @@ package org.jetbrains.kotlin.idea.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.PsiReference;
|
||||
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.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionUtils;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer;
|
||||
@@ -49,34 +47,29 @@ import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class KotlinCallHierarchyNodeDescriptor extends HierarchyNodeDescriptor implements Navigatable {
|
||||
private int usageCount = 0;
|
||||
private final Set<PsiReference> references = new HashSet<PsiReference>();
|
||||
private int usageCount = 1;
|
||||
private final Set<PsiReference> references = new HashSet<>();
|
||||
private final CallHierarchyNodeDescriptor javaDelegate;
|
||||
|
||||
public KotlinCallHierarchyNodeDescriptor(@NotNull Project project,
|
||||
HierarchyNodeDescriptor parentDescriptor,
|
||||
@NotNull PsiElement element,
|
||||
public KotlinCallHierarchyNodeDescriptor(
|
||||
@Nullable HierarchyNodeDescriptor parentDescriptor,
|
||||
@NotNull KtElement element,
|
||||
boolean isBase,
|
||||
boolean navigateToReference) {
|
||||
super(project, parentDescriptor, element, isBase);
|
||||
super(element.getProject(), parentDescriptor, element, isBase);
|
||||
this.javaDelegate = new CallHierarchyNodeDescriptor(myProject, null, element, isBase, navigateToReference);
|
||||
}
|
||||
|
||||
public final CallHierarchyNodeDescriptor getJavaDelegate() {
|
||||
return javaDelegate;
|
||||
public final void incrementUsageCount() {
|
||||
usageCount++;
|
||||
javaDelegate.incrementUsageCount();
|
||||
}
|
||||
|
||||
public final void addReference(PsiReference reference) {
|
||||
if (references.add(reference)) {
|
||||
usageCount++;
|
||||
}
|
||||
references.add(reference);
|
||||
javaDelegate.addReference(reference);
|
||||
}
|
||||
|
||||
public final PsiElement getTargetElement(){
|
||||
return getPsiElement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isValid(){
|
||||
//noinspection ConstantConditions
|
||||
@@ -96,7 +89,7 @@ public class KotlinCallHierarchyNodeDescriptor extends HierarchyNodeDescriptor i
|
||||
|
||||
boolean changes = super.update();
|
||||
|
||||
PsiElement targetElement = getTargetElement();
|
||||
PsiElement targetElement = getPsiElement();
|
||||
String elementText = renderElement(targetElement);
|
||||
|
||||
if (elementText == null) {
|
||||
@@ -122,16 +115,7 @@ public class KotlinCallHierarchyNodeDescriptor extends HierarchyNodeDescriptor i
|
||||
mainTextAttributes = new TextAttributes(myColor, null, null, null, Font.PLAIN);
|
||||
}
|
||||
|
||||
String packageName = null;
|
||||
if (targetElement instanceof KtElement) {
|
||||
packageName = KtPsiUtil.getPackageName((KtElement) targetElement);
|
||||
}
|
||||
else {
|
||||
PsiClass enclosingClass = PsiTreeUtil.getParentOfType(targetElement, PsiClass.class, false);
|
||||
if (enclosingClass != null) {
|
||||
packageName = JavaHierarchyUtil.getPackageName(enclosingClass);
|
||||
}
|
||||
}
|
||||
String packageName = KtPsiUtil.getPackageName((KtElement) targetElement);
|
||||
|
||||
myHighlightedText.getEnding().addText(elementText, mainTextAttributes);
|
||||
|
||||
@@ -188,7 +172,7 @@ public class KotlinCallHierarchyNodeDescriptor extends HierarchyNodeDescriptor i
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (element instanceof KtNamedFunction || element instanceof KtSecondaryConstructor) {
|
||||
else if (element instanceof KtNamedFunction || element instanceof KtConstructor) {
|
||||
elementText = renderNamedFunction((FunctionDescriptor) descriptor);
|
||||
}
|
||||
else if (element instanceof KtProperty) {
|
||||
@@ -219,12 +203,7 @@ public class KotlinCallHierarchyNodeDescriptor extends HierarchyNodeDescriptor i
|
||||
String name = descriptorForName.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());
|
||||
}
|
||||
},
|
||||
descriptor1 -> DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(descriptor1.getType()),
|
||||
", "
|
||||
);
|
||||
return name + "(" + paramTypes + ")";
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.kotlin.idea.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.kotlin.idea.KotlinLanguage;
|
||||
import org.jetbrains.kotlin.idea.hierarchy.HierarchyUtilsKt;
|
||||
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil;
|
||||
import org.jetbrains.kotlin.psi.KtFile;
|
||||
|
||||
public class KotlinCallHierarchyProvider implements HierarchyProvider {
|
||||
@Override
|
||||
public PsiElement getTarget(@NotNull DataContext dataContext) {
|
||||
Project project = CommonDataKeys.PROJECT.getData(dataContext);
|
||||
if (project == null) return null;
|
||||
|
||||
PsiElement element = getCurrentElement(dataContext, project);
|
||||
if (element == null) return null;
|
||||
|
||||
element = HierarchyUtilsKt.getCallHierarchyElement(element);
|
||||
if (element instanceof KtFile || element == null || element.getLanguage() != KotlinLanguage.INSTANCE) return null;
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
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 (!ProjectRootsUtil.isInProjectOrLibSource(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,40 @@
|
||||
/*
|
||||
* Copyright 2010-2017 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.kotlin.idea.hierarchy.calls
|
||||
|
||||
import com.intellij.ide.hierarchy.CallHierarchyBrowserBase
|
||||
import com.intellij.ide.hierarchy.HierarchyBrowser
|
||||
import com.intellij.ide.hierarchy.HierarchyProvider
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.idea.refactoring.psiElement
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
class KotlinCallHierarchyProvider : HierarchyProvider {
|
||||
override fun getTarget(dataContext: DataContext): KtElement? {
|
||||
val element = dataContext.psiElement?.let { getCallHierarchyElement(it) }
|
||||
if (element is KtFile) return null
|
||||
return element
|
||||
}
|
||||
|
||||
override fun createHierarchyBrowser(target: PsiElement) = KotlinCallHierarchyBrowser(target)
|
||||
|
||||
override fun browserActivated(hierarchyBrowser: HierarchyBrowser) {
|
||||
(hierarchyBrowser as KotlinCallHierarchyBrowser).changeView(CallHierarchyBrowserBase.CALLER_TYPE)
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
* Copyright 2010-2017 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.
|
||||
@@ -19,20 +19,15 @@ package org.jetbrains.kotlin.idea.hierarchy.calls
|
||||
import com.intellij.ide.hierarchy.HierarchyNodeDescriptor
|
||||
import com.intellij.ide.hierarchy.call.CallReferenceProcessor
|
||||
import com.intellij.ide.hierarchy.call.JavaCallHierarchyData
|
||||
import com.intellij.ide.util.treeView.NodeDescriptor
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiReference
|
||||
import org.jetbrains.kotlin.idea.references.KtReference
|
||||
|
||||
class KotlinCallReferenceProcessor : CallReferenceProcessor {
|
||||
override fun process(reference: PsiReference, data: JavaCallHierarchyData): Boolean {
|
||||
if (reference !is KtReference) return true
|
||||
val nodeDescriptor = data.nodeDescriptor as? HierarchyNodeDescriptor ?: return true
|
||||
val nodeDescriptor = data.nodeDescriptor as? HierarchyNodeDescriptor ?: return false
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return !KotlinCallerMethodsTreeStructure.defaultQueryProcessor(
|
||||
if (nodeDescriptor is KotlinCallHierarchyNodeDescriptor) nodeDescriptor.javaDelegate else nodeDescriptor,
|
||||
data.resultMap as MutableMap<PsiElement, HierarchyNodeDescriptor>,
|
||||
false,
|
||||
true
|
||||
).process(reference)
|
||||
KotlinCallerTreeStructure.processReference(reference, reference.element, nodeDescriptor, data.resultMap as MutableMap<PsiElement, NodeDescriptor<*>>, true)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,176 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.kotlin.idea.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.PsiReference;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
import kotlin.collections.CollectionsKt;
|
||||
import kotlin.jvm.functions.Function1;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.asJava.LightClassUtil;
|
||||
import org.jetbrains.kotlin.asJava.LightClassUtilsKt;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.jetbrains.kotlin.asJava.LightClassUtilsKt.toLightClass;
|
||||
|
||||
public abstract class KotlinCallTreeStructure extends HierarchyTreeStructure {
|
||||
protected final String scopeType;
|
||||
|
||||
public KotlinCallTreeStructure(@NotNull Project project, PsiElement element, String scopeType) {
|
||||
super(project, createNodeDescriptor(project, element, null, false, false));
|
||||
this.scopeType = scopeType;
|
||||
}
|
||||
|
||||
protected static KtElement getEnclosingElementForLocalDeclaration(PsiElement element) {
|
||||
return element instanceof KtNamedDeclaration
|
||||
? KtPsiUtil.getEnclosingElementForLocalDeclaration((KtNamedDeclaration) element)
|
||||
: null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static HierarchyNodeDescriptor createNodeDescriptor(
|
||||
Project project, PsiElement element, HierarchyNodeDescriptor parent, boolean navigateToReference, boolean wrapAsLightElements
|
||||
) {
|
||||
PsiElement nodeElement = element;
|
||||
if (wrapAsLightElements && element instanceof KtElement) {
|
||||
nodeElement = CollectionsKt.firstOrNull(LightClassUtilsKt.toLightElements((KtElement) element));
|
||||
}
|
||||
if (nodeElement == null) return null;
|
||||
boolean root = (parent == null);
|
||||
return nodeElement instanceof KtElement
|
||||
? new KotlinCallHierarchyNodeDescriptor(project, parent, nodeElement, root, navigateToReference)
|
||||
: new CallHierarchyNodeDescriptor(project, parent, nodeElement, root, navigateToReference);
|
||||
}
|
||||
|
||||
protected static PsiElement getTargetElement(HierarchyNodeDescriptor descriptor) {
|
||||
return descriptor instanceof CallHierarchyNodeDescriptor
|
||||
? ((CallHierarchyNodeDescriptor) descriptor).getEnclosingElement()
|
||||
: ((KotlinCallHierarchyNodeDescriptor)descriptor).getTargetElement();
|
||||
}
|
||||
|
||||
private static final Function1<PsiElement, Boolean> IS_NON_LOCAL_DECLARATION = new Function1<PsiElement, Boolean>() {
|
||||
@Override
|
||||
public Boolean invoke(@javax.annotation.Nullable PsiElement input) {
|
||||
return input instanceof PsiMethod
|
||||
|| ((input instanceof KtNamedFunction || input instanceof KtClassOrObject || input instanceof KtProperty)
|
||||
&& !KtPsiUtil.isLocal((KtNamedDeclaration) input));
|
||||
}
|
||||
};
|
||||
|
||||
@Nullable
|
||||
protected static PsiMethod getRepresentativePsiMethod(PsiElement element) {
|
||||
while (true) {
|
||||
element = PsiUtilsKt.getParentOfTypesAndPredicate(element, false, ArrayUtil.EMPTY_CLASS_ARRAY, IS_NON_LOCAL_DECLARATION);
|
||||
if (element == null) return null;
|
||||
|
||||
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 KtNamedFunction || element instanceof KtSecondaryConstructor) {
|
||||
return LightClassUtil.INSTANCE.getLightClassMethod((KtFunction) element);
|
||||
}
|
||||
|
||||
if (element instanceof KtProperty) {
|
||||
LightClassUtil.PropertyAccessorsPsiMethods propertyMethods =
|
||||
LightClassUtil.INSTANCE.getLightClassPropertyMethods((KtProperty) element);
|
||||
return (propertyMethods.getGetter() != null) ? propertyMethods.getGetter() : propertyMethods.getSetter();
|
||||
}
|
||||
|
||||
if (element instanceof KtClassOrObject) {
|
||||
PsiClass psiClass = toLightClass((KtClassOrObject) element);
|
||||
if (psiClass == null) return null;
|
||||
|
||||
PsiMethod[] constructors = psiClass.getConstructors();
|
||||
if (constructors.length > 0) return constructors[0];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected static CallHierarchyNodeDescriptor getJavaNodeDescriptor(HierarchyNodeDescriptor originalDescriptor) {
|
||||
if (originalDescriptor instanceof CallHierarchyNodeDescriptor) return (CallHierarchyNodeDescriptor) originalDescriptor;
|
||||
|
||||
assert originalDescriptor instanceof KotlinCallHierarchyNodeDescriptor;
|
||||
return ((KotlinCallHierarchyNodeDescriptor) originalDescriptor).getJavaDelegate();
|
||||
}
|
||||
|
||||
protected Object[] collectNodeDescriptors(
|
||||
HierarchyNodeDescriptor descriptor, Map<PsiReference, PsiElement> referencesToCalleeElements, PsiClass basePsiClass
|
||||
) {
|
||||
HashMap<PsiElement, HierarchyNodeDescriptor> declarationToDescriptorMap = new HashMap<PsiElement, HierarchyNodeDescriptor>();
|
||||
for (Map.Entry<PsiReference, PsiElement> refToCallee : referencesToCalleeElements.entrySet()) {
|
||||
PsiReference ref = refToCallee.getKey();
|
||||
PsiElement callee = refToCallee.getValue();
|
||||
|
||||
if (basePsiClass != null && !isInScope(basePsiClass, callee, scopeType)) continue;
|
||||
|
||||
addNodeDescriptorForElement(ref, callee, declarationToDescriptorMap, descriptor, false);
|
||||
}
|
||||
return declarationToDescriptorMap.values().toArray(new Object[declarationToDescriptorMap.size()]);
|
||||
}
|
||||
|
||||
protected static void addNodeDescriptorForElement(
|
||||
PsiReference reference,
|
||||
PsiElement element,
|
||||
Map<PsiElement, HierarchyNodeDescriptor> declarationToDescriptorMap,
|
||||
HierarchyNodeDescriptor descriptor,
|
||||
boolean wrapAsLightElements
|
||||
) {
|
||||
HierarchyNodeDescriptor d = declarationToDescriptorMap.get(element);
|
||||
if (d == null) {
|
||||
d = createNodeDescriptor(element.getProject(), element, descriptor, true, wrapAsLightElements);
|
||||
if (d == null) return;
|
||||
declarationToDescriptorMap.put(element, d);
|
||||
}
|
||||
else if (d instanceof CallHierarchyNodeDescriptor) {
|
||||
((CallHierarchyNodeDescriptor) d).incrementUsageCount();
|
||||
}
|
||||
|
||||
if (d instanceof CallHierarchyNodeDescriptor) {
|
||||
((CallHierarchyNodeDescriptor) d).addReference(reference);
|
||||
}
|
||||
else if (d instanceof KotlinCallHierarchyNodeDescriptor) {
|
||||
((KotlinCallHierarchyNodeDescriptor) d).addReference(reference);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAlwaysShowPlus() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
-140
@@ -1,140 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.kotlin.idea.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.psi.PsiReference;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.asJava.elements.KtLightMethod;
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionUtils;
|
||||
import org.jetbrains.kotlin.idea.references.ReferenceUtilKt;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class KotlinCalleeMethodsTreeStructure extends KotlinCallTreeStructure {
|
||||
private final CalleeMethodsTreeStructure javaTreeStructure;
|
||||
private final PsiClass representativePsiClass;
|
||||
|
||||
public KotlinCalleeMethodsTreeStructure(@NotNull Project project, @NotNull PsiElement element, String scopeType) {
|
||||
super(project, element, scopeType);
|
||||
|
||||
PsiMethod representativePsiMethod = getRepresentativePsiMethod(element);
|
||||
assert representativePsiMethod != null;
|
||||
|
||||
this.representativePsiClass = representativePsiMethod.getContainingClass();
|
||||
this.javaTreeStructure = new CalleeMethodsTreeStructure(project, representativePsiMethod, scopeType);
|
||||
}
|
||||
|
||||
private static Map<PsiReference, PsiElement> getReferencesToCalleeElements(@NotNull KtElement rootElement) {
|
||||
List<KtElement> elementsToAnalyze = new ArrayList<KtElement>();
|
||||
if (rootElement instanceof KtNamedFunction) {
|
||||
elementsToAnalyze.add(((KtNamedFunction) rootElement).getBodyExpression());
|
||||
} else if (rootElement instanceof KtProperty) {
|
||||
for (KtPropertyAccessor accessor : ((KtProperty) rootElement).getAccessors()) {
|
||||
KtExpression body = accessor.getBodyExpression();
|
||||
if (body != null) {
|
||||
elementsToAnalyze.add(body);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
KtClassOrObject classOrObject = (KtClassOrObject) rootElement;
|
||||
for (KtSuperTypeListEntry specifier : classOrObject.getSuperTypeListEntries()) {
|
||||
if (specifier instanceof KtCallElement) {
|
||||
elementsToAnalyze.add(specifier);
|
||||
}
|
||||
}
|
||||
|
||||
KtClassBody body = classOrObject.getBody();
|
||||
if (body != null) {
|
||||
for (KtAnonymousInitializer initializer : body.getAnonymousInitializers()) {
|
||||
KtExpression initializerBody = initializer.getBody();
|
||||
if (initializerBody != null) {
|
||||
elementsToAnalyze.add(initializerBody);
|
||||
}
|
||||
}
|
||||
for (KtProperty property : body.getProperties()) {
|
||||
KtExpression initializer = property.getInitializer();
|
||||
if (initializer != null) {
|
||||
elementsToAnalyze.add(initializer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final Map<PsiReference, PsiElement> referencesToCalleeElements = new HashMap<PsiReference, PsiElement>();
|
||||
for (KtElement element : elementsToAnalyze) {
|
||||
element.accept(
|
||||
new CalleeReferenceVisitorBase(ResolutionUtils.analyze(element, BodyResolveMode.FULL), false) {
|
||||
@Override
|
||||
protected void processDeclaration(KtSimpleNameExpression reference, PsiElement declaration) {
|
||||
referencesToCalleeElements.put(ReferenceUtilKt.getMainReference(reference), declaration);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return referencesToCalleeElements;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected Object[] buildChildren(@NotNull 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 KtClass) {
|
||||
return buildChildrenByKotlinTarget(descriptor, (KtElement) navigationElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Kotlin function or property invoked from Java code
|
||||
if (targetElement instanceof KtLightMethod) {
|
||||
return buildChildrenByKotlinTarget(descriptor, ((KtLightMethod) targetElement).getKotlinOrigin());
|
||||
}
|
||||
|
||||
if (targetElement instanceof KtElement) {
|
||||
return buildChildrenByKotlinTarget(descriptor, (KtElement) targetElement);
|
||||
}
|
||||
|
||||
CallHierarchyNodeDescriptor javaDescriptor = descriptor instanceof CallHierarchyNodeDescriptor
|
||||
? (CallHierarchyNodeDescriptor) descriptor
|
||||
: ((KotlinCallHierarchyNodeDescriptor)descriptor).getJavaDelegate();
|
||||
return javaTreeStructure.getChildElements(javaDescriptor);
|
||||
}
|
||||
|
||||
private Object[] buildChildrenByKotlinTarget(HierarchyNodeDescriptor descriptor, KtElement targetElement) {
|
||||
Map<PsiReference, PsiElement> referencesToCalleeElements = getReferencesToCalleeElements(targetElement);
|
||||
return collectNodeDescriptors(descriptor, referencesToCalleeElements, representativePsiClass);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2010-2017 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.kotlin.idea.hierarchy.calls
|
||||
|
||||
import com.intellij.ide.hierarchy.HierarchyNodeDescriptor
|
||||
import com.intellij.ide.hierarchy.HierarchyTreeStructure
|
||||
import com.intellij.ide.hierarchy.call.CallHierarchyNodeDescriptor
|
||||
import com.intellij.ide.hierarchy.call.CalleeMethodsTreeStructure
|
||||
import com.intellij.ide.util.treeView.NodeDescriptor
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiMethod
|
||||
import com.intellij.util.ArrayUtil
|
||||
import com.intellij.util.containers.HashMap
|
||||
import org.jetbrains.kotlin.asJava.unwrapped
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchRequest
|
||||
import org.jetbrains.kotlin.idea.search.declarationsSearch.searchOverriders
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
||||
|
||||
class KotlinCalleeTreeStructure(
|
||||
element: KtElement,
|
||||
private val scopeType: String
|
||||
) : HierarchyTreeStructure(element.project,
|
||||
KotlinCallHierarchyNodeDescriptor(null, element, true, false)) {
|
||||
private fun KtElement.getCalleeSearchScope(): List<KtElement> {
|
||||
return when (this) {
|
||||
is KtNamedFunction, is KtFunctionLiteral, is KtPropertyAccessor -> listOf((this as KtDeclarationWithBody).bodyExpression)
|
||||
is KtProperty -> accessors.map { it.bodyExpression }
|
||||
is KtClassOrObject -> {
|
||||
superTypeListEntries.filterIsInstance<KtCallElement>() +
|
||||
getAnonymousInitializers().map { it.body } +
|
||||
declarations.filterIsInstance<KtProperty>().map { it.initializer }
|
||||
}
|
||||
else -> emptyList()
|
||||
}.filterNotNull()
|
||||
}
|
||||
|
||||
override fun buildChildren(nodeDescriptor: HierarchyNodeDescriptor): Array<Any> {
|
||||
if (nodeDescriptor is CallHierarchyNodeDescriptor) {
|
||||
val psiMethod = nodeDescriptor.enclosingElement as? PsiMethod ?: return ArrayUtil.EMPTY_OBJECT_ARRAY
|
||||
return CalleeMethodsTreeStructure(myProject, psiMethod, scopeType).getChildElements(nodeDescriptor)
|
||||
}
|
||||
|
||||
val element = nodeDescriptor.psiElement as? KtElement ?: return ArrayUtil.EMPTY_OBJECT_ARRAY
|
||||
|
||||
val result = LinkedHashSet<HierarchyNodeDescriptor>()
|
||||
val baseClass = (element as? KtDeclaration)?.containingClassOrObject
|
||||
val calleeToDescriptorMap = HashMap<PsiElement, NodeDescriptor<*>>()
|
||||
|
||||
element.getCalleeSearchScope().forEach {
|
||||
it.accept(
|
||||
object : CalleeReferenceVisitorBase(it.analyze(), false) {
|
||||
override fun processDeclaration(reference: KtSimpleNameExpression, declaration: PsiElement) {
|
||||
if (!isInScope(baseClass, declaration, scopeType)) return
|
||||
result += (getOrCreateNodeDescriptor(nodeDescriptor, declaration, null, false, calleeToDescriptorMap, false) ?: return)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
for (it in HierarchySearchRequest(element, element.useScope).searchOverriders()) {
|
||||
val overrider = it.unwrapped as? KtElement ?: continue
|
||||
if (!isInScope(baseClass, overrider, scopeType)) continue
|
||||
result += KotlinCallHierarchyNodeDescriptor(nodeDescriptor, overrider, false, false)
|
||||
}
|
||||
|
||||
return result.toArray()
|
||||
}
|
||||
}
|
||||
-259
@@ -1,259 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.kotlin.idea.hierarchy.calls;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
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.impl.light.LightMemberReference;
|
||||
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.ContainerUtil;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
import gnu.trove.TObjectHashingStrategy;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.asJava.LightClassUtil;
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionUtils;
|
||||
import org.jetbrains.kotlin.idea.hierarchy.HierarchyUtilsKt;
|
||||
import org.jetbrains.kotlin.idea.references.KtReference;
|
||||
import org.jetbrains.kotlin.idea.references.ReferenceUtilKt;
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.UtilsKt;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class KotlinCallerMethodsTreeStructure extends KotlinCallTreeStructure {
|
||||
private final CallerMethodsTreeStructure javaTreeStructure;
|
||||
private final PsiClass basePsiClass;
|
||||
|
||||
public KotlinCallerMethodsTreeStructure(@NotNull Project project, @NotNull PsiElement element, String scopeType) {
|
||||
super(project, element, scopeType);
|
||||
|
||||
PsiMethod basePsiMethod = getRepresentativePsiMethod(element);
|
||||
assert basePsiMethod != null : "Can't generate light method: " + element.getText();
|
||||
|
||||
basePsiClass = basePsiMethod.getContainingClass();
|
||||
javaTreeStructure = new CallerMethodsTreeStructure(project, basePsiMethod, scopeType);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected Object[] buildChildren(@NotNull HierarchyNodeDescriptor descriptor) {
|
||||
final PsiElement element = getTargetElement(descriptor);
|
||||
|
||||
KtElement codeBlockForLocalDeclaration = getEnclosingElementForLocalDeclaration(element);
|
||||
if (codeBlockForLocalDeclaration != null) {
|
||||
BindingContext bindingContext = ResolutionUtils.analyze((KtElement) element, BodyResolveMode.FULL);
|
||||
|
||||
final Map<PsiReference, PsiElement> referencesToElements = new HashMap<PsiReference, PsiElement>();
|
||||
codeBlockForLocalDeclaration.accept(new CalleeReferenceVisitorBase(bindingContext, true) {
|
||||
@Override
|
||||
protected void processDeclaration(KtSimpleNameExpression reference, PsiElement declaration) {
|
||||
if (!declaration.equals(element)) return;
|
||||
|
||||
//noinspection unchecked
|
||||
PsiElement container = PsiTreeUtil.getParentOfType(
|
||||
reference,
|
||||
KtNamedFunction.class, KtPropertyAccessor.class, KtClassOrObject.class
|
||||
);
|
||||
if (container instanceof KtPropertyAccessor) {
|
||||
container = PsiTreeUtil.getParentOfType(container, KtProperty.class);
|
||||
}
|
||||
|
||||
if (container != null) {
|
||||
referencesToElements.put(ReferenceUtilKt.getMainReference(reference), container);
|
||||
}
|
||||
}
|
||||
});
|
||||
return collectNodeDescriptors(descriptor, referencesToElements, null);
|
||||
}
|
||||
|
||||
SearchScope searchScope = getSearchScope(scopeType, basePsiClass);
|
||||
Map<PsiElement, HierarchyNodeDescriptor> methodToDescriptorMap = Maps.newHashMap();
|
||||
|
||||
Object[] javaCallers = null;
|
||||
if (element instanceof PsiMethod) {
|
||||
javaCallers = javaTreeStructure.getChildElements(getJavaNodeDescriptor(descriptor));
|
||||
processPsiMethodCallers(
|
||||
Collections.singleton((PsiMethod) element), descriptor, methodToDescriptorMap, searchScope, true
|
||||
);
|
||||
}
|
||||
if (element instanceof KtNamedFunction || element instanceof KtSecondaryConstructor) {
|
||||
Collection<PsiMethod> lightMethods = LightClassUtil.INSTANCE.getLightClassMethods((KtFunction) element);
|
||||
processPsiMethodCallers(lightMethods, descriptor, methodToDescriptorMap, searchScope, false);
|
||||
}
|
||||
if (element instanceof KtProperty) {
|
||||
LightClassUtil.PropertyAccessorsPsiMethods propertyMethods =
|
||||
LightClassUtil.INSTANCE.getLightClassPropertyMethods((KtProperty) element);
|
||||
processPsiMethodCallers(propertyMethods, descriptor, methodToDescriptorMap, searchScope, false);
|
||||
}
|
||||
if (element instanceof KtClassOrObject) {
|
||||
KtPrimaryConstructor constructor = ((KtClassOrObject) element).getPrimaryConstructor();
|
||||
if (constructor != null) {
|
||||
PsiMethod lightMethod = LightClassUtil.INSTANCE.getLightClassMethod(constructor);
|
||||
processPsiMethodCallers(Collections.singleton(lightMethod), descriptor, methodToDescriptorMap, searchScope, false);
|
||||
}
|
||||
else {
|
||||
processKtClassOrObjectCallers((KtClassOrObject) element, descriptor, methodToDescriptorMap, searchScope);
|
||||
}
|
||||
}
|
||||
|
||||
Object[] callers = methodToDescriptorMap.values().toArray(new Object[methodToDescriptorMap.size()]);
|
||||
return (javaCallers != null) ? ArrayUtil.mergeArrays(javaCallers, callers) : callers;
|
||||
}
|
||||
|
||||
private static void processPsiMethodCallers(
|
||||
Iterable<PsiMethod> lightMethods,
|
||||
HierarchyNodeDescriptor descriptor,
|
||||
Map<PsiElement, HierarchyNodeDescriptor> methodToDescriptorMap,
|
||||
SearchScope searchScope,
|
||||
boolean kotlinOnly
|
||||
) {
|
||||
Set<PsiMethod> methodsToFind = new HashSet<PsiMethod>();
|
||||
for (PsiMethod lightMethod : lightMethods) {
|
||||
if (lightMethod == null) continue;
|
||||
|
||||
PsiMethod[] superMethods = lightMethod.findDeepestSuperMethods();
|
||||
methodsToFind.add(lightMethod);
|
||||
ContainerUtil.addAll(methodsToFind, superMethods);
|
||||
}
|
||||
|
||||
if (methodsToFind.isEmpty()) return;
|
||||
|
||||
Set<PsiReference> references = ContainerUtil.newTroveSet(
|
||||
new TObjectHashingStrategy<PsiReference>() {
|
||||
@Override
|
||||
public int computeHashCode(PsiReference object) {
|
||||
return object.getElement().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(PsiReference o1, PsiReference o2) {
|
||||
return o1.getElement().equals(o2.getElement());
|
||||
}
|
||||
}
|
||||
);
|
||||
for (PsiMethod superMethod: methodsToFind) {
|
||||
ContainerUtil.addAll(references, MethodReferencesSearch.search(superMethod, searchScope, true));
|
||||
}
|
||||
ContainerUtil.process(references, defaultQueryProcessor(descriptor, methodToDescriptorMap, kotlinOnly, false));
|
||||
}
|
||||
|
||||
private static void processKtClassOrObjectCallers(
|
||||
final KtClassOrObject 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 UtilsKt.isConstructorUsage(reference, classOrObject);
|
||||
}
|
||||
},
|
||||
defaultQueryProcessor(descriptor, methodToDescriptorMap, false, false)
|
||||
);
|
||||
ReferencesSearch.search(classOrObject, searchScope, false).forEach(processor);
|
||||
}
|
||||
|
||||
static Processor<PsiReference> defaultQueryProcessor(
|
||||
final HierarchyNodeDescriptor descriptor,
|
||||
final Map<PsiElement, HierarchyNodeDescriptor> methodToDescriptorMap,
|
||||
boolean kotlinOnly,
|
||||
final boolean wrapAsLightElements
|
||||
) {
|
||||
return new CalleeReferenceProcessor(kotlinOnly) {
|
||||
@Override
|
||||
protected void onAccept(@NotNull PsiReference ref, @NotNull PsiElement element) {
|
||||
addNodeDescriptorForElement(ref, element, methodToDescriptorMap, descriptor, wrapAsLightElements);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static abstract class CalleeReferenceProcessor extends ReadActionProcessor<PsiReference> {
|
||||
private final boolean kotlinOnly;
|
||||
|
||||
public CalleeReferenceProcessor(boolean only) {
|
||||
kotlinOnly = only;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean processInReadAction(PsiReference ref) {
|
||||
// copied from Java
|
||||
if (!(ref instanceof PsiReferenceExpression || ref instanceof KtReference)) {
|
||||
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 if (ref instanceof LightMemberReference) {
|
||||
PsiElement refTarget = ref.resolve();
|
||||
// Accept implicit superclass constructor reference in Java code
|
||||
if (!(refTarget instanceof PsiMethod && ((PsiMethod) refTarget).isConstructor())) return true;
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
PsiElement refElement = ref.getElement();
|
||||
if (PsiTreeUtil.getParentOfType(refElement, KtImportDirective.class, true) != null) return true;
|
||||
|
||||
PsiElement element = HierarchyUtilsKt.getCallHierarchyElement(refElement);
|
||||
|
||||
if (kotlinOnly && !(element instanceof KtNamedDeclaration)) return true;
|
||||
|
||||
// If reference belongs to property initializer, show enclosing declaration instead
|
||||
if (element instanceof KtProperty) {
|
||||
KtProperty property = (KtProperty) element;
|
||||
if (PsiTreeUtil.isAncestor(property.getInitializer(), refElement, false)) {
|
||||
element = HierarchyUtilsKt.getCallHierarchyElement(element.getParent());
|
||||
}
|
||||
}
|
||||
|
||||
if (element != null) {
|
||||
onAccept(ref, element);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected abstract void onAccept(@NotNull PsiReference ref, @NotNull PsiElement element);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright 2010-2017 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.kotlin.idea.hierarchy.calls
|
||||
|
||||
import com.google.common.collect.Maps
|
||||
import com.intellij.find.findUsages.JavaFindUsagesOptions
|
||||
import com.intellij.ide.hierarchy.HierarchyNodeDescriptor
|
||||
import com.intellij.ide.hierarchy.HierarchyTreeStructure
|
||||
import com.intellij.ide.hierarchy.call.CallHierarchyNodeDescriptor
|
||||
import com.intellij.ide.hierarchy.call.CallerMethodsTreeStructure
|
||||
import com.intellij.ide.util.treeView.NodeDescriptor
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiMember
|
||||
import com.intellij.psi.PsiMethod
|
||||
import com.intellij.psi.PsiReference
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.util.ArrayUtil
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||
import org.jetbrains.kotlin.idea.core.getDeepestSuperDeclarations
|
||||
import org.jetbrains.kotlin.idea.findUsages.KotlinClassFindUsagesOptions
|
||||
import org.jetbrains.kotlin.idea.findUsages.KotlinFunctionFindUsagesOptions
|
||||
import org.jetbrains.kotlin.idea.findUsages.KotlinPropertyFindUsagesOptions
|
||||
import org.jetbrains.kotlin.idea.findUsages.processAllUsages
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||
|
||||
class KotlinCallerTreeStructure(
|
||||
element: KtElement,
|
||||
private val scopeType: String
|
||||
) : HierarchyTreeStructure(element.project,
|
||||
KotlinCallHierarchyNodeDescriptor(null, element, true, false)) {
|
||||
companion object {
|
||||
internal fun processReference(
|
||||
reference: PsiReference?,
|
||||
refElement: PsiElement,
|
||||
nodeDescriptor: HierarchyNodeDescriptor,
|
||||
callerToDescriptorMap: MutableMap<PsiElement, NodeDescriptor<*>>,
|
||||
isJavaMap: Boolean
|
||||
) {
|
||||
var callerElement: PsiElement? = when (refElement) {
|
||||
is KtElement -> getCallHierarchyElement(refElement)
|
||||
else -> {
|
||||
val psiMember = refElement.getNonStrictParentOfType<PsiMember>()
|
||||
psiMember as? PsiMethod ?: psiMember?.containingClass
|
||||
}
|
||||
}
|
||||
if (callerElement is KtProperty) {
|
||||
if (PsiTreeUtil.isAncestor(callerElement.initializer, refElement, false)) {
|
||||
callerElement = getCallHierarchyElement(callerElement.parent)
|
||||
}
|
||||
}
|
||||
if (callerElement == null) return
|
||||
|
||||
getOrCreateNodeDescriptor(nodeDescriptor, callerElement, reference, true, callerToDescriptorMap, isJavaMap)
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildChildren(
|
||||
element: PsiElement,
|
||||
nodeDescriptor: HierarchyNodeDescriptor,
|
||||
callerToDescriptorMap: MutableMap<PsiElement, NodeDescriptor<*>>
|
||||
): Collection<Any> {
|
||||
if (nodeDescriptor is CallHierarchyNodeDescriptor) {
|
||||
val psiMethod = nodeDescriptor.enclosingElement as? PsiMethod ?: return emptyList()
|
||||
return CallerMethodsTreeStructure(myProject, psiMethod, scopeType).getChildElements(nodeDescriptor).toList()
|
||||
}
|
||||
|
||||
if (element !is KtDeclaration) return emptyList()
|
||||
|
||||
val baseClass = (element as? KtDeclaration)?.containingClassOrObject
|
||||
val searchScope = getSearchScope(scopeType, baseClass)
|
||||
|
||||
val findOptions: JavaFindUsagesOptions = when (element) {
|
||||
is KtNamedFunction, is KtConstructor<*> -> KotlinFunctionFindUsagesOptions(myProject)
|
||||
is KtProperty -> KotlinPropertyFindUsagesOptions(myProject)
|
||||
is KtPropertyAccessor -> KotlinPropertyFindUsagesOptions(myProject).apply {
|
||||
isReadAccess = element.isGetter
|
||||
isWriteAccess = element.isSetter
|
||||
}
|
||||
is KtClass -> KotlinClassFindUsagesOptions(myProject).apply {
|
||||
isUsages = false
|
||||
}
|
||||
else -> return emptyList()
|
||||
}
|
||||
findOptions.isSkipImportStatements = true
|
||||
findOptions.searchScope = searchScope
|
||||
findOptions.isSearchForTextOccurrences = false
|
||||
|
||||
val elementToSearch = when (element) {
|
||||
is KtPropertyAccessor -> element.property
|
||||
else -> element
|
||||
}
|
||||
|
||||
// If reference belongs to property initializer, show enclosing declaration instead
|
||||
elementToSearch.processAllUsages(findOptions) {
|
||||
processReference(it.reference, it.element ?: return@processAllUsages, nodeDescriptor, callerToDescriptorMap, false)
|
||||
}
|
||||
|
||||
return callerToDescriptorMap.values
|
||||
}
|
||||
|
||||
override fun buildChildren(nodeDescriptor: HierarchyNodeDescriptor): Array<Any> {
|
||||
val element = nodeDescriptor.psiElement as? KtDeclaration ?: return ArrayUtil.EMPTY_OBJECT_ARRAY
|
||||
val callerToDescriptorMap = Maps.newHashMap<PsiElement, NodeDescriptor<*>>()
|
||||
val descriptor = element.resolveToDescriptor()
|
||||
if (descriptor is CallableMemberDescriptor) {
|
||||
return descriptor.getDeepestSuperDeclarations().flatMap { rootDescriptor ->
|
||||
val rootElement = DescriptorToSourceUtilsIde.getAnyDeclaration(myProject, rootDescriptor)
|
||||
?: return@flatMap emptyList<Any>()
|
||||
val rootNodeDescriptor = when (rootElement) {
|
||||
is KtElement -> nodeDescriptor
|
||||
is PsiMethod -> CallHierarchyNodeDescriptor(
|
||||
myProject,
|
||||
nodeDescriptor.parentDescriptor as HierarchyNodeDescriptor?,
|
||||
rootElement,
|
||||
nodeDescriptor.parentDescriptor == null,
|
||||
false
|
||||
)
|
||||
else -> return@flatMap emptyList<Any>()
|
||||
}
|
||||
buildChildren(rootElement, rootNodeDescriptor, callerToDescriptorMap)
|
||||
}.toTypedArray()
|
||||
}
|
||||
else {
|
||||
return buildChildren(element, nodeDescriptor, callerToDescriptorMap).toTypedArray()
|
||||
}
|
||||
}
|
||||
|
||||
override fun isAlwaysShowPlus() = true
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2010-2017 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.kotlin.idea.hierarchy.calls
|
||||
|
||||
import com.intellij.ide.hierarchy.HierarchyNodeDescriptor
|
||||
import com.intellij.ide.hierarchy.call.CallHierarchyNodeDescriptor
|
||||
import com.intellij.ide.util.treeView.NodeDescriptor
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiMember
|
||||
import com.intellij.psi.PsiReference
|
||||
import org.jetbrains.kotlin.asJava.toLightElements
|
||||
import org.jetbrains.kotlin.asJava.toLightMethods
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
||||
|
||||
fun isCallHierarchyElement(e: PsiElement): Boolean {
|
||||
return (e is KtNamedFunction && e.name != null) ||
|
||||
e is KtSecondaryConstructor ||
|
||||
(e is KtProperty && !e.isLocal) ||
|
||||
e is KtObjectDeclaration ||
|
||||
(e is KtClass && !e.isInterface()) ||
|
||||
e is KtFile
|
||||
}
|
||||
|
||||
fun getCallHierarchyElement(element: PsiElement) = element.parentsWithSelf.firstOrNull(::isCallHierarchyElement) as? KtElement
|
||||
|
||||
private fun NodeDescriptor<*>.incrementUsageCount() {
|
||||
when (this) {
|
||||
is KotlinCallHierarchyNodeDescriptor -> incrementUsageCount()
|
||||
is CallHierarchyNodeDescriptor -> incrementUsageCount()
|
||||
}
|
||||
}
|
||||
|
||||
private fun NodeDescriptor<*>.addReference(reference: PsiReference) {
|
||||
when (this) {
|
||||
is KotlinCallHierarchyNodeDescriptor -> addReference(reference)
|
||||
is CallHierarchyNodeDescriptor -> addReference(reference)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun getOrCreateNodeDescriptor(
|
||||
parent: HierarchyNodeDescriptor,
|
||||
originalElement: PsiElement,
|
||||
reference: PsiReference?,
|
||||
navigateToReference: Boolean,
|
||||
elementToDescriptorMap: MutableMap<PsiElement, NodeDescriptor<*>>,
|
||||
isJavaMap: Boolean
|
||||
): HierarchyNodeDescriptor? {
|
||||
val element = (if (isJavaMap && originalElement is KtElement) originalElement.toLightElements().firstOrNull() else originalElement)
|
||||
?: return null
|
||||
|
||||
val existingDescriptor = elementToDescriptorMap[element] as? HierarchyNodeDescriptor
|
||||
val result = if (existingDescriptor != null) {
|
||||
existingDescriptor.incrementUsageCount()
|
||||
existingDescriptor
|
||||
}
|
||||
else {
|
||||
val newDescriptor: HierarchyNodeDescriptor = when (element) {
|
||||
is KtElement -> KotlinCallHierarchyNodeDescriptor(parent, element, false, navigateToReference)
|
||||
is PsiMember -> CallHierarchyNodeDescriptor(element.project, parent, element, false, navigateToReference)
|
||||
else -> return null
|
||||
}
|
||||
elementToDescriptorMap[element] = newDescriptor
|
||||
newDescriptor
|
||||
}
|
||||
|
||||
if (reference != null) {
|
||||
result.addReference(reference)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
+2
-2
@@ -33,9 +33,9 @@ class KotlinOverrideTreeStructure(project: Project, val element: PsiElement) : H
|
||||
setBaseElement(javaTreeStructures.first().baseDescriptor!!)
|
||||
}
|
||||
|
||||
override fun buildChildren(descriptor: HierarchyNodeDescriptor): Array<Any> {
|
||||
override fun buildChildren(nodeDescriptor: HierarchyNodeDescriptor): Array<Any> {
|
||||
fun buildChildrenByTreeStructure(javaTreeStructure: MethodHierarchyTreeStructure): Array<Any> {
|
||||
return javaTreeStructure.getChildElements(descriptor as MethodHierarchyNodeDescriptor) ?: ArrayUtil.EMPTY_OBJECT_ARRAY
|
||||
return javaTreeStructure.getChildElements(nodeDescriptor as MethodHierarchyNodeDescriptor) ?: ArrayUtil.EMPTY_OBJECT_ARRAY
|
||||
}
|
||||
|
||||
return javaTreeStructures
|
||||
|
||||
+3
-4
@@ -40,13 +40,12 @@ import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getJavaMethodDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.hierarchy.calls.CalleeReferenceProcessor
|
||||
import org.jetbrains.kotlin.idea.hierarchy.calls.KotlinCallHierarchyNodeDescriptor
|
||||
import org.jetbrains.kotlin.idea.hierarchy.calls.KotlinCallerMethodsTreeStructure
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
import org.jetbrains.kotlin.psi.KtFunction
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
|
||||
import java.util.HashSet
|
||||
import java.util.LinkedHashSet
|
||||
import java.util.*
|
||||
|
||||
class KotlinCallerChooser(
|
||||
declaration: PsiElement,
|
||||
@@ -110,7 +109,7 @@ class KotlinMethodNode(
|
||||
|
||||
val callers = LinkedHashSet<PsiElement>()
|
||||
|
||||
val processor = object: KotlinCallerMethodsTreeStructure.CalleeReferenceProcessor(false) {
|
||||
val processor = object: CalleeReferenceProcessor(false) {
|
||||
override fun onAccept(ref: PsiReference, element: PsiElement) {
|
||||
if ((element is KtFunction || element is KtClass || element is PsiMethod) && element !in myCalled) {
|
||||
callers.add(element)
|
||||
|
||||
@@ -20,9 +20,13 @@ 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.PsiElement
|
||||
|
||||
val DataContext.project: Project
|
||||
get() = CommonDataKeys.PROJECT.getData(this)!!
|
||||
|
||||
val DataContext.hostEditor: Editor?
|
||||
get() = CommonDataKeys.HOST_EDITOR.getData(this)
|
||||
get() = CommonDataKeys.HOST_EDITOR.getData(this)
|
||||
|
||||
val DataContext.psiElement : PsiElement?
|
||||
get() = CommonDataKeys.PSI_ELEMENT.getData(this)
|
||||
idea/testData/hierarchy/calls/callers/callInsideAnonymousFun/CallInsideAnonymousFun_verification.xml
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
<node text="foo() ()" base="true">
|
||||
<node text="bar() ()">
|
||||
<node text="baz() ()"/>
|
||||
</node>
|
||||
</node>
|
||||
@@ -0,0 +1,11 @@
|
||||
fun <caret>foo() = 1
|
||||
|
||||
fun bar() {
|
||||
val x = fun () {
|
||||
val y = foo()
|
||||
}
|
||||
}
|
||||
|
||||
fun baz() {
|
||||
bar()
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
<node text="foo() ()" base="true">
|
||||
<node text="bar() ()">
|
||||
<node text="baz() ()"/>
|
||||
</node>
|
||||
</node>
|
||||
@@ -0,0 +1,11 @@
|
||||
fun <caret>foo() = 1
|
||||
|
||||
fun bar() {
|
||||
val x = {
|
||||
val y = foo()
|
||||
}
|
||||
}
|
||||
|
||||
fun baz() {
|
||||
bar()
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import com.intellij.lang.LanguageExtension;
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys;
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.fileEditor.impl.text.TextEditorPsiDataProvider;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
@@ -36,10 +37,11 @@ import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.Processor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.idea.KotlinHierarchyViewTestBase;
|
||||
import org.jetbrains.kotlin.idea.hierarchy.calls.KotlinCalleeMethodsTreeStructure;
|
||||
import org.jetbrains.kotlin.idea.hierarchy.calls.KotlinCallerMethodsTreeStructure;
|
||||
import org.jetbrains.kotlin.idea.hierarchy.calls.KotlinCalleeTreeStructure;
|
||||
import org.jetbrains.kotlin.idea.hierarchy.calls.KotlinCallerTreeStructure;
|
||||
import org.jetbrains.kotlin.idea.hierarchy.overrides.KotlinOverrideTreeStructure;
|
||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase;
|
||||
import org.jetbrains.kotlin.psi.KtElement;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
@@ -132,9 +134,8 @@ public abstract class AbstractHierarchyTest extends KotlinHierarchyViewTestBase
|
||||
return new Computable<HierarchyTreeStructure>() {
|
||||
@Override
|
||||
public HierarchyTreeStructure compute() {
|
||||
return new KotlinCallerMethodsTreeStructure(
|
||||
getProject(),
|
||||
getElementAtCaret(LanguageCallHierarchy.INSTANCE),
|
||||
return new KotlinCallerTreeStructure(
|
||||
(KtElement) getElementAtCaret(LanguageCallHierarchy.INSTANCE),
|
||||
HierarchyBrowserBaseEx.SCOPE_PROJECT
|
||||
);
|
||||
}
|
||||
@@ -158,9 +159,8 @@ public abstract class AbstractHierarchyTest extends KotlinHierarchyViewTestBase
|
||||
return new Computable<HierarchyTreeStructure>() {
|
||||
@Override
|
||||
public HierarchyTreeStructure compute() {
|
||||
return new KotlinCalleeMethodsTreeStructure(
|
||||
getProject(),
|
||||
getElementAtCaret(LanguageCallHierarchy.INSTANCE),
|
||||
return new KotlinCalleeTreeStructure(
|
||||
(KtElement) getElementAtCaret(LanguageCallHierarchy.INSTANCE),
|
||||
HierarchyBrowserBaseEx.SCOPE_PROJECT
|
||||
);
|
||||
}
|
||||
@@ -189,12 +189,16 @@ public abstract class AbstractHierarchyTest extends KotlinHierarchyViewTestBase
|
||||
|
||||
private DataContext getDataContext() {
|
||||
Editor editor = getEditor();
|
||||
PsiFile psiFile = getFile();
|
||||
|
||||
MapDataContext context = new MapDataContext();
|
||||
context.put(CommonDataKeys.PROJECT, getProject());
|
||||
context.put(CommonDataKeys.EDITOR, editor);
|
||||
context.put(CommonDataKeys.PSI_ELEMENT, psiFile.findElementAt(editor.getCaretModel().getOffset()));
|
||||
PsiElement targetElement = (PsiElement) new TextEditorPsiDataProvider().getData(
|
||||
CommonDataKeys.PSI_ELEMENT.getName(),
|
||||
editor,
|
||||
editor.getCaretModel().getCurrentCaret()
|
||||
);
|
||||
context.put(CommonDataKeys.PSI_ELEMENT, targetElement);
|
||||
return context;
|
||||
}
|
||||
|
||||
|
||||
@@ -323,6 +323,18 @@ public class HierarchyTestGenerated extends AbstractHierarchyTest {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/hierarchy/calls/callers"), Pattern.compile("^([^\\.]+)$"), TargetBackend.ANY, false);
|
||||
}
|
||||
|
||||
@TestMetadata("callInsideAnonymousFun")
|
||||
public void testCallInsideAnonymousFun() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/hierarchy/calls/callers/callInsideAnonymousFun/");
|
||||
doCallerHierarchyTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("callInsideLambda")
|
||||
public void testCallInsideLambda() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/hierarchy/calls/callers/callInsideLambda/");
|
||||
doCallerHierarchyTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("kotlinClass")
|
||||
public void testKotlinClass() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/hierarchy/calls/callers/kotlinClass/");
|
||||
|
||||
Reference in New Issue
Block a user