Ordinary completion prefers item matching expected name
This commit is contained in:
@@ -21,20 +21,15 @@ import com.intellij.codeInsight.lookup.LookupElementWeigher
|
||||
import com.intellij.codeInsight.lookup.WeighingContext
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||
import org.jetbrains.kotlin.idea.completion.smart.SMART_COMPLETION_ITEM_PRIORITY_KEY
|
||||
import org.jetbrains.kotlin.idea.completion.smart.SmartCompletionItemPriority
|
||||
import org.jetbrains.kotlin.idea.completion.smart.fuzzyTypesForSmartCompletion
|
||||
import org.jetbrains.kotlin.idea.completion.smart.*
|
||||
import org.jetbrains.kotlin.idea.core.SmartCastCalculator
|
||||
import org.jetbrains.kotlin.idea.core.completion.DeclarationLookupObject
|
||||
import org.jetbrains.kotlin.idea.util.FuzzyType
|
||||
import org.jetbrains.kotlin.idea.util.ImportInsertHelper
|
||||
import org.jetbrains.kotlin.idea.util.makeNotNullable
|
||||
import org.jetbrains.kotlin.idea.util.nullability
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
|
||||
import org.jetbrains.kotlin.psi.JetFile
|
||||
import org.jetbrains.kotlin.resolve.ImportPath
|
||||
import org.jetbrains.kotlin.types.typeUtil.TypeNullability
|
||||
import java.util.HashSet
|
||||
|
||||
object PriorityWeigher : LookupElementWeigher("kotlin.priority") {
|
||||
@@ -194,30 +189,47 @@ class ExpectedInfoMatchWeigher(
|
||||
private val expectedInfos: Collection<ExpectedInfo>,
|
||||
private val smartCastCalculator: SmartCastCalculator
|
||||
) : LookupElementWeigher("kotlin.expectedInfoMatch") {
|
||||
private enum class Weight {
|
||||
matches,
|
||||
matchesIfNotNull,
|
||||
doesNotMatch
|
||||
|
||||
private val NO_MATCH = 0L
|
||||
|
||||
private fun fullMatch(nameSimilarity: Int): Long {
|
||||
return -((2L shl 32) + nameSimilarity)
|
||||
}
|
||||
|
||||
override fun weigh(element: LookupElement): Weight {
|
||||
private fun ifNotNullMatch(nameSimilarity: Int): Long {
|
||||
return -((1L shl 32) + nameSimilarity)
|
||||
}
|
||||
|
||||
override fun weigh(element: LookupElement): Long {
|
||||
// TODO: keywords with type
|
||||
val o = element.`object`
|
||||
val fuzzyTypes = when (o) {
|
||||
val (fuzzyTypes, name) = when (o) {
|
||||
is DeclarationLookupObject -> {
|
||||
val descriptor = o.descriptor ?: return Weight.doesNotMatch
|
||||
descriptor.fuzzyTypesForSmartCompletion(smartCastCalculator)
|
||||
val descriptor = o.descriptor ?: return NO_MATCH
|
||||
descriptor.fuzzyTypesForSmartCompletion(smartCastCalculator) to descriptor.name
|
||||
}
|
||||
|
||||
is ThisItemLookupObject -> smartCastCalculator.types(o.receiverParameter).map { FuzzyType(it, emptyList()) }
|
||||
is ThisItemLookupObject -> smartCastCalculator.types(o.receiverParameter).map { FuzzyType(it, emptyList()) } to null
|
||||
|
||||
else -> return Weight.doesNotMatch
|
||||
else -> return NO_MATCH
|
||||
}
|
||||
|
||||
return when {
|
||||
fuzzyTypes.any { type -> expectedInfos.any { it.matchingSubstitutor(type) != null } } -> Weight.matches
|
||||
fuzzyTypes.any { type -> type.nullability() == TypeNullability.NULLABLE && expectedInfos.any { it.matchingSubstitutor(type.makeNotNullable()) != null } } -> Weight.matchesIfNotNull
|
||||
else -> Weight.doesNotMatch
|
||||
if (fuzzyTypes.isEmpty()) return NO_MATCH
|
||||
|
||||
val classified: Collection<Pair<ExpectedInfo, ExpectedInfoClassification>> = expectedInfos.map { it to fuzzyTypes.classifyExpectedInfo(it) }
|
||||
if (classified.all { it.second == ExpectedInfoClassification.noMatch }) return NO_MATCH
|
||||
|
||||
val nameSimilarity = if (name != null) {
|
||||
val matchingInfos = classified.filter { it.second != ExpectedInfoClassification.noMatch }.map { it.first }
|
||||
calcNameSimilarity(name.asString(), matchingInfos)
|
||||
}
|
||||
else {
|
||||
0
|
||||
}
|
||||
|
||||
return if (classified.any { it.second.isMatch() })
|
||||
fullMatch(nameSimilarity)
|
||||
else
|
||||
ifNotNullMatch(nameSimilarity)
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -54,7 +54,7 @@ object KeywordValues {
|
||||
|
||||
if (!skipTrueFalse) {
|
||||
val booleanInfoClassifier = { info: ExpectedInfo ->
|
||||
if (info.fuzzyType?.type == KotlinBuiltIns.getInstance().getBooleanType()) ExpectedInfoClassification.matches(TypeSubstitutor.EMPTY) else ExpectedInfoClassification.notMatches
|
||||
if (info.fuzzyType?.type == KotlinBuiltIns.getInstance().getBooleanType()) ExpectedInfoClassification.match(TypeSubstitutor.EMPTY) else ExpectedInfoClassification.noMatch
|
||||
}
|
||||
collection.addLookupElements(null, expectedInfos, booleanInfoClassifier) { LookupElementBuilder.create("true").bold().assignSmartCompletionPriority(SmartCompletionItemPriority.TRUE) }
|
||||
collection.addLookupElements(null, expectedInfos, booleanInfoClassifier) { LookupElementBuilder.create("false").bold().assignSmartCompletionPriority(SmartCompletionItemPriority.FALSE) }
|
||||
@@ -62,9 +62,9 @@ object KeywordValues {
|
||||
|
||||
val classifier = { info: ExpectedInfo ->
|
||||
if (info.fuzzyType != null && info.fuzzyType!!.type.isMarkedNullable())
|
||||
ExpectedInfoClassification.matches(TypeSubstitutor.EMPTY)
|
||||
ExpectedInfoClassification.match(TypeSubstitutor.EMPTY)
|
||||
else
|
||||
ExpectedInfoClassification.notMatches
|
||||
ExpectedInfoClassification.noMatch
|
||||
}
|
||||
collection.addLookupElements(null, expectedInfos, classifier) {
|
||||
LookupElementBuilder.create("null").bold().assignSmartCompletionPriority(SmartCompletionItemPriority.NULL)
|
||||
|
||||
+1
-1
@@ -75,7 +75,7 @@ class StaticMembers(
|
||||
classifier = { expectedInfo -> returnType.classifyExpectedInfo(expectedInfo) }
|
||||
}
|
||||
else if (DescriptorUtils.isEnumEntry(descriptor) && !enumEntriesToSkip.contains(descriptor)) {
|
||||
classifier = { ExpectedInfoClassification.matches(TypeSubstitutor.EMPTY) } /* we do not need to check type of enum entry because it's taken from proper enum */
|
||||
classifier = { ExpectedInfoClassification.match(TypeSubstitutor.EMPTY) } /* we do not need to check type of enum entry because it's taken from proper enum */
|
||||
}
|
||||
else {
|
||||
return
|
||||
|
||||
@@ -124,10 +124,12 @@ private constructor(
|
||||
val substitutor: TypeSubstitutor?,
|
||||
val makeNotNullable: Boolean
|
||||
) {
|
||||
fun isMatch() = substitutor != null && !makeNotNullable
|
||||
|
||||
companion object {
|
||||
val notMatches = ExpectedInfoClassification(null, false)
|
||||
fun matches(substitutor: TypeSubstitutor) = ExpectedInfoClassification(substitutor, false)
|
||||
fun matchesIfNotNullable(substitutor: TypeSubstitutor) = ExpectedInfoClassification(substitutor, true)
|
||||
val noMatch = ExpectedInfoClassification(null, false)
|
||||
fun match(substitutor: TypeSubstitutor) = ExpectedInfoClassification(substitutor, false)
|
||||
fun ifNotNullMatch(substitutor: TypeSubstitutor) = ExpectedInfoClassification(substitutor, true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,17 +137,17 @@ fun Collection<FuzzyType>.classifyExpectedInfo(expectedInfo: ExpectedInfo): Expe
|
||||
val sequence = asSequence()
|
||||
val substitutor = sequence.map { expectedInfo.matchingSubstitutor(it) }.firstOrNull()
|
||||
if (substitutor != null) {
|
||||
return ExpectedInfoClassification.matches(substitutor)
|
||||
return ExpectedInfoClassification.match(substitutor)
|
||||
}
|
||||
|
||||
if (sequence.any { it.nullability() == TypeNullability.NULLABLE }) {
|
||||
val substitutor2 = sequence.map { expectedInfo.matchingSubstitutor(it.makeNotNullable()) }.firstOrNull()
|
||||
if (substitutor2 != null) {
|
||||
return ExpectedInfoClassification.matchesIfNotNullable(substitutor2)
|
||||
return ExpectedInfoClassification.ifNotNullMatch(substitutor2)
|
||||
}
|
||||
}
|
||||
|
||||
return ExpectedInfoClassification.notMatches
|
||||
return ExpectedInfoClassification.noMatch
|
||||
}
|
||||
|
||||
fun FuzzyType.classifyExpectedInfo(expectedInfo: ExpectedInfo) = listOf(this).classifyExpectedInfo(expectedInfo)
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
fun foo(xxx1: String?, xxx2: String, xxx3: Any, xp: String?) {
|
||||
bar(x<caret>)
|
||||
}
|
||||
|
||||
fun bar(pXxx: String){}
|
||||
|
||||
// ORDER: xxx2
|
||||
// ORDER: xxx1
|
||||
// ORDER: xp
|
||||
// ORDER: xxx3
|
||||
+6
@@ -157,6 +157,12 @@ public class BasicCompletionWeigherTestGenerated extends AbstractBasicCompletion
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("NameSimilarity.kt")
|
||||
public void testNameSimilarity() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/weighers/basic/expectedInfo/NameSimilarity.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("PreferMatchingThis.kt")
|
||||
public void testPreferMatchingThis() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/weighers/basic/expectedInfo/PreferMatchingThis.kt");
|
||||
|
||||
Reference in New Issue
Block a user