diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/BasicCompletionSession.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/BasicCompletionSession.kt index 50ed2aa717b..4a7caea804e 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/BasicCompletionSession.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/BasicCompletionSession.kt @@ -30,8 +30,9 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.ClassifierDescriptor 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.toExpressionWithType +import org.jetbrains.kotlin.idea.completion.smart.SmartCompletionItemPriority import org.jetbrains.kotlin.idea.project.ProjectStructureUtil import org.jetbrains.kotlin.idea.stubindex.PackageIndexUtil import org.jetbrains.kotlin.name.FqName @@ -95,13 +96,9 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration, else null - private val expectedInfos = if (expression != null) - ExpectedInfos(bindingContext, resolutionFacade, moduleDescriptor).calculate(expression.toExpressionWithType()) - else - null - 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 { @@ -198,6 +195,20 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration, } 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) 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 (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() if (!configuration.completeNonImportedDeclarations && isNoQualifierContext()) { @@ -300,8 +305,8 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration, sorter = sorter.weighBefore(DeprecatedWeigher.toString(), ParameterNameAndTypeCompletion.Weigher) } - if (smartCompletion != null && expectedInfos != null && expectedInfos.isNotEmpty()) { - sorter = sorter.weighBefore(KindWeigher.toString(), ExpectedInfoMatchWeigher(expectedInfos, smartCompletion), SmartCompletionPriorityWeigher) + if (smartCompletion != null) { + sorter = sorter.weighBefore(KindWeigher.toString(), SmartCompletionInBasicWeigher(smartCompletion), SmartCompletionPriorityWeigher) } return sorter diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionUtils.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionUtils.kt index 0de71e9ed7d..05f1edd4a86 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionUtils.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionUtils.kt @@ -315,17 +315,23 @@ fun LookupElementFactory.createLookupElementForType(type: JetType): LookupElemen 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) { super.renderElement(presentation) 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(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 hashCode() = fullText.hashCode() diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ExpectedInfos.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ExpectedInfos.kt index 8177abf3c94..15ce3db8c51 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ExpectedInfos.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ExpectedInfos.kt @@ -86,16 +86,36 @@ class ByExpectedTypeFilter(val fuzzyType: FuzzyType) : ByTypeFilter { override fun hashCode() = fuzzyType.hashCode() } -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) +data class ExpectedInfo( + val filter: ByTypeFilter, + 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) - : this(FuzzyType(type, emptyList()), expectedName, tail, itemOptions) + constructor(fuzzyType: FuzzyType, expectedName: String?, tail: Tail?, itemOptions: ItemOptions = ItemOptions.DEFAULT, additionalData: ExpectedInfo.AdditionalData? = null) + : 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: 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? @@ -106,26 +126,17 @@ data class ArgumentPosition(val argumentIndex: Int, val argumentName: Name?, val 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) - : ExpectedInfo(FuzzyType(type, function.typeParameters), name, tail, itemOptions) { - +class ArgumentAdditionalData(val function: FunctionDescriptor, val position: ArgumentPosition) : ExpectedInfo.AdditionalData { 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() = function.hashCode() } -class ReturnValueExpectedInfo( - type: JetType?, - val callable: CallableDescriptor -) : ExpectedInfo( - if (type != null) ByExpectedTypeFilter(FuzzyType(type, emptyList())) else ByTypeFilter.All, - callable.name.asString(), - null -) { +class ReturnValueAdditionalData(val callable: CallableDescriptor) : ExpectedInfo.AdditionalData { override fun equals(other: Any?) - = other is ReturnValueExpectedInfo && super.equals(other) && callable == other.callable + = other is ReturnValueAdditionalData && callable == other.callable override fun hashCode() = callable.hashCode() @@ -180,7 +191,14 @@ class ExpectedInfos( public fun calculateForArgument(call: Call, argument: ValueArgument): Collection? { 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 expectedFuzzyTypes = ExpectedInfos(bindingContext, resolutionFacade, moduleDescriptor, useHeuristicSignatures, useOuterCallsExpectedTypeCount - 1) .calculate(callExpression) @@ -198,7 +216,7 @@ class ExpectedInfos( return results } - private fun calculateForArgument(call: Call, callExpectedType: JetType, argument: ValueArgument): Collection? { + private fun calculateForArgument(call: Call, callExpectedType: JetType, argument: ValueArgument): Collection? { val argumentIndex = call.getValueArguments().indexOf(argument) assert(argumentIndex >= 0) { "Could not find argument '$argument' among arguments of call: $call" @@ -229,7 +247,7 @@ class ExpectedInfos( val callResolver = createContainerForMacros(project, moduleDescriptor).callResolver val results: OverloadResolutionResults = callResolver.resolveFunctionCall(callResolutionContext) - val expectedInfos = LinkedHashSet() + val expectedInfos = LinkedHashSet() for (candidate: ResolvedCall in results.getAllCandidates()!!) { val status = candidate.getStatus() if (status == ResolutionStatus.RECEIVER_TYPE_ERROR || status == ResolutionStatus.RECEIVER_PRESENCE_ERROR) continue @@ -289,11 +307,11 @@ class ExpectedInfos( tail 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 - expectedInfos.add(ArgumentExpectedInfo(parameter.getType(), expectedName, varargTail, descriptor, argumentPosition, starOptions)) + expectedInfos.add(ExpectedInfo.createForArgument(parameter.getType(), expectedName, varargTail, descriptor, argumentPosition, starOptions)) } else { if (alreadyHasStar) continue @@ -305,11 +323,11 @@ class ExpectedInfos( if (isFunctionLiteralArgument) { if (KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(parameterType)) { - expectedInfos.add(ArgumentExpectedInfo(parameterType, expectedName, null, descriptor, argumentPosition)) + expectedInfos.add(ExpectedInfo.createForArgument(parameterType, expectedName, null, descriptor, argumentPosition)) } } 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() } - private fun functionReturnValueExpectedInfo(descriptor: FunctionDescriptor, expectType: Boolean): ReturnValueExpectedInfo? { + private fun functionReturnValueExpectedInfo(descriptor: FunctionDescriptor, expectType: Boolean): ExpectedInfo? { return when (descriptor) { is SimpleFunctionDescriptor -> { val expectedType = if (expectType) descriptor.returnType else null - ReturnValueExpectedInfo(expectedType, descriptor) + ExpectedInfo.createForReturnValue(expectedType, descriptor) } is PropertyGetterDescriptor -> { if (descriptor !is PropertyGetterDescriptor) return null val property = descriptor.getCorrespondingProperty() val expectedType = if (expectType) property.type else null - ReturnValueExpectedInfo(expectedType, property) + ExpectedInfo.createForReturnValue(expectedType, property) } else -> null diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/Weighers.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/Weighers.kt index f238b53824b..a0f66532e4e 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/Weighers.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/Weighers.kt @@ -186,13 +186,9 @@ class DeclarationRemotenessWeigher(private val file: JetFile) : LookupElementWei } } -class ExpectedInfoMatchWeigher( - private val expectedInfos: Collection, - private val smartCompletion: SmartCompletion -) : LookupElementWeigher("kotlin.expectedInfoMatch") { - - private val smartCastCalculator = smartCompletion.smartCastCalculator +class SmartCompletionInBasicWeigher(private val smartCompletion: SmartCompletion) : LookupElementWeigher("kotlin.smartInBasic") { private val descriptorsToSkip = smartCompletion.descriptorsToSkip + private val expectedInfos = smartCompletion.expectedInfos private fun fullMatchWeight(nameSimilarity: Int): Long { return -((3L shl 32) + nameSimilarity) @@ -218,10 +214,16 @@ class ExpectedInfoMatchWeigher( // TODO: keywords with type 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) { is DeclarationLookupObject -> { val descriptor = o.descriptor ?: return NO_MATCH_WEIGHT - if (descriptor in descriptorsToSkip) return DESCRIPTOR_TO_SKIP_WEIGHT descriptor.fuzzyTypesForSmartCompletion(smartCastCalculator) to descriptor.name } diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/MultipleArgumentsItemProvider.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/MultipleArgumentsItemProvider.kt index 5661e786724..b6842b0d23c 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/MultipleArgumentsItemProvider.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/MultipleArgumentsItemProvider.kt @@ -43,8 +43,8 @@ class MultipleArgumentsItemProvider(val bindingContext: BindingContext, val added = HashSet() for (expectedInfo in expectedInfos) { - if (expectedInfo is ArgumentExpectedInfo && expectedInfo.position == ArgumentPosition(0)) { - val parameters = expectedInfo.function.getValueParameters() + if (expectedInfo.additionalData is ArgumentAdditionalData && expectedInfo.additionalData.position == ArgumentPosition(0)) { + val parameters = expectedInfo.additionalData.function.valueParameters if (parameters.size() > 1) { val variables = ArrayList() for ((i, parameter) in parameters.withIndex()) { diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt index 36266b9ff1d..3e75adabf07 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt @@ -48,18 +48,22 @@ interface InheritanceItemsSearcher { } class SmartCompletion( - val expression: JetExpression, - val resolutionFacade: ResolutionFacade, - val moduleDescriptor: ModuleDescriptor, - val bindingContext: BindingContext, - val visibilityFilter: (DeclarationDescriptor) -> Boolean, - val inDescriptor: DeclarationDescriptor, - val prefixMatcher: PrefixMatcher, - val inheritorSearchScope: GlobalSearchScope, - val toFromOriginalFileMapper: ToFromOriginalFileMapper, - val lookupElementFactory: LookupElementFactory + private val expression: JetExpression, + private val resolutionFacade: ResolutionFacade, + private val moduleDescriptor: ModuleDescriptor, + private val bindingContext: BindingContext, + private val visibilityFilter: (DeclarationDescriptor) -> Boolean, + private val inDescriptor: DeclarationDescriptor, + private val prefixMatcher: PrefixMatcher, + private val inheritorSearchScope: GlobalSearchScope, + private val toFromOriginalFileMapper: ToFromOriginalFileMapper, + private val lookupElementFactory: LookupElementFactory, + private val forBasicCompletion: Boolean = false ) { private val receiver = if (expression is JetSimpleNameExpression) expression.getReceiverExpression() else null + private val expressionWithType = receiver?.parent as? JetExpression ?: expression + + public val expectedInfos: Collection? = calcExpectedInfos(expressionWithType) public val smartCastCalculator: SmartCastCalculator = SmartCastCalculator(bindingContext, moduleDescriptor, expression) @@ -107,11 +111,11 @@ class SmartCompletion( } private fun executeInternal(): Result? { - val asTypePositionResult = buildForAsTypePosition() - if (asTypePositionResult != null) return asTypePositionResult + val (additionalItems, inheritanceSearcher) = additionalItems() - val expectedInfos = calcExpectedInfos(expression.toExpressionWithType()) - if (expectedInfos == null || expectedInfos.isEmpty()) return null + if (expectedInfos == null || expectedInfos.isEmpty()) { + return Result(null, additionalItems, inheritanceSearcher) + } fun filterDeclaration(descriptor: DeclarationDescriptor): Collection { if (descriptor in descriptorsToSkip) return emptyList() @@ -137,26 +141,28 @@ class SmartCompletion( return result } - val (additionalItems, inheritanceSearcher) = additionalItems(expectedInfos) - return Result(::filterDeclaration, additionalItems, inheritanceSearcher) } - public fun additionalItems( - expectedInfos: Collection, - forOrdinaryCompletion: Boolean = false - ): Pair, InheritanceItemsSearcher?> { + public fun additionalItems(): Pair, InheritanceItemsSearcher?> { + val asTypePositionItems = buildForAsTypePosition() + if (asTypePositionItems != null) { + assert(expectedInfos == null) + return Pair(asTypePositionItems, null) + } + val items = ArrayList() val inheritanceSearchers = ArrayList() - 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) if (expression is JetSimpleNameExpression) { StaticMembers(bindingContext, lookupElementFactory).addToCollection(items, expectedInfos, expression, descriptorsToSkip) } - if (!forOrdinaryCompletion) { + if (!forBasicCompletion) { items.addThisItems(expression, expectedInfos, smartCastCalculator) LambdaItems.addToCollection(items, expectedInfos) @@ -193,6 +199,12 @@ class SmartCompletion( } private fun calcExpectedInfos(expression: JetExpression): Collection? { + 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) val declaration = implicitlyTypedDeclarationFromInitializer(expression) if (declaration != null) { @@ -227,7 +239,6 @@ class SmartCompletion( } public val descriptorsToSkip: Set by lazy> { - val expressionWithType = expression.toExpressionWithType() val parent = expressionWithType.getParent() when (parent) { is JetBinaryExpression -> { @@ -324,7 +335,7 @@ class SmartCompletion( return null } - private fun buildForAsTypePosition(): Result? { + private fun buildForAsTypePosition(): Collection? { val binaryExpression = ((expression.getParent() as? JetUserType) ?.getParent() as? JetTypeReference) ?.getParent() as? JetBinaryExpressionWithTypeRHS @@ -341,7 +352,7 @@ class SmartCompletion( val lookupElement = lookupElementFactory.createLookupElementForType(type) ?: continue items.add(lookupElement.addTailAndNameSimilarity(infos)) } - return Result(null, items, null) + return items } companion object { diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt index af86ebc24d0..b63573d36b4 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt @@ -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.WithTailInsertHandler 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.descriptorUtil.builtIns import org.jetbrains.kotlin.types.JetType @@ -41,7 +38,6 @@ import org.jetbrains.kotlin.types.typeUtil.isNothing import org.jetbrains.kotlin.util.descriptorsEqualWithSubstitution import java.util.ArrayList import java.util.HashMap -import java.util.HashSet class ArtificialElementInsertHandler( val textBeforeCaret: String, val textAfterCaret: String, val shortenRefs: Boolean) : InsertHandler{ @@ -59,8 +55,7 @@ class ArtificialElementInsertHandler( } fun mergeTails(tails: Collection): Tail? { - if (tails.size() == 1) return tails.single() - return if (HashSet(tails).size() == 1) tails.first() else null + return tails.singleOrNull() ?: tails.toSet().singleOrNull() } fun LookupElement.addTail(tail: Tail?): LookupElement { @@ -187,7 +182,7 @@ fun MutableCollection.addLoo val lookupElement = itemData.createLookupElement() if (lookupElement != null) { 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 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.filterFunctionExpected() = filter { it.fuzzyType != null && KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(it.fuzzyType!!.type) } diff --git a/idea/idea-completion/testData/basic/common/fromSmart/AfterAs.kt b/idea/idea-completion/testData/basic/common/fromSmart/AfterAs.kt new file mode 100644 index 00000000000..62c2f3c5a44 --- /dev/null +++ b/idea/idea-completion/testData/basic/common/fromSmart/AfterAs.kt @@ -0,0 +1,7 @@ +fun foo(list: List){} + +fun bar(o: Any) { + foo(o as ) +} + +// EXIST: { itemText: "List" } diff --git a/idea/idea-completion/testData/basic/common/fromSmart/AfterAsNoDuplicates.kt b/idea/idea-completion/testData/basic/common/fromSmart/AfterAsNoDuplicates.kt new file mode 100644 index 00000000000..b4bd29c3dee --- /dev/null +++ b/idea/idea-completion/testData/basic/common/fromSmart/AfterAsNoDuplicates.kt @@ -0,0 +1,10 @@ +class Xyz + +fun foo(xyz: Xyz){} + +fun bar(o: Any) { + foo(o as Xy) +} + +// EXIST: Xyz +// NOTHING_ELSE diff --git a/idea/idea-completion/testData/handlers/basic/NoTailFromSmart.kt b/idea/idea-completion/testData/handlers/basic/NoTailFromSmart.kt new file mode 100644 index 00000000000..423ed9c963d --- /dev/null +++ b/idea/idea-completion/testData/handlers/basic/NoTailFromSmart.kt @@ -0,0 +1,13 @@ +class CCC { + companion object { + fun create(): CCC = CCC() + } +} + +fun f(ccc: CCC, p: Int){} + +fun g() { + f() +} + +// ELEMENT: create diff --git a/idea/idea-completion/testData/handlers/basic/NoTailFromSmart.kt.after b/idea/idea-completion/testData/handlers/basic/NoTailFromSmart.kt.after new file mode 100644 index 00000000000..368ec8e2bca --- /dev/null +++ b/idea/idea-completion/testData/handlers/basic/NoTailFromSmart.kt.after @@ -0,0 +1,13 @@ +class CCC { + companion object { + fun create(): CCC = CCC() + } +} + +fun f(ccc: CCC, p: Int){} + +fun g() { + f(CCC.create()) +} + +// ELEMENT: create diff --git a/idea/idea-completion/testData/weighers/basic/expectedInfo/AfterAs.kt b/idea/idea-completion/testData/weighers/basic/expectedInfo/AfterAs.kt new file mode 100644 index 00000000000..62f24b0c80a --- /dev/null +++ b/idea/idea-completion/testData/weighers/basic/expectedInfo/AfterAs.kt @@ -0,0 +1,13 @@ +class Xx1 +class Xx2 +class Xx3 + +fun foo(xx: Xx2){} + +fun bar(o: Any) { + foo(o as Xx) +} + +// ORDER: Xx2 +// ORDER: Xx1 +// ORDER: Xx3 diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java index 061bfc14d9a..94a48c75cf0 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java @@ -1310,6 +1310,18 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) 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 { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/fromSmart"), Pattern.compile("^(.+)\\.kt$"), true); } diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java index af0952532ad..842800421d6 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java @@ -1310,6 +1310,18 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) 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 { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/basic/common/fromSmart"), Pattern.compile("^(.+)\\.kt$"), true); } diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/BasicCompletionHandlerTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/BasicCompletionHandlerTestGenerated.java index dbb6b299632..652258695cc 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/BasicCompletionHandlerTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/BasicCompletionHandlerTestGenerated.java @@ -77,6 +77,12 @@ public class BasicCompletionHandlerTestGenerated extends AbstractBasicCompletion 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") public void testPlatformStaticInClass() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/handlers/basic/PlatformStaticInClass.kt"); diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/weighers/BasicCompletionWeigherTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/weighers/BasicCompletionWeigherTestGenerated.java index 5308f8ee4ae..e9a56a1f505 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/weighers/BasicCompletionWeigherTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/weighers/BasicCompletionWeigherTestGenerated.java @@ -147,6 +147,12 @@ public class BasicCompletionWeigherTestGenerated extends AbstractBasicCompletion @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) 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 { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/weighers/basic/expectedInfo"), Pattern.compile("^([^\\.]+)\\.kt$"), true); }