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.ClassKind
|
||||||
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
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.core.SmartCastCalculator
|
||||||
import org.jetbrains.kotlin.idea.project.ProjectStructureUtil
|
import org.jetbrains.kotlin.idea.project.ProjectStructureUtil
|
||||||
import org.jetbrains.kotlin.idea.stubindex.PackageIndexUtil
|
import org.jetbrains.kotlin.idea.stubindex.PackageIndexUtil
|
||||||
@@ -284,7 +285,8 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (expression != null) {
|
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()) {
|
if (expectedInfos != null && expectedInfos.isNotEmpty()) {
|
||||||
val smartCastCalculator = SmartCastCalculator(bindingContext, moduleDescriptor, expression)
|
val smartCastCalculator = SmartCastCalculator(bindingContext, moduleDescriptor, expression)
|
||||||
|
|||||||
@@ -71,10 +71,18 @@ data class ItemOptions(val starPrefix: Boolean) {
|
|||||||
|
|
||||||
interface ByTypeFilter {
|
interface ByTypeFilter {
|
||||||
fun matchingSubstitutor(descriptorType: FuzzyType): TypeSubstitutor?
|
fun matchingSubstitutor(descriptorType: FuzzyType): TypeSubstitutor?
|
||||||
|
|
||||||
|
object All : ByTypeFilter {
|
||||||
|
override fun matchingSubstitutor(descriptorType: FuzzyType) = TypeSubstitutor.EMPTY
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ByExpectedTypeFilter(val fuzzyType: FuzzyType) : ByTypeFilter {
|
class ByExpectedTypeFilter(val fuzzyType: FuzzyType) : ByTypeFilter {
|
||||||
override fun matchingSubstitutor(descriptorType: FuzzyType) = descriptorType.checkIsSubtypeOf(fuzzyType)
|
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) {
|
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()
|
= 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?)
|
override fun equals(other: Any?)
|
||||||
= other is ReturnValueExpectedInfo && super.equals(other) && callable == other.callable
|
= other is ReturnValueExpectedInfo && super.equals(other) && callable == other.callable
|
||||||
|
|
||||||
@@ -413,30 +428,39 @@ class ExpectedInfos(
|
|||||||
val property = expressionWithType.getParent() as? JetProperty ?: return null
|
val property = expressionWithType.getParent() as? JetProperty ?: return null
|
||||||
if (expressionWithType != property.getInitializer()) return null
|
if (expressionWithType != property.getInitializer()) return null
|
||||||
val propertyDescriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, property] as? VariableDescriptor ?: 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>? {
|
private fun calculateForExpressionBody(expressionWithType: JetExpression): Collection<ExpectedInfo>? {
|
||||||
val declaration = expressionWithType.getParent() as? JetDeclarationWithBody ?: return null
|
val declaration = expressionWithType.getParent() as? JetDeclarationWithBody ?: return null
|
||||||
if (expressionWithType != declaration.getBodyExpression() || declaration.hasBlockBody()) return null
|
if (expressionWithType != declaration.getBodyExpression() || declaration.hasBlockBody()) return null
|
||||||
val descriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, declaration] as? FunctionDescriptor ?: 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>? {
|
private fun calculateForReturn(expressionWithType: JetExpression): Collection<ExpectedInfo>? {
|
||||||
val returnExpression = expressionWithType.getParent() as? JetReturnExpression ?: return null
|
val returnExpression = expressionWithType.getParent() as? JetReturnExpression ?: return null
|
||||||
val descriptor = returnExpression.getTargetFunctionDescriptor(bindingContext) ?: 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) {
|
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 -> {
|
is PropertyGetterDescriptor -> {
|
||||||
if (descriptor !is PropertyGetterDescriptor) return null
|
if (descriptor !is PropertyGetterDescriptor) return null
|
||||||
val property = descriptor.getCorrespondingProperty()
|
val property = descriptor.getCorrespondingProperty()
|
||||||
ReturnValueExpectedInfo(property.getType(), property)
|
val expectedType = if (expectType) property.type else null
|
||||||
|
ReturnValueExpectedInfo(expectedType, property)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> null
|
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(
|
class ExpectedInfoMatchWeigher(
|
||||||
private val expectedInfos: Collection<ExpectedInfo>,
|
private val expectedInfos: Collection<ExpectedInfo>,
|
||||||
private val smartCastCalculator: SmartCastCalculator
|
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.completion.handlers.WithTailInsertHandler
|
||||||
import org.jetbrains.kotlin.idea.core.SmartCastCalculator
|
import org.jetbrains.kotlin.idea.core.SmartCastCalculator
|
||||||
import org.jetbrains.kotlin.idea.util.*
|
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.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||||
import org.jetbrains.kotlin.types.JetType
|
import org.jetbrains.kotlin.types.JetType
|
||||||
@@ -341,3 +344,7 @@ fun DeclarationDescriptor.fuzzyTypesForSmartCompletion(smartCastCalculator: Smar
|
|||||||
return emptyList()
|
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);
|
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")
|
@TestMetadata("NameSimilarity.kt")
|
||||||
public void testNameSimilarity() throws Exception {
|
public void testNameSimilarity() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/weighers/basic/expectedInfo/NameSimilarity.kt");
|
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/weighers/basic/expectedInfo/NameSimilarity.kt");
|
||||||
doTest(fileName);
|
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")
|
@TestMetadata("PreferMatchingThis.kt")
|
||||||
public void testPreferMatchingThis() throws Exception {
|
public void testPreferMatchingThis() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/weighers/basic/expectedInfo/PreferMatchingThis.kt");
|
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/weighers/basic/expectedInfo/PreferMatchingThis.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user