"Delegates" members in smart completion after "by"
#KT-6231 Fixed
This commit is contained in:
+1
-1
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
+26
-12
@@ -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
|
||||
|
||||
+6
@@ -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");
|
||||
|
||||
+6
@@ -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");
|
||||
|
||||
+6
@@ -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>? {
|
||||
|
||||
Reference in New Issue
Block a user