From 22e79d89f4b8a125d32f6425aea97f60f9fa2db5 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Wed, 5 Aug 2015 18:54:08 +0300 Subject: [PATCH] Completion sorts items by name similarity in intializer of variable with no explicit type --- .../idea/completion/BasicCompletionSession.kt | 4 +- .../kotlin/idea/completion/ExpectedInfos.kt | 38 +++++++++++++++---- .../kotlin/idea/completion/Weighers.kt | 1 - .../kotlin/idea/completion/smart/Utils.kt | 7 ++++ .../basic/expectedInfo/ExpectedType2.kt | 13 +++++++ .../NameSimilarityAndNoExpectedType.kt | 13 +++++++ .../NameSimilarityAndNoExpectedType2.kt | 11 ++++++ .../BasicCompletionWeigherTestGenerated.java | 18 +++++++++ 8 files changed, 96 insertions(+), 9 deletions(-) create mode 100644 idea/idea-completion/testData/weighers/basic/expectedInfo/ExpectedType2.kt create mode 100644 idea/idea-completion/testData/weighers/basic/expectedInfo/NameSimilarityAndNoExpectedType.kt create mode 100644 idea/idea-completion/testData/weighers/basic/expectedInfo/NameSimilarityAndNoExpectedType2.kt diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/BasicCompletionSession.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/BasicCompletionSession.kt index 33e3cc1a1f7..0da092e3383 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/BasicCompletionSession.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/BasicCompletionSession.kt @@ -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) diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ExpectedInfos.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ExpectedInfos.kt index 450df1007b5..958ff7a157e 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ExpectedInfos.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ExpectedInfos.kt @@ -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? { 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? { 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 diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/Weighers.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/Weighers.kt index 5d5693a1397..0397ef7b501 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/Weighers.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/Weighers.kt @@ -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, private val smartCastCalculator: SmartCastCalculator diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt index d6f9f6b813a..01907d1e0c6 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt @@ -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 +} \ No newline at end of file diff --git a/idea/idea-completion/testData/weighers/basic/expectedInfo/ExpectedType2.kt b/idea/idea-completion/testData/weighers/basic/expectedInfo/ExpectedType2.kt new file mode 100644 index 00000000000..0c98ee4ec12 --- /dev/null +++ b/idea/idea-completion/testData/weighers/basic/expectedInfo/ExpectedType2.kt @@ -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 +} + +// ORDER: takeYyy +// ORDER: takeXxx +// ORDER: takeZzz diff --git a/idea/idea-completion/testData/weighers/basic/expectedInfo/NameSimilarityAndNoExpectedType.kt b/idea/idea-completion/testData/weighers/basic/expectedInfo/NameSimilarityAndNoExpectedType.kt new file mode 100644 index 00000000000..d60bbeaae13 --- /dev/null +++ b/idea/idea-completion/testData/weighers/basic/expectedInfo/NameSimilarityAndNoExpectedType.kt @@ -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 +} + +// ORDER: takeYyy +// ORDER: takeXxx +// ORDER: takeZzz diff --git a/idea/idea-completion/testData/weighers/basic/expectedInfo/NameSimilarityAndNoExpectedType2.kt b/idea/idea-completion/testData/weighers/basic/expectedInfo/NameSimilarityAndNoExpectedType2.kt new file mode 100644 index 00000000000..0250398f78a --- /dev/null +++ b/idea/idea-completion/testData/weighers/basic/expectedInfo/NameSimilarityAndNoExpectedType2.kt @@ -0,0 +1,11 @@ +interface I { + fun takeXxx(): Int = 0 + fun takeYyy(): Int = 0 + fun takeZzz(): Int = 0 +} + +fun takeYyy(i: I) = i.take + +// ORDER: takeYyy +// ORDER: takeXxx +// ORDER: takeZzz diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/weighers/BasicCompletionWeigherTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/weighers/BasicCompletionWeigherTestGenerated.java index 5e17ef86d42..bf3123079fa 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/weighers/BasicCompletionWeigherTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/weighers/BasicCompletionWeigherTestGenerated.java @@ -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");