Create from usage: Support extension members for Java classes

This commit is contained in:
Alexey Sedunov
2015-02-10 15:16:35 +03:00
parent 9f3ba50dc5
commit e75bd1b41b
4 changed files with 58 additions and 21 deletions
@@ -29,6 +29,15 @@ import org.jetbrains.kotlin.resolve.jvm.resolveMethod
import org.jetbrains.kotlin.load.java.structure.impl.JavaMethodImpl import org.jetbrains.kotlin.load.java.structure.impl.JavaMethodImpl
import org.jetbrains.kotlin.load.java.structure.impl.JavaConstructorImpl import org.jetbrains.kotlin.load.java.structure.impl.JavaConstructorImpl
import org.jetbrains.kotlin.resolve.jvm.resolveConstructor import org.jetbrains.kotlin.resolve.jvm.resolveConstructor
import com.intellij.psi.PsiClass
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl
import com.intellij.psi.PsiField
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.resolve.jvm.resolveField
import org.jetbrains.kotlin.load.java.structure.impl.JavaFieldImpl
import com.intellij.psi.PsiMember
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
public object JavaResolveExtension : CacheExtension<(PsiElement) -> Pair<JavaDescriptorResolver, BindingContext>> { public object JavaResolveExtension : CacheExtension<(PsiElement) -> Pair<JavaDescriptorResolver, BindingContext>> {
override val platform: TargetPlatform = TargetPlatform.JVM override val platform: TargetPlatform = TargetPlatform.JVM
@@ -48,8 +57,6 @@ public object JavaResolveExtension : CacheExtension<(PsiElement) -> Pair<JavaDes
} }
fun PsiMethod.getJavaMethodDescriptor(): FunctionDescriptor { fun PsiMethod.getJavaMethodDescriptor(): FunctionDescriptor {
if (this is KotlinLightMethod) throw AssertionError("Light methods are not allowed here: ${getOrigin()?.getText()}")
val resolver = JavaResolveExtension.getResolver(getProject(), this) val resolver = JavaResolveExtension.getResolver(getProject(), this)
val methodDescriptor = when { val methodDescriptor = when {
this.isConstructor() -> resolver.resolveConstructor(JavaConstructorImpl(this)) this.isConstructor() -> resolver.resolveConstructor(JavaConstructorImpl(this))
@@ -59,3 +66,28 @@ fun PsiMethod.getJavaMethodDescriptor(): FunctionDescriptor {
return methodDescriptor!! return methodDescriptor!!
} }
fun PsiClass.getJavaClassDescriptor(): ClassDescriptor {
val resolver = JavaResolveExtension.getResolver(getProject(), this)
val classDescriptor = resolver.resolveClass(JavaClassImpl(this))
assert(classDescriptor != null) { "No descriptor found for " + getText() }
return classDescriptor!!
}
fun PsiField.getJavaFieldDescriptor(): PropertyDescriptor {
val resolver = JavaResolveExtension.getResolver(getProject(), this)
val fieldDescriptor = resolver.resolveField(JavaFieldImpl(this))
assert(fieldDescriptor != null) { "No descriptor found for " + getText() }
return fieldDescriptor!!
}
fun PsiMember.getJavaMemberDescriptor(): DeclarationDescriptor? {
return when (this) {
is PsiClass -> getJavaClassDescriptor()
is PsiMethod -> getJavaMethodDescriptor()
is PsiField -> getJavaFieldDescriptor()
else -> null
}
}
@@ -31,6 +31,8 @@ import org.jetbrains.kotlin.psi.JetExpression
import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList
import java.util.HashSet import java.util.HashSet
import org.jetbrains.kotlin.psi.JetElement import org.jetbrains.kotlin.psi.JetElement
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
public class CreateCallableFromUsageFix( public class CreateCallableFromUsageFix(
originalExpression: JetExpression, originalExpression: JetExpression,
@@ -71,11 +73,11 @@ public class CreateCallableFromUsageFix(
val popupTitle = JetBundle.message("choose.target.class.or.trait.title") val popupTitle = JetBundle.message("choose.target.class.or.trait.title")
val receiverTypeCandidates = callableBuilder.computeTypeCandidates(callableInfo.receiverTypeInfo) val receiverTypeCandidates = callableBuilder.computeTypeCandidates(callableInfo.receiverTypeInfo)
if (receiverTypeCandidates.isNotEmpty()) { if (receiverTypeCandidates.isNotEmpty()) {
// TODO: Support generation of Java class members
val containers = receiverTypeCandidates val containers = receiverTypeCandidates
.map { candidate -> .map { candidate ->
val descriptor = candidate.theType.getConstructor().getDeclarationDescriptor() val descriptor = candidate.theType.getConstructor().getDeclarationDescriptor()
(DescriptorToDeclarationUtil.getDeclaration(file, descriptor) as? JetClassOrObject)?.let { candidate to it } val declaration = DescriptorToDeclarationUtil.getDeclaration(file, descriptor)
if (declaration is JetClassOrObject || declaration is PsiClass) candidate to declaration else null
} }
.filterNotNull() .filterNotNull()
@@ -73,6 +73,8 @@ import com.intellij.psi.PsiPackage
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.psi.psiUtil.parents import org.jetbrains.kotlin.psi.psiUtil.parents
import com.intellij.refactoring.util.RefactoringUIUtil import com.intellij.refactoring.util.RefactoringUIUtil
import com.intellij.psi.PsiMember
import org.jetbrains.kotlin.idea.caches.resolve.getJavaMemberDescriptor
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind
import org.jetbrains.kotlin.resolve.OverridingUtil import org.jetbrains.kotlin.resolve.OverridingUtil
@@ -299,13 +301,13 @@ public fun chooseContainerElement<T>(
editor: Editor, editor: Editor,
title: String, title: String,
highlightSelection: Boolean, highlightSelection: Boolean,
toPsi: (T) -> JetElement, toPsi: (T) -> PsiElement,
onSelect: (T) -> Unit) { onSelect: (T) -> Unit) {
return getPsiElementPopup( return getPsiElementPopup(
editor, editor,
containers, containers,
object : PsiElementListCellRenderer<JetElement>() { object : PsiElementListCellRenderer<PsiElement>() {
private fun JetElement.renderName(): String { private fun PsiElement.renderName(): String {
if (this is JetPropertyAccessor) { if (this is JetPropertyAccessor) {
return (getParent() as JetProperty).renderName() + if (isGetter()) ".get" else ".set" return (getParent() as JetProperty).renderName() + if (isGetter()) ".get" else ".set"
} }
@@ -315,9 +317,13 @@ public fun chooseContainerElement<T>(
return (this as? PsiNamedElement)?.getName() ?: "<anonymous>" return (this as? PsiNamedElement)?.getName() ?: "<anonymous>"
} }
private fun JetElement.renderDeclaration(): String? { private fun PsiElement.renderDeclaration(): String? {
val descriptor = when {
this is JetElement -> analyze()[BindingContext.DECLARATION_TO_DESCRIPTOR, this]
this is PsiMember -> getJavaMemberDescriptor()
else -> null
} ?: return null
val name = renderName() val name = renderName()
val descriptor = this.analyze()[BindingContext.DECLARATION_TO_DESCRIPTOR, this]
val params = (descriptor as? FunctionDescriptor)?.let { descriptor -> val params = (descriptor as? FunctionDescriptor)?.let { descriptor ->
descriptor.getValueParameters() descriptor.getValueParameters()
.map { DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(it.getType()) } .map { DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(it.getType()) }
@@ -326,11 +332,11 @@ public fun chooseContainerElement<T>(
return "$name$params" return "$name$params"
} }
private fun JetElement.renderText(): String { private fun PsiElement.renderText(): String {
return StringUtil.shortenTextWithEllipsis(getText()!!.collapseSpaces(), 53, 0) return StringUtil.shortenTextWithEllipsis(getText()!!.collapseSpaces(), 53, 0)
} }
private fun JetElement.getRepresentativeElement(): JetElement { private fun PsiElement.getRepresentativeElement(): PsiElement {
return when (this) { return when (this) {
is JetBlockExpression -> (getParent() as? JetDeclarationWithBody) ?: this is JetBlockExpression -> (getParent() as? JetDeclarationWithBody) ?: this
is JetClassBody -> getParent() as JetClassOrObject is JetClassBody -> getParent() as JetClassOrObject
@@ -338,20 +344,17 @@ public fun chooseContainerElement<T>(
} }
} }
override fun getElementText(element: JetElement): String? { override fun getElementText(element: PsiElement): String? {
val representativeElement = element.getRepresentativeElement() val representativeElement = element.getRepresentativeElement()
return when (representativeElement) { return representativeElement.renderDeclaration() ?: representativeElement.renderText()
is JetFile, is JetDeclarationWithBody, is JetClassOrObject -> representativeElement.renderDeclaration()
else -> representativeElement.renderText()
}
} }
override fun getContainerText(element: JetElement?, name: String?): String? = null override fun getContainerText(element: PsiElement, name: String?): String? = null
override fun getIconFlags(): Int = 0 override fun getIconFlags(): Int = 0
override fun getIcon(element: PsiElement?): Icon? = override fun getIcon(element: PsiElement): Icon? =
super.getIcon((element as? JetElement)?.getRepresentativeElement()) super.getIcon(element.getRepresentativeElement())
}, },
title, title,
highlightSelection, highlightSelection,
@@ -368,7 +371,7 @@ public fun chooseContainerElementIfNecessary<T>(
editor: Editor, editor: Editor,
title: String, title: String,
highlightSelection: Boolean, highlightSelection: Boolean,
toPsi: (T) -> JetElement, toPsi: (T) -> PsiElement,
onSelect: (T) -> Unit onSelect: (T) -> Unit
) { ) {
when { when {
@@ -5,6 +5,6 @@ fun test(): Int {
return A().foo(1, "2") return A().foo(1, "2")
} }
fun Any.foo(i: Int, s: String): Int { fun A.foo(i: Int, s: String): Int {
throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates. throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates.
} }