Completion sorts items by name similarity in intializer of variable with no explicit type

This commit is contained in:
Valentin Kipyatkov
2015-08-05 18:54:08 +03:00
parent 9cba912fe5
commit 22e79d89f4
8 changed files with 96 additions and 9 deletions
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.completion.smart.toExpressionWithType
import org.jetbrains.kotlin.idea.core.SmartCastCalculator
import org.jetbrains.kotlin.idea.project.ProjectStructureUtil
import org.jetbrains.kotlin.idea.stubindex.PackageIndexUtil
@@ -284,7 +285,8 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration,
}
if (expression != null) {
val expectedInfos = ExpectedInfos(bindingContext, resolutionFacade, moduleDescriptor).calculate(expression)
val expressionWithType = expression.toExpressionWithType()
val expectedInfos = ExpectedInfos(bindingContext, resolutionFacade, moduleDescriptor).calculate(expressionWithType)
if (expectedInfos != null && expectedInfos.isNotEmpty()) {
val smartCastCalculator = SmartCastCalculator(bindingContext, moduleDescriptor, expression)
@@ -71,10 +71,18 @@ data class ItemOptions(val starPrefix: Boolean) {
interface ByTypeFilter {
fun matchingSubstitutor(descriptorType: FuzzyType): TypeSubstitutor?
object All : ByTypeFilter {
override fun matchingSubstitutor(descriptorType: FuzzyType) = TypeSubstitutor.EMPTY
}
}
class ByExpectedTypeFilter(val fuzzyType: FuzzyType) : ByTypeFilter {
override fun matchingSubstitutor(descriptorType: FuzzyType) = descriptorType.checkIsSubtypeOf(fuzzyType)
override fun equals(other: Any?) = other is ByExpectedTypeFilter && fuzzyType == other.fuzzyType
override fun hashCode() = fuzzyType.hashCode()
}
open data class ExpectedInfo(val filter: ByTypeFilter, val expectedName: String?, val tail: Tail?, val itemOptions: ItemOptions = ItemOptions.DEFAULT) {
@@ -107,7 +115,14 @@ class ArgumentExpectedInfo(type: JetType, name: String?, tail: Tail?, val functi
= function.hashCode()
}
class ReturnValueExpectedInfo(type: JetType, val callable: CallableDescriptor) : ExpectedInfo(type, callable.getName().asString(), null) {
class ReturnValueExpectedInfo(
type: JetType?,
val callable: CallableDescriptor
) : ExpectedInfo(
if (type != null) ByExpectedTypeFilter(FuzzyType(type, emptyList())) else ByTypeFilter.All,
callable.name.asString(),
null
) {
override fun equals(other: Any?)
= other is ReturnValueExpectedInfo && super.equals(other) && callable == other.callable
@@ -413,30 +428,39 @@ class ExpectedInfos(
val property = expressionWithType.getParent() as? JetProperty ?: return null
if (expressionWithType != property.getInitializer()) return null
val propertyDescriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, property] as? VariableDescriptor ?: return null
return listOf(ExpectedInfo(propertyDescriptor.getType(), propertyDescriptor.getName().asString(), null))
val expectedName = propertyDescriptor.name.asString()
val expectedInfo = if (property.typeReference != null)
ExpectedInfo(propertyDescriptor.getType(), expectedName, null)
else
ExpectedInfo(ByTypeFilter.All, expectedName, null) // no explicit type - only expected name known
return listOf(expectedInfo)
}
private fun calculateForExpressionBody(expressionWithType: JetExpression): Collection<ExpectedInfo>? {
val declaration = expressionWithType.getParent() as? JetDeclarationWithBody ?: return null
if (expressionWithType != declaration.getBodyExpression() || declaration.hasBlockBody()) return null
val descriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, declaration] as? FunctionDescriptor ?: return null
return functionReturnValueExpectedInfo(descriptor).toList()
return functionReturnValueExpectedInfo(descriptor, expectType = declaration.hasDeclaredReturnType()).toList()
}
private fun calculateForReturn(expressionWithType: JetExpression): Collection<ExpectedInfo>? {
val returnExpression = expressionWithType.getParent() as? JetReturnExpression ?: return null
val descriptor = returnExpression.getTargetFunctionDescriptor(bindingContext) ?: return null
return functionReturnValueExpectedInfo(descriptor).toList()
return functionReturnValueExpectedInfo(descriptor, expectType = true).toList()
}
private fun functionReturnValueExpectedInfo(descriptor: FunctionDescriptor): ReturnValueExpectedInfo? {
private fun functionReturnValueExpectedInfo(descriptor: FunctionDescriptor, expectType: Boolean): ReturnValueExpectedInfo? {
return when (descriptor) {
is SimpleFunctionDescriptor -> ReturnValueExpectedInfo(descriptor.getReturnType() ?: return null, descriptor)
is SimpleFunctionDescriptor -> {
val expectedType = if (expectType) descriptor.returnType else null
ReturnValueExpectedInfo(expectedType, descriptor)
}
is PropertyGetterDescriptor -> {
if (descriptor !is PropertyGetterDescriptor) return null
val property = descriptor.getCorrespondingProperty()
ReturnValueExpectedInfo(property.getType(), property)
val expectedType = if (expectType) property.type else null
ReturnValueExpectedInfo(expectedType, property)
}
else -> null
@@ -184,7 +184,6 @@ class DeclarationRemotenessWeigher(private val file: JetFile) : LookupElementWei
}
}
//TODO: prefer by name too (and only by name too)
class ExpectedInfoMatchWeigher(
private val expectedInfos: Collection<ExpectedInfo>,
private val smartCastCalculator: SmartCastCalculator
@@ -29,6 +29,9 @@ import org.jetbrains.kotlin.idea.completion.handlers.WithExpressionPrefixInsertH
import org.jetbrains.kotlin.idea.completion.handlers.WithTailInsertHandler
import org.jetbrains.kotlin.idea.core.SmartCastCalculator
import org.jetbrains.kotlin.idea.util.*
import org.jetbrains.kotlin.psi.JetExpression
import org.jetbrains.kotlin.psi.JetSimpleNameExpression
import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.types.JetType
@@ -341,3 +344,7 @@ fun DeclarationDescriptor.fuzzyTypesForSmartCompletion(smartCastCalculator: Smar
return emptyList()
}
}
fun JetExpression.toExpressionWithType(): JetExpression {
return (this as? JetSimpleNameExpression)?.getReceiverExpression()?.parent as? JetExpression ?: this
}
@@ -0,0 +1,13 @@
interface I {
fun takeXxx(): Int = 0
fun takeYyy(): String = ""
fun takeZzz(): Int = 0
}
fun foo(i: I): String {
return i.take<caret>
}
// ORDER: takeYyy
// ORDER: takeXxx
// ORDER: takeZzz
@@ -0,0 +1,13 @@
interface I {
fun takeXxx(): Int = 0
fun takeYyy(): Int = 0
fun takeZzz(): Int = 0
}
fun foo(i: I) {
val yyy = i.take<caret>
}
// ORDER: takeYyy
// ORDER: takeXxx
// ORDER: takeZzz
@@ -0,0 +1,11 @@
interface I {
fun takeXxx(): Int = 0
fun takeYyy(): Int = 0
fun takeZzz(): Int = 0
}
fun takeYyy(i: I) = i.take<caret>
// ORDER: takeYyy
// ORDER: takeXxx
// ORDER: takeZzz
@@ -157,12 +157,30 @@ public class BasicCompletionWeigherTestGenerated extends AbstractBasicCompletion
doTest(fileName);
}
@TestMetadata("ExpectedType2.kt")
public void testExpectedType2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/weighers/basic/expectedInfo/ExpectedType2.kt");
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("NameSimilarityAndNoExpectedType.kt")
public void testNameSimilarityAndNoExpectedType() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/weighers/basic/expectedInfo/NameSimilarityAndNoExpectedType.kt");
doTest(fileName);
}
@TestMetadata("NameSimilarityAndNoExpectedType2.kt")
public void testNameSimilarityAndNoExpectedType2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/weighers/basic/expectedInfo/NameSimilarityAndNoExpectedType2.kt");
doTest(fileName);
}
@TestMetadata("PreferMatchingThis.kt")
public void testPreferMatchingThis() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/weighers/basic/expectedInfo/PreferMatchingThis.kt");