"Delegates" members in smart completion after "by"

#KT-6231 Fixed
This commit is contained in:
Valentin Kipyatkov
2016-03-25 17:14:52 +03:00
parent ae0d840aab
commit 88bdf88bbb
13 changed files with 95 additions and 16 deletions
@@ -209,7 +209,7 @@ class SmartCompletion(
.addTo(items, inheritanceSearchers, expectedInfos) .addTo(items, inheritanceSearchers, expectedInfos)
if (expression is KtSimpleNameExpression) { if (expression is KtSimpleNameExpression) {
StaticMembers(bindingContext, lookupElementFactory, resolutionFacade) StaticMembers(bindingContext, lookupElementFactory, resolutionFacade, moduleDescriptor)
.addToCollection(items, expectedInfos, expression, descriptorsToSkip) .addToCollection(items, expectedInfos, expression, descriptorsToSkip)
} }
@@ -18,13 +18,16 @@ package org.jetbrains.kotlin.idea.completion.smart
import com.intellij.codeInsight.lookup.LookupElement import com.intellij.codeInsight.lookup.LookupElement
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
import org.jetbrains.kotlin.idea.completion.LookupElementFactory import org.jetbrains.kotlin.idea.completion.LookupElementFactory
import org.jetbrains.kotlin.idea.completion.decorateAsStaticMember import org.jetbrains.kotlin.idea.completion.decorateAsStaticMember
import org.jetbrains.kotlin.idea.core.ExpectedInfo import org.jetbrains.kotlin.idea.core.ExpectedInfo
import org.jetbrains.kotlin.idea.core.PropertyDelegateAdditionalData
import org.jetbrains.kotlin.idea.core.isVisible import org.jetbrains.kotlin.idea.core.isVisible
import org.jetbrains.kotlin.idea.core.multipleFuzzyTypes import org.jetbrains.kotlin.idea.core.multipleFuzzyTypes
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.util.fuzzyReturnType import org.jetbrains.kotlin.idea.util.fuzzyReturnType
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtSimpleNameExpression import org.jetbrains.kotlin.psi.KtSimpleNameExpression
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
@@ -36,23 +39,33 @@ import java.util.*
class StaticMembers( class StaticMembers(
private val bindingContext: BindingContext, private val bindingContext: BindingContext,
private val lookupElementFactory: LookupElementFactory, private val lookupElementFactory: LookupElementFactory,
private val resolutionFacade: ResolutionFacade private val resolutionFacade: ResolutionFacade,
private val moduleDescriptor: ModuleDescriptor
) { ) {
fun addToCollection(collection: MutableCollection<LookupElement>, fun addToCollection(
expectedInfos: Collection<ExpectedInfo>, collection: MutableCollection<LookupElement>,
context: KtSimpleNameExpression, expectedInfos: Collection<ExpectedInfo>,
enumEntriesToSkip: Set<DeclarationDescriptor>) { context: KtSimpleNameExpression,
enumEntriesToSkip: Set<DeclarationDescriptor>
) {
val expectedInfosByClass = HashMap<ClassDescriptor, MutableList<ExpectedInfo>>() val expectedInfosByClass = HashMap<ClassDescriptor, MutableList<ExpectedInfo>>()
for (expectedInfo in expectedInfos) { for (expectedInfo in expectedInfos) {
for (fuzzyType in expectedInfo.multipleFuzzyTypes) { for (fuzzyType in expectedInfo.multipleFuzzyTypes) {
val classDescriptor = fuzzyType.type.constructor.declarationDescriptor as? ClassDescriptor ?: continue val classDescriptor = fuzzyType.type.constructor.declarationDescriptor as? ClassDescriptor ?: continue
expectedInfosByClass.getOrPut(classDescriptor) { ArrayList() }.add(expectedInfo) expectedInfosByClass.getOrPut(classDescriptor) { ArrayList() }.add(expectedInfo)
} }
if (expectedInfo.additionalData is PropertyDelegateAdditionalData) {
val delegatesClass = resolutionFacade.resolveImportReference(moduleDescriptor, FqName("kotlin.properties.Delegates")).singleOrNull()
if (delegatesClass is ClassDescriptor) {
addToCollection(collection, delegatesClass, listOf(expectedInfo), context, enumEntriesToSkip, SmartCompletionItemPriority.DELEGATES_STATIC_MEMBER)
}
}
} }
for ((classDescriptor, expectedInfosForClass) in expectedInfosByClass) { for ((classDescriptor, expectedInfosForClass) in expectedInfosByClass) {
if (!classDescriptor.name.isSpecial) { if (!classDescriptor.name.isSpecial) {
addToCollection(collection, classDescriptor, expectedInfosForClass, context, enumEntriesToSkip) addToCollection(collection, classDescriptor, expectedInfosForClass, context, enumEntriesToSkip, SmartCompletionItemPriority.STATIC_MEMBER)
} }
} }
} }
@@ -62,8 +75,9 @@ class StaticMembers(
classDescriptor: ClassDescriptor, classDescriptor: ClassDescriptor,
expectedInfos: Collection<ExpectedInfo>, expectedInfos: Collection<ExpectedInfo>,
context: KtSimpleNameExpression, context: KtSimpleNameExpression,
enumEntriesToSkip: Set<DeclarationDescriptor>) { enumEntriesToSkip: Set<DeclarationDescriptor>,
priority: SmartCompletionItemPriority
) {
fun processMember(descriptor: DeclarationDescriptor) { fun processMember(descriptor: DeclarationDescriptor) {
if (descriptor is DeclarationDescriptorWithVisibility && !descriptor.isVisible(context, null, bindingContext, resolutionFacade)) return if (descriptor is DeclarationDescriptorWithVisibility && !descriptor.isVisible(context, null, bindingContext, resolutionFacade)) return
@@ -79,7 +93,7 @@ class StaticMembers(
return return
} }
collection.addLookupElements(descriptor, expectedInfos, matcher) { createLookupElements(it) } collection.addLookupElements(descriptor, expectedInfos, matcher) { createLookupElements(it, priority) }
} }
classDescriptor.staticScope.getContributedDescriptors().forEach(::processMember) classDescriptor.staticScope.getContributedDescriptors().forEach(::processMember)
@@ -92,17 +106,17 @@ class StaticMembers(
} }
var members = classDescriptor.defaultType.memberScope.getContributedDescriptors() var members = classDescriptor.defaultType.memberScope.getContributedDescriptors()
if (classDescriptor.kind != ClassKind.ENUM_CLASS) { if (classDescriptor.kind != ClassKind.ENUM_CLASS && classDescriptor.kind != ClassKind.OBJECT) {
members = members.filter { DescriptorUtils.isNonCompanionObject(it) } members = members.filter { DescriptorUtils.isNonCompanionObject(it) }
} }
members.forEach(::processMember) members.forEach(::processMember)
} }
private fun createLookupElements(memberDescriptor: DeclarationDescriptor): Collection<LookupElement> { private fun createLookupElements(memberDescriptor: DeclarationDescriptor, priority: SmartCompletionItemPriority): Collection<LookupElement> {
return lookupElementFactory.createStandardLookupElementsForDescriptor(memberDescriptor, useReceiverTypes = false) return lookupElementFactory.createStandardLookupElementsForDescriptor(memberDescriptor, useReceiverTypes = false)
.map { .map {
it.decorateAsStaticMember(memberDescriptor, classNameAsLookupString = true)!! it.decorateAsStaticMember(memberDescriptor, classNameAsLookupString = true)!!
.assignSmartCompletionPriority(SmartCompletionItemPriority.STATIC_MEMBER) .assignSmartCompletionPriority(priority)
} }
} }
} }
@@ -259,6 +259,7 @@ enum class SmartCompletionItemPriority {
FALSE, FALSE,
CLASS_LITERAL, CLASS_LITERAL,
THIS, THIS,
DELEGATES_STATIC_MEMBER,
DEFAULT, DEFAULT,
NULLABLE, NULLABLE,
INSTANTIATION, INSTANTIATION,
@@ -0,0 +1,9 @@
class X
class C {
val property by D<caret>
}
// EXIST: { itemText: "Delegates.notNull", tailText:"() (kotlin.properties)", typeText: "ReadWriteProperty<Any?, T>", attributes:"" }
// EXIST: { itemText: "Delegates.observable", tailText:"(initialValue: T, crossinline onChange: (KProperty<*>, T, T) -> Unit) (kotlin.properties)", typeText: "ReadWriteProperty<Any?, T>", attributes:"" }
// EXIST: { itemText: "Delegates.vetoable", tailText:"(initialValue: T, crossinline onChange: (KProperty<*>, T, T) -> Boolean) (kotlin.properties)", typeText: "ReadWriteProperty<Any?, T>", attributes:"" }
@@ -27,9 +27,18 @@ class C {
} }
// EXIST: lazy // EXIST: lazy
// EXIST: { itemText: "Delegates.notNull", tailText:"() (kotlin.properties)", typeText: "ReadWriteProperty<Any?, CharSequence>", attributes:"" }
// EXIST: { itemText: "Delegates.observable", tailText:"(initialValue: CharSequence, crossinline onChange: (KProperty<*>, CharSequence, CharSequence) -> Unit) (kotlin.properties)", typeText: "ReadWriteProperty<Any?, CharSequence>", attributes:"" }
// EXIST: { itemText: "Delegates.vetoable", tailText:"(initialValue: CharSequence, crossinline onChange: (KProperty<*>, CharSequence, CharSequence) -> Boolean) (kotlin.properties)", typeText: "ReadWriteProperty<Any?, CharSequence>", attributes:"" }
// EXIST: createX1 // EXIST: createX1
// EXIST: createX2 // EXIST: createX2
// ABSENT: createX3 // ABSENT: createX3
// EXIST: createY1 // EXIST: createY1
// ABSENT: createY2 // ABSENT: createY2
/*TODO: add constructors*/
// EXIST: X1
// EXIST: X2
// ABSENT: X3
// EXIST: Y1
// ABSENT: Y2
@@ -24,6 +24,9 @@ class C {
} }
// ABSENT: lazy // ABSENT: lazy
// EXIST: { itemText: "Delegates.notNull", tailText:"() (kotlin.properties)", typeText: "ReadWriteProperty<Any?, CharSequence>", attributes:"" }
// EXIST: { itemText: "Delegates.observable", tailText:"(initialValue: CharSequence, crossinline onChange: (KProperty<*>, CharSequence, CharSequence) -> Unit) (kotlin.properties)", typeText: "ReadWriteProperty<Any?, CharSequence>", attributes:"" }
// EXIST: { itemText: "Delegates.vetoable", tailText:"(initialValue: CharSequence, crossinline onChange: (KProperty<*>, CharSequence, CharSequence) -> Boolean) (kotlin.properties)", typeText: "ReadWriteProperty<Any?, CharSequence>", attributes:"" }
// ABSENT: createX1 // ABSENT: createX1
// EXIST: createX2 // EXIST: createX2
// ABSENT: createX3 // ABSENT: createX3
@@ -0,0 +1,16 @@
import kotlin.reflect.KProperty
class X {
operator fun getValue(thisRef: C, property: KProperty<*>): String = ""
}
class C {
val property by <caret>
}
// WITH_ORDER
// EXIST: { itemText: "Delegates.notNull", tailText:"() (kotlin.properties)", typeText: "ReadWriteProperty<Any?, T>", attributes:"" }
// EXIST: { itemText: "Delegates.observable", tailText:"(initialValue: T, crossinline onChange: (KProperty<*>, T, T) -> Unit) (kotlin.properties)", typeText: "ReadWriteProperty<Any?, T>", attributes:"" }
// EXIST: { itemText: "Delegates.vetoable", tailText:"(initialValue: T, crossinline onChange: (KProperty<*>, T, T) -> Boolean) (kotlin.properties)", typeText: "ReadWriteProperty<Any?, T>", attributes:"" }
// EXIST: lazy
// EXIST: X
@@ -34,6 +34,9 @@ class C {
} }
// EXIST: lazy // EXIST: lazy
// EXIST: { itemText: "Delegates.notNull", tailText:"() (kotlin.properties)", typeText: "ReadWriteProperty<Any?, T>", attributes:"" }
// EXIST: { itemText: "Delegates.observable", tailText:"(initialValue: T, crossinline onChange: (KProperty<*>, T, T) -> Unit) (kotlin.properties)", typeText: "ReadWriteProperty<Any?, T>", attributes:"" }
// EXIST: { itemText: "Delegates.vetoable", tailText:"(initialValue: T, crossinline onChange: (KProperty<*>, T, T) -> Boolean) (kotlin.properties)", typeText: "ReadWriteProperty<Any?, T>", attributes:"" }
// EXIST: createX1 // EXIST: createX1
// ABSENT: createX2 // ABSENT: createX2
@@ -42,11 +42,16 @@ class C {
} }
// ABSENT: lazy // ABSENT: lazy
// EXIST: { itemText: "Delegates.notNull", tailText:"() (kotlin.properties)", typeText: "ReadWriteProperty<Any?, T>", attributes:"" }
// EXIST: { itemText: "Delegates.observable", tailText:"(initialValue: T, crossinline onChange: (KProperty<*>, T, T) -> Unit) (kotlin.properties)", typeText: "ReadWriteProperty<Any?, T>", attributes:"" }
// EXIST: { itemText: "Delegates.vetoable", tailText:"(initialValue: T, crossinline onChange: (KProperty<*>, T, T) -> Boolean) (kotlin.properties)", typeText: "ReadWriteProperty<Any?, T>", attributes:"" }
// EXIST: createX1 // EXIST: createX1
// ABSENT: createX2 // ABSENT: createX2
// ABSENT: createX3 // ABSENT: createX3
// EXIST: createX4 // EXIST: createX4
// ABSENT: createX5 // ABSENT: createX5
// EXIST: X1 // EXIST: X1
// ABSENT: X2 // ABSENT: X2
// ABSENT: X3 // ABSENT: X3
@@ -1437,6 +1437,12 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
doTest(fileName); doTest(fileName);
} }
@TestMetadata("PropertyDelegate.kt")
public void testPropertyDelegate() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromSmart/PropertyDelegate.kt");
doTest(fileName);
}
@TestMetadata("WhenByEnum.kt") @TestMetadata("WhenByEnum.kt")
public void testWhenByEnum() throws Exception { public void testWhenByEnum() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromSmart/WhenByEnum.kt"); String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromSmart/WhenByEnum.kt");
@@ -1437,6 +1437,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
doTest(fileName); doTest(fileName);
} }
@TestMetadata("PropertyDelegate.kt")
public void testPropertyDelegate() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromSmart/PropertyDelegate.kt");
doTest(fileName);
}
@TestMetadata("WhenByEnum.kt") @TestMetadata("WhenByEnum.kt")
public void testWhenByEnum() throws Exception { public void testWhenByEnum() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromSmart/WhenByEnum.kt"); String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromSmart/WhenByEnum.kt");
@@ -1504,6 +1504,12 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT
doTest(fileName); doTest(fileName);
} }
@TestMetadata("Order.kt")
public void testOrder() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/smart/propertyDelegate/Order.kt");
doTest(fileName);
}
@TestMetadata("ValInClass.kt") @TestMetadata("ValInClass.kt")
public void testValInClass() throws Exception { public void testValInClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/smart/propertyDelegate/ValInClass.kt"); String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/smart/propertyDelegate/ValInClass.kt");
@@ -148,6 +148,8 @@ class WhenEntryAdditionalData(val whenWithSubject: Boolean) : ExpectedInfo.Addit
object IfConditionAdditionalData : ExpectedInfo.AdditionalData object IfConditionAdditionalData : ExpectedInfo.AdditionalData
object PropertyDelegateAdditionalData : ExpectedInfo.AdditionalData
class ExpectedInfos( class ExpectedInfos(
private val bindingContext: BindingContext, private val bindingContext: BindingContext,
private val resolutionFacade: ResolutionFacade, private val resolutionFacade: ResolutionFacade,
@@ -662,8 +664,7 @@ class ExpectedInfos(
result result
} }
} }
return listOf(ExpectedInfo(byTypeFilter, null, null)) return listOf(ExpectedInfo(byTypeFilter, null, null, additionalData = PropertyDelegateAdditionalData))
//TODO: special items for "Delegates...."
} }
private fun getFromBindingContext(expressionWithType: KtExpression): Collection<ExpectedInfo>? { private fun getFromBindingContext(expressionWithType: KtExpression): Collection<ExpectedInfo>? {