diff --git a/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallHierarchyNodeDescriptor.java b/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallHierarchyNodeDescriptor.java index 5ef28e6f5e9..b0a8ebda856 100644 --- a/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallHierarchyNodeDescriptor.java +++ b/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallHierarchyNodeDescriptor.java @@ -211,7 +211,7 @@ public class KotlinCallHierarchyNodeDescriptor extends HierarchyNodeDescriptor i return containerText != null ? containerText + "." + elementText : elementText; } - private static String renderNamedFunction(FunctionDescriptor descriptor) { + public static String renderNamedFunction(FunctionDescriptor descriptor) { DeclarationDescriptor descriptorForName = descriptor instanceof ConstructorDescriptor ? descriptor.getContainingDeclaration() : descriptor; diff --git a/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallerMethodsTreeStructure.java b/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallerMethodsTreeStructure.java index 5ae8945ba0e..df1604e4ec7 100644 --- a/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallerMethodsTreeStructure.java +++ b/idea/src/org/jetbrains/kotlin/idea/hierarchy/calls/KotlinCallerMethodsTreeStructure.java @@ -187,29 +187,39 @@ public class KotlinCallerMethodsTreeStructure extends KotlinCallTreeStructure { private Processor defaultQueryProcessor( final HierarchyNodeDescriptor descriptor, final Map methodToDescriptorMap, - final boolean kotlinOnly + boolean kotlinOnly ) { - return new ReadActionProcessor() { + return new CalleeReferenceProcessor(kotlinOnly) { @Override - public boolean processInReadAction(PsiReference ref) { - // copied from Java - if (!(ref instanceof PsiReferenceExpression || ref instanceof JetReference)) { - if (!(ref instanceof PsiElement)) { + protected void onAccept(@NotNull PsiReference ref, @NotNull PsiElement element) { + addNodeDescriptorForElement(ref, element, methodToDescriptorMap, descriptor); + } + }; + } + + public static abstract class CalleeReferenceProcessor extends ReadActionProcessor { + 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 JetReference)) { + if (!(ref instanceof PsiElement)) { + return true; + } + + PsiElement parent = ((PsiElement) ref).getParent(); + if (parent instanceof PsiNewExpression) { + if (((PsiNewExpression) parent).getClassReference() != ref) { 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 { + } + else if (parent instanceof PsiAnonymousClass) { + if (((PsiAnonymousClass) parent).getBaseClassReference() != ref) { return true; } } @@ -218,28 +228,33 @@ public class KotlinCallerMethodsTreeStructure extends KotlinCallTreeStructure { // Accept implicit superclass constructor reference in Java code if (!(refTarget instanceof PsiMethod && ((PsiMethod) refTarget).isConstructor())) return true; } - - PsiElement refElement = ref.getElement(); - if (PsiTreeUtil.getParentOfType(refElement, JetImportDirective.class, true) != null) return true; - - PsiElement element = HierarchyUtils.getCallHierarchyElement(refElement); - - 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(), refElement, false)) { - element = HierarchyUtils.getCallHierarchyElement(element.getParent()); - } + else { + return true; } - - if (element != null) { - addNodeDescriptorForElement(ref, element, methodToDescriptorMap, descriptor); - } - - return true; } - }; + + PsiElement refElement = ref.getElement(); + if (PsiTreeUtil.getParentOfType(refElement, JetImportDirective.class, true) != null) return true; + + PsiElement element = HierarchyUtils.getCallHierarchyElement(refElement); + + 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(), refElement, false)) { + element = HierarchyUtils.getCallHierarchyElement(element.getParent()); + } + } + + if (element != null) { + onAccept(ref, element); + } + + return true; + } + + protected abstract void onAccept(@NotNull PsiReference ref, @NotNull PsiElement element); } } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/ui/JetChangeSignatureDialog.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/ui/JetChangeSignatureDialog.kt index b18bd7add18..3c6d9d51f3b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/ui/JetChangeSignatureDialog.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/ui/JetChangeSignatureDialog.kt @@ -155,8 +155,8 @@ public class JetChangeSignatureDialog( return true } - override fun createCallerChooser(title: String, treeToReuse: Tree, callback: Consumer>) = - throw UnsupportedOperationException() + override fun createCallerChooser(title: String, treeToReuse: Tree?, callback: Consumer>) = + KotlinCallerChooser(myMethod.getMethod(), myProject, title, treeToReuse, callback) override fun getTableEditor(table: JTable, item: ParameterTableModelItemBase): JBTableRowEditor? { return object : JBTableRowEditor() { diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/ui/KotlinCallerChooser.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/ui/KotlinCallerChooser.kt new file mode 100644 index 00000000000..7c71b4f010a --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/ui/KotlinCallerChooser.kt @@ -0,0 +1,126 @@ +/* + * 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.refactoring.changeSignature.ui + +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiClassOwner +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiMethod +import com.intellij.psi.PsiReference +import com.intellij.psi.search.searches.MethodReferencesSearch +import com.intellij.psi.search.searches.ReferencesSearch +import com.intellij.refactoring.changeSignature.CallerChooserBase +import com.intellij.refactoring.changeSignature.MethodNodeBase +import com.intellij.ui.ColoredTreeCellRenderer +import com.intellij.ui.JBColor +import com.intellij.ui.SimpleTextAttributes +import com.intellij.ui.treeStructure.Tree +import com.intellij.util.Consumer +import com.intellij.util.containers +import com.intellij.util.ui.UIUtil +import org.jetbrains.kotlin.asJava.getRepresentativeLightMethod +import org.jetbrains.kotlin.asJava.namedUnwrappedElement +import org.jetbrains.kotlin.asJava.toLightMethods +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.idea.JetFileType +import org.jetbrains.kotlin.idea.caches.resolve.getJavaMethodDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor +import org.jetbrains.kotlin.idea.hierarchy.calls.KotlinCallHierarchyNodeDescriptor +import org.jetbrains.kotlin.idea.hierarchy.calls.KotlinCallerMethodsTreeStructure +import org.jetbrains.kotlin.psi.JetClass +import org.jetbrains.kotlin.psi.JetFunction +import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext +import java.util.HashSet +import java.util.LinkedHashSet + +public class KotlinCallerChooser( + declaration: PsiElement, + project: Project, + title: String, + previousTree: Tree?, + callback: Consumer> +): CallerChooserBase(declaration, project, title, previousTree, "dummy." + JetFileType.EXTENSION, callback) { + override fun createTreeNode(method: PsiElement?, called: containers.HashSet, cancelCallback: Runnable): KotlinMethodNode { + return KotlinMethodNode(method, called, myProject, cancelCallback) + } + + override fun findDeepestSuperMethods(method: PsiElement) = + method.toLightMethods().singleOrNull()?.findDeepestSuperMethods() + + override fun getEmptyCallerText() = + "Caller text \nwith highlighted callee call would be shown here" + + override fun getEmptyCalleeText() = + "Callee text would be shown here" +} + +public class KotlinMethodNode( + method: PsiElement?, + called: HashSet, + project: Project, + cancelCallback: Runnable +): MethodNodeBase(method?.namedUnwrappedElement ?: method, called, project, cancelCallback) { + override fun createNode(caller: PsiElement, called: HashSet) = + KotlinMethodNode(caller, called, myProject, myCancelCallback) + + override fun customizeRendererText(renderer: ColoredTreeCellRenderer) { + val descriptor = when (myMethod) { + is JetFunction -> myMethod.resolveToDescriptor() as FunctionDescriptor + is JetClass -> (myMethod.resolveToDescriptor() as ClassDescriptor).getUnsubstitutedPrimaryConstructor() ?: return + is PsiMethod -> myMethod.getJavaMethodDescriptor() + else -> throw AssertionError("Invalid declaration: ${myMethod.getElementTextWithContext()}") + } + val containerName = sequence(descriptor) { it.getContainingDeclaration() } + .firstOrNull { it is ClassDescriptor } + ?.getName() + + val renderedFunction = KotlinCallHierarchyNodeDescriptor.renderNamedFunction(descriptor) + val renderedFunctionWithContainer = + containerName?.let { + "${if (it.isSpecial()) "[Anonymous]" else it.asString()}.$renderedFunction" + } ?: renderedFunction + + val attributes = if (isEnabled()) + SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, UIUtil.getTreeForeground()) + else + SimpleTextAttributes.EXCLUDED_ATTRIBUTES + renderer.append(renderedFunctionWithContainer, attributes) + + val packageName = (myMethod.getContainingFile() as? PsiClassOwner)?.getPackageName() ?: "" + renderer.append(" ($packageName)", SimpleTextAttributes(SimpleTextAttributes.STYLE_ITALIC, JBColor.GRAY)) + } + + override fun computeCallers(): List { + if (myMethod == null) return emptyList() + + val callers = LinkedHashSet() + + val processor = object: KotlinCallerMethodsTreeStructure.CalleeReferenceProcessor(false) { + override fun onAccept(ref: PsiReference, element: PsiElement) { + if ((element is JetFunction || element is JetClass || element is PsiMethod) && element !in myCalled) { + callers.add(element) + } + } + } + val query = myMethod.getRepresentativeLightMethod()?.let { MethodReferencesSearch.search(it, it.getUseScope(), true) } + ?: ReferencesSearch.search(myMethod, myMethod.getUseScope()) + query.forEach { processor.process(it) } + return callers.toList() + } +} \ No newline at end of file