Ordinary completion prefers item matching expected name

This commit is contained in:
Valentin Kipyatkov
2015-08-05 17:51:15 +03:00
parent 07311234db
commit 9cba912fe5
6 changed files with 60 additions and 30 deletions
@@ -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)
}
}
@@ -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)
@@ -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
@@ -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");