Basic completion shows items from smart completion after "as" + do not insert any tail in basic completion

This commit is contained in:
Valentin Kipyatkov
2015-08-06 20:23:50 +03:00
parent be0a0e4401
commit e6f5cefce3
16 changed files with 216 additions and 91 deletions
@@ -30,8 +30,9 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.completion.smart.SMART_COMPLETION_ITEM_PRIORITY_KEY
import org.jetbrains.kotlin.idea.completion.smart.SmartCompletion import org.jetbrains.kotlin.idea.completion.smart.SmartCompletion
import org.jetbrains.kotlin.idea.completion.smart.toExpressionWithType import org.jetbrains.kotlin.idea.completion.smart.SmartCompletionItemPriority
import org.jetbrains.kotlin.idea.project.ProjectStructureUtil import org.jetbrains.kotlin.idea.project.ProjectStructureUtil
import org.jetbrains.kotlin.idea.stubindex.PackageIndexUtil import org.jetbrains.kotlin.idea.stubindex.PackageIndexUtil
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
@@ -95,13 +96,9 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration,
else else
null null
private val expectedInfos = if (expression != null)
ExpectedInfos(bindingContext, resolutionFacade, moduleDescriptor).calculate(expression.toExpressionWithType())
else
null
private val smartCompletion = expression?.let { private val smartCompletion = expression?.let {
SmartCompletion(it, resolutionFacade, moduleDescriptor, bindingContext, isVisibleFilter, inDescriptor, prefixMatcher, GlobalSearchScope.EMPTY_SCOPE, toFromOriginalFileMapper, lookupElementFactory) SmartCompletion(it, resolutionFacade, moduleDescriptor, bindingContext, isVisibleFilter, inDescriptor, prefixMatcher,
GlobalSearchScope.EMPTY_SCOPE, toFromOriginalFileMapper, lookupElementFactory, forBasicCompletion = true)
} }
private fun calcCompletionKind(): CompletionKind { private fun calcCompletionKind(): CompletionKind {
@@ -198,6 +195,20 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration,
} }
if (completionKind != CompletionKind.NAMED_ARGUMENTS_ONLY) { if (completionKind != CompletionKind.NAMED_ARGUMENTS_ONLY) {
if (smartCompletion != null) {
@suppress("UNUSED_VARIABLE") // we don't use InheritanceSearcher
val (additionalItems, inheritanceSearcher) = smartCompletion.additionalItems()
// all additional items should have SMART_COMPLETION_ITEM_PRIORITY_KEY to be recognized by SmartCompletionInBasicWeigher
for (item in additionalItems) {
if (item.getUserData(SMART_COMPLETION_ITEM_PRIORITY_KEY) == null) {
item.putUserData(SMART_COMPLETION_ITEM_PRIORITY_KEY, SmartCompletionItemPriority.DEFAULT)
}
}
collector.addElements(additionalItems)
}
collector.addDescriptorElements(referenceVariants, suppressAutoInsertion = false) collector.addDescriptorElements(referenceVariants, suppressAutoInsertion = false)
val keywordsPrefix = prefix.substringBefore('@') // if there is '@' in the prefix - use shorter prefix to not loose 'this' etc val keywordsPrefix = prefix.substringBefore('@') // if there is '@' in the prefix - use shorter prefix to not loose 'this' etc
@@ -251,12 +262,6 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration,
} }
if (completionKind != CompletionKind.KEYWORDS_ONLY) { if (completionKind != CompletionKind.KEYWORDS_ONLY) {
if (smartCompletion != null && expectedInfos != null && expectedInfos.isNotEmpty()) {
@suppress("UNUSED_VARIABLE") // we don't use InheritanceSearcher
val (additionalItems, inheritanceSearcher) = smartCompletion.additionalItems(expectedInfos, forOrdinaryCompletion = true)
collector.addElements(additionalItems)
}
flushToResultSet() flushToResultSet()
if (!configuration.completeNonImportedDeclarations && isNoQualifierContext()) { if (!configuration.completeNonImportedDeclarations && isNoQualifierContext()) {
@@ -300,8 +305,8 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration,
sorter = sorter.weighBefore(DeprecatedWeigher.toString(), ParameterNameAndTypeCompletion.Weigher) sorter = sorter.weighBefore(DeprecatedWeigher.toString(), ParameterNameAndTypeCompletion.Weigher)
} }
if (smartCompletion != null && expectedInfos != null && expectedInfos.isNotEmpty()) { if (smartCompletion != null) {
sorter = sorter.weighBefore(KindWeigher.toString(), ExpectedInfoMatchWeigher(expectedInfos, smartCompletion), SmartCompletionPriorityWeigher) sorter = sorter.weighBefore(KindWeigher.toString(), SmartCompletionInBasicWeigher(smartCompletion), SmartCompletionPriorityWeigher)
} }
return sorter return sorter
@@ -315,17 +315,23 @@ fun LookupElementFactory.createLookupElementForType(type: JetType): LookupElemen
val itemText = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(type) val itemText = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(type)
return object : BaseTypeLookupElement(type, baseLookupElement) { val typeLookupElement = object : BaseTypeLookupElement(type, baseLookupElement) {
override fun renderElement(presentation: LookupElementPresentation) { override fun renderElement(presentation: LookupElementPresentation) {
super.renderElement(presentation) super.renderElement(presentation)
presentation.setItemText(itemText) presentation.setItemText(itemText)
} }
} }
// if type is simply classifier without anything else, use classifier's lookup element to avoid duplicates (works after "as" in basic completion)
return if (typeLookupElement.fullText == IdeDescriptorRenderers.SOURCE_CODE.renderClassifierName(classifier))
baseLookupElement
else
typeLookupElement
} }
} }
private open class BaseTypeLookupElement(type: JetType, baseLookupElement: LookupElement) : LookupElementDecorator<LookupElement>(baseLookupElement) { private open class BaseTypeLookupElement(type: JetType, baseLookupElement: LookupElement) : LookupElementDecorator<LookupElement>(baseLookupElement) {
private val fullText = IdeDescriptorRenderers.SOURCE_CODE.renderType(type) val fullText = IdeDescriptorRenderers.SOURCE_CODE.renderType(type)
override fun equals(other: Any?) = other is BaseTypeLookupElement && fullText == other.fullText override fun equals(other: Any?) = other is BaseTypeLookupElement && fullText == other.fullText
override fun hashCode() = fullText.hashCode() override fun hashCode() = fullText.hashCode()
@@ -86,16 +86,36 @@ class ByExpectedTypeFilter(val fuzzyType: FuzzyType) : ByTypeFilter {
override fun hashCode() = fuzzyType.hashCode() override fun hashCode() = fuzzyType.hashCode()
} }
open data class ExpectedInfo(val filter: ByTypeFilter, val expectedName: String?, val tail: Tail?, val itemOptions: ItemOptions = ItemOptions.DEFAULT) { data class ExpectedInfo(
constructor(fuzzyType: FuzzyType, expectedName: String?, tail: Tail?, itemOptions: ItemOptions = ItemOptions.DEFAULT) val filter: ByTypeFilter,
: this(ByExpectedTypeFilter(fuzzyType), expectedName, tail, itemOptions) val expectedName: String?,
val tail: Tail?,
val itemOptions: ItemOptions = ItemOptions.DEFAULT,
val additionalData: ExpectedInfo.AdditionalData? = null
) {
// just a marker interface
interface AdditionalData {}
constructor(type: JetType, expectedName: String?, tail: Tail?, itemOptions: ItemOptions = ItemOptions.DEFAULT) constructor(fuzzyType: FuzzyType, expectedName: String?, tail: Tail?, itemOptions: ItemOptions = ItemOptions.DEFAULT, additionalData: ExpectedInfo.AdditionalData? = null)
: this(FuzzyType(type, emptyList()), expectedName, tail, itemOptions) : this(ByExpectedTypeFilter(fuzzyType), expectedName, tail, itemOptions, additionalData)
constructor(type: JetType, expectedName: String?, tail: Tail?, itemOptions: ItemOptions = ItemOptions.DEFAULT, additionalData: ExpectedInfo.AdditionalData? = null)
: this(FuzzyType(type, emptyList()), expectedName, tail, itemOptions, additionalData)
fun matchingSubstitutor(descriptorType: FuzzyType): TypeSubstitutor? = filter.matchingSubstitutor(descriptorType) fun matchingSubstitutor(descriptorType: FuzzyType): TypeSubstitutor? = filter.matchingSubstitutor(descriptorType)
fun matchingSubstitutor(descriptorType: JetType): TypeSubstitutor? = matchingSubstitutor(FuzzyType(descriptorType, emptyList())) fun matchingSubstitutor(descriptorType: JetType): TypeSubstitutor? = matchingSubstitutor(FuzzyType(descriptorType, emptyList()))
companion object {
fun createForArgument(type: JetType, name: String?, tail: Tail?, function: FunctionDescriptor, position: ArgumentPosition, itemOptions: ItemOptions = ItemOptions.DEFAULT): ExpectedInfo {
return ExpectedInfo(FuzzyType(type, function.typeParameters), name, tail, itemOptions, ArgumentAdditionalData(function, position))
}
fun createForReturnValue(type: JetType?, callable: CallableDescriptor): ExpectedInfo {
val filter = if (type != null) ByExpectedTypeFilter(FuzzyType(type, emptyList())) else ByTypeFilter.All
return ExpectedInfo(filter, callable.name.asString(), null, additionalData = ReturnValueAdditionalData(callable))
}
}
} }
val ExpectedInfo.fuzzyType: FuzzyType? val ExpectedInfo.fuzzyType: FuzzyType?
@@ -106,26 +126,17 @@ data class ArgumentPosition(val argumentIndex: Int, val argumentName: Name?, val
constructor(argumentIndex: Int, argumentName: Name?) : this(argumentIndex, argumentName, false) constructor(argumentIndex: Int, argumentName: Name?) : this(argumentIndex, argumentName, false)
} }
class ArgumentExpectedInfo(type: JetType, name: String?, tail: Tail?, val function: FunctionDescriptor, val position: ArgumentPosition, itemOptions: ItemOptions = ItemOptions.DEFAULT) class ArgumentAdditionalData(val function: FunctionDescriptor, val position: ArgumentPosition) : ExpectedInfo.AdditionalData {
: ExpectedInfo(FuzzyType(type, function.typeParameters), name, tail, itemOptions) {
override fun equals(other: Any?) override fun equals(other: Any?)
= other is ArgumentExpectedInfo && super.equals(other) && function == other.function && position == other.position = other is ArgumentAdditionalData && function == other.function && position == other.position
override fun hashCode() override fun hashCode()
= function.hashCode() = function.hashCode()
} }
class ReturnValueExpectedInfo( class ReturnValueAdditionalData(val callable: CallableDescriptor) : ExpectedInfo.AdditionalData {
type: JetType?,
val callable: CallableDescriptor
) : ExpectedInfo(
if (type != null) ByExpectedTypeFilter(FuzzyType(type, emptyList())) else ByTypeFilter.All,
callable.name.asString(),
null
) {
override fun equals(other: Any?) override fun equals(other: Any?)
= other is ReturnValueExpectedInfo && super.equals(other) && callable == other.callable = other is ReturnValueAdditionalData && callable == other.callable
override fun hashCode() override fun hashCode()
= callable.hashCode() = callable.hashCode()
@@ -180,7 +191,14 @@ class ExpectedInfos(
public fun calculateForArgument(call: Call, argument: ValueArgument): Collection<ExpectedInfo>? { public fun calculateForArgument(call: Call, argument: ValueArgument): Collection<ExpectedInfo>? {
val results = calculateForArgument(call, TypeUtils.NO_EXPECTED_TYPE, argument) ?: return null val results = calculateForArgument(call, TypeUtils.NO_EXPECTED_TYPE, argument) ?: return null
if (useOuterCallsExpectedTypeCount > 0 && results.any { it.fuzzyType != null && it.fuzzyType!!.freeParameters.isNotEmpty() && it.function.fuzzyReturnType()?.freeParameters?.isNotEmpty() ?: false }) { fun makesSenseToUseOuterCallExpectedType(info: ExpectedInfo): Boolean {
val data = info.additionalData as ArgumentAdditionalData
return info.fuzzyType != null
&& info.fuzzyType!!.freeParameters.isNotEmpty()
&& data.function.fuzzyReturnType()?.freeParameters?.isNotEmpty() ?: false
}
if (useOuterCallsExpectedTypeCount > 0 && results.any(::makesSenseToUseOuterCallExpectedType)) {
val callExpression = (call.callElement as? JetExpression)?.getQualifiedExpressionForSelectorOrThis() ?: return results val callExpression = (call.callElement as? JetExpression)?.getQualifiedExpressionForSelectorOrThis() ?: return results
val expectedFuzzyTypes = ExpectedInfos(bindingContext, resolutionFacade, moduleDescriptor, useHeuristicSignatures, useOuterCallsExpectedTypeCount - 1) val expectedFuzzyTypes = ExpectedInfos(bindingContext, resolutionFacade, moduleDescriptor, useHeuristicSignatures, useOuterCallsExpectedTypeCount - 1)
.calculate(callExpression) .calculate(callExpression)
@@ -198,7 +216,7 @@ class ExpectedInfos(
return results return results
} }
private fun calculateForArgument(call: Call, callExpectedType: JetType, argument: ValueArgument): Collection<ArgumentExpectedInfo>? { private fun calculateForArgument(call: Call, callExpectedType: JetType, argument: ValueArgument): Collection<ExpectedInfo>? {
val argumentIndex = call.getValueArguments().indexOf(argument) val argumentIndex = call.getValueArguments().indexOf(argument)
assert(argumentIndex >= 0) { assert(argumentIndex >= 0) {
"Could not find argument '$argument' among arguments of call: $call" "Could not find argument '$argument' among arguments of call: $call"
@@ -229,7 +247,7 @@ class ExpectedInfos(
val callResolver = createContainerForMacros(project, moduleDescriptor).callResolver val callResolver = createContainerForMacros(project, moduleDescriptor).callResolver
val results: OverloadResolutionResults<FunctionDescriptor> = callResolver.resolveFunctionCall(callResolutionContext) val results: OverloadResolutionResults<FunctionDescriptor> = callResolver.resolveFunctionCall(callResolutionContext)
val expectedInfos = LinkedHashSet<ArgumentExpectedInfo>() val expectedInfos = LinkedHashSet<ExpectedInfo>()
for (candidate: ResolvedCall<FunctionDescriptor> in results.getAllCandidates()!!) { for (candidate: ResolvedCall<FunctionDescriptor> in results.getAllCandidates()!!) {
val status = candidate.getStatus() val status = candidate.getStatus()
if (status == ResolutionStatus.RECEIVER_TYPE_ERROR || status == ResolutionStatus.RECEIVER_PRESENCE_ERROR) continue if (status == ResolutionStatus.RECEIVER_TYPE_ERROR || status == ResolutionStatus.RECEIVER_PRESENCE_ERROR) continue
@@ -289,11 +307,11 @@ class ExpectedInfos(
tail tail
if (!alreadyHasStar) { if (!alreadyHasStar) {
expectedInfos.add(ArgumentExpectedInfo(varargElementType, expectedName?.unpluralize(), varargTail, descriptor, argumentPosition)) expectedInfos.add(ExpectedInfo.createForArgument(varargElementType, expectedName?.unpluralize(), varargTail, descriptor, argumentPosition))
} }
val starOptions = if (!alreadyHasStar) ItemOptions.STAR_PREFIX else ItemOptions.DEFAULT val starOptions = if (!alreadyHasStar) ItemOptions.STAR_PREFIX else ItemOptions.DEFAULT
expectedInfos.add(ArgumentExpectedInfo(parameter.getType(), expectedName, varargTail, descriptor, argumentPosition, starOptions)) expectedInfos.add(ExpectedInfo.createForArgument(parameter.getType(), expectedName, varargTail, descriptor, argumentPosition, starOptions))
} }
else { else {
if (alreadyHasStar) continue if (alreadyHasStar) continue
@@ -305,11 +323,11 @@ class ExpectedInfos(
if (isFunctionLiteralArgument) { if (isFunctionLiteralArgument) {
if (KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(parameterType)) { if (KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(parameterType)) {
expectedInfos.add(ArgumentExpectedInfo(parameterType, expectedName, null, descriptor, argumentPosition)) expectedInfos.add(ExpectedInfo.createForArgument(parameterType, expectedName, null, descriptor, argumentPosition))
} }
} }
else { else {
expectedInfos.add(ArgumentExpectedInfo(parameterType, expectedName, tail, descriptor, argumentPosition)) expectedInfos.add(ExpectedInfo.createForArgument(parameterType, expectedName, tail, descriptor, argumentPosition))
} }
} }
} }
@@ -457,18 +475,18 @@ class ExpectedInfos(
return functionReturnValueExpectedInfo(descriptor, expectType = true).singletonOrEmptyList() return functionReturnValueExpectedInfo(descriptor, expectType = true).singletonOrEmptyList()
} }
private fun functionReturnValueExpectedInfo(descriptor: FunctionDescriptor, expectType: Boolean): ReturnValueExpectedInfo? { private fun functionReturnValueExpectedInfo(descriptor: FunctionDescriptor, expectType: Boolean): ExpectedInfo? {
return when (descriptor) { return when (descriptor) {
is SimpleFunctionDescriptor -> { is SimpleFunctionDescriptor -> {
val expectedType = if (expectType) descriptor.returnType else null val expectedType = if (expectType) descriptor.returnType else null
ReturnValueExpectedInfo(expectedType, descriptor) ExpectedInfo.createForReturnValue(expectedType, descriptor)
} }
is PropertyGetterDescriptor -> { is PropertyGetterDescriptor -> {
if (descriptor !is PropertyGetterDescriptor) return null if (descriptor !is PropertyGetterDescriptor) return null
val property = descriptor.getCorrespondingProperty() val property = descriptor.getCorrespondingProperty()
val expectedType = if (expectType) property.type else null val expectedType = if (expectType) property.type else null
ReturnValueExpectedInfo(expectedType, property) ExpectedInfo.createForReturnValue(expectedType, property)
} }
else -> null else -> null
@@ -186,13 +186,9 @@ class DeclarationRemotenessWeigher(private val file: JetFile) : LookupElementWei
} }
} }
class ExpectedInfoMatchWeigher( class SmartCompletionInBasicWeigher(private val smartCompletion: SmartCompletion) : LookupElementWeigher("kotlin.smartInBasic") {
private val expectedInfos: Collection<ExpectedInfo>,
private val smartCompletion: SmartCompletion
) : LookupElementWeigher("kotlin.expectedInfoMatch") {
private val smartCastCalculator = smartCompletion.smartCastCalculator
private val descriptorsToSkip = smartCompletion.descriptorsToSkip private val descriptorsToSkip = smartCompletion.descriptorsToSkip
private val expectedInfos = smartCompletion.expectedInfos
private fun fullMatchWeight(nameSimilarity: Int): Long { private fun fullMatchWeight(nameSimilarity: Int): Long {
return -((3L shl 32) + nameSimilarity) return -((3L shl 32) + nameSimilarity)
@@ -218,10 +214,16 @@ class ExpectedInfoMatchWeigher(
// TODO: keywords with type // TODO: keywords with type
val o = element.`object` val o = element.`object`
if ((o as? DeclarationLookupObject)?.descriptor in descriptorsToSkip) return DESCRIPTOR_TO_SKIP_WEIGHT
if (expectedInfos == null || expectedInfos.isEmpty()) return NO_MATCH_WEIGHT
val smartCastCalculator = smartCompletion.smartCastCalculator
val (fuzzyTypes, name) = when (o) { val (fuzzyTypes, name) = when (o) {
is DeclarationLookupObject -> { is DeclarationLookupObject -> {
val descriptor = o.descriptor ?: return NO_MATCH_WEIGHT val descriptor = o.descriptor ?: return NO_MATCH_WEIGHT
if (descriptor in descriptorsToSkip) return DESCRIPTOR_TO_SKIP_WEIGHT
descriptor.fuzzyTypesForSmartCompletion(smartCastCalculator) to descriptor.name descriptor.fuzzyTypesForSmartCompletion(smartCastCalculator) to descriptor.name
} }
@@ -43,8 +43,8 @@ class MultipleArgumentsItemProvider(val bindingContext: BindingContext,
val added = HashSet<String>() val added = HashSet<String>()
for (expectedInfo in expectedInfos) { for (expectedInfo in expectedInfos) {
if (expectedInfo is ArgumentExpectedInfo && expectedInfo.position == ArgumentPosition(0)) { if (expectedInfo.additionalData is ArgumentAdditionalData && expectedInfo.additionalData.position == ArgumentPosition(0)) {
val parameters = expectedInfo.function.getValueParameters() val parameters = expectedInfo.additionalData.function.valueParameters
if (parameters.size() > 1) { if (parameters.size() > 1) {
val variables = ArrayList<VariableDescriptor>() val variables = ArrayList<VariableDescriptor>()
for ((i, parameter) in parameters.withIndex()) { for ((i, parameter) in parameters.withIndex()) {
@@ -48,18 +48,22 @@ interface InheritanceItemsSearcher {
} }
class SmartCompletion( class SmartCompletion(
val expression: JetExpression, private val expression: JetExpression,
val resolutionFacade: ResolutionFacade, private val resolutionFacade: ResolutionFacade,
val moduleDescriptor: ModuleDescriptor, private val moduleDescriptor: ModuleDescriptor,
val bindingContext: BindingContext, private val bindingContext: BindingContext,
val visibilityFilter: (DeclarationDescriptor) -> Boolean, private val visibilityFilter: (DeclarationDescriptor) -> Boolean,
val inDescriptor: DeclarationDescriptor, private val inDescriptor: DeclarationDescriptor,
val prefixMatcher: PrefixMatcher, private val prefixMatcher: PrefixMatcher,
val inheritorSearchScope: GlobalSearchScope, private val inheritorSearchScope: GlobalSearchScope,
val toFromOriginalFileMapper: ToFromOriginalFileMapper, private val toFromOriginalFileMapper: ToFromOriginalFileMapper,
val lookupElementFactory: LookupElementFactory private val lookupElementFactory: LookupElementFactory,
private val forBasicCompletion: Boolean = false
) { ) {
private val receiver = if (expression is JetSimpleNameExpression) expression.getReceiverExpression() else null private val receiver = if (expression is JetSimpleNameExpression) expression.getReceiverExpression() else null
private val expressionWithType = receiver?.parent as? JetExpression ?: expression
public val expectedInfos: Collection<ExpectedInfo>? = calcExpectedInfos(expressionWithType)
public val smartCastCalculator: SmartCastCalculator = SmartCastCalculator(bindingContext, moduleDescriptor, expression) public val smartCastCalculator: SmartCastCalculator = SmartCastCalculator(bindingContext, moduleDescriptor, expression)
@@ -107,11 +111,11 @@ class SmartCompletion(
} }
private fun executeInternal(): Result? { private fun executeInternal(): Result? {
val asTypePositionResult = buildForAsTypePosition() val (additionalItems, inheritanceSearcher) = additionalItems()
if (asTypePositionResult != null) return asTypePositionResult
val expectedInfos = calcExpectedInfos(expression.toExpressionWithType()) if (expectedInfos == null || expectedInfos.isEmpty()) {
if (expectedInfos == null || expectedInfos.isEmpty()) return null return Result(null, additionalItems, inheritanceSearcher)
}
fun filterDeclaration(descriptor: DeclarationDescriptor): Collection<LookupElement> { fun filterDeclaration(descriptor: DeclarationDescriptor): Collection<LookupElement> {
if (descriptor in descriptorsToSkip) return emptyList() if (descriptor in descriptorsToSkip) return emptyList()
@@ -137,26 +141,28 @@ class SmartCompletion(
return result return result
} }
val (additionalItems, inheritanceSearcher) = additionalItems(expectedInfos)
return Result(::filterDeclaration, additionalItems, inheritanceSearcher) return Result(::filterDeclaration, additionalItems, inheritanceSearcher)
} }
public fun additionalItems( public fun additionalItems(): Pair<Collection<LookupElement>, InheritanceItemsSearcher?> {
expectedInfos: Collection<ExpectedInfo>, val asTypePositionItems = buildForAsTypePosition()
forOrdinaryCompletion: Boolean = false if (asTypePositionItems != null) {
): Pair<Collection<LookupElement>, InheritanceItemsSearcher?> { assert(expectedInfos == null)
return Pair(asTypePositionItems, null)
}
val items = ArrayList<LookupElement>() val items = ArrayList<LookupElement>()
val inheritanceSearchers = ArrayList<InheritanceItemsSearcher>() val inheritanceSearchers = ArrayList<InheritanceItemsSearcher>()
if (receiver == null) {
TypeInstantiationItems(resolutionFacade, moduleDescriptor, bindingContext, visibilityFilter, toFromOriginalFileMapper, inheritorSearchScope, lookupElementFactory, forOrdinaryCompletion) if (expectedInfos != null && expectedInfos.isNotEmpty() && receiver == null) {
TypeInstantiationItems(resolutionFacade, moduleDescriptor, bindingContext, visibilityFilter, toFromOriginalFileMapper, inheritorSearchScope, lookupElementFactory, forBasicCompletion)
.addTo(items, inheritanceSearchers, expectedInfos) .addTo(items, inheritanceSearchers, expectedInfos)
if (expression is JetSimpleNameExpression) { if (expression is JetSimpleNameExpression) {
StaticMembers(bindingContext, lookupElementFactory).addToCollection(items, expectedInfos, expression, descriptorsToSkip) StaticMembers(bindingContext, lookupElementFactory).addToCollection(items, expectedInfos, expression, descriptorsToSkip)
} }
if (!forOrdinaryCompletion) { if (!forBasicCompletion) {
items.addThisItems(expression, expectedInfos, smartCastCalculator) items.addThisItems(expression, expectedInfos, smartCastCalculator)
LambdaItems.addToCollection(items, expectedInfos) LambdaItems.addToCollection(items, expectedInfos)
@@ -193,6 +199,12 @@ class SmartCompletion(
} }
private fun calcExpectedInfos(expression: JetExpression): Collection<ExpectedInfo>? { private fun calcExpectedInfos(expression: JetExpression): Collection<ExpectedInfo>? {
if (forBasicCompletion) {
return ExpectedInfos(bindingContext, resolutionFacade, moduleDescriptor, useOuterCallsExpectedTypeCount = 0)
.calculate(expression)
?.map { it.copy(tail = null) }
}
// if our expression is initializer of implicitly typed variable - take type of variable from original file (+ the same for function) // if our expression is initializer of implicitly typed variable - take type of variable from original file (+ the same for function)
val declaration = implicitlyTypedDeclarationFromInitializer(expression) val declaration = implicitlyTypedDeclarationFromInitializer(expression)
if (declaration != null) { if (declaration != null) {
@@ -227,7 +239,6 @@ class SmartCompletion(
} }
public val descriptorsToSkip: Set<DeclarationDescriptor> by lazy<Set<DeclarationDescriptor>> { public val descriptorsToSkip: Set<DeclarationDescriptor> by lazy<Set<DeclarationDescriptor>> {
val expressionWithType = expression.toExpressionWithType()
val parent = expressionWithType.getParent() val parent = expressionWithType.getParent()
when (parent) { when (parent) {
is JetBinaryExpression -> { is JetBinaryExpression -> {
@@ -324,7 +335,7 @@ class SmartCompletion(
return null return null
} }
private fun buildForAsTypePosition(): Result? { private fun buildForAsTypePosition(): Collection<LookupElement>? {
val binaryExpression = ((expression.getParent() as? JetUserType) val binaryExpression = ((expression.getParent() as? JetUserType)
?.getParent() as? JetTypeReference) ?.getParent() as? JetTypeReference)
?.getParent() as? JetBinaryExpressionWithTypeRHS ?.getParent() as? JetBinaryExpressionWithTypeRHS
@@ -341,7 +352,7 @@ class SmartCompletion(
val lookupElement = lookupElementFactory.createLookupElementForType(type) ?: continue val lookupElement = lookupElementFactory.createLookupElementForType(type) ?: continue
items.add(lookupElement.addTailAndNameSimilarity(infos)) items.add(lookupElement.addTailAndNameSimilarity(infos))
} }
return Result(null, items, null) return items
} }
companion object { companion object {
@@ -29,9 +29,6 @@ import org.jetbrains.kotlin.idea.completion.*
import org.jetbrains.kotlin.idea.completion.handlers.WithExpressionPrefixInsertHandler import org.jetbrains.kotlin.idea.completion.handlers.WithExpressionPrefixInsertHandler
import org.jetbrains.kotlin.idea.completion.handlers.WithTailInsertHandler import org.jetbrains.kotlin.idea.completion.handlers.WithTailInsertHandler
import org.jetbrains.kotlin.idea.util.* import org.jetbrains.kotlin.idea.util.*
import org.jetbrains.kotlin.psi.JetExpression
import org.jetbrains.kotlin.psi.JetSimpleNameExpression
import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.types.JetType import org.jetbrains.kotlin.types.JetType
@@ -41,7 +38,6 @@ import org.jetbrains.kotlin.types.typeUtil.isNothing
import org.jetbrains.kotlin.util.descriptorsEqualWithSubstitution import org.jetbrains.kotlin.util.descriptorsEqualWithSubstitution
import java.util.ArrayList import java.util.ArrayList
import java.util.HashMap import java.util.HashMap
import java.util.HashSet
class ArtificialElementInsertHandler( class ArtificialElementInsertHandler(
val textBeforeCaret: String, val textAfterCaret: String, val shortenRefs: Boolean) : InsertHandler<LookupElement>{ val textBeforeCaret: String, val textAfterCaret: String, val shortenRefs: Boolean) : InsertHandler<LookupElement>{
@@ -59,8 +55,7 @@ class ArtificialElementInsertHandler(
} }
fun mergeTails(tails: Collection<Tail?>): Tail? { fun mergeTails(tails: Collection<Tail?>): Tail? {
if (tails.size() == 1) return tails.single() return tails.singleOrNull() ?: tails.toSet().singleOrNull()
return if (HashSet(tails).size() == 1) tails.first() else null
} }
fun LookupElement.addTail(tail: Tail?): LookupElement { fun LookupElement.addTail(tail: Tail?): LookupElement {
@@ -187,7 +182,7 @@ fun<TDescriptor: DeclarationDescriptor?> MutableCollection<LookupElement>.addLoo
val lookupElement = itemData.createLookupElement() val lookupElement = itemData.createLookupElement()
if (lookupElement != null) { if (lookupElement != null) {
val nameSimilarityInfos = if (noNameSimilarityForReturnItself && descriptor is CallableDescriptor) { val nameSimilarityInfos = if (noNameSimilarityForReturnItself && descriptor is CallableDescriptor) {
infos.filter { (it as? ReturnValueExpectedInfo)?.callable != descriptor } // do not calculate name similarity with function itself in its return infos.filter { (it.additionalData as? ReturnValueAdditionalData)?.callable != descriptor } // do not calculate name similarity with function itself in its return
} }
else else
infos infos
@@ -343,10 +338,6 @@ fun DeclarationDescriptor.fuzzyTypesForSmartCompletion(smartCastCalculator: Smar
} }
} }
fun JetExpression.toExpressionWithType(): JetExpression {
return (this as? JetSimpleNameExpression)?.getReceiverExpression()?.parent as? JetExpression ?: this
}
fun Collection<ExpectedInfo>.filterFunctionExpected() fun Collection<ExpectedInfo>.filterFunctionExpected()
= filter { it.fuzzyType != null && KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(it.fuzzyType!!.type) } = filter { it.fuzzyType != null && KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(it.fuzzyType!!.type) }
@@ -0,0 +1,7 @@
fun foo(list: List<String>){}
fun bar(o: Any) {
foo(o as <caret>)
}
// EXIST: { itemText: "List<String>" }
@@ -0,0 +1,10 @@
class Xyz
fun foo(xyz: Xyz){}
fun bar(o: Any) {
foo(o as Xy<caret>)
}
// EXIST: Xyz
// NOTHING_ELSE
@@ -0,0 +1,13 @@
class CCC {
companion object {
fun create(): CCC = CCC()
}
}
fun f(ccc: CCC, p: Int){}
fun g() {
f(<caret>)
}
// ELEMENT: create
@@ -0,0 +1,13 @@
class CCC {
companion object {
fun create(): CCC = CCC()
}
}
fun f(ccc: CCC, p: Int){}
fun g() {
f(CCC.create()<caret>)
}
// ELEMENT: create
@@ -0,0 +1,13 @@
class Xx1
class Xx2
class Xx3
fun foo(xx: Xx2){}
fun bar(o: Any) {
foo(o as Xx<caret>)
}
// ORDER: Xx2
// ORDER: Xx1
// ORDER: Xx3
@@ -1310,6 +1310,18 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class) @RunWith(JUnit3RunnerWithInners.class)
public static class FromSmart extends AbstractJSBasicCompletionTest { public static class FromSmart extends AbstractJSBasicCompletionTest {
@TestMetadata("AfterAs.kt")
public void testAfterAs() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromSmart/AfterAs.kt");
doTest(fileName);
}
@TestMetadata("AfterAsNoDuplicates.kt")
public void testAfterAsNoDuplicates() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromSmart/AfterAsNoDuplicates.kt");
doTest(fileName);
}
public void testAllFilesPresentInFromSmart() throws Exception { public void testAllFilesPresentInFromSmart() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/fromSmart"), Pattern.compile("^(.+)\\.kt$"), true); JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/fromSmart"), Pattern.compile("^(.+)\\.kt$"), true);
} }
@@ -1310,6 +1310,18 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class) @RunWith(JUnit3RunnerWithInners.class)
public static class FromSmart extends AbstractJvmBasicCompletionTest { public static class FromSmart extends AbstractJvmBasicCompletionTest {
@TestMetadata("AfterAs.kt")
public void testAfterAs() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromSmart/AfterAs.kt");
doTest(fileName);
}
@TestMetadata("AfterAsNoDuplicates.kt")
public void testAfterAsNoDuplicates() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/fromSmart/AfterAsNoDuplicates.kt");
doTest(fileName);
}
public void testAllFilesPresentInFromSmart() throws Exception { public void testAllFilesPresentInFromSmart() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/fromSmart"), Pattern.compile("^(.+)\\.kt$"), true); JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/fromSmart"), Pattern.compile("^(.+)\\.kt$"), true);
} }
@@ -77,6 +77,12 @@ public class BasicCompletionHandlerTestGenerated extends AbstractBasicCompletion
doTest(fileName); doTest(fileName);
} }
@TestMetadata("NoTailFromSmart.kt")
public void testNoTailFromSmart() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/NoTailFromSmart.kt");
doTest(fileName);
}
@TestMetadata("PlatformStaticInClass.kt") @TestMetadata("PlatformStaticInClass.kt")
public void testPlatformStaticInClass() throws Exception { public void testPlatformStaticInClass() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/PlatformStaticInClass.kt"); String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/PlatformStaticInClass.kt");
@@ -147,6 +147,12 @@ public class BasicCompletionWeigherTestGenerated extends AbstractBasicCompletion
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class) @RunWith(JUnit3RunnerWithInners.class)
public static class ExpectedInfo extends AbstractBasicCompletionWeigherTest { public static class ExpectedInfo extends AbstractBasicCompletionWeigherTest {
@TestMetadata("AfterAs.kt")
public void testAfterAs() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/weighers/basic/expectedInfo/AfterAs.kt");
doTest(fileName);
}
public void testAllFilesPresentInExpectedInfo() throws Exception { public void testAllFilesPresentInExpectedInfo() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/weighers/basic/expectedInfo"), Pattern.compile("^([^\\.]+)\\.kt$"), true); JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/weighers/basic/expectedInfo"), Pattern.compile("^([^\\.]+)\\.kt$"), true);
} }