diff --git a/idea/src/org/jetbrains/jet/plugin/completion/CompletionSorting.kt b/idea/src/org/jetbrains/jet/plugin/completion/CompletionSorting.kt index f0421d83a7c..d15652ce419 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/CompletionSorting.kt +++ b/idea/src/org/jetbrains/jet/plugin/completion/CompletionSorting.kt @@ -26,7 +26,6 @@ import com.intellij.codeInsight.lookup.WeighingContext import org.jetbrains.jet.plugin.completion.* import org.jetbrains.jet.lang.descriptors.impl.LocalVariableDescriptor import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor -import org.jetbrains.jet.lang.descriptors.PropertyDescriptor import org.jetbrains.jet.lang.descriptors.PackageViewDescriptor import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns import org.jetbrains.jet.lang.resolve.DescriptorUtils @@ -41,6 +40,9 @@ import java.util.HashSet import org.jetbrains.jet.lang.resolve.name.FqName import org.jetbrains.jet.lang.resolve.java.mapping.JavaToKotlinClassMap import org.jetbrains.jet.plugin.quickfix.ImportInsertHelper +import org.jetbrains.jet.lang.descriptors.PropertyDescriptor +import org.jetbrains.jet.lang.descriptors.FunctionDescriptor +import org.jetbrains.jet.lang.types.JetType public fun CompletionResultSet.addKotlinSorting(parameters: CompletionParameters): CompletionResultSet { var sorter = CompletionSorter.defaultSorter(parameters, getPrefixMatcher())!! @@ -53,7 +55,7 @@ public fun CompletionResultSet.addKotlinSorting(parameters: CompletionParameters sorter = sorter.weighAfter( "stats", - JetDeclarationRemotenessWeigher(parameters.getOriginalFile() as JetFile)/*TODO: shouldn't it have bigger priority?*/, + JetDeclarationRemotenessWeigher(parameters.getOriginalFile() as JetFile), DeprecatedWeigher) sorter = sorter.weighBefore("middleMatching", PreferMatchingItemWeigher) @@ -75,24 +77,39 @@ private object KindWeigher : LookupElementWeigher("kotlin.kind") { private enum class Weight { localOrParameter property + function keyword default packages } - override fun weigh(element: LookupElement): Comparable { + private data class CompoundWeight(val weight: Weight, val callableWeight: CallableWeight? = null) : Comparable { + override fun compareTo(other: CompoundWeight): Int { + if (callableWeight != null && other.callableWeight != null && callableWeight != other.callableWeight) { + return callableWeight.compareTo(other.callableWeight) + } + return weight.compareTo(other.weight) + } + } + + override fun weigh(element: LookupElement): CompoundWeight { val o = element.getObject() + return when (o) { - is DeclarationDescriptorLookupObject -> when (o.descriptor) { - is LocalVariableDescriptor, is ValueParameterDescriptor -> Weight.localOrParameter - is PropertyDescriptor -> Weight.property - is PackageViewDescriptor -> Weight.packages - else -> Weight.default + is DeclarationDescriptorLookupObject -> { + val descriptor = o.descriptor + when (descriptor) { + is LocalVariableDescriptor, is ValueParameterDescriptor -> CompoundWeight(Weight.localOrParameter) + is PropertyDescriptor -> CompoundWeight(Weight.property, element.getUserData(CALLABLE_WEIGHT_KEY)) + is FunctionDescriptor -> CompoundWeight(Weight.function, element.getUserData(CALLABLE_WEIGHT_KEY)) + is PackageViewDescriptor -> CompoundWeight(Weight.packages) + else -> CompoundWeight(Weight.default) + } } - is KeywordLookupObject -> Weight.keyword + is KeywordLookupObject -> CompoundWeight(Weight.keyword) - else -> Weight.default + else -> CompoundWeight(Weight.default) } } } @@ -125,7 +142,7 @@ private class JetDeclarationRemotenessWeigher(private val file: JetFile) : Looku notToBeUsedInKotlin } - override fun weigh(element: LookupElement): Comparable { + override fun weigh(element: LookupElement): Weight { val o = element.getObject() if (o is DeclarationDescriptorLookupObject) { val elementFile = o.psiElement?.getContainingFile() diff --git a/idea/src/org/jetbrains/jet/plugin/completion/CompletionUtils.kt b/idea/src/org/jetbrains/jet/plugin/completion/CompletionUtils.kt index b051eae8e16..8ea63e4bb17 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/CompletionUtils.kt +++ b/idea/src/org/jetbrains/jet/plugin/completion/CompletionUtils.kt @@ -100,4 +100,16 @@ fun LookupElementPresentation.prependTailText(text: String, grayed: Boolean) { clearTail() appendTailText(text, grayed) tails.forEach { appendTailText(it.text, it.isGrayed()) } -} \ No newline at end of file +} + +enum class CallableWeight { + local // local non-extension + thisClassMember + baseClassMember + thisTypeExtension + baseTypeExtension + global // global non-extension + notApplicableReceiverNullable +} + +val CALLABLE_WEIGHT_KEY = Key("CALLABLE_WEIGHT_KEY") diff --git a/idea/src/org/jetbrains/jet/plugin/completion/LookupElementFactory.kt b/idea/src/org/jetbrains/jet/plugin/completion/LookupElementFactory.kt index 972b74ab681..d1f1b1e6cc9 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/LookupElementFactory.kt +++ b/idea/src/org/jetbrains/jet/plugin/completion/LookupElementFactory.kt @@ -36,8 +36,8 @@ import com.intellij.codeInsight.lookup.LookupElementPresentation import org.jetbrains.jet.lang.types.JetType import org.jetbrains.jet.plugin.caches.resolve.ResolutionFacade import java.awt.Color -import org.jetbrains.jet.lang.types.TypeUtils import com.intellij.codeInsight.lookup.DefaultLookupItemRenderer +import org.jetbrains.jet.lang.types.TypeUtils public class LookupElementFactory( private val receiverTypes: Collection? @@ -59,30 +59,16 @@ public class LookupElementFactory( } private fun LookupElement.boldIfImmediate(descriptor: DeclarationDescriptor): LookupElement { - if (receiverTypes == null) return this - if (descriptor !is CallableMemberDescriptor) return this - - val isReceiverNullable = receiverTypes.all { it.isNullable() } - val receiverParameter = descriptor.getExtensionReceiverParameter() - - val style: Style = if (receiverParameter != null) { - val receiverParamType = receiverParameter.getType() - if (isReceiverNullable && !receiverParamType.isNullable()) - Style.GRAYED - else if (receiverTypes.any { TypeUtils.equalTypes(it, receiverParamType) }) - Style.BOLD - else - Style.NORMAL - } - else { - if (isReceiverNullable) - Style.GRAYED - else if (descriptor.getContainingDeclaration() is ClassifierDescriptor && descriptor.getKind() == CallableMemberDescriptor.Kind.DECLARATION) - Style.BOLD - else - Style.NORMAL + val weight = callableWeight(descriptor) + if (weight != null) { + putUserData(CALLABLE_WEIGHT_KEY, weight) // store for use in lookup elements sorting } + val style = when (weight) { + CallableWeight.thisClassMember, CallableWeight.thisTypeExtension -> Style.BOLD + CallableWeight.notApplicableReceiverNullable -> Style.GRAYED + else -> Style.NORMAL + } return if (style != Style.NORMAL) { object : LookupElementDecorator(this) { override fun renderElement(presentation: LookupElementPresentation) { @@ -217,6 +203,44 @@ public class LookupElementFactory( return element } + private fun callableWeight(descriptor: DeclarationDescriptor): CallableWeight? { + if (receiverTypes == null) return null + if (descriptor !is CallableMemberDescriptor) return null + + val isReceiverNullable = receiverTypes.all { it.isNullable() } + val receiverParameter = descriptor.getExtensionReceiverParameter() + + if (receiverParameter != null) { + val receiverParamType = receiverParameter.getType() + return if (isReceiverNullable && !receiverParamType.isNullable()) + CallableWeight.notApplicableReceiverNullable + else if (receiverTypes.any { TypeUtils.equalTypes(it, receiverParamType) }) + CallableWeight.thisTypeExtension + else + CallableWeight.baseTypeExtension + } + else { + if (isReceiverNullable) { + return CallableWeight.notApplicableReceiverNullable + } + else { + val container = descriptor.getContainingDeclaration() + return when (container) { + is PackageFragmentDescriptor -> CallableWeight.global + + is ClassifierDescriptor -> { + if (descriptor.getKind() == CallableMemberDescriptor.Kind.DECLARATION) + CallableWeight.thisClassMember + else + CallableWeight.baseClassMember + } + + else -> CallableWeight.local + } + } + } + } + class object { public fun getDefaultInsertHandler(descriptor: DeclarationDescriptor): InsertHandler { return when (descriptor) { diff --git a/idea/testData/completion/weighers/basic/AfterNullable.kt b/idea/testData/completion/weighers/basic/AfterNullable.kt new file mode 100644 index 00000000000..c937227af80 --- /dev/null +++ b/idea/testData/completion/weighers/basic/AfterNullable.kt @@ -0,0 +1,11 @@ +fun String?.forNullableString(){} +fun Any?.forNullableAny(){} +fun String.forString(){} + +fun foo(s: String?) { + s.for +} + +// ORDER: forNullableString +// ORDER: forNullableAny +// ORDER: forString diff --git a/idea/testData/completion/weighers/basic/Callables.kt b/idea/testData/completion/weighers/basic/Callables.kt new file mode 100644 index 00000000000..c925c28d9d2 --- /dev/null +++ b/idea/testData/completion/weighers/basic/Callables.kt @@ -0,0 +1,38 @@ +fun aaGlobalFun(){} +val aaGlobalProp = 1 + +open class Base { + fun aaBaseFun(){} + val aaBaseProp = 1 +} + +class Derived : Base() { + fun aaDerivedFun(){} + val aaDerivedProp = 1 + + fun foo() { + val aaLocalVal = 1 + fun aaLocalFun(){} + + aa + } +} + +fun Any.aAnyExtensionFun(){} +fun Derived.aaExtensionFun(){} + +val Any.aAnyExtensionProp: Int get() = 1 +val Derived.aaExtensionProp: Int get() = 1 + +// ORDER: aaLocalVal +// ORDER: aaLocalFun +// ORDER: aaDerivedProp +// ORDER: aaDerivedFun +// ORDER: aaBaseProp +// ORDER: aaBaseFun +// ORDER: aaExtensionProp +// ORDER: aaExtensionFun +// ORDER: aAnyExtensionProp +// ORDER: aAnyExtensionFun +// ORDER: aaGlobalProp +// ORDER: aaGlobalFun diff --git a/idea/testData/completion/weighers/basic/PropertiesBeforeKeywords.kt b/idea/testData/completion/weighers/basic/PropertiesBeforeKeywords.kt index 24b5f080eb1..3e11bb3db2d 100644 --- a/idea/testData/completion/weighers/basic/PropertiesBeforeKeywords.kt +++ b/idea/testData/completion/weighers/basic/PropertiesBeforeKeywords.kt @@ -15,5 +15,5 @@ class Test(val br2 = 12) { // "br" function is before other elements because of exact prefix match -// ORDER: br, br4, br5, br1, br2, br3, break, brf +// ORDER: br, br4, br5, br2, br3, brf, br1, break // SELECTED: 0 \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/completion/weighers/BasicCompletionWeigherTestGenerated.java b/idea/tests/org/jetbrains/jet/completion/weighers/BasicCompletionWeigherTestGenerated.java index a34ea2a535d..0059def5664 100644 --- a/idea/tests/org/jetbrains/jet/completion/weighers/BasicCompletionWeigherTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/completion/weighers/BasicCompletionWeigherTestGenerated.java @@ -31,10 +31,22 @@ import java.util.regex.Pattern; @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) public class BasicCompletionWeigherTestGenerated extends AbstractBasicCompletionWeigherTest { + @TestMetadata("AfterNullable.kt") + public void testAfterNullable() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/weighers/basic/AfterNullable.kt"); + doTest(fileName); + } + public void testAllFilesPresentInBasic() throws Exception { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/completion/weighers/basic"), Pattern.compile("^([^\\.]+)\\.kt$"), true); } + @TestMetadata("Callables.kt") + public void testCallables() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/weighers/basic/Callables.kt"); + doTest(fileName); + } + @TestMetadata("DeprecatedFun.kt") public void testDeprecatedFun() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/weighers/basic/DeprecatedFun.kt");