KT-8968 Special completion after "super."

#KT-8968 Fixed
This commit is contained in:
Valentin Kipyatkov
2015-11-03 22:16:08 +03:00
parent 99b35257c6
commit e33e1ef8d7
14 changed files with 158 additions and 11 deletions
@@ -420,7 +420,7 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
protected fun createLookupElementFactory(contextVariablesProvider: ContextVariablesProvider): LookupElementFactory { protected fun createLookupElementFactory(contextVariablesProvider: ContextVariablesProvider): LookupElementFactory {
return LookupElementFactory(basicLookupElementFactory, resolutionFacade, receiverTypes, return LookupElementFactory(basicLookupElementFactory, resolutionFacade, receiverTypes,
callTypeAndReceiver.callType, contextVariablesProvider) callTypeAndReceiver.callType, inDescriptor, contextVariablesProvider)
} }
private fun detectCallTypeAndReceiverTypes(): Pair<CallTypeAndReceiver<*, *>, Collection<KotlinType>?> { private fun detectCallTypeAndReceiverTypes(): Pair<CallTypeAndReceiver<*, *>, Collection<KotlinType>?> {
@@ -16,7 +16,10 @@
package org.jetbrains.kotlin.idea.completion 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.codeInsight.lookup.*
import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.util.Key import com.intellij.openapi.util.Key
@@ -62,6 +65,7 @@ tailrec fun <T : Any> LookupElement.getUserDataDeep(key: Key<T>): T? {
} }
enum class ItemPriority { enum class ItemPriority {
SUPER_METHOD_WITH_ARGUMENTS,
DEFAULT, DEFAULT,
IMPLEMENT, IMPLEMENT,
OVERRIDE, OVERRIDE,
@@ -34,11 +34,13 @@ import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.renderer.render import org.jetbrains.kotlin.renderer.render
import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension 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.SamAdapterExtensionFunctionDescriptor
import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
import org.jetbrains.kotlin.utils.addIfNotNull
data /* we need copy() */ data /* we need copy() */
class LookupElementFactory( class LookupElementFactory(
@@ -46,6 +48,7 @@ class LookupElementFactory(
private val resolutionFacade: ResolutionFacade, private val resolutionFacade: ResolutionFacade,
private val receiverTypes: Collection<KotlinType>?, private val receiverTypes: Collection<KotlinType>?,
private val callType: CallType<*>?, private val callType: CallType<*>?,
private val inDescriptor: DeclarationDescriptor,
private val contextVariablesProvider: ContextVariablesProvider private val contextVariablesProvider: ContextVariablesProvider
) { ) {
companion object { companion object {
@@ -57,6 +60,14 @@ class LookupElementFactory(
val insertHandlerProvider = basicFactory.insertHandlerProvider val insertHandlerProvider = basicFactory.insertHandlerProvider
private val superFunctions: Set<FunctionDescriptor> by lazy {
inDescriptor.parentsWithSelf
.filterIsInstance<FunctionDescriptor>()
.toList()
.flatMap { it.overriddenDescriptors }
.toSet()
}
public fun createStandardLookupElementsForDescriptor(descriptor: DeclarationDescriptor, useReceiverTypes: Boolean): Collection<LookupElement> { public fun createStandardLookupElementsForDescriptor(descriptor: DeclarationDescriptor, useReceiverTypes: Boolean): Collection<LookupElement> {
val result = SmartList<LookupElement>() val result = SmartList<LookupElement>()
@@ -66,8 +77,13 @@ class LookupElementFactory(
result.add(lookupElement) result.add(lookupElement)
// add special item for function with one argument of function type with more than one parameter // 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) { if (descriptor is FunctionDescriptor && isNormalCall) {
result.addSpecialFunctionCallElements(descriptor, useReceiverTypes) if (callType != CallType.SUPER_MEMBERS) {
result.addSpecialFunctionCallElements(descriptor, useReceiverTypes)
}
else if (useReceiverTypes) {
result.addIfNotNull(createSuperFunctionCallWithArguments(descriptor))
}
} }
return result return result
@@ -94,7 +110,7 @@ class LookupElementFactory(
val fuzzyParameterType = FuzzyType(parameterType, descriptor.typeParameters) val fuzzyParameterType = FuzzyType(parameterType, descriptor.typeParameters)
for ((variable, substitutor) in contextVariablesProvider.functionTypeVariables(fuzzyParameterType)) { for ((variable, substitutor) in contextVariablesProvider.functionTypeVariables(fuzzyParameterType)) {
val substitutedDescriptor = descriptor.substitute(substitutor) 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 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) var lookupElement = createLookupElement(descriptor, useReceiverTypes)
val needTypeArguments = (insertHandlerProvider.insertHandler(descriptor) as KotlinFunctionInsertHandler.Normal).inputTypeArguments 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, originalLookupElement: LookupElement,
private val descriptor: FunctionDescriptor, private val descriptor: FunctionDescriptor,
private val argumentText: String, private val argumentText: String,
private val needTypeArguments: Boolean private val needTypeArguments: Boolean
) : LookupElementDecorator<LookupElement>(originalLookupElement) { ) : LookupElementDecorator<LookupElement>(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 hashCode() = delegate.hashCode() * 17 + argumentText.hashCode()
override fun renderElement(presentation: LookupElementPresentation) { override fun renderElement(presentation: LookupElementPresentation) {
@@ -0,0 +1,18 @@
open class B<T> {
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<String>() {
override fun xxx_foo(p1: String, vararg p2: String) {
super.xxx_<caret>
}
}
// 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
@@ -0,0 +1,17 @@
open class B {
open fun foo() {}
open fun bar() {}
}
class C : B() {
override fun foo() {
super.<caret>
}
}
// 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
@@ -9,3 +9,4 @@ class Derived : Base() {
} }
// ELEMENT: foo // ELEMENT: foo
// TAIL_TEXT: "(p: Int)"
@@ -9,3 +9,4 @@ class Derived : Base() {
} }
// ELEMENT: foo // ELEMENT: foo
// TAIL_TEXT: "(p: Int)"
@@ -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.<caret>
}
}
// ELEMENT: foo
// TAIL_TEXT: "(p1, *p2)"
@@ -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)<caret>
}
}
// ELEMENT: foo
// TAIL_TEXT: "(p1, *p2)"
+16
View File
@@ -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.<caret>
}
}
// 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
@@ -703,6 +703,18 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
doTest(fileName); 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") @TestMetadata("TopLevelClassCompletionInQualifiedCall.kt")
public void testTopLevelClassCompletionInQualifiedCall() throws Exception { public void testTopLevelClassCompletionInQualifiedCall() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/TopLevelClassCompletionInQualifiedCall.kt"); String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/TopLevelClassCompletionInQualifiedCall.kt");
@@ -703,6 +703,18 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
doTest(fileName); 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") @TestMetadata("TopLevelClassCompletionInQualifiedCall.kt")
public void testTopLevelClassCompletionInQualifiedCall() throws Exception { public void testTopLevelClassCompletionInQualifiedCall() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/TopLevelClassCompletionInQualifiedCall.kt"); String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/TopLevelClassCompletionInQualifiedCall.kt");
@@ -485,6 +485,12 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT
doTest(fileName); 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") @TestMetadata("ThisConstructorArgument.kt")
public void testThisConstructorArgument() throws Exception { public void testThisConstructorArgument() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/smart/ThisConstructorArgument.kt"); String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/smart/ThisConstructorArgument.kt");
@@ -149,6 +149,12 @@ public class BasicCompletionHandlerTestGenerated extends AbstractBasicCompletion
doTest(fileName); 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") @TestMetadata("SuperTypeArg.kt")
public void testSuperTypeArg() throws Exception { public void testSuperTypeArg() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/SuperTypeArg.kt"); String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/SuperTypeArg.kt");