Completion: immediate members get higher priority in the list
This commit is contained in:
@@ -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
|
||||
+12
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user