KT-5079 Support smart completion for when values
#KT-5079 Fixed
This commit is contained in:
@@ -53,6 +53,9 @@ import org.jetbrains.jet.lang.descriptors.Visibilities
|
||||
import org.jetbrains.jet.lang.psi.JetBlockExpression
|
||||
import org.jetbrains.jet.plugin.util.makeNullable
|
||||
import org.jetbrains.jet.plugin.util.makeNotNullable
|
||||
import org.jetbrains.jet.lang.psi.JetWhenConditionWithExpression
|
||||
import org.jetbrains.jet.lang.psi.JetWhenEntry
|
||||
import org.jetbrains.jet.lang.psi.JetWhenExpression
|
||||
|
||||
enum class Tail {
|
||||
COMMA
|
||||
@@ -70,6 +73,7 @@ class ExpectedInfos(val bindingContext: BindingContext, val moduleDescriptor: Mo
|
||||
?: calculateForIf(expressionWithType)
|
||||
?: calculateForElvis(expressionWithType)
|
||||
?: calculateForBlockExpression(expressionWithType)
|
||||
?: calculateForWhenEntryValue(expressionWithType)
|
||||
?: getFromBindingContext(expressionWithType)
|
||||
}
|
||||
|
||||
@@ -219,6 +223,20 @@ class ExpectedInfos(val bindingContext: BindingContext, val moduleDescriptor: Mo
|
||||
return calculate(block)?.map { ExpectedInfo(it.`type`, null) }
|
||||
}
|
||||
|
||||
private fun calculateForWhenEntryValue(expressionWithType: JetExpression): Collection<ExpectedInfo>? {
|
||||
val condition = expressionWithType.getParent() as? JetWhenConditionWithExpression ?: return null
|
||||
val entry = condition.getParent() as JetWhenEntry
|
||||
val whenExpression = entry.getParent() as JetWhenExpression
|
||||
val subject = whenExpression.getSubjectExpression()
|
||||
if (subject != null) {
|
||||
val subjectType = bindingContext[BindingContext.EXPRESSION_TYPE, subject] ?: return null
|
||||
return listOf(ExpectedInfo(subjectType, null))
|
||||
}
|
||||
else {
|
||||
return listOf(ExpectedInfo(KotlinBuiltIns.getInstance().getBooleanType(), null))
|
||||
}
|
||||
}
|
||||
|
||||
private fun getFromBindingContext(expressionWithType: JetExpression): Collection<ExpectedInfo>? {
|
||||
val expectedType = bindingContext[BindingContext.EXPECTED_EXPRESSION_TYPE, expressionWithType] ?: return null
|
||||
return listOf(ExpectedInfo(expectedType, null))
|
||||
|
||||
@@ -20,14 +20,39 @@ import com.intellij.codeInsight.lookup.LookupElement
|
||||
import org.jetbrains.jet.plugin.completion.ExpectedInfo
|
||||
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder
|
||||
import org.jetbrains.jet.lang.psi.*
|
||||
import com.intellij.codeInsight.lookup.LookupElementDecorator
|
||||
import com.intellij.codeInsight.completion.InsertionContext
|
||||
import org.jetbrains.jet.plugin.completion.handlers.WithTailInsertHandler
|
||||
|
||||
object KeywordValues {
|
||||
public fun addToCollection(collection: MutableCollection<LookupElement>, expectedInfos: Collection<ExpectedInfo>) {
|
||||
val booleanInfoClassifier = { (info: ExpectedInfo) ->
|
||||
if (info.`type` == KotlinBuiltIns.getInstance().getBooleanType()) ExpectedInfoClassification.MATCHES else ExpectedInfoClassification.NOT_MATCHES
|
||||
public fun addToCollection(collection: MutableCollection<LookupElement>, expectedInfos: Collection<ExpectedInfo>, expressionWithType: JetExpression) {
|
||||
var skipTrueFalse = false
|
||||
|
||||
val whenCondition = expressionWithType.getParent() as? JetWhenConditionWithExpression
|
||||
if (whenCondition != null) {
|
||||
val entry = whenCondition.getParent() as JetWhenEntry
|
||||
val whenExpression = entry.getParent() as JetWhenExpression
|
||||
if (whenExpression.getElseExpression() == null && entry == whenExpression.getEntries().last) {
|
||||
val lookupElement = LookupElementBuilder.create("else").bold().withTailText(" ->")
|
||||
collection.add(object: LookupElementDecorator<LookupElement>(lookupElement) {
|
||||
override fun handleInsert(context: InsertionContext) {
|
||||
WithTailInsertHandler("->", spaceBefore = true, spaceAfter = true).handleInsert(context, getDelegate())
|
||||
}
|
||||
})
|
||||
}
|
||||
if (whenExpression.getSubjectExpression() == null) { // no sense in true or false entries for when with no subject
|
||||
skipTrueFalse = true
|
||||
}
|
||||
}
|
||||
|
||||
if (!skipTrueFalse) {
|
||||
val booleanInfoClassifier = { (info: ExpectedInfo) ->
|
||||
if (info.`type` == KotlinBuiltIns.getInstance().getBooleanType()) ExpectedInfoClassification.MATCHES else ExpectedInfoClassification.NOT_MATCHES
|
||||
}
|
||||
collection.addLookupElements(expectedInfos, booleanInfoClassifier, { LookupElementBuilder.create("true").bold() })
|
||||
collection.addLookupElements(expectedInfos, booleanInfoClassifier, { LookupElementBuilder.create("false").bold() })
|
||||
}
|
||||
collection.addLookupElements(expectedInfos, booleanInfoClassifier, { LookupElementBuilder.create("true").bold() })
|
||||
collection.addLookupElements(expectedInfos, booleanInfoClassifier, { LookupElementBuilder.create("false").bold() })
|
||||
|
||||
collection.addLookupElements(expectedInfos,
|
||||
{ info -> if (info.`type`.isNullable()) ExpectedInfoClassification.MATCHES else ExpectedInfoClassification.NOT_MATCHES },
|
||||
|
||||
@@ -106,25 +106,25 @@ class SmartCompletion(val expression: JetSimpleNameExpression,
|
||||
if (receiver == null) {
|
||||
TypeInstantiationItems(bindingContext, resolveSession, visibilityFilter).addToCollection(result, expectedInfos)
|
||||
|
||||
StaticMembers(bindingContext, resolveSession).addToCollection(result, expectedInfos, expression)
|
||||
StaticMembers(bindingContext, resolveSession).addToCollection(result, expectedInfos, expression, itemsToSkip)
|
||||
|
||||
ThisItems(bindingContext).addToCollection(result, expressionWithType, expectedInfos)
|
||||
|
||||
LambdaItems.addToCollection(result, functionExpectedInfos)
|
||||
|
||||
KeywordValues.addToCollection(result, expectedInfos)
|
||||
KeywordValues.addToCollection(result, expectedInfos, expressionWithType)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun calcItemsToSkip(expression: JetExpression): Collection<DeclarationDescriptor> {
|
||||
private fun calcItemsToSkip(expression: JetExpression): Set<DeclarationDescriptor> {
|
||||
val parent = expression.getParent()
|
||||
when(parent) {
|
||||
is JetProperty -> {
|
||||
//TODO: this can be filtered out by ordinary completion
|
||||
if (expression == parent.getInitializer()) {
|
||||
return resolveSession.resolveToElement(parent)[BindingContext.DECLARATION_TO_DESCRIPTOR, parent].toList()
|
||||
return resolveSession.resolveToElement(parent)[BindingContext.DECLARATION_TO_DESCRIPTOR, parent].toSet()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,13 +134,36 @@ class SmartCompletion(val expression: JetSimpleNameExpression,
|
||||
if (operationToken == JetTokens.EQ || operationToken == JetTokens.EQEQ || operationToken == JetTokens.EXCLEQ) {
|
||||
val left = parent.getLeft()
|
||||
if (left is JetReferenceExpression) {
|
||||
return resolveSession.resolveToElement(left)[BindingContext.REFERENCE_TARGET, left].toList()
|
||||
return resolveSession.resolveToElement(left)[BindingContext.REFERENCE_TARGET, left].toSet()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is JetWhenConditionWithExpression -> {
|
||||
val entry = parent.getParent() as JetWhenEntry
|
||||
val whenExpression = entry.getParent() as JetWhenExpression
|
||||
val subject = whenExpression.getSubjectExpression() ?: return setOf()
|
||||
val subjectType = bindingContext[BindingContext.EXPRESSION_TYPE, subject] ?: return setOf()
|
||||
val classDescriptor = TypeUtils.getClassDescriptor(subjectType)
|
||||
if (classDescriptor != null && DescriptorUtils.isEnumClass(classDescriptor)) {
|
||||
val usedEnumEntries = HashSet<ClassDescriptor>()
|
||||
val conditions = whenExpression.getEntries()
|
||||
.flatMap { it.getConditions().toList() }
|
||||
.filterIsInstance(javaClass<JetWhenConditionWithExpression>())
|
||||
for (condition in conditions) {
|
||||
val selectorExpr = (condition.getExpression() as? JetDotQualifiedExpression)
|
||||
?.getSelectorExpression() as? JetReferenceExpression ?: continue
|
||||
val target = bindingContext[BindingContext.REFERENCE_TARGET, selectorExpr] as? ClassDescriptor ?: continue
|
||||
if (DescriptorUtils.isEnumEntry(target)) {
|
||||
usedEnumEntries.add(target)
|
||||
}
|
||||
}
|
||||
return usedEnumEntries
|
||||
}
|
||||
}
|
||||
}
|
||||
return listOf()
|
||||
return setOf()
|
||||
}
|
||||
|
||||
private fun toFunctionReferenceLookupElement(descriptor: DeclarationDescriptor,
|
||||
|
||||
@@ -40,14 +40,18 @@ import org.jetbrains.jet.plugin.util.makeNotNullable
|
||||
|
||||
// adds java static members, enum members and members from class object
|
||||
class StaticMembers(val bindingContext: BindingContext, val resolveSession: ResolveSessionForBodies) {
|
||||
public fun addToCollection(collection: MutableCollection<LookupElement>, expectedInfos: Collection<ExpectedInfo>, context: JetExpression) {
|
||||
public fun addToCollection(collection: MutableCollection<LookupElement>,
|
||||
expectedInfos: Collection<ExpectedInfo>,
|
||||
context: JetExpression,
|
||||
enumEntriesToSkip: Set<DeclarationDescriptor>) {
|
||||
|
||||
val scope = bindingContext[BindingContext.RESOLUTION_SCOPE, context]
|
||||
if (scope == null) return
|
||||
|
||||
val expectedInfosByClass = expectedInfos.groupBy { TypeUtils.getClassDescriptor(it.`type`) }
|
||||
for ((classDescriptor, expectedInfosForClass) in expectedInfosByClass) {
|
||||
if (classDescriptor != null && !classDescriptor.getName().isSpecial()) {
|
||||
addToCollection(collection, classDescriptor, expectedInfosForClass, scope)
|
||||
addToCollection(collection, classDescriptor, expectedInfosForClass, scope, enumEntriesToSkip)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,7 +60,8 @@ class StaticMembers(val bindingContext: BindingContext, val resolveSession: Reso
|
||||
collection: MutableCollection<LookupElement>,
|
||||
classDescriptor: ClassDescriptor,
|
||||
expectedInfos: Collection<ExpectedInfo>,
|
||||
scope: JetScope) {
|
||||
scope: JetScope,
|
||||
enumEntriesToSkip: Set<DeclarationDescriptor>) {
|
||||
|
||||
fun processMember(descriptor: DeclarationDescriptor) {
|
||||
if (descriptor is DeclarationDescriptorWithVisibility && !Visibilities.isVisible(descriptor, scope.getContainingDeclaration())) return
|
||||
@@ -74,7 +79,7 @@ class StaticMembers(val bindingContext: BindingContext, val resolveSession: Reso
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (DescriptorUtils.isEnumEntry(descriptor)) {
|
||||
else if (DescriptorUtils.isEnumEntry(descriptor) && !enumEntriesToSkip.contains(descriptor)) {
|
||||
classifier = { ExpectedInfoClassification.MATCHES } /* we do not need to check type of enum entry because it's taken from proper enum */
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -175,5 +175,6 @@ fun createLookupElement(descriptor: DeclarationDescriptor, resolveSession: Resol
|
||||
fun JetType.isSubtypeOf(expectedType: JetType) = !isError() && JetTypeChecker.INSTANCE.isSubtypeOf(this, expectedType)
|
||||
|
||||
fun <T : Any> T?.toList(): List<T> = if (this != null) listOf(this) else listOf()
|
||||
fun <T : Any> T?.toSet(): Set<T> = if (this != null) setOf(this) else setOf()
|
||||
|
||||
fun String?.isNullOrEmpty() = this == null || this.isEmpty()
|
||||
|
||||
Reference in New Issue
Block a user