Completion sorts items by name similarity in intializer of variable with no explicit type
This commit is contained in:
+3
-1
@@ -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
|
||||
Vendored
+13
@@ -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
|
||||
Vendored
+11
@@ -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
|
||||
+18
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user