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 {
return LookupElementFactory(basicLookupElementFactory, resolutionFacade, receiverTypes,
callTypeAndReceiver.callType, contextVariablesProvider)
callTypeAndReceiver.callType, inDescriptor, contextVariablesProvider)
}
private fun detectCallTypeAndReceiverTypes(): Pair<CallTypeAndReceiver<*, *>, Collection<KotlinType>?> {
@@ -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 <T : Any> LookupElement.getUserDataDeep(key: Key<T>): T? {
}
enum class ItemPriority {
SUPER_METHOD_WITH_ARGUMENTS,
DEFAULT,
IMPLEMENT,
OVERRIDE,
@@ -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<KotlinType>?,
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<FunctionDescriptor> by lazy {
inDescriptor.parentsWithSelf
.filterIsInstance<FunctionDescriptor>()
.toList()
.flatMap { it.overriddenDescriptors }
.toSet()
}
public fun createStandardLookupElementsForDescriptor(descriptor: DeclarationDescriptor, useReceiverTypes: Boolean): Collection<LookupElement> {
val result = SmartList<LookupElement>()
@@ -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<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 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
@@ -8,4 +8,5 @@ class Derived : Base() {
}
}
// ELEMENT: foo
// ELEMENT: foo
// TAIL_TEXT: "(p: Int)"
@@ -8,4 +8,5 @@ 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);
}
@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");
@@ -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");
@@ -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");
@@ -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");