Completion: immediate members get higher priority in the list

This commit is contained in:
Valentin Kipyatkov
2014-12-01 22:35:19 +03:00
parent 72ff719001
commit cf35fc1fd5
7 changed files with 150 additions and 36 deletions
@@ -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<Weight> {
private data class CompoundWeight(val weight: Weight, val callableWeight: CallableWeight? = null) : Comparable<CompoundWeight> {
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<Weight> {
override fun weigh(element: LookupElement): Weight {
val o = element.getObject()
if (o is DeclarationDescriptorLookupObject) {
val elementFile = o.psiElement?.getContainingFile()
@@ -100,4 +100,16 @@ fun LookupElementPresentation.prependTailText(text: String, grayed: Boolean) {
clearTail()
appendTailText(text, grayed)
tails.forEach { appendTailText(it.text, it.isGrayed()) }
}
}
enum class CallableWeight {
local // local non-extension
thisClassMember
baseClassMember
thisTypeExtension
baseTypeExtension
global // global non-extension
notApplicableReceiverNullable
}
val CALLABLE_WEIGHT_KEY = Key<CallableWeight>("CALLABLE_WEIGHT_KEY")
@@ -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<JetType>?
@@ -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<LookupElement>(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<LookupElement> {
return when (descriptor) {
@@ -0,0 +1,11 @@
fun String?.forNullableString(){}
fun Any?.forNullableAny(){}
fun String.forString(){}
fun foo(s: String?) {
s.for<caret>
}
// ORDER: forNullableString
// ORDER: forNullableAny
// ORDER: forString
@@ -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<caret>
}
}
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
@@ -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
@@ -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");