"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)
if (expression is KtSimpleNameExpression) {
StaticMembers(bindingContext, lookupElementFactory, resolutionFacade)
StaticMembers(bindingContext, lookupElementFactory, resolutionFacade, moduleDescriptor)
.addToCollection(items, expectedInfos, expression, descriptorsToSkip)
}
@@ -18,13 +18,16 @@ package org.jetbrains.kotlin.idea.completion.smart
import com.intellij.codeInsight.lookup.LookupElement
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.decorateAsStaticMember
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.multipleFuzzyTypes
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.util.fuzzyReturnType
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils
@@ -36,23 +39,33 @@ import java.util.*
class StaticMembers(
private val bindingContext: BindingContext,
private val lookupElementFactory: LookupElementFactory,
private val resolutionFacade: ResolutionFacade
private val resolutionFacade: ResolutionFacade,
private val moduleDescriptor: ModuleDescriptor
) {
fun addToCollection(collection: MutableCollection<LookupElement>,
expectedInfos: Collection<ExpectedInfo>,
context: KtSimpleNameExpression,
enumEntriesToSkip: Set<DeclarationDescriptor>) {
fun addToCollection(
collection: MutableCollection<LookupElement>,
expectedInfos: Collection<ExpectedInfo>,
context: KtSimpleNameExpression,
enumEntriesToSkip: Set<DeclarationDescriptor>
) {
val expectedInfosByClass = HashMap<ClassDescriptor, MutableList<ExpectedInfo>>()
for (expectedInfo in expectedInfos) {
for (fuzzyType in expectedInfo.multipleFuzzyTypes) {
val classDescriptor = fuzzyType.type.constructor.declarationDescriptor as? ClassDescriptor ?: continue
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) {
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,
expectedInfos: Collection<ExpectedInfo>,
context: KtSimpleNameExpression,
enumEntriesToSkip: Set<DeclarationDescriptor>) {
enumEntriesToSkip: Set<DeclarationDescriptor>,
priority: SmartCompletionItemPriority
) {
fun processMember(descriptor: DeclarationDescriptor) {
if (descriptor is DeclarationDescriptorWithVisibility && !descriptor.isVisible(context, null, bindingContext, resolutionFacade)) return
@@ -79,7 +93,7 @@ class StaticMembers(
return
}
collection.addLookupElements(descriptor, expectedInfos, matcher) { createLookupElements(it) }
collection.addLookupElements(descriptor, expectedInfos, matcher) { createLookupElements(it, priority) }
}
classDescriptor.staticScope.getContributedDescriptors().forEach(::processMember)
@@ -92,17 +106,17 @@ class StaticMembers(
}
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.forEach(::processMember)
}
private fun createLookupElements(memberDescriptor: DeclarationDescriptor): Collection<LookupElement> {
private fun createLookupElements(memberDescriptor: DeclarationDescriptor, priority: SmartCompletionItemPriority): Collection<LookupElement> {
return lookupElementFactory.createStandardLookupElementsForDescriptor(memberDescriptor, useReceiverTypes = false)
.map {
it.decorateAsStaticMember(memberDescriptor, classNameAsLookupString = true)!!
.assignSmartCompletionPriority(SmartCompletionItemPriority.STATIC_MEMBER)
.assignSmartCompletionPriority(priority)
}
}
}
@@ -259,6 +259,7 @@ enum class SmartCompletionItemPriority {
FALSE,
CLASS_LITERAL,
THIS,
DELEGATES_STATIC_MEMBER,
DEFAULT,
NULLABLE,
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: { 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: createX2
// ABSENT: createX3
// EXIST: createY1
// ABSENT: createY2
/*TODO: add constructors*/
// EXIST: X1
// EXIST: X2
// ABSENT: X3
// EXIST: Y1
// ABSENT: Y2
@@ -24,6 +24,9 @@ class C {
}
// 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
// EXIST: createX2
// 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: { 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
// ABSENT: createX2
@@ -42,11 +42,16 @@ class C {
}
// 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
// ABSENT: createX2
// ABSENT: createX3
// EXIST: createX4
// ABSENT: createX5
// EXIST: X1
// ABSENT: X2
// ABSENT: X3
@@ -1437,6 +1437,12 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
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")
public void testWhenByEnum() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromSmart/WhenByEnum.kt");
@@ -1437,6 +1437,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
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")
public void testWhenByEnum() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromSmart/WhenByEnum.kt");
@@ -1504,6 +1504,12 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT
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")
public void testValInClass() throws Exception {
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 PropertyDelegateAdditionalData : ExpectedInfo.AdditionalData
class ExpectedInfos(
private val bindingContext: BindingContext,
private val resolutionFacade: ResolutionFacade,
@@ -662,8 +664,7 @@ class ExpectedInfos(
result
}
}
return listOf(ExpectedInfo(byTypeFilter, null, null))
//TODO: special items for "Delegates...."
return listOf(ExpectedInfo(byTypeFilter, null, null, additionalData = PropertyDelegateAdditionalData))
}
private fun getFromBindingContext(expressionWithType: KtExpression): Collection<ExpectedInfo>? {