From e33e1ef8d743671b8f43a6434a420253fc3d6fa6 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Tue, 3 Nov 2015 22:16:08 +0300 Subject: [PATCH] KT-8968 Special completion after "super." #KT-8968 Fixed --- .../idea/completion/CompletionSession.kt | 2 +- .../kotlin/idea/completion/CompletionUtils.kt | 6 ++- .../idea/completion/LookupElementFactory.kt | 44 ++++++++++++++++--- .../testData/basic/common/SuperMembers3.kt | 18 ++++++++ .../testData/basic/common/SuperMembers4.kt | 17 +++++++ .../testData/handlers/basic/SuperMethod.kt | 3 +- .../handlers/basic/SuperMethod.kt.after | 3 +- .../testData/handlers/basic/SuperMethod2.kt | 12 +++++ .../handlers/basic/SuperMethod2.kt.after | 12 +++++ .../testData/smart/SuperMembers2.kt | 16 +++++++ .../test/JSBasicCompletionTestGenerated.java | 12 +++++ .../test/JvmBasicCompletionTestGenerated.java | 12 +++++ .../test/JvmSmartCompletionTestGenerated.java | 6 +++ .../BasicCompletionHandlerTestGenerated.java | 6 +++ 14 files changed, 158 insertions(+), 11 deletions(-) create mode 100644 idea/idea-completion/testData/basic/common/SuperMembers3.kt create mode 100644 idea/idea-completion/testData/basic/common/SuperMembers4.kt create mode 100644 idea/idea-completion/testData/handlers/basic/SuperMethod2.kt create mode 100644 idea/idea-completion/testData/handlers/basic/SuperMethod2.kt.after create mode 100644 idea/idea-completion/testData/smart/SuperMembers2.kt diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt index 94de219ce86..d0ebb525a1e 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt @@ -420,7 +420,7 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC protected fun createLookupElementFactory(contextVariablesProvider: ContextVariablesProvider): LookupElementFactory { return LookupElementFactory(basicLookupElementFactory, resolutionFacade, receiverTypes, - callTypeAndReceiver.callType, contextVariablesProvider) + callTypeAndReceiver.callType, inDescriptor, contextVariablesProvider) } private fun detectCallTypeAndReceiverTypes(): Pair, Collection?> { diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionUtils.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionUtils.kt index 35a06ab506a..e973faf9ec1 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionUtils.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionUtils.kt @@ -16,7 +16,10 @@ package org.jetbrains.kotlin.idea.completion -import com.intellij.codeInsight.completion.* +import com.intellij.codeInsight.completion.CompletionProgressIndicator +import com.intellij.codeInsight.completion.CompletionService +import com.intellij.codeInsight.completion.InsertionContext +import com.intellij.codeInsight.completion.PrefixMatcher import com.intellij.codeInsight.lookup.* import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.openapi.util.Key @@ -62,6 +65,7 @@ tailrec fun LookupElement.getUserDataDeep(key: Key): T? { } enum class ItemPriority { + SUPER_METHOD_WITH_ARGUMENTS, DEFAULT, IMPLEMENT, OVERRIDE, diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementFactory.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementFactory.kt index eefacdfa4d4..74cd26da33d 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementFactory.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementFactory.kt @@ -34,11 +34,13 @@ import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.renderer.render import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension +import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf import org.jetbrains.kotlin.synthetic.SamAdapterExtensionFunctionDescriptor import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf +import org.jetbrains.kotlin.utils.addIfNotNull data /* we need copy() */ class LookupElementFactory( @@ -46,6 +48,7 @@ class LookupElementFactory( private val resolutionFacade: ResolutionFacade, private val receiverTypes: Collection?, private val callType: CallType<*>?, + private val inDescriptor: DeclarationDescriptor, private val contextVariablesProvider: ContextVariablesProvider ) { companion object { @@ -57,6 +60,14 @@ class LookupElementFactory( val insertHandlerProvider = basicFactory.insertHandlerProvider + private val superFunctions: Set by lazy { + inDescriptor.parentsWithSelf + .filterIsInstance() + .toList() + .flatMap { it.overriddenDescriptors } + .toSet() + } + public fun createStandardLookupElementsForDescriptor(descriptor: DeclarationDescriptor, useReceiverTypes: Boolean): Collection { val result = SmartList() @@ -66,8 +77,13 @@ class LookupElementFactory( result.add(lookupElement) // add special item for function with one argument of function type with more than one parameter - if (descriptor is FunctionDescriptor && isNormalCall && callType != CallType.SUPER_MEMBERS) { - result.addSpecialFunctionCallElements(descriptor, useReceiverTypes) + if (descriptor is FunctionDescriptor && isNormalCall) { + if (callType != CallType.SUPER_MEMBERS) { + result.addSpecialFunctionCallElements(descriptor, useReceiverTypes) + } + else if (useReceiverTypes) { + result.addIfNotNull(createSuperFunctionCallWithArguments(descriptor)) + } } return result @@ -94,7 +110,7 @@ class LookupElementFactory( val fuzzyParameterType = FuzzyType(parameterType, descriptor.typeParameters) for ((variable, substitutor) in contextVariablesProvider.functionTypeVariables(fuzzyParameterType)) { val substitutedDescriptor = descriptor.substitute(substitutor) - add(createFunctionCallElementWithArgument(substitutedDescriptor, variable.name.render(), useReceiverTypes)) + add(createFunctionCallElementWithArguments(substitutedDescriptor, variable.name.render(), useReceiverTypes)) } } } @@ -137,21 +153,35 @@ class LookupElementFactory( return lookupElement } - private fun createFunctionCallElementWithArgument(descriptor: FunctionDescriptor, argumentText: String, useReceiverTypes: Boolean): LookupElement { + private fun createSuperFunctionCallWithArguments(descriptor: FunctionDescriptor): LookupElement? { + if (descriptor.valueParameters.isEmpty()) return null + if (descriptor !in superFunctions) return null + + val argumentText = descriptor.valueParameters.map { + (if (it.varargElementType != null) "*" else "") + it.name.render() + }.joinToString(", ") + + val lookupElement = createFunctionCallElementWithArguments(descriptor, argumentText, true) + lookupElement.assignPriority(ItemPriority.SUPER_METHOD_WITH_ARGUMENTS) + lookupElement.putUserData(KotlinCompletionCharFilter.SUPPRESS_ITEM_SELECTION_BY_CHARS_ON_TYPING, Unit) + return lookupElement + } + + private fun createFunctionCallElementWithArguments(descriptor: FunctionDescriptor, argumentText: String, useReceiverTypes: Boolean): LookupElement { var lookupElement = createLookupElement(descriptor, useReceiverTypes) val needTypeArguments = (insertHandlerProvider.insertHandler(descriptor) as KotlinFunctionInsertHandler.Normal).inputTypeArguments - return FunctionCallWithArgumentLookupElement(lookupElement, descriptor, argumentText, needTypeArguments) + return FunctionCallWithArgumentsLookupElement(lookupElement, descriptor, argumentText, needTypeArguments) } - private inner class FunctionCallWithArgumentLookupElement( + private inner class FunctionCallWithArgumentsLookupElement( originalLookupElement: LookupElement, private val descriptor: FunctionDescriptor, private val argumentText: String, private val needTypeArguments: Boolean ) : LookupElementDecorator(originalLookupElement) { - override fun equals(other: Any?) = other is FunctionCallWithArgumentLookupElement && delegate == other.delegate && argumentText == other.argumentText + override fun equals(other: Any?) = other is FunctionCallWithArgumentsLookupElement && delegate == other.delegate && argumentText == other.argumentText override fun hashCode() = delegate.hashCode() * 17 + argumentText.hashCode() override fun renderElement(presentation: LookupElementPresentation) { diff --git a/idea/idea-completion/testData/basic/common/SuperMembers3.kt b/idea/idea-completion/testData/basic/common/SuperMembers3.kt new file mode 100644 index 00000000000..3674108e631 --- /dev/null +++ b/idea/idea-completion/testData/basic/common/SuperMembers3.kt @@ -0,0 +1,18 @@ +open class B { + open fun xxx_foo(p1: T, vararg p2: String) {} + open fun xxx_bar(p1: Int, p2: String) {} + open val xxx_val: Int = 0 +} + +class C : B() { + override fun xxx_foo(p1: String, vararg p2: String) { + super.xxx_ + } +} + +// WITH_ORDER +// EXIST: { lookupString: "xxx_foo", itemText: "xxx_foo", tailText: "(p1, *p2)", typeText: "Unit", attributes: "bold" } +// EXIST: { lookupString: "xxx_foo", itemText: "xxx_foo", tailText: "(p1: String, vararg p2: String)", typeText: "Unit", attributes: "bold" } +// EXIST: { lookupString: "xxx_val", itemText: "xxx_val", tailText: null, typeText: "Int", attributes: "bold" } +// EXIST: { lookupString: "xxx_bar", itemText: "xxx_bar", tailText: "(p1: Int, p2: String)", typeText: "Unit", attributes: "bold" } +// NOTHING_ELSE diff --git a/idea/idea-completion/testData/basic/common/SuperMembers4.kt b/idea/idea-completion/testData/basic/common/SuperMembers4.kt new file mode 100644 index 00000000000..e408e9715d1 --- /dev/null +++ b/idea/idea-completion/testData/basic/common/SuperMembers4.kt @@ -0,0 +1,17 @@ +open class B { + open fun foo() {} + open fun bar() {} +} + +class C : B() { + override fun foo() { + super. + } +} + +// EXIST: { lookupString: "foo", itemText: "foo", tailText: "()", typeText: "Unit", attributes: "bold" } +// EXIST: { lookupString: "bar", itemText: "bar", tailText: "()", typeText: "Unit", attributes: "bold" } +// EXIST: equals +// EXIST: hashCode +// EXIST: toString +// NOTHING_ELSE diff --git a/idea/idea-completion/testData/handlers/basic/SuperMethod.kt b/idea/idea-completion/testData/handlers/basic/SuperMethod.kt index 90da7db5ec4..8b889210fe6 100644 --- a/idea/idea-completion/testData/handlers/basic/SuperMethod.kt +++ b/idea/idea-completion/testData/handlers/basic/SuperMethod.kt @@ -8,4 +8,5 @@ class Derived : Base() { } } -// ELEMENT: foo \ No newline at end of file +// ELEMENT: foo +// TAIL_TEXT: "(p: Int)" \ No newline at end of file diff --git a/idea/idea-completion/testData/handlers/basic/SuperMethod.kt.after b/idea/idea-completion/testData/handlers/basic/SuperMethod.kt.after index 33f8f33224c..956b401a404 100644 --- a/idea/idea-completion/testData/handlers/basic/SuperMethod.kt.after +++ b/idea/idea-completion/testData/handlers/basic/SuperMethod.kt.after @@ -8,4 +8,5 @@ class Derived : Base() { } } -// ELEMENT: foo \ No newline at end of file +// ELEMENT: foo +// TAIL_TEXT: "(p: Int)" \ No newline at end of file diff --git a/idea/idea-completion/testData/handlers/basic/SuperMethod2.kt b/idea/idea-completion/testData/handlers/basic/SuperMethod2.kt new file mode 100644 index 00000000000..fb5dc4fc320 --- /dev/null +++ b/idea/idea-completion/testData/handlers/basic/SuperMethod2.kt @@ -0,0 +1,12 @@ +open class Base { + open fun foo(p1: Int, vararg p2: Int){} +} + +class Derived : Base() { + override fun foo(p1: Int, vararg p2: Int) { + super. + } +} + +// ELEMENT: foo +// TAIL_TEXT: "(p1, *p2)" \ No newline at end of file diff --git a/idea/idea-completion/testData/handlers/basic/SuperMethod2.kt.after b/idea/idea-completion/testData/handlers/basic/SuperMethod2.kt.after new file mode 100644 index 00000000000..88834bd676b --- /dev/null +++ b/idea/idea-completion/testData/handlers/basic/SuperMethod2.kt.after @@ -0,0 +1,12 @@ +open class Base { + open fun foo(p1: Int, vararg p2: Int){} +} + +class Derived : Base() { + override fun foo(p1: Int, vararg p2: Int) { + super.foo(p1, *p2) + } +} + +// ELEMENT: foo +// TAIL_TEXT: "(p1, *p2)" \ No newline at end of file diff --git a/idea/idea-completion/testData/smart/SuperMembers2.kt b/idea/idea-completion/testData/smart/SuperMembers2.kt new file mode 100644 index 00000000000..c4a1511e2cc --- /dev/null +++ b/idea/idea-completion/testData/smart/SuperMembers2.kt @@ -0,0 +1,16 @@ +open class B { + open fun foo(p1: Int, p2: String): Int = 0 + open fun bar(p1: Int, p2: String): Int = 0 +} + +class C : B() { + override fun foo(p1: Int, p2: String): Int { + return super. + } +} + +// EXIST: { lookupString: "foo", itemText: "foo", tailText: "(p1, p2)", typeText: "Int", attributes: "bold" } +// EXIST: { lookupString: "foo", itemText: "foo", tailText: "(p1: Int, p2: String)", typeText: "Int", attributes: "bold" } +// EXIST: { lookupString: "bar", itemText: "bar", tailText: "(p1: Int, p2: String)", typeText: "Int", attributes: "bold" } +// EXIST: hashCode +// NOTHING_ELSE diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java index 924fae8d84c..eab90b8dcaa 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java @@ -703,6 +703,18 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes doTest(fileName); } + @TestMetadata("SuperMembers3.kt") + public void testSuperMembers3() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/SuperMembers3.kt"); + doTest(fileName); + } + + @TestMetadata("SuperMembers4.kt") + public void testSuperMembers4() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/SuperMembers4.kt"); + doTest(fileName); + } + @TestMetadata("TopLevelClassCompletionInQualifiedCall.kt") public void testTopLevelClassCompletionInQualifiedCall() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/TopLevelClassCompletionInQualifiedCall.kt"); diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java index a292a6d9cc4..d4178748198 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java @@ -703,6 +703,18 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT doTest(fileName); } + @TestMetadata("SuperMembers3.kt") + public void testSuperMembers3() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/SuperMembers3.kt"); + doTest(fileName); + } + + @TestMetadata("SuperMembers4.kt") + public void testSuperMembers4() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/SuperMembers4.kt"); + doTest(fileName); + } + @TestMetadata("TopLevelClassCompletionInQualifiedCall.kt") public void testTopLevelClassCompletionInQualifiedCall() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/TopLevelClassCompletionInQualifiedCall.kt"); diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmSmartCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmSmartCompletionTestGenerated.java index a7013b9bdec..10e38ae578e 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmSmartCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmSmartCompletionTestGenerated.java @@ -485,6 +485,12 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT doTest(fileName); } + @TestMetadata("SuperMembers2.kt") + public void testSuperMembers2() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/SuperMembers2.kt"); + doTest(fileName); + } + @TestMetadata("ThisConstructorArgument.kt") public void testThisConstructorArgument() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/smart/ThisConstructorArgument.kt"); diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/BasicCompletionHandlerTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/BasicCompletionHandlerTestGenerated.java index f2620a7cb7d..28e65ea373a 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/BasicCompletionHandlerTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/BasicCompletionHandlerTestGenerated.java @@ -149,6 +149,12 @@ public class BasicCompletionHandlerTestGenerated extends AbstractBasicCompletion doTest(fileName); } + @TestMetadata("SuperMethod2.kt") + public void testSuperMethod2() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/SuperMethod2.kt"); + doTest(fileName); + } + @TestMetadata("SuperTypeArg.kt") public void testSuperTypeArg() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/SuperTypeArg.kt");