From 7d5b86e000d9877284b8cf94459198a4cc0593dc Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Fri, 11 Apr 2014 17:40:08 +0400 Subject: [PATCH] Smart completion: no duplicates among static members --- .../jet/plugin/completion/SmartCompletion.kt | 103 ++++++++++-------- .../completion/handlers/smart/MergeTail3.kt | 16 +++ .../handlers/smart/MergeTail3.kt.after | 16 +++ .../completion/handlers/smart/MergeTail4.kt | 16 +++ .../handlers/smart/MergeTail4.kt.after | 16 +++ .../SmartCompletionHandlerTestGenerated.java | 10 ++ 6 files changed, 131 insertions(+), 46 deletions(-) create mode 100644 idea/testData/completion/handlers/smart/MergeTail3.kt create mode 100644 idea/testData/completion/handlers/smart/MergeTail3.kt.after create mode 100644 idea/testData/completion/handlers/smart/MergeTail4.kt create mode 100644 idea/testData/completion/handlers/smart/MergeTail4.kt.after diff --git a/idea/src/org/jetbrains/jet/plugin/completion/SmartCompletion.kt b/idea/src/org/jetbrains/jet/plugin/completion/SmartCompletion.kt index cef221a33ab..406878eacf1 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/SmartCompletion.kt +++ b/idea/src/org/jetbrains/jet/plugin/completion/SmartCompletion.kt @@ -32,6 +32,7 @@ import org.jetbrains.jet.lang.resolve.calls.util.CallMaker import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue import com.intellij.lang.ASTNode +import org.jetbrains.jet.lang.resolve.scopes.JetScope trait SmartCompletionData{ fun toElement(descriptor: DeclarationDescriptor): LookupElement? @@ -69,14 +70,11 @@ fun buildSmartCompletionData(expression: JetSimpleNameExpression, resolveSession val additionalElements = ArrayList() if (receiver == null) { - for (expectedType in expectedTypes) { - //TODO: there can be duplicates here for multiple expected types - staticMembers(expressionWithType, expectedType, resolveSession, bindingContext).toCollection(additionalElements) - } - additionalElements.addTypeInstantiationItems(expectedTypes, resolveSession, bindingContext) - thisItems(expressionWithType, expectedTypes, bindingContext).toCollection(additionalElements) + additionalElements.addStaticMembers(expressionWithType, expectedTypes, resolveSession, bindingContext) + + additionalElements.addThisItems(expressionWithType, expectedTypes, bindingContext) } val dataFlowInfo = bindingContext[BindingContext.EXPRESSION_DATA_FLOW_INFO, expressionWithType] @@ -301,12 +299,11 @@ private fun MutableCollection.addTypeInstantiationItems(jetType: } } -private fun thisItems(context: JetExpression, expectedTypes: Collection, bindingContext: BindingContext): Iterable { +private fun MutableCollection.addThisItems(context: JetExpression, expectedTypes: Collection, bindingContext: BindingContext) { val scope = bindingContext[BindingContext.RESOLUTION_SCOPE, context] - if (scope == null) return listOf() + if (scope == null) return val receivers: List = scope.getImplicitReceiversHierarchy() - val result = ArrayList() for (i in 0..receivers.size - 1) { val receiver = receivers[i] val thisType = receiver.getType() @@ -318,10 +315,9 @@ private fun thisItems(context: JetExpression, expectedTypes: Collection { - val classDescriptor = TypeUtils.getClassDescriptor(expectedType.`type`) - if (classDescriptor == null) return listOf() - if (classDescriptor.getName().isSpecial()) return listOf() +private fun MutableCollection.addStaticMembers(context: JetExpression, expectedTypes: Collection, + resolveSession: ResolveSessionForBodies, bindingContext: BindingContext) { val scope = bindingContext[BindingContext.RESOLUTION_SCOPE, context] - if (scope == null) return listOf() + if (scope == null) return - val descriptors = ArrayList() - - val isSuitableCallable: (DeclarationDescriptor) -> Boolean = { - it is CallableDescriptor && it.getReturnType()?.let { isSubtypeOf(it, expectedType.`type`) } ?: false + val expectedTypesByClass = expectedTypes.groupBy { TypeUtils.getClassDescriptor(it.`type`) } + for ((classDescriptor, expectedTypesForClass) in expectedTypesByClass) { + if (classDescriptor != null && !classDescriptor.getName().isSpecial()) { + addStaticMembers(classDescriptor, expectedTypesForClass, scope, resolveSession, bindingContext) + } } +} - if (classDescriptor is JavaClassDescriptor) { - val pseudoPackage = classDescriptor.getCorrespondingPackageFragment() - if (pseudoPackage != null) { - pseudoPackage.getMemberScope().getAllDescriptors().filterTo(descriptors, isSuitableCallable) +private fun MutableCollection.addStaticMembers(classDescriptor: ClassDescriptor, expectedTypes: Collection, + scope: JetScope, resolveSession: ResolveSessionForBodies, bindingContext: BindingContext) { + + val memberDescriptors = HashMap>() + + for (expectedType in expectedTypes) { + fun addMemberDescriptor(descriptor: DeclarationDescriptor) { + val list = memberDescriptors[descriptor] + if (list != null) { + list.add(expectedType) + } + else{ + if (descriptor is DeclarationDescriptorWithVisibility && !Visibilities.isVisible(descriptor, scope.getContainingDeclaration())) return + + val newList = ArrayList() + newList.add(expectedType) + memberDescriptors[descriptor] = newList + } + } + + fun isSuitableCallable(descriptor: DeclarationDescriptor) + = descriptor is CallableDescriptor && descriptor.getReturnType()?.let { isSubtypeOf(it, expectedType.`type`) } ?: false + + if (classDescriptor is JavaClassDescriptor) { + val pseudoPackage = classDescriptor.getCorrespondingPackageFragment() + if (pseudoPackage != null) { + pseudoPackage.getMemberScope().getAllDescriptors().filter(::isSuitableCallable).forEach(::addMemberDescriptor) + } + } + + val classObject = classDescriptor.getClassObjectDescriptor() + if (classObject != null) { + classObject.getDefaultType().getMemberScope().getAllDescriptors().filter(::isSuitableCallable).forEach(::addMemberDescriptor) + } + + if (classDescriptor.getKind() == ClassKind.ENUM_CLASS) { + classDescriptor.getDefaultType().getMemberScope().getAllDescriptors() + .filter{ it is ClassDescriptor && it.getKind() == ClassKind.ENUM_ENTRY }.forEach(::addMemberDescriptor) } } - val classObject = classDescriptor.getClassObjectDescriptor() - if (classObject != null) { - classObject.getDefaultType().getMemberScope().getAllDescriptors().filterTo(descriptors, isSuitableCallable) - } - - if (classDescriptor.getKind() == ClassKind.ENUM_CLASS) { - classDescriptor.getDefaultType().getMemberScope().getAllDescriptors() - .filterTo(descriptors) { it is ClassDescriptor && it.getKind() == ClassKind.ENUM_ENTRY } - } - - fun toLookupElement(descriptor: DeclarationDescriptor): LookupElement { + for ((descriptor, descriptorExpectedTypes) in memberDescriptors) { val lookupElement = DescriptorLookupConverter.createLookupElement(resolveSession, bindingContext, descriptor) val qualifierPresentation = classDescriptor.getName().asString() val lookupString = qualifierPresentation + "." + lookupElement.getLookupString() @@ -480,12 +500,9 @@ private fun staticMembers(context: JetExpression, expectedType: ExpectedTypeInfo } } - return decorateLookupElement(lookupElementDecorated, expectedType.tail) + val tail = mergeTails(descriptorExpectedTypes.map { it.tail }) + add(decorateLookupElement(lookupElementDecorated, tail)) } - - return descriptors - .filter { !(it is DeclarationDescriptorWithVisibility) || Visibilities.isVisible(it, scope.getContainingDeclaration()) } - .map(::toLookupElement) } private fun mergeTails(tails: Collection): Tail? { @@ -517,10 +534,4 @@ private fun isSubtypeOf(t: JetType, expectedType: JetType): Boolean{ private fun T?.toList(): List = if (this != null) listOf(this) else listOf() -private fun MutableCollection.addAll(iterator: Iterator) { - for (item in iterator) { - add(item) - } -} - private fun String?.isNullOrEmpty() = this == null || this.isEmpty() diff --git a/idea/testData/completion/handlers/smart/MergeTail3.kt b/idea/testData/completion/handlers/smart/MergeTail3.kt new file mode 100644 index 00000000000..bbffdfe6a77 --- /dev/null +++ b/idea/testData/completion/handlers/smart/MergeTail3.kt @@ -0,0 +1,16 @@ +class C { + class object { + fun f1(): C = C() + fun f2(): C = C() + fun f3(): C? = C() + } +} + +fun foo(c: C){} +fun foo(c: C?, i: Int){} + +fun foo() { + foo( +} + +// ELEMENT: C.f1 diff --git a/idea/testData/completion/handlers/smart/MergeTail3.kt.after b/idea/testData/completion/handlers/smart/MergeTail3.kt.after new file mode 100644 index 00000000000..1935ad5014c --- /dev/null +++ b/idea/testData/completion/handlers/smart/MergeTail3.kt.after @@ -0,0 +1,16 @@ +class C { + class object { + fun f1(): C = C() + fun f2(): C = C() + fun f3(): C? = C() + } +} + +fun foo(c: C){} +fun foo(c: C?, i: Int){} + +fun foo() { + foo(C.f1() +} + +// ELEMENT: C.f1 diff --git a/idea/testData/completion/handlers/smart/MergeTail4.kt b/idea/testData/completion/handlers/smart/MergeTail4.kt new file mode 100644 index 00000000000..72027284571 --- /dev/null +++ b/idea/testData/completion/handlers/smart/MergeTail4.kt @@ -0,0 +1,16 @@ +class C { + class object { + fun f1(): C = C() + fun f2(): C = C() + fun f3(): C? = C() + } +} + +fun foo(c: C){} +fun foo(c: C?, i: Int){} + +fun foo() { + foo( +} + +// ELEMENT: C.f3 diff --git a/idea/testData/completion/handlers/smart/MergeTail4.kt.after b/idea/testData/completion/handlers/smart/MergeTail4.kt.after new file mode 100644 index 00000000000..2779f738243 --- /dev/null +++ b/idea/testData/completion/handlers/smart/MergeTail4.kt.after @@ -0,0 +1,16 @@ +class C { + class object { + fun f1(): C = C() + fun f2(): C = C() + fun f3(): C? = C() + } +} + +fun foo(c: C){} +fun foo(c: C?, i: Int){} + +fun foo() { + foo(C.f3(), +} + +// ELEMENT: C.f3 diff --git a/idea/tests/org/jetbrains/jet/completion/handlers/SmartCompletionHandlerTestGenerated.java b/idea/tests/org/jetbrains/jet/completion/handlers/SmartCompletionHandlerTestGenerated.java index 7b640ed2482..fdc3c86e104 100644 --- a/idea/tests/org/jetbrains/jet/completion/handlers/SmartCompletionHandlerTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/completion/handlers/SmartCompletionHandlerTestGenerated.java @@ -196,6 +196,16 @@ public class SmartCompletionHandlerTestGenerated extends AbstractSmartCompletion doTest("idea/testData/completion/handlers/smart/MergeTail2.kt"); } + @TestMetadata("MergeTail3.kt") + public void testMergeTail3() throws Exception { + doTest("idea/testData/completion/handlers/smart/MergeTail3.kt"); + } + + @TestMetadata("MergeTail4.kt") + public void testMergeTail4() throws Exception { + doTest("idea/testData/completion/handlers/smart/MergeTail4.kt"); + } + @TestMetadata("TabReplaceComma1.kt") public void testTabReplaceComma1() throws Exception { doTest("idea/testData/completion/handlers/smart/TabReplaceComma1.kt");