Refactored smart completion so that completion in for-loop range and after "in" are covered by ExpectedInfo's
This commit is contained in:
@@ -21,7 +21,9 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.frontend.di.createContainerForMacros
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.completion.smart.TypesWithContainsDetector
|
||||
import org.jetbrains.kotlin.idea.completion.smart.toList
|
||||
import org.jetbrains.kotlin.idea.core.IterableTypesDetector
|
||||
import org.jetbrains.kotlin.idea.core.mapArgumentsToParameters
|
||||
import org.jetbrains.kotlin.idea.util.FuzzyType
|
||||
import org.jetbrains.kotlin.idea.util.fuzzyReturnType
|
||||
@@ -45,10 +47,12 @@ import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus
|
||||
import org.jetbrains.kotlin.resolve.calls.util.DelegatingCall
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue
|
||||
import org.jetbrains.kotlin.types.JetType
|
||||
import org.jetbrains.kotlin.types.TypeSubstitutor
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
|
||||
import org.jetbrains.kotlin.types.typeUtil.containsError
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.check
|
||||
import java.util.LinkedHashSet
|
||||
|
||||
enum class Tail {
|
||||
@@ -65,10 +69,29 @@ data class ItemOptions(val starPrefix: Boolean) {
|
||||
}
|
||||
}
|
||||
|
||||
open data class ExpectedInfo(val fuzzyType: FuzzyType, val expectedName: String?, val tail: Tail?, val itemOptions: ItemOptions = ItemOptions.DEFAULT) {
|
||||
constructor(type: JetType, expectedName: String?, tail: Tail?) : this(FuzzyType(type, emptyList()), expectedName, tail)
|
||||
interface ByTypeFilter {
|
||||
fun matchingSubstitutor(descriptorType: FuzzyType): TypeSubstitutor?
|
||||
}
|
||||
|
||||
class ByExpectedTypeFilter(val fuzzyType: FuzzyType) : ByTypeFilter {
|
||||
override fun matchingSubstitutor(descriptorType: FuzzyType) = descriptorType.checkIsSubtypeOf(fuzzyType)
|
||||
}
|
||||
|
||||
open data class ExpectedInfo(val filter: ByTypeFilter, val expectedName: String?, val tail: Tail?, val itemOptions: ItemOptions = ItemOptions.DEFAULT) {
|
||||
constructor(fuzzyType: FuzzyType, expectedName: String?, tail: Tail?, itemOptions: ItemOptions = ItemOptions.DEFAULT)
|
||||
: this(ByExpectedTypeFilter(fuzzyType), expectedName, tail, itemOptions)
|
||||
|
||||
constructor(type: JetType, expectedName: String?, tail: Tail?, itemOptions: ItemOptions = ItemOptions.DEFAULT)
|
||||
: this(FuzzyType(type, emptyList()), expectedName, tail, itemOptions)
|
||||
|
||||
fun matchingSubstitutor(descriptorType: FuzzyType): TypeSubstitutor? = filter.matchingSubstitutor(descriptorType)
|
||||
|
||||
fun matchingSubstitutor(descriptorType: JetType): TypeSubstitutor? = matchingSubstitutor(FuzzyType(descriptorType, emptyList()))
|
||||
}
|
||||
|
||||
val ExpectedInfo.fuzzyType: FuzzyType?
|
||||
get() = (this.filter as? ByExpectedTypeFilter)?.fuzzyType
|
||||
|
||||
data class ArgumentPosition(val argumentIndex: Int, val argumentName: Name?, val isFunctionLiteralArgument: Boolean) {
|
||||
constructor(argumentIndex: Int, isFunctionLiteralArgument: Boolean = false) : this(argumentIndex, null, isFunctionLiteralArgument)
|
||||
constructor(argumentIndex: Int, argumentName: Name?) : this(argumentIndex, argumentName, false)
|
||||
@@ -111,6 +134,8 @@ class ExpectedInfos(
|
||||
?: calculateForInitializer(expressionWithType)
|
||||
?: calculateForExpressionBody(expressionWithType)
|
||||
?: calculateForReturn(expressionWithType)
|
||||
?: calculateForLoopRange(expressionWithType)
|
||||
?: calculateForInOperatorArgument(expressionWithType)
|
||||
?: getFromBindingContext(expressionWithType)
|
||||
}
|
||||
|
||||
@@ -137,11 +162,12 @@ class ExpectedInfos(
|
||||
public fun calculateForArgument(call: Call, argument: ValueArgument): Collection<ExpectedInfo>? {
|
||||
val results = calculateForArgument(call, TypeUtils.NO_EXPECTED_TYPE, argument) ?: return null
|
||||
|
||||
if (useOuterCallsExpectedTypeCount > 0 && results.any { it.fuzzyType.freeParameters.isNotEmpty() && it.function.fuzzyReturnType()?.freeParameters?.isNotEmpty() ?: false }) {
|
||||
if (useOuterCallsExpectedTypeCount > 0 && results.any { it.fuzzyType != null && it.fuzzyType!!.freeParameters.isNotEmpty() && it.function.fuzzyReturnType()?.freeParameters?.isNotEmpty() ?: false }) {
|
||||
val callExpression = (call.callElement as? JetExpression)?.getQualifiedExpressionForSelectorOrThis() ?: return results
|
||||
val expectedFuzzyTypes = ExpectedInfos(bindingContext, resolutionFacade, moduleDescriptor, useHeuristicSignatures, useOuterCallsExpectedTypeCount - 1)
|
||||
.calculate(callExpression)
|
||||
?.map { it.fuzzyType } ?: return results
|
||||
?.map { it.fuzzyType }
|
||||
?.filterNotNull() ?: return results
|
||||
if (expectedFuzzyTypes.isEmpty() || expectedFuzzyTypes.any { it.freeParameters.isNotEmpty() }) return results
|
||||
|
||||
return expectedFuzzyTypes
|
||||
@@ -304,13 +330,13 @@ class ExpectedInfos(
|
||||
return when (expressionWithType) {
|
||||
ifExpression.getCondition() -> listOf(ExpectedInfo(KotlinBuiltIns.getInstance().getBooleanType(), null, Tail.RPARENTH))
|
||||
|
||||
ifExpression.getThen() -> calculate(ifExpression)?.map { ExpectedInfo(it.fuzzyType, it.expectedName, Tail.ELSE) }
|
||||
ifExpression.getThen() -> calculate(ifExpression)?.map { ExpectedInfo(it.filter, it.expectedName, Tail.ELSE) }
|
||||
|
||||
ifExpression.getElse() -> {
|
||||
val ifExpectedInfo = calculate(ifExpression)
|
||||
val thenType = ifExpression.getThen()?.let { bindingContext.getType(it) }
|
||||
if (thenType != null && !thenType.isError())
|
||||
ifExpectedInfo?.filter { it.fuzzyType.checkIsSubtypeOf(thenType) != null }
|
||||
ifExpectedInfo?.filter { it.matchingSubstitutor(thenType) != null }
|
||||
else
|
||||
ifExpectedInfo
|
||||
}
|
||||
@@ -330,7 +356,7 @@ class ExpectedInfos(
|
||||
val expectedInfos = calculate(binaryExpression)
|
||||
if (expectedInfos != null) {
|
||||
return if (leftTypeNotNullable != null)
|
||||
expectedInfos.filter { it.fuzzyType.checkIsSuperTypeOf(leftTypeNotNullable) != null }
|
||||
expectedInfos.filter { it.matchingSubstitutor(leftTypeNotNullable) != null }
|
||||
else
|
||||
expectedInfos
|
||||
}
|
||||
@@ -351,6 +377,7 @@ class ExpectedInfos(
|
||||
val literalExpression = functionLiteral.parent as JetFunctionLiteralExpression
|
||||
return calculate(literalExpression)
|
||||
?.map { it.fuzzyType }
|
||||
?.filterNotNull()
|
||||
?.filter { KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(it.type) }
|
||||
?.map {
|
||||
val returnType = KotlinBuiltIns.getReturnTypeFromFunctionType(it.type)
|
||||
@@ -358,7 +385,7 @@ class ExpectedInfos(
|
||||
}
|
||||
}
|
||||
else {
|
||||
return calculate(block)?.map { ExpectedInfo(it.fuzzyType, it.expectedName, null) }
|
||||
return calculate(block)?.map { ExpectedInfo(it.filter, it.expectedName, null) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -416,6 +443,45 @@ class ExpectedInfos(
|
||||
}
|
||||
}
|
||||
|
||||
private fun calculateForLoopRange(expressionWithType: JetExpression): Collection<ExpectedInfo>? {
|
||||
val forExpression = (expressionWithType.parent as? JetContainerNode)
|
||||
?.parent as? JetForExpression ?: return null
|
||||
if (expressionWithType != forExpression.loopRange) return null
|
||||
|
||||
val loopVar = forExpression.loopParameter
|
||||
val loopVarType = if (loopVar != null && loopVar.typeReference != null)
|
||||
(resolutionFacade.resolveToDescriptor(loopVar) as VariableDescriptor).type.check { !it.isError }
|
||||
else
|
||||
null
|
||||
|
||||
val scope = bindingContext.get(BindingContext.RESOLUTION_SCOPE, expressionWithType)!!
|
||||
val iterableDetector = IterableTypesDetector(forExpression.project, moduleDescriptor, scope)
|
||||
|
||||
val byTypeFilter = object : ByTypeFilter {
|
||||
override fun matchingSubstitutor(descriptorType: FuzzyType): TypeSubstitutor? {
|
||||
return if (iterableDetector.isIterable(descriptorType, loopVarType)) TypeSubstitutor.EMPTY else null
|
||||
}
|
||||
}
|
||||
return listOf(ExpectedInfo(byTypeFilter, null, Tail.RPARENTH))
|
||||
}
|
||||
|
||||
private fun calculateForInOperatorArgument(expressionWithType: JetExpression): Collection<ExpectedInfo>? {
|
||||
val binaryExpression = expressionWithType.parent as? JetBinaryExpression ?: return null
|
||||
val operationToken = binaryExpression.operationToken
|
||||
if (operationToken != JetTokens.IN_KEYWORD && operationToken != JetTokens.NOT_IN || expressionWithType != binaryExpression.right) return null
|
||||
|
||||
val leftOperandType = binaryExpression.left?.let { bindingContext.getType(it) } ?: return null
|
||||
val scope = bindingContext.get(BindingContext.RESOLUTION_SCOPE, expressionWithType)!!
|
||||
val detector = TypesWithContainsDetector(scope, leftOperandType, binaryExpression.project, moduleDescriptor)
|
||||
|
||||
val byTypeFilter = object : ByTypeFilter {
|
||||
override fun matchingSubstitutor(descriptorType: FuzzyType): TypeSubstitutor? {
|
||||
return if (detector.hasContains(descriptorType)) TypeSubstitutor.EMPTY else null
|
||||
}
|
||||
}
|
||||
return listOf(ExpectedInfo(byTypeFilter, null, null))
|
||||
}
|
||||
|
||||
private fun getFromBindingContext(expressionWithType: JetExpression): Collection<ExpectedInfo>? {
|
||||
val expectedType = bindingContext[BindingContext.EXPECTED_EXPRESSION_TYPE, expressionWithType] ?: return null
|
||||
return listOf(ExpectedInfo(expectedType, null, null))
|
||||
|
||||
+6
-3
@@ -28,11 +28,10 @@ import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.completion.ExpectedInfos
|
||||
import org.jetbrains.kotlin.idea.completion.fuzzyType
|
||||
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.idea.util.application.executeCommand
|
||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.jetbrains.kotlin.psi.JetExpression
|
||||
import org.jetbrains.kotlin.psi.JetFile
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
@@ -81,7 +80,11 @@ private fun needExplicitParameterTypes(context: InsertionContext, placeholderRan
|
||||
val bindingContext = resolutionFacade.analyze(expression, BodyResolveMode.PARTIAL)
|
||||
val expectedInfos = ExpectedInfos(bindingContext, resolutionFacade, resolutionFacade.findModuleDescriptor(file), useHeuristicSignatures = false)
|
||||
.calculate(expression) ?: return false
|
||||
val functionTypes = expectedInfos.map { it.fuzzyType.type }.filter { KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(it) }.toSet()
|
||||
val functionTypes = expectedInfos
|
||||
.map { it.fuzzyType?.type }
|
||||
.filterNotNull()
|
||||
.filter { KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(it) }
|
||||
.toSet()
|
||||
if (functionTypes.size() <= 1) return false
|
||||
|
||||
val lambdaParameterCount = KotlinBuiltIns.getParameterTypeProjectionsFromFunctionType(lambdaType).size()
|
||||
|
||||
+17
-8
@@ -16,14 +16,18 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.completion.smart
|
||||
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import org.jetbrains.kotlin.idea.completion.ExpectedInfo
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import com.intellij.codeInsight.lookup.LookupElementDecorator
|
||||
import com.intellij.codeInsight.completion.InsertionContext
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder
|
||||
import com.intellij.codeInsight.lookup.LookupElementDecorator
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.idea.completion.ExpectedInfo
|
||||
import org.jetbrains.kotlin.idea.completion.fuzzyType
|
||||
import org.jetbrains.kotlin.idea.completion.handlers.WithTailInsertHandler
|
||||
import org.jetbrains.kotlin.psi.JetExpression
|
||||
import org.jetbrains.kotlin.psi.JetWhenConditionWithExpression
|
||||
import org.jetbrains.kotlin.psi.JetWhenEntry
|
||||
import org.jetbrains.kotlin.psi.JetWhenExpression
|
||||
import org.jetbrains.kotlin.types.TypeSubstitutor
|
||||
|
||||
object KeywordValues {
|
||||
@@ -50,13 +54,18 @@ object KeywordValues {
|
||||
|
||||
if (!skipTrueFalse) {
|
||||
val booleanInfoClassifier = { info: ExpectedInfo ->
|
||||
if (info.fuzzyType.type == KotlinBuiltIns.getInstance().getBooleanType()) ExpectedInfoClassification.matches(TypeSubstitutor.EMPTY) else ExpectedInfoClassification.notMatches
|
||||
if (info.fuzzyType?.type == KotlinBuiltIns.getInstance().getBooleanType()) ExpectedInfoClassification.matches(TypeSubstitutor.EMPTY) else ExpectedInfoClassification.notMatches
|
||||
}
|
||||
collection.addLookupElements(null, expectedInfos, booleanInfoClassifier) { LookupElementBuilder.create("true").bold().assignSmartCompletionPriority(SmartCompletionItemPriority.TRUE) }
|
||||
collection.addLookupElements(null, expectedInfos, booleanInfoClassifier) { LookupElementBuilder.create("false").bold().assignSmartCompletionPriority(SmartCompletionItemPriority.FALSE) }
|
||||
}
|
||||
|
||||
val classifier = { info: ExpectedInfo -> if (info.fuzzyType.type.isMarkedNullable()) ExpectedInfoClassification.matches(TypeSubstitutor.EMPTY) else ExpectedInfoClassification.notMatches }
|
||||
val classifier = { info: ExpectedInfo ->
|
||||
if (info.fuzzyType != null && info.fuzzyType!!.type.isMarkedNullable())
|
||||
ExpectedInfoClassification.matches(TypeSubstitutor.EMPTY)
|
||||
else
|
||||
ExpectedInfoClassification.notMatches
|
||||
}
|
||||
collection.addLookupElements(null, expectedInfos, classifier) {
|
||||
LookupElementBuilder.create("null").bold().assignSmartCompletionPriority(SmartCompletionItemPriority.NULL)
|
||||
}
|
||||
|
||||
@@ -21,10 +21,11 @@ import com.intellij.codeInsight.lookup.LookupElementBuilder
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.idea.completion.ExpectedInfo
|
||||
import org.jetbrains.kotlin.idea.completion.fuzzyType
|
||||
import org.jetbrains.kotlin.idea.completion.handlers.buildLambdaPresentation
|
||||
import org.jetbrains.kotlin.idea.completion.handlers.insertLambdaTemplate
|
||||
import org.jetbrains.kotlin.idea.completion.suppressAutoInsertion
|
||||
import java.util.*
|
||||
import java.util.ArrayList
|
||||
|
||||
object LambdaItems {
|
||||
public fun collect(functionExpectedInfos: Collection<ExpectedInfo>): Collection<LookupElement> {
|
||||
@@ -34,7 +35,10 @@ object LambdaItems {
|
||||
}
|
||||
|
||||
public fun addToCollection(collection: MutableCollection<LookupElement>, functionExpectedInfos: Collection<ExpectedInfo>) {
|
||||
val distinctTypes = functionExpectedInfos.map { it.fuzzyType.type }.toSet()
|
||||
val distinctTypes = functionExpectedInfos
|
||||
.map { it.fuzzyType?.type }
|
||||
.filterNotNull()
|
||||
.toSet()
|
||||
|
||||
val singleType = if (distinctTypes.size() == 1) distinctTypes.single() else null
|
||||
val singleSignatureLength = singleType?.let { KotlinBuiltIns.getParameterTypeProjectionsFromFunctionType(it).size() }
|
||||
@@ -60,7 +64,7 @@ object LambdaItems {
|
||||
})
|
||||
.suppressAutoInsertion()
|
||||
.assignSmartCompletionPriority(SmartCompletionItemPriority.LAMBDA)
|
||||
.addTailAndNameSimilarity(functionExpectedInfos.filter { it.fuzzyType.type == functionType })
|
||||
.addTailAndNameSimilarity(functionExpectedInfos.filter { it.fuzzyType?.type == functionType })
|
||||
collection.add(lookupElement)
|
||||
}
|
||||
}
|
||||
|
||||
+14
-82
@@ -29,9 +29,11 @@ import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.completion.*
|
||||
import org.jetbrains.kotlin.idea.core.IterableTypesDetector
|
||||
import org.jetbrains.kotlin.idea.core.SmartCastCalculator
|
||||
import org.jetbrains.kotlin.idea.util.*
|
||||
import org.jetbrains.kotlin.idea.util.FuzzyType
|
||||
import org.jetbrains.kotlin.idea.util.fuzzyReturnType
|
||||
import org.jetbrains.kotlin.idea.util.isAlmostEverything
|
||||
import org.jetbrains.kotlin.idea.util.makeNullable
|
||||
import org.jetbrains.kotlin.lexer.JetTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression
|
||||
@@ -39,7 +41,6 @@ import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.types.JetType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.typeUtil.TypeNullability
|
||||
import org.jetbrains.kotlin.types.typeUtil.isNothing
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||
import java.util.ArrayList
|
||||
@@ -61,7 +62,6 @@ class SmartCompletion(
|
||||
val toFromOriginalFileMapper: ToFromOriginalFileMapper,
|
||||
val lookupElementFactory: LookupElementFactory
|
||||
) {
|
||||
private val project = expression.getProject()
|
||||
private val receiver = if (expression is JetSimpleNameExpression) expression.getReceiverExpression() else null
|
||||
|
||||
public class Result(
|
||||
@@ -118,28 +118,21 @@ class SmartCompletion(
|
||||
expression
|
||||
}
|
||||
|
||||
val loopRangePositionResult = buildForLoopRangePosition(expressionWithType, receiver)
|
||||
if (loopRangePositionResult != null) return loopRangePositionResult
|
||||
|
||||
val inOperatorArgumentResult = buildForInOperatorArgument(expressionWithType, receiver)
|
||||
if (inOperatorArgumentResult != null) return inOperatorArgumentResult
|
||||
|
||||
val allExpectedInfos = calcExpectedInfos(expressionWithType) ?: return null
|
||||
val filteredExpectedInfos = allExpectedInfos.filter { !it.fuzzyType.type.isError() }
|
||||
if (filteredExpectedInfos.isEmpty()) return null
|
||||
var originalExpectedInfos = calcExpectedInfos(expressionWithType) ?: return null
|
||||
originalExpectedInfos = originalExpectedInfos.filterNot { it.fuzzyType?.type?.isError ?: false }
|
||||
|
||||
// if we complete argument of == or !=, make types in expected info's nullable to allow nullable items too
|
||||
val expectedInfos = if ((expressionWithType.getParent() as? JetBinaryExpression)?.getOperationToken() in COMPARISON_TOKENS)
|
||||
filteredExpectedInfos.map { ExpectedInfo(it.fuzzyType.makeNullable(), it.expectedName, it.tail) }
|
||||
originalExpectedInfos.map { if (it.fuzzyType != null) ExpectedInfo(it.fuzzyType!!.makeNullable(), it.expectedName, it.tail) else it }
|
||||
else
|
||||
filteredExpectedInfos
|
||||
originalExpectedInfos
|
||||
|
||||
val smartCastTypes: (VariableDescriptor) -> Collection<JetType>
|
||||
= SmartCastCalculator(bindingContext, moduleDescriptor).calculate(expressionWithType, receiver)
|
||||
|
||||
val itemsToSkip = calcItemsToSkip(expressionWithType)
|
||||
|
||||
val functionExpectedInfos = expectedInfos.filter { KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(it.fuzzyType.type) }
|
||||
val functionExpectedInfos = expectedInfos.filter { it.fuzzyType != null && KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(it.fuzzyType!!.type) }
|
||||
|
||||
fun filterDeclaration(descriptor: DeclarationDescriptor): Collection<LookupElement> {
|
||||
if (descriptor in itemsToSkip) return listOf()
|
||||
@@ -179,7 +172,7 @@ class SmartCompletion(
|
||||
|
||||
LambdaItems.addToCollection(additionalItems, functionExpectedInfos)
|
||||
|
||||
KeywordValues.addToCollection(additionalItems, filteredExpectedInfos/* use filteredExpectedInfos to not include null after == */, expression)
|
||||
KeywordValues.addToCollection(additionalItems, originalExpectedInfos/* use originalExpectedInfos to not include null after == */, expression)
|
||||
|
||||
MultipleArgumentsItemProvider(bindingContext, smartCastTypes).addToCollection(additionalItems, expectedInfos, expression)
|
||||
}
|
||||
@@ -246,7 +239,7 @@ class SmartCompletion(
|
||||
while (true) {
|
||||
val infos = ExpectedInfos(bindingContext, resolutionFacade, moduleDescriptor, useOuterCallsExpectedTypeCount = count)
|
||||
.calculate(expression) ?: return null
|
||||
if (count == 2 /* use two outer calls maximum */ || infos.none { it.fuzzyType.isAlmostEverything() }) return infos
|
||||
if (count == 2 /* use two outer calls maximum */ || infos.none { it.fuzzyType?.isAlmostEverything() ?: false }) return infos
|
||||
count++
|
||||
}
|
||||
//TODO: we could always give higher priority to results with outer call expected type used
|
||||
@@ -319,7 +312,7 @@ class SmartCompletion(
|
||||
fun toLookupElement(descriptor: FunctionDescriptor): LookupElement? {
|
||||
val functionType = functionType(descriptor) ?: return null
|
||||
|
||||
val matchedExpectedInfos = functionExpectedInfos.filter { it.fuzzyType.checkIsSuperTypeOf(functionType) != null }
|
||||
val matchedExpectedInfos = functionExpectedInfos.filter { it.matchingSubstitutor(functionType) != null }
|
||||
if (matchedExpectedInfos.isEmpty()) return null
|
||||
|
||||
var lookupElement = lookupElementFactory.createLookupElement(descriptor, bindingContext, true)
|
||||
@@ -367,78 +360,17 @@ class SmartCompletion(
|
||||
if (elementType != JetTokens.AS_KEYWORD && elementType != JetTokens.AS_SAFE) return null
|
||||
val expectedInfos = calcExpectedInfos(binaryExpression) ?: return null
|
||||
|
||||
val expectedInfosGrouped: Map<JetType, List<ExpectedInfo>> = expectedInfos.groupBy { it.fuzzyType.type.makeNotNullable() }
|
||||
val expectedInfosGrouped: Map<JetType?, List<ExpectedInfo>> = expectedInfos.groupBy { it.fuzzyType?.type?.makeNotNullable() }
|
||||
|
||||
val items = ArrayList<LookupElement>()
|
||||
for ((type, infos) in expectedInfosGrouped) {
|
||||
if (type == null) continue
|
||||
val lookupElement = lookupElementFactory.createLookupElementForType(type) ?: continue
|
||||
items.add(lookupElement.addTailAndNameSimilarity(infos))
|
||||
}
|
||||
return Result(null, items, null)
|
||||
}
|
||||
|
||||
private fun buildForLoopRangePosition(expressionWithType: JetExpression, receiver: JetExpression?): Result? {
|
||||
val forExpression = (expressionWithType.getParent() as? JetContainerNode)
|
||||
?.getParent() as? JetForExpression ?: return null
|
||||
if (expressionWithType != forExpression.getLoopRange()) return null
|
||||
|
||||
val loopVar = forExpression.getLoopParameter()
|
||||
val loopVarType = if (loopVar != null && loopVar.getTypeReference() != null) {
|
||||
val type = (resolutionFacade.resolveToDescriptor(loopVar) as VariableDescriptor).getType()
|
||||
if (type.isError()) null else type
|
||||
}
|
||||
else {
|
||||
null
|
||||
}
|
||||
|
||||
val scope = bindingContext.get(BindingContext.RESOLUTION_SCOPE, expressionWithType)!!
|
||||
val iterableDetector = IterableTypesDetector(project, moduleDescriptor, scope)
|
||||
|
||||
return buildResultByTypeFilter(expressionWithType, receiver, Tail.RPARENTH) { iterableDetector.isIterable(it, loopVarType) }
|
||||
}
|
||||
|
||||
private fun buildForInOperatorArgument(expressionWithType: JetExpression, receiver: JetExpression?): Result? {
|
||||
val binaryExpression = expressionWithType.getParent() as? JetBinaryExpression ?: return null
|
||||
val operationToken = binaryExpression.getOperationToken()
|
||||
if (operationToken != JetTokens.IN_KEYWORD && operationToken != JetTokens.NOT_IN || expressionWithType != binaryExpression.getRight()) return null
|
||||
|
||||
val leftOperandType = binaryExpression.getLeft()?.let { bindingContext.getType(it) } ?: return null
|
||||
val scope = bindingContext.get(BindingContext.RESOLUTION_SCOPE, expressionWithType)!!
|
||||
val detector = TypesWithContainsDetector(scope, leftOperandType, project, moduleDescriptor)
|
||||
|
||||
return buildResultByTypeFilter(expressionWithType, receiver, null) { detector.hasContains(it) }
|
||||
}
|
||||
|
||||
private fun buildResultByTypeFilter(
|
||||
position: JetExpression,
|
||||
receiver: JetExpression?,
|
||||
tail: Tail?,
|
||||
typeFilter: (FuzzyType) -> Boolean
|
||||
): Result {
|
||||
val smartCastTypes: (VariableDescriptor) -> Collection<JetType> = SmartCastCalculator(
|
||||
bindingContext, moduleDescriptor).calculate(position, receiver)
|
||||
|
||||
fun filterDeclaration(descriptor: DeclarationDescriptor): Collection<LookupElement> {
|
||||
val types = descriptor.fuzzyTypes(smartCastTypes)
|
||||
|
||||
fun createLookupElement(): LookupElement {
|
||||
return lookupElementFactory.createLookupElement(descriptor, bindingContext, true)
|
||||
}
|
||||
|
||||
if (types.any { typeFilter(it) }) {
|
||||
return listOf(createLookupElement().addTail(tail))
|
||||
}
|
||||
|
||||
if (types.any { it.nullability() == TypeNullability.NULLABLE && typeFilter(it.makeNotNullable()) }) {
|
||||
return lookupElementsForNullable(::createLookupElement).map { it.addTail(tail) }
|
||||
}
|
||||
|
||||
return listOf()
|
||||
}
|
||||
|
||||
return Result(::filterDeclaration, listOf(), null)
|
||||
}
|
||||
|
||||
companion object {
|
||||
public val OLD_ARGUMENTS_REPLACEMENT_OFFSET: OffsetKey = OffsetKey.create("nonFunctionReplacementOffset")
|
||||
public val MULTIPLE_ARGUMENTS_REPLACEMENT_OFFSET: OffsetKey = OffsetKey.create("multipleArgumentsReplacementOffset")
|
||||
|
||||
+4
-1
@@ -23,6 +23,7 @@ import com.intellij.codeInsight.lookup.LookupElementPresentation
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.idea.completion.ExpectedInfo
|
||||
import org.jetbrains.kotlin.idea.completion.LookupElementFactory
|
||||
import org.jetbrains.kotlin.idea.completion.fuzzyType
|
||||
import org.jetbrains.kotlin.idea.completion.shortenReferences
|
||||
import org.jetbrains.kotlin.idea.core.isVisible
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
@@ -46,7 +47,9 @@ class StaticMembers(
|
||||
context: JetSimpleNameExpression,
|
||||
enumEntriesToSkip: Set<DeclarationDescriptor>) {
|
||||
|
||||
val expectedInfosByClass = expectedInfos.groupBy { TypeUtils.getClassDescriptor(it.fuzzyType.type) }
|
||||
val expectedInfosByClass = expectedInfos.groupBy {
|
||||
expectedInfo -> expectedInfo.fuzzyType?.type?.let { TypeUtils.getClassDescriptor(it) }
|
||||
}
|
||||
for ((classDescriptor, expectedInfosForClass) in expectedInfosByClass) {
|
||||
if (classDescriptor != null && !classDescriptor.getName().isSpecial()) {
|
||||
addToCollection(collection, classDescriptor, expectedInfosForClass, context, enumEntriesToSkip)
|
||||
|
||||
+3
-2
@@ -35,7 +35,6 @@ import org.jetbrains.kotlin.idea.completion.handlers.KotlinFunctionInsertHandler
|
||||
import org.jetbrains.kotlin.idea.core.overrideImplement.ImplementMethodsHandler
|
||||
import org.jetbrains.kotlin.idea.core.psiClassToDescriptor
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||
import org.jetbrains.kotlin.load.java.descriptors.SamConstructorDescriptor
|
||||
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
|
||||
import org.jetbrains.kotlin.psi.JetClassOrObject
|
||||
@@ -48,6 +47,7 @@ import org.jetbrains.kotlin.resolve.PossiblyBareType
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.resolveTopLevelClass
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
|
||||
class TypeInstantiationItems(
|
||||
@@ -64,8 +64,9 @@ class TypeInstantiationItems(
|
||||
inheritanceSearchers: MutableCollection<InheritanceItemsSearcher>,
|
||||
expectedInfos: Collection<ExpectedInfo>
|
||||
) {
|
||||
val expectedInfosGrouped: Map<JetType, List<ExpectedInfo>> = expectedInfos.groupBy { it.fuzzyType.type.makeNotNullable() }
|
||||
val expectedInfosGrouped: Map<JetType?, List<ExpectedInfo>> = expectedInfos.groupBy { it.fuzzyType?.type?.makeNotNullable() }
|
||||
for ((type, infos) in expectedInfosGrouped) {
|
||||
if (type == null) continue
|
||||
val tail = mergeTails(infos.map { it.tail })
|
||||
addTo(items, inheritanceSearchers, type, tail)
|
||||
}
|
||||
|
||||
@@ -133,13 +133,13 @@ private constructor(
|
||||
|
||||
fun Collection<FuzzyType>.classifyExpectedInfo(expectedInfo: ExpectedInfo): ExpectedInfoClassification {
|
||||
val sequence = asSequence()
|
||||
val substitutor = sequence.map { it.checkIsSubtypeOf(expectedInfo.fuzzyType) }.firstOrNull()
|
||||
val substitutor = sequence.map { expectedInfo.matchingSubstitutor(it) }.firstOrNull()
|
||||
if (substitutor != null) {
|
||||
return ExpectedInfoClassification.matches(substitutor)
|
||||
}
|
||||
|
||||
if (sequence.any { it.nullability() == TypeNullability.NULLABLE }) {
|
||||
val substitutor2 = sequence.map { it.makeNotNullable().checkIsSubtypeOf(expectedInfo.fuzzyType) }.firstOrNull()
|
||||
val substitutor2 = sequence.map { expectedInfo.matchingSubstitutor(it.makeNotNullable()) }.firstOrNull()
|
||||
if (substitutor2 != null) {
|
||||
return ExpectedInfoClassification.matchesIfNotNullable(substitutor2)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user