KT-12798 DSL-friendly completion

#KT-12798 Fixed
This commit is contained in:
Valentin Kipyatkov
2016-07-19 18:09:23 +03:00
parent efac7a11b3
commit a60d9d546d
4 changed files with 118 additions and 27 deletions
@@ -124,7 +124,7 @@ fun LookupElementPresentation.prependTailText(text: String, grayed: Boolean) {
tails.forEach { appendTailText(it.text, it.isGrayed) }
}
enum class CallableWeight {
enum class CallableWeightEnum {
local, // local non-extension
thisClassMember,
baseClassMember,
@@ -135,6 +135,14 @@ enum class CallableWeight {
receiverCastRequired
}
class CallableWeight(val enum: CallableWeightEnum, val receiverIndex: Int?) {
companion object {
val local = CallableWeight(CallableWeightEnum.local, null)
val globalOrStatic = CallableWeight(CallableWeightEnum.globalOrStatic, null)
val receiverCastRequired = CallableWeight(CallableWeightEnum.receiverCastRequired, null)
}
}
val CALLABLE_WEIGHT_KEY = Key<CallableWeight>("CALLABLE_WEIGHT_KEY")
fun InsertionContext.isAfterDot(): Boolean {
@@ -104,7 +104,7 @@ class LookupElementFactory(
}
// special "[]" item for get-operator
if (callType == CallType.DOT && descriptor is FunctionDescriptor && descriptor.isOperator() && descriptor.name == OperatorNameConventions.GET) {
if (callType == CallType.DOT && descriptor is FunctionDescriptor && descriptor.isOperator && descriptor.name == OperatorNameConventions.GET) {
val baseLookupElement = createLookupElement(descriptor, useReceiverTypes)
val lookupElement = object : LookupElementDecorator<LookupElement>(baseLookupElement) {
override fun getLookupString() = "[]"
@@ -261,9 +261,9 @@ class LookupElementFactory(
}
private fun LookupElement.boldIfImmediate(weight: CallableWeight?): LookupElement {
val style = when (weight) {
CallableWeight.thisClassMember, CallableWeight.thisTypeExtension -> Style.BOLD
CallableWeight.receiverCastRequired -> Style.GRAYED
val style = when (weight?.enum) {
CallableWeightEnum.thisClassMember, CallableWeightEnum.thisTypeExtension -> Style.BOLD
CallableWeightEnum.receiverCastRequired -> Style.GRAYED
else -> Style.NORMAL
}
return if (style != Style.NORMAL) {
@@ -310,12 +310,15 @@ class LookupElementFactory(
if (descriptor.overriddenDescriptors.isNotEmpty()) {
// Optimization: when one of direct overridden fits, then nothing can fit better
descriptor.overriddenDescriptors.mapNotNull {
it.callableWeightBasedOnReceiver(receiverTypes, onReceiverTypeMismatch = null)
}.min()?.let { return it }
descriptor.overriddenDescriptors
.mapNotNull { it.callableWeightBasedOnReceiver(receiverTypes, onReceiverTypeMismatch = null) }
.minBy { it.enum }
?.let { return it }
val overridden = descriptor.overriddenTreeUniqueAsSequence(useOriginal = false)
return overridden.map { callableWeightBasic(it, receiverTypes)!! }.min()!!
return overridden
.map { callableWeightBasic(it, receiverTypes)!! }
.minBy { it.enum }!!
}
return callableWeightBasic(descriptor, receiverTypes)
@@ -334,24 +337,38 @@ class LookupElementFactory(
receiverTypes: Collection<KotlinType>,
onReceiverTypeMismatch: CallableWeight?
): CallableWeight? {
val receiverParameter = extensionReceiverParameter ?: dispatchReceiverParameter
if (receiverParameter != null) {
return if (receiverTypes.any { TypeUtils.equalTypes(it, receiverParameter.type) }) {
when {
isExtensionForTypeParameter() -> CallableWeight.typeParameterExtension
isExtension -> CallableWeight.thisTypeExtension
else -> CallableWeight.thisClassMember
}
}
else if (receiverTypes.any { it.isSubtypeOf(receiverParameter.type) }) {
if (isExtension) CallableWeight.baseTypeExtension else CallableWeight.baseClassMember
}
else {
onReceiverTypeMismatch
val receiverParameter = extensionReceiverParameter
?: dispatchReceiverParameter
?: return null
for ((receiverIndex, receiverType) in receiverTypes.withIndex()) {
val weight = callableWeightForReceiverType(receiverType, receiverParameter.type)
if (weight != null) {
// if descriptor is matching all receivers then use null as receiverIndex - otherwise e.g. all members of Any would have too high priority
val receiverIndexToUse = if (receiverIndex == 0 && receiverTypes.drop(1).all { callableWeightForReceiverType(it, receiverParameter.type) != null })
null
else
receiverIndex
return CallableWeight(weight, receiverIndexToUse)
}
}
return onReceiverTypeMismatch
}
return null
private fun CallableDescriptor.callableWeightForReceiverType(receiverType: KotlinType, receiverParameterType: KotlinType): CallableWeightEnum? {
if (TypeUtils.equalTypes(receiverType, receiverParameterType)) {
return when {
isExtensionForTypeParameter() -> CallableWeightEnum.typeParameterExtension
isExtension -> CallableWeightEnum.thisTypeExtension
else -> CallableWeightEnum.thisClassMember
}
}
else if (receiverType.isSubtypeOf(receiverParameterType)) {
return if (isExtension) CallableWeightEnum.baseTypeExtension else CallableWeightEnum.baseClassMember
}
else {
return null
}
}
private fun CallableDescriptor.isExtensionForTypeParameter(): Boolean {
@@ -138,7 +138,55 @@ object KindWeigher : LookupElementWeigher("kotlin.kind") {
}
object CallableWeigher : LookupElementWeigher("kotlin.callableWeight") {
override fun weigh(element: LookupElement) = element.getUserData(CALLABLE_WEIGHT_KEY)
private enum class Weight1 {
local,
memberOrExtension,
globalOrStatic,
typeParameterExtension,
receiverCastRequired
}
private enum class Weight2 {
thisClassMember,
baseClassMember,
thisTypeExtension,
baseTypeExtension,
other
}
private class CompoundWeight(val weight1: Weight1, val receiverIndex: Int, val weight2: Weight2) : Comparable<CompoundWeight> {
override fun compareTo(other: CompoundWeight): Int {
if (weight1 != other.weight1) return weight1.compareTo(other.weight1)
if (receiverIndex != other.receiverIndex) return receiverIndex.compareTo(other.receiverIndex)
return weight2.compareTo(other.weight2)
}
}
override fun weigh(element: LookupElement): Comparable<*>? {
val weight = element.getUserData(CALLABLE_WEIGHT_KEY) ?: return null
val w1 = when (weight.enum) {
CallableWeightEnum.local -> Weight1.local
CallableWeightEnum.thisClassMember,
CallableWeightEnum.baseClassMember,
CallableWeightEnum.thisTypeExtension,
CallableWeightEnum.baseTypeExtension -> Weight1.memberOrExtension
CallableWeightEnum.globalOrStatic -> Weight1.globalOrStatic
CallableWeightEnum.typeParameterExtension -> Weight1.typeParameterExtension
CallableWeightEnum.receiverCastRequired -> Weight1.receiverCastRequired
}
val w2 = when (weight.enum) {
CallableWeightEnum.thisClassMember -> Weight2.thisClassMember
CallableWeightEnum.baseClassMember -> Weight2.baseClassMember
CallableWeightEnum.thisTypeExtension -> Weight2.thisTypeExtension
CallableWeightEnum.baseTypeExtension -> Weight2.baseTypeExtension
else -> Weight2.other
}
return CompoundWeight(w1, weight.receiverIndex ?: Int.MAX_VALUE, w2)
}
}
object VariableOrFunctionWeigher : LookupElementWeigher("kotlin.variableOrFunction"){
+20 -2
View File
@@ -1,3 +1,5 @@
public inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
fun aaGlobalFun(){}
val aaGlobalProp = 1
@@ -10,14 +12,23 @@ class Derived : Base() {
fun aaDerivedFun(){}
val aaDerivedProp = 1
fun foo() {
fun foo(y: Y) {
val aaLocalVal = 1
fun aaLocalFun(){}
aa<caret>
with (y) {
aa<caret>
}
}
}
interface X {
fun aaX()
}
interface Y : X {
fun aaY()
}
fun Any.aaAnyExtensionFun(){}
fun Derived.aaExtensionFun(){}
@@ -26,8 +37,15 @@ val Derived.aaExtensionProp: Int get() = 1
fun <T> T.aaTypeParamExt(){}
fun X.aaXExt(){}
fun Y.aaYExt(){}
// ORDER: aaLocalVal
// ORDER: aaLocalFun
// ORDER: aaY
// ORDER: aaX
// ORDER: aaYExt
// ORDER: aaXExt
// ORDER: aaDerivedProp
// ORDER: aaDerivedFun
// ORDER: aaBaseProp