Experimental feature to show properties in completion after prefix with "get" or "set"

This commit is contained in:
Valentin Kipyatkov
2015-07-28 18:22:24 +03:00
parent 23dca520d5
commit a4c9aa43a5
8 changed files with 93 additions and 27 deletions
@@ -42,14 +42,17 @@ import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.util.CallType
import org.jetbrains.kotlin.idea.util.ShadowedDeclarationsFilter
import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import org.jetbrains.kotlin.psi.psiUtil.prevLeaf
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo
import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastUtils
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
import org.jetbrains.kotlin.util.capitalizeDecapitalize.decapitalizeSmart
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
class CompletionSessionConfiguration(
@@ -120,6 +123,18 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
protected val prefixMatcher: PrefixMatcher = CamelHumpMatcher(prefix)
protected val descriptorNameFilter: (Name) -> Boolean = run {
val nameFilter = prefixMatcher.asNameFilter()
val getOrSetPrefix = listOf("get", "set").firstOrNull { prefix.startsWith(it) }
if (getOrSetPrefix != null)
nameFilter or prefixMatcher.cloneWithPrefix(prefix.removePrefix(getOrSetPrefix).decapitalizeSmart()).asNameFilter()
else
nameFilter
}
private fun ((Name) -> Boolean).or(otherFilter: (Name) -> Boolean): (Name) -> Boolean
= { this(it) || otherFilter(it) }
protected val isVisibleFilter: (DeclarationDescriptor) -> Boolean = { isVisibleDescriptor(it) }
protected val referenceVariantsHelper: ReferenceVariantsHelper = ReferenceVariantsHelper(bindingContext, moduleDescriptor, project, isVisibleFilter)
@@ -207,7 +222,7 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
referenceVariantsHelper.getReferenceVariants(
nameExpression!!,
descriptorKindFilter!!,
prefixMatcher.asNameFilter(),
descriptorNameFilter,
filterOutJavaGettersAndSetters = configuration.filterOutJavaGettersAndSetters
).excludeNonInitializedVariable(nameExpression)
}
@@ -231,7 +246,7 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
protected fun getRuntimeReceiverTypeReferenceVariants(): Collection<DeclarationDescriptor> {
val restrictedKindFilter = descriptorKindFilter!!.restrictedToKinds(DescriptorKindFilter.FUNCTIONS_MASK or DescriptorKindFilter.VARIABLES_MASK) // optimization
val descriptors = referenceVariantsHelper.getReferenceVariants(nameExpression!!, restrictedKindFilter, prefixMatcher.asNameFilter(), useRuntimeReceiverType = true)
val descriptors = referenceVariantsHelper.getReferenceVariants(nameExpression!!, restrictedKindFilter, descriptorNameFilter, useRuntimeReceiverType = true)
return descriptors.filter { descriptor ->
referenceVariants.none { comparePossiblyOverridingDescriptors(project, it, descriptor) }
}
@@ -86,7 +86,7 @@ class KDocNameCompletionSession(parameters: CompletionParameters,
private fun addLinkCompletions(declarationDescriptor: DeclarationDescriptor) {
val scope = getResolutionScope(resolutionFacade, declarationDescriptor)
scope.getDescriptors(nameFilter = prefixMatcher.asNameFilter()).forEach {
scope.getDescriptors(nameFilter = descriptorNameFilter).forEach {
val element = lookupElementFactory.createLookupElement(it, false)
collector.addElement(object: LookupElementDecorator<LookupElement>(element) {
override fun handleInsert(context: InsertionContext?) {
@@ -29,8 +29,7 @@ import org.jetbrains.kotlin.idea.JetDescriptorIconProvider
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.completion.handlers.*
import org.jetbrains.kotlin.idea.core.completion.DeclarationLookupObject
import org.jetbrains.kotlin.types.typeUtil.TypeNullability
import org.jetbrains.kotlin.types.typeUtil.nullability
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.psiUtil.parents
@@ -40,7 +39,8 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.types.TypeUtils
import javax.swing.Icon
import org.jetbrains.kotlin.types.typeUtil.TypeNullability
import org.jetbrains.kotlin.types.typeUtil.nullability
public data class PackageLookupObject(val fqName: FqName) : DeclarationLookupObject {
override val psiElement: PsiElement? get() = null
@@ -255,6 +255,7 @@ public class LookupElementFactory(
if (descriptor is CallableDescriptor) {
val original = descriptor.original
val extensionReceiver = original.extensionReceiverParameter
when {
original is SyntheticJavaPropertyDescriptor -> {
var from = original.getMethod.getName().asString() + "()"
@@ -262,9 +263,8 @@ public class LookupElementFactory(
element = element.appendTailText(" (from $from)", true)
}
descriptor.getExtensionReceiverParameter() != null -> {
val originalReceiver = descriptor.getOriginal().getExtensionReceiverParameter()!!
val receiverPresentation = DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(originalReceiver.getType())
extensionReceiver != null -> {
val receiverPresentation = DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(extensionReceiver.type)
element = element.appendTailText(" for $receiverPresentation", true)
val container = descriptor.getContainingDeclaration()
@@ -289,6 +289,16 @@ public class LookupElementFactory(
}
}
if (descriptor is PropertyDescriptor) {
val getterName = JvmAbi.getterName(name)
if (getterName != name) {
element = element.withLookupString(getterName)
}
if (descriptor.isVar) {
element = element.withLookupString(JvmAbi.setterName(name))
}
}
if (lookupObject.isDeprecated) {
element = element.withStrikeoutness(true)
}
@@ -0,0 +1,18 @@
class C : Thread() {
val property1: Int = 0
var property2: Int = 0
var xxx: Int = 0
fun getPPP() = 1
}
fun foo(c: C) {
c.getP<caret>
}
// EXIST: property1
// EXIST: property2
// EXIST: getPPP
// EXIST_JAVA_ONLY: priority
// ABSENT: getPriority
// ABSENT: xxx
@@ -0,0 +1,16 @@
class C {
val property1: Int = 0
var property2: Int = 0
var xxx: Int = 0
fun setPPP(){}
}
fun foo(c: C) {
c.setP<caret>
}
// ABSENT: property1
// EXIST: property2
// EXIST: setPPP
// ABSENT: xxx
@@ -1,5 +0,0 @@
fun foo(thread: Thread) {
thread.get<caret>
}
// EXIST_JAVA_ONLY: getPriority
@@ -253,6 +253,12 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
doTest(fileName);
}
@TestMetadata("GetPrefixForProperties.kt")
public void testGetPrefixForProperties() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/GetPrefixForProperties.kt");
doTest(fileName);
}
@TestMetadata("HigherOrderFunction1.kt")
public void testHigherOrderFunction1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/HigherOrderFunction1.kt");
@@ -769,6 +775,12 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
doTest(fileName);
}
@TestMetadata("SetPrefixForProperties.kt")
public void testSetPrefixForProperties() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/SetPrefixForProperties.kt");
doTest(fileName);
}
@TestMetadata("ShortClassNamesInTypePosition.kt")
public void testShortClassNamesInTypePosition() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/ShortClassNamesInTypePosition.kt");
@@ -1227,12 +1239,6 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
doTest(fileName);
}
@TestMetadata("ShowGetMethodWhenNothingElseMatch.kt")
public void testShowGetMethodWhenNothingElseMatch() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/extensions/ShowGetMethodWhenNothingElseMatch.kt");
doTest(fileName);
}
@TestMetadata("ShowGetSetOnSecondCompletion.kt")
public void testShowGetSetOnSecondCompletion() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/extensions/ShowGetSetOnSecondCompletion.kt");
@@ -253,6 +253,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
doTest(fileName);
}
@TestMetadata("GetPrefixForProperties.kt")
public void testGetPrefixForProperties() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/GetPrefixForProperties.kt");
doTest(fileName);
}
@TestMetadata("HigherOrderFunction1.kt")
public void testHigherOrderFunction1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/HigherOrderFunction1.kt");
@@ -769,6 +775,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
doTest(fileName);
}
@TestMetadata("SetPrefixForProperties.kt")
public void testSetPrefixForProperties() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/SetPrefixForProperties.kt");
doTest(fileName);
}
@TestMetadata("ShortClassNamesInTypePosition.kt")
public void testShortClassNamesInTypePosition() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/ShortClassNamesInTypePosition.kt");
@@ -1227,12 +1239,6 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
doTest(fileName);
}
@TestMetadata("ShowGetMethodWhenNothingElseMatch.kt")
public void testShowGetMethodWhenNothingElseMatch() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/extensions/ShowGetMethodWhenNothingElseMatch.kt");
doTest(fileName);
}
@TestMetadata("ShowGetSetOnSecondCompletion.kt")
public void testShowGetSetOnSecondCompletion() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/extensions/ShowGetSetOnSecondCompletion.kt");