Initial implementation of smart completion after "by"
#KT-11787 Fixed
This commit is contained in:
@@ -158,6 +158,7 @@ class ExpectedInfos(
|
||||
?: calculateForReturn(expressionWithType)
|
||||
?: calculateForLoopRange(expressionWithType)
|
||||
?: calculateForInOperatorArgument(expressionWithType)
|
||||
?: calculateForPropertyDelegate(expressionWithType)
|
||||
?: getFromBindingContext(expressionWithType)
|
||||
?: return emptyList()
|
||||
return expectedInfos.filterNot { it.fuzzyType?.type?.isError ?: false }
|
||||
@@ -590,12 +591,44 @@ class ExpectedInfos(
|
||||
|
||||
val byTypeFilter = object : ByTypeFilter {
|
||||
override fun matchingSubstitutor(descriptorType: FuzzyType): TypeSubstitutor? {
|
||||
return if (detector.hasContains(descriptorType)) TypeSubstitutor.EMPTY else null
|
||||
return if (detector.findOperator(descriptorType) != null) TypeSubstitutor.EMPTY else null
|
||||
}
|
||||
}
|
||||
return listOf(ExpectedInfo(byTypeFilter, null, null))
|
||||
}
|
||||
|
||||
private fun calculateForPropertyDelegate(expressionWithType: KtExpression): Collection<ExpectedInfo>? {
|
||||
val delegate = expressionWithType.parent as? KtPropertyDelegate ?: return null
|
||||
val propertyDeclaration = delegate.parent as? KtProperty ?: return null
|
||||
val property = resolutionFacade.resolveToDescriptor(propertyDeclaration) as? PropertyDescriptor ?: return null
|
||||
|
||||
val scope = expressionWithType.getResolutionScope(bindingContext, resolutionFacade)
|
||||
val propertyOwnerType = property.extensionReceiverParameter?.type
|
||||
?: property.dispatchReceiverParameter?.type
|
||||
?: return null
|
||||
|
||||
val explicitPropertyType = property.returnType?.check { propertyDeclaration.typeReference != null }
|
||||
val typesWithGetDetector = TypesWithGetValueDetector(scope, propertyOwnerType, explicitPropertyType)
|
||||
val typesWithSetDetector = if (property.isVar) TypesWithSetValueDetector(scope, propertyOwnerType) else null
|
||||
|
||||
val byTypeFilter = object : ByTypeFilter {
|
||||
override fun matchingSubstitutor(descriptorType: FuzzyType): TypeSubstitutor? {
|
||||
val getValueOperator = typesWithGetDetector.findOperator(descriptorType) ?: return null
|
||||
|
||||
if (typesWithSetDetector != null) {
|
||||
val setValueOperator = typesWithSetDetector.findOperator(descriptorType) ?: return null
|
||||
val propertyType = explicitPropertyType ?: getValueOperator.returnType!!
|
||||
val setParamType = FuzzyType(setValueOperator.valueParameters.last().type, setValueOperator.typeParameters)
|
||||
if (setParamType.checkIsSuperTypeOf(propertyType) == null) return null
|
||||
}
|
||||
|
||||
return TypeSubstitutor.EMPTY //TODO: substitutor from property type
|
||||
}
|
||||
}
|
||||
return listOf(ExpectedInfo(byTypeFilter, null, null))
|
||||
//TODO: special items for "Delegates...."
|
||||
}
|
||||
|
||||
private fun getFromBindingContext(expressionWithType: KtExpression): Collection<ExpectedInfo>? {
|
||||
val expectedType = bindingContext[BindingContext.EXPECTED_EXPRESSION_TYPE, expressionWithType] ?: return null
|
||||
return listOf(ExpectedInfo(expectedType, null, null))
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.core
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.idea.util.FuzzyType
|
||||
import org.jetbrains.kotlin.idea.util.nullability
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.collectFunctions
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.typeUtil.TypeNullability
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlin.util.isValidOperator
|
||||
import java.util.*
|
||||
|
||||
internal class TypesWithContainsDetector(
|
||||
private val scope: LexicalScope,
|
||||
private val argumentType: KotlinType
|
||||
) {
|
||||
private val cache = HashMap<FuzzyType, Boolean>()
|
||||
|
||||
private val typesWithExtensionContains: Collection<KotlinType> = scope
|
||||
.collectFunctions(OperatorNameConventions.CONTAINS, NoLookupLocation.FROM_IDE)
|
||||
.filter { it.extensionReceiverParameter != null && isGoodContainsFunction(it, listOf()) }
|
||||
.map { it.extensionReceiverParameter!!.type }
|
||||
|
||||
fun hasContains(type: FuzzyType): Boolean {
|
||||
return cache.getOrPut(type, { hasContainsNoCache(type) })
|
||||
}
|
||||
|
||||
private fun hasContainsNoCache(type: FuzzyType): Boolean {
|
||||
return type.nullability() != TypeNullability.NULLABLE &&
|
||||
type.type.memberScope.getContributedFunctions(OperatorNameConventions.CONTAINS, NoLookupLocation.FROM_IDE).any { isGoodContainsFunction(it, type.freeParameters) }
|
||||
|| typesWithExtensionContains.any { type.checkIsSubtypeOf(it) != null }
|
||||
}
|
||||
|
||||
private fun isGoodContainsFunction(function: FunctionDescriptor, freeTypeParams: Collection<TypeParameterDescriptor>): Boolean {
|
||||
if (!function.isValidOperator()) return false
|
||||
val parameter = function.valueParameters.single()
|
||||
val fuzzyParameterType = FuzzyType(parameter.type, function.typeParameters + freeTypeParams)
|
||||
return fuzzyParameterType.checkIsSuperTypeOf(argumentType) != null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.core
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.idea.util.FuzzyType
|
||||
import org.jetbrains.kotlin.idea.util.nullability
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.collectFunctions
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.typeUtil.TypeNullability
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlin.util.isValidOperator
|
||||
import java.util.*
|
||||
|
||||
//TODO: support not imported extensions
|
||||
abstract class TypesWithOperatorDetector(
|
||||
private val name: Name,
|
||||
private val scope: LexicalScope
|
||||
) {
|
||||
protected abstract fun isSuitableByType(function: FunctionDescriptor, freeTypeParams: Collection<TypeParameterDescriptor>): Boolean
|
||||
|
||||
private val cache = HashMap<FuzzyType, FunctionDescriptor?>()
|
||||
|
||||
private val typesWithExtension: Map<KotlinType, FunctionDescriptor> by lazy {
|
||||
scope.collectFunctions(name, NoLookupLocation.FROM_IDE)
|
||||
.filter { it.extensionReceiverParameter != null && it.isValidOperator() && isSuitableByType(it, it.typeParameters) }
|
||||
.map { it.extensionReceiverParameter!!.type to it }
|
||||
.toMap()
|
||||
}
|
||||
|
||||
fun findOperator(type: FuzzyType): FunctionDescriptor? {
|
||||
if (cache.containsKey(type)) {
|
||||
return cache[type]
|
||||
}
|
||||
else {
|
||||
val result = findOperatorNoCache(type)
|
||||
cache[type] = result
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
private fun findOperatorNoCache(type: FuzzyType): FunctionDescriptor? {
|
||||
if (type.nullability() != TypeNullability.NULLABLE) {
|
||||
val memberFunction = type.type.memberScope.getContributedFunctions(name, NoLookupLocation.FROM_IDE)
|
||||
.firstOrNull { it.isValidOperator() && isSuitableByType(it, type.freeParameters) }
|
||||
if (memberFunction != null) return memberFunction
|
||||
}
|
||||
|
||||
for ((typeWithExtension, operator) in typesWithExtension) {
|
||||
if (type.checkIsSubtypeOf(typeWithExtension) != null) {
|
||||
return operator //TODO: substitution
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
class TypesWithContainsDetector(
|
||||
scope: LexicalScope,
|
||||
private val argumentType: KotlinType
|
||||
) : TypesWithOperatorDetector(OperatorNameConventions.CONTAINS, scope) {
|
||||
|
||||
override fun isSuitableByType(function: FunctionDescriptor, freeTypeParams: Collection<TypeParameterDescriptor>): Boolean {
|
||||
val parameter = function.valueParameters.single()
|
||||
val fuzzyParameterType = FuzzyType(parameter.type, function.typeParameters + freeTypeParams)
|
||||
return fuzzyParameterType.checkIsSuperTypeOf(argumentType) != null
|
||||
}
|
||||
}
|
||||
|
||||
class TypesWithGetValueDetector(
|
||||
scope: LexicalScope,
|
||||
private val propertyOwnerType: KotlinType,
|
||||
private val propertyType: KotlinType?
|
||||
) : TypesWithOperatorDetector(OperatorNameConventions.GET_VALUE, scope) {
|
||||
|
||||
override fun isSuitableByType(function: FunctionDescriptor, freeTypeParams: Collection<TypeParameterDescriptor>): Boolean {
|
||||
val paramType = FuzzyType(function.valueParameters.first().type, freeTypeParams)
|
||||
if (paramType.checkIsSuperTypeOf(propertyOwnerType) == null) return false
|
||||
|
||||
if (propertyType != null) {
|
||||
val returnType = FuzzyType(function.returnType ?: return false, freeTypeParams)
|
||||
return returnType.checkIsSubtypeOf(propertyType) != null
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
class TypesWithSetValueDetector(
|
||||
scope: LexicalScope,
|
||||
private val propertyOwnerType: KotlinType
|
||||
) : TypesWithOperatorDetector(OperatorNameConventions.SET_VALUE, scope) {
|
||||
|
||||
override fun isSuitableByType(function: FunctionDescriptor, freeTypeParams: Collection<TypeParameterDescriptor>): Boolean {
|
||||
val paramType = FuzzyType(function.valueParameters.first().type, freeTypeParams)
|
||||
return paramType.checkIsSuperTypeOf(propertyOwnerType) != null
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user