From 363295f6afbe95b5ced5a9cb48bb9915195b371c Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Wed, 26 Aug 2015 18:02:14 +0300 Subject: [PATCH] Moved creation of additional items like special items for functions accepting lambda into LookupElementFactory to provide them in smart completion too --- .../idea/completion/BasicCompletionSession.kt | 2 +- .../idea/completion/CompletionSession.kt | 18 ++-- .../kotlin/idea/completion/CompletionUtils.kt | 5 +- .../completion/KDocCompletionContributor.kt | 5 +- .../idea/completion/LookupElementFactory.kt | 79 ++++++++++++++++- .../completion/LookupElementsCollector.kt | 85 ++----------------- .../completion/PackageDirectiveCompletion.kt | 3 +- .../ParameterNameAndTypeCompletion.kt | 2 +- .../idea/completion/smart/KeywordValues.kt | 7 +- .../idea/completion/smart/SmartCompletion.kt | 16 ++-- .../idea/completion/smart/StaticMembers.kt | 66 +++++++------- .../smart/TypeInstantiationItems.kt | 10 +-- .../kotlin/idea/completion/smart/Utils.kt | 85 ++++++++----------- .../handlers/smart/ClassObjectMethod4.kt | 1 + .../smart/ClassObjectMethod4.kt.after | 1 + .../testData/smart/HighOrderFunction.kt | 6 ++ .../testData/smart/SAMExpected2.kt | 7 ++ .../test/JvmSmartCompletionTestGenerated.java | 12 +++ 18 files changed, 213 insertions(+), 197 deletions(-) create mode 100644 idea/idea-completion/testData/smart/HighOrderFunction.kt create mode 100644 idea/idea-completion/testData/smart/SAMExpected2.kt 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 876ffb80654..36d8c9cb45b 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 @@ -334,7 +334,7 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration, } superClasses - .map { lookupElementFactory.createLookupElement(it, boldImmediateMembers = false, qualifyNestedClasses = true, includeClassTypeArguments = false) } + .map { lookupElementFactory.createLookupElement(it, useReceiverTypes = false, qualifyNestedClasses = true, includeClassTypeArguments = false) } .forEach { collector.addElement(it) } } diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt index 419f4fea905..c7290942c63 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt @@ -140,6 +140,13 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC protected val receiversData: ReferenceVariantsHelper.ReceiversData? = nameExpression?.let { referenceVariantsHelper.getReferenceVariantsReceivers(it) } + private val factoryContext = if (expression?.getParent() is JetSimpleNameStringTemplateEntry) + LookupElementFactory.Context.STRING_TEMPLATE_AFTER_DOLLAR + else if (receiversData?.callType == CallType.INFIX) + LookupElementFactory.Context.INFIX_CALL + else + LookupElementFactory.Context.NORMAL + protected val lookupElementFactory: LookupElementFactory = run { var receiverTypes = emptyList() if (receiversData != null) { @@ -161,19 +168,12 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC } } - LookupElementFactory(resolutionFacade, receiverTypes, { expectedInfos }) + LookupElementFactory(resolutionFacade, receiverTypes, factoryContext, inDescriptor, { expectedInfos }) } - private val collectorContext = if (expression?.getParent() is JetSimpleNameStringTemplateEntry) - LookupElementsCollector.Context.STRING_TEMPLATE_AFTER_DOLLAR - else if (receiversData?.callType == CallType.INFIX) - LookupElementsCollector.Context.INFIX_CALL - else - LookupElementsCollector.Context.NORMAL - // LookupElementsCollector instantiation is deferred because virtual call to createSorter uses data from derived classes protected val collector: LookupElementsCollector by lazy { - LookupElementsCollector(prefixMatcher, parameters, resultSet, resolutionFacade, lookupElementFactory, createSorter(), inDescriptor, collectorContext) + LookupElementsCollector(prefixMatcher, parameters, resultSet, lookupElementFactory, createSorter()) } protected val originalSearchScope: GlobalSearchScope = getResolveScope(parameters.getOriginalFile() as JetFile) 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 a4d906b3298..f78a24ff6b5 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 @@ -295,6 +295,7 @@ fun breakOrContinueExpressionItems(position: JetElement, breakOrContinue: String fun LookupElementFactory.createBackingFieldLookupElement( property: PropertyDescriptor, + useReceiverTypes: Boolean, inDescriptor: DeclarationDescriptor, resolutionFacade: ResolutionFacade ): LookupElement? { @@ -312,7 +313,7 @@ fun LookupElementFactory.createBackingFieldLookupElement( val bindingContext = resolutionFacade.analyze(declaration) if (!bindingContext[BindingContext.BACKING_FIELD_REQUIRED, property]!!) return null - val lookupElement = createLookupElement(property, true) + val lookupElement = createLookupElement(property, useReceiverTypes) return object : LookupElementDecorator(lookupElement) { override fun getLookupString() = "$" + super.getLookupString() override fun getAllLookupStrings() = setOf(getLookupString()) @@ -335,7 +336,7 @@ fun LookupElementFactory.createLookupElementForType(type: JetType): LookupElemen } else { val classifier = type.getConstructor().getDeclarationDescriptor() ?: return null - val baseLookupElement = createLookupElement(classifier, false, qualifyNestedClasses = true, includeClassTypeArguments = false) + val baseLookupElement = createLookupElement(classifier, useReceiverTypes = false, qualifyNestedClasses = true, includeClassTypeArguments = false) val itemText = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(type) diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KDocCompletionContributor.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KDocCompletionContributor.kt index b59de0f9632..d0d9f18f086 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KDocCompletionContributor.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KDocCompletionContributor.kt @@ -37,7 +37,6 @@ import org.jetbrains.kotlin.psi.psiUtil.getParentOfType import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo -import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered @@ -87,7 +86,7 @@ class KDocNameCompletionSession(parameters: CompletionParameters, .filter { it.getName().asString() !in documentedParameters } descriptors.forEach { - collector.addElement(lookupElementFactory.createLookupElement(it, false)) + collector.addElement(lookupElementFactory.createLookupElement(it, useReceiverTypes = false)) } } @@ -108,7 +107,7 @@ class KDocNameCompletionSession(parameters: CompletionParameters, } scope.getDescriptorsFiltered(nameFilter = descriptorNameFilter).filter(::isApplicable).forEach { - val element = lookupElementFactory.createLookupElement(it, false) + val element = lookupElementFactory.createLookupElement(it, useReceiverTypes = false) collector.addElement(object: LookupElementDecorator(element) { override fun handleInsert(context: InsertionContext?) { // insert only plain name here, no qualifier/parentheses/etc. diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementFactory.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementFactory.kt index 96b08841591..a89a58800eb 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementFactory.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementFactory.kt @@ -17,11 +17,13 @@ package org.jetbrains.kotlin.idea.completion import com.intellij.codeInsight.completion.InsertHandler +import com.intellij.codeInsight.completion.InsertionContext import com.intellij.codeInsight.lookup.* import com.intellij.codeInsight.lookup.impl.LookupCellRenderer import com.intellij.psi.PsiClass import com.intellij.psi.PsiElement import com.intellij.util.PlatformIcons +import com.intellij.util.SmartList import org.jetbrains.kotlin.asJava.KotlinLightClass import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.* @@ -62,13 +64,86 @@ public data class PackageLookupObject(val fqName: FqName) : DeclarationLookupObj public class LookupElementFactory( private val resolutionFacade: ResolutionFacade, private val receiverTypes: Collection, + private val context: LookupElementFactory.Context, + private val inDescriptor: DeclarationDescriptor?, expectedInfosCalculator: () -> Collection ) { + public enum class Context { + NORMAL, + STRING_TEMPLATE_AFTER_DOLLAR, + INFIX_CALL + } + private val expectedInfos by lazy { expectedInfosCalculator() } + public fun createStandardLookupElementsForDescriptor(descriptor: DeclarationDescriptor, useReceiverTypes: Boolean): Collection { + val result = SmartList() + + var lookupElement = createLookupElement(descriptor, useReceiverTypes) + if (context == Context.STRING_TEMPLATE_AFTER_DOLLAR && (descriptor is FunctionDescriptor || descriptor is ClassifierDescriptor)) { + lookupElement = lookupElement.withBracesSurrounding() + } + result.add(lookupElement) + + // add special item for function with one argument of function type with more than one parameter + if (context != Context.INFIX_CALL && descriptor is FunctionDescriptor) { + result.addSpecialFunctionCallElements(descriptor, useReceiverTypes) + } + + if (descriptor is PropertyDescriptor && inDescriptor != null) { + var backingFieldElement = createBackingFieldLookupElement(descriptor, useReceiverTypes, inDescriptor, resolutionFacade) + if (backingFieldElement != null) { + if (context == Context.STRING_TEMPLATE_AFTER_DOLLAR) { + backingFieldElement = backingFieldElement.withBracesSurrounding() + } + result.add(backingFieldElement) + } + } + + return result + } + + private fun MutableCollection.addSpecialFunctionCallElements(descriptor: FunctionDescriptor, useReceiverTypes: Boolean) { + val singleParameter = descriptor.valueParameters.singleOrNull() ?: return + val parameterType = singleParameter.type + if (KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(parameterType)) { + val functionParameterCount = KotlinBuiltIns.getParameterTypeProjectionsFromFunctionType(parameterType).size() + if (functionParameterCount > 1) { + add(createFunctionCallElementWithExplicitLambdaParameters(descriptor, parameterType, useReceiverTypes)) + } + } + } + + private fun createFunctionCallElementWithExplicitLambdaParameters(descriptor: FunctionDescriptor, parameterType: JetType, useReceiverTypes: Boolean): LookupElement { + var lookupElement = createLookupElement(descriptor, useReceiverTypes) + val needTypeArguments = (getDefaultInsertHandler(descriptor) as KotlinFunctionInsertHandler).needTypeArguments + val lambdaInfo = GenerateLambdaInfo(parameterType, true) + + lookupElement = object : LookupElementDecorator(lookupElement) { + override fun renderElement(presentation: LookupElementPresentation) { + super.renderElement(presentation) + + val tails = presentation.getTailFragments() + presentation.clearTail() + presentation.appendTailText(" " + buildLambdaPresentation(parameterType) + " ", false) + tails.forEach { presentation.appendTailText(it.text, true) } + } + + override fun handleInsert(context: InsertionContext) { + KotlinFunctionInsertHandler(needTypeArguments, needValueArguments = true, lambdaInfo = lambdaInfo).handleInsert(context, this) + } + } + + if (context == Context.STRING_TEMPLATE_AFTER_DOLLAR) { + lookupElement = lookupElement.withBracesSurrounding() + } + + return lookupElement + } + public fun createLookupElement( descriptor: DeclarationDescriptor, - boldImmediateMembers: Boolean, + useReceiverTypes: Boolean, qualifyNestedClasses: Boolean = false, includeClassTypeArguments: Boolean = true ): LookupElement { @@ -84,7 +159,7 @@ public class LookupElementFactory( element.putUserData(CALLABLE_WEIGHT_KEY, weight) // store for use in lookup elements sorting } - if (boldImmediateMembers) { + if (useReceiverTypes) { element = element.boldIfImmediate(weight) } return element diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementsCollector.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementsCollector.kt index 378c4674977..0bb50d99c0a 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementsCollector.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementsCollector.kt @@ -19,35 +19,21 @@ package org.jetbrains.kotlin.idea.completion import com.intellij.codeInsight.completion.* import com.intellij.codeInsight.lookup.LookupElement import com.intellij.codeInsight.lookup.LookupElementDecorator -import com.intellij.codeInsight.lookup.LookupElementPresentation import com.intellij.openapi.util.TextRange import com.intellij.patterns.ElementPattern -import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.descriptors.ClassifierDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.descriptors.PropertyDescriptor -import org.jetbrains.kotlin.idea.completion.handlers.* +import org.jetbrains.kotlin.idea.completion.handlers.WithExpressionPrefixInsertHandler +import org.jetbrains.kotlin.idea.completion.handlers.WithTailInsertHandler import org.jetbrains.kotlin.idea.core.completion.DeclarationLookupObject -import org.jetbrains.kotlin.idea.resolve.ResolutionFacade -import org.jetbrains.kotlin.types.JetType import java.util.* class LookupElementsCollector( private val defaultPrefixMatcher: PrefixMatcher, private val completionParameters: CompletionParameters, resultSet: CompletionResultSet, - private val resolutionFacade: ResolutionFacade, private val lookupElementFactory: LookupElementFactory, - private val sorter: CompletionSorter, - private val inDescriptor: DeclarationDescriptor, - private val context: LookupElementsCollector.Context + private val sorter: CompletionSorter ) { - public enum class Context { - NORMAL, - STRING_TEMPLATE_AFTER_DOLLAR, - INFIX_CALL - } private val elements = LinkedHashMap>() @@ -89,73 +75,14 @@ class LookupElementsCollector( public fun addDescriptorElements(descriptor: DeclarationDescriptor, notImported: Boolean = false, withReceiverCast: Boolean = false) { run { - var lookupElement = lookupElementFactory.createLookupElement(descriptor, true) + var lookupElements = lookupElementFactory.createStandardLookupElementsForDescriptor(descriptor, useReceiverTypes = true) if (withReceiverCast) { - lookupElement = lookupElement.withReceiverCast() + lookupElements = lookupElements.map { it.withReceiverCast() } } - if (context == Context.STRING_TEMPLATE_AFTER_DOLLAR && (descriptor is FunctionDescriptor || descriptor is ClassifierDescriptor)) { - lookupElement = lookupElement.withBracesSurrounding() - } - - addElement(lookupElement, notImported = notImported) + addElements(lookupElements, notImported = notImported) } - - // add special item for function with one argument of function type with more than one parameter - if (context != Context.INFIX_CALL && descriptor is FunctionDescriptor) { - addSpecialFunctionDescriptorElementIfNeeded(descriptor) - } - - if (descriptor is PropertyDescriptor) { - var lookupElement = lookupElementFactory.createBackingFieldLookupElement(descriptor, inDescriptor, resolutionFacade) - if (lookupElement != null) { - if (context == Context.STRING_TEMPLATE_AFTER_DOLLAR) { - lookupElement = lookupElement.withBracesSurrounding() - } - addElement(lookupElement) - } - } - } - - private fun addSpecialFunctionDescriptorElementIfNeeded(descriptor: FunctionDescriptor) { - val parameters = descriptor.getValueParameters() - if (parameters.size() == 1) { - val parameterType = parameters.get(0).getType() - if (KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(parameterType)) { - val parameterCount = KotlinBuiltIns.getParameterTypeProjectionsFromFunctionType(parameterType).size() - if (parameterCount > 1) { - addSpecialFunctionDescriptorElement(descriptor, parameterType) - } - } - } - } - - private fun addSpecialFunctionDescriptorElement(descriptor: FunctionDescriptor, parameterType: JetType) { - var lookupElement = lookupElementFactory.createLookupElement(descriptor, true) - val needTypeArguments = (lookupElementFactory.getDefaultInsertHandler(descriptor) as KotlinFunctionInsertHandler).needTypeArguments - val lambdaInfo = GenerateLambdaInfo(parameterType, true) - - lookupElement = object : LookupElementDecorator(lookupElement) { - override fun renderElement(presentation: LookupElementPresentation) { - super.renderElement(presentation) - - val tails = presentation.getTailFragments() - presentation.clearTail() - presentation.appendTailText(" " + buildLambdaPresentation(parameterType) + " ", false) - tails.forEach { presentation.appendTailText(it.text, true) } - } - - override fun handleInsert(context: InsertionContext) { - KotlinFunctionInsertHandler(needTypeArguments, needValueArguments = true, lambdaInfo = lambdaInfo).handleInsert(context, this) - } - } - - if (context == Context.STRING_TEMPLATE_AFTER_DOLLAR) { - lookupElement = lookupElement.withBracesSurrounding() - } - - addElement(lookupElement) } public fun addElement(element: LookupElement, prefixMatcher: PrefixMatcher = defaultPrefixMatcher, notImported: Boolean = false) { diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/PackageDirectiveCompletion.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/PackageDirectiveCompletion.kt index e7ca51a7cc5..a453b230c39 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/PackageDirectiveCompletion.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/PackageDirectiveCompletion.kt @@ -53,8 +53,9 @@ object PackageDirectiveCompletion { val bindingContext = resolutionFacade.analyze(expression) val variants = ReferenceVariantsHelper(bindingContext, resolutionFacade, { true }).getPackageReferenceVariants(expression, prefixMatcher.asNameFilter()) + val lookupElementFactory = LookupElementFactory(resolutionFacade, emptyList(), LookupElementFactory.Context.NORMAL, null, { emptyList() }) for (variant in variants) { - val lookupElement = LookupElementFactory(resolutionFacade, emptyList(), { emptyList() }).createLookupElement(variant, false) + val lookupElement = lookupElementFactory.createLookupElement(variant, useReceiverTypes = false) if (!lookupElement.getLookupString().contains(DUMMY_IDENTIFIER)) { result.addElement(lookupElement) } diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ParameterNameAndTypeCompletion.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ParameterNameAndTypeCompletion.kt index 1c7d7e1896c..bb662376d66 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ParameterNameAndTypeCompletion.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ParameterNameAndTypeCompletion.kt @@ -170,7 +170,7 @@ class ParameterNameAndTypeCompletion( private class DescriptorType(private val classifier: ClassifierDescriptor) : Type(IdeDescriptorRenderers.SOURCE_CODE.renderClassifierName(classifier)) { override fun createTypeLookupElement(lookupElementFactory: LookupElementFactory) - = lookupElementFactory.createLookupElement(classifier, false, qualifyNestedClasses = true) + = lookupElementFactory.createLookupElement(classifier, useReceiverTypes = false, qualifyNestedClasses = true) } private class JavaClassType(private val psiClass: PsiClass) : Type(psiClass.getQualifiedName()!!) { diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/KeywordValues.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/KeywordValues.kt index 10fa7df93be..bad777a2874 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/KeywordValues.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/KeywordValues.kt @@ -27,6 +27,7 @@ import org.jetbrains.kotlin.idea.completion.handlers.WithTailInsertHandler import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.types.TypeSubstitutor import org.jetbrains.kotlin.types.typeUtil.isBooleanOrNullableBoolean +import org.jetbrains.kotlin.utils.addToStdlib.singletonList object KeywordValues { public fun addToCollection(collection: MutableCollection, expectedInfos: Collection, expressionWithType: JetExpression) { @@ -54,8 +55,8 @@ object KeywordValues { val booleanInfoClassifier = { info: ExpectedInfo -> if (info.fuzzyType?.type?.isBooleanOrNullableBoolean() ?: false) ExpectedInfoClassification.match(TypeSubstitutor.EMPTY) else ExpectedInfoClassification.noMatch } - collection.addLookupElements(null, expectedInfos, booleanInfoClassifier) { LookupElementBuilder.create("true").bold().assignSmartCompletionPriority(SmartCompletionItemPriority.TRUE) } - collection.addLookupElements(null, expectedInfos, booleanInfoClassifier) { LookupElementBuilder.create("false").bold().assignSmartCompletionPriority(SmartCompletionItemPriority.FALSE) } + collection.addLookupElements(null, expectedInfos, booleanInfoClassifier) { LookupElementBuilder.create("true").bold().assignSmartCompletionPriority(SmartCompletionItemPriority.TRUE).singletonList() } + collection.addLookupElements(null, expectedInfos, booleanInfoClassifier) { LookupElementBuilder.create("false").bold().assignSmartCompletionPriority(SmartCompletionItemPriority.FALSE).singletonList() } } if (!shouldSkipNull(expressionWithType)) { @@ -66,7 +67,7 @@ object KeywordValues { ExpectedInfoClassification.noMatch } collection.addLookupElements(null, expectedInfos, classifier) { - LookupElementBuilder.create("null").bold().assignSmartCompletionPriority(SmartCompletionItemPriority.NULL) + LookupElementBuilder.create("null").bold().assignSmartCompletionPriority(SmartCompletionItemPriority.NULL).singletonList() } } } 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 84dd04662f5..cdbd9116104 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 @@ -40,9 +40,9 @@ import org.jetbrains.kotlin.types.JetType import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.typeUtil.makeNotNullable import org.jetbrains.kotlin.utils.addToStdlib.check +import org.jetbrains.kotlin.utils.addToStdlib.singletonList import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptySet -import java.util.ArrayList -import java.util.HashSet +import java.util.* interface InheritanceItemsSearcher { fun search(nameFilter: (String) -> Boolean, consumer: (LookupElement) -> Unit) @@ -146,13 +146,7 @@ class SmartCompletion( val infoClassifier = { expectedInfo: ExpectedInfo -> types.classifyExpectedInfo(expectedInfo) } result.addLookupElements(descriptor, expectedInfos, infoClassifier, noNameSimilarityForReturnItself = receiver == null) { descriptor -> - lookupElementFactory.createLookupElement(descriptor, bindingContext, true) - } - - if (descriptor is PropertyDescriptor) { - result.addLookupElements(descriptor, expectedInfos, infoClassifier) { descriptor -> - lookupElementFactory.createBackingFieldLookupElement(descriptor, inDescriptor, resolutionFacade) - } + lookupElementFactory.createLookupElementsInSmartCompletion(descriptor, bindingContext, true) } if (receiver == null) { @@ -232,7 +226,7 @@ class SmartCompletion( val types = smartCastCalculator.types(item.receiverParameter).map { FuzzyType(it, emptyList()) } val classifier = { expectedInfo: ExpectedInfo -> types.classifyExpectedInfo(expectedInfo) } addLookupElements(null, expectedInfos, classifier) { - item.createLookupElement().assignSmartCompletionPriority(SmartCompletionItemPriority.THIS) + item.createLookupElement().assignSmartCompletionPriority(SmartCompletionItemPriority.THIS).singletonList() } } } @@ -287,7 +281,7 @@ class SmartCompletion( val matchedExpectedInfos = functionExpectedInfos.filter { it.matchingSubstitutor(functionType) != null } if (matchedExpectedInfos.isEmpty()) return null - var lookupElement = lookupElementFactory.createLookupElement(descriptor, bindingContext, true) + var lookupElement = lookupElementFactory.createLookupElement(descriptor, useReceiverTypes = false) val text = "::" + (if (descriptor is ConstructorDescriptor) descriptor.getContainingDeclaration().getName() else descriptor.getName()) lookupElement = object: LookupElementDecorator(lookupElement) { override fun getLookupString() = text diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/StaticMembers.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/StaticMembers.kt index 29b217d20fb..de61b499b5c 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/StaticMembers.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/StaticMembers.kt @@ -82,7 +82,7 @@ class StaticMembers( } collection.addLookupElements(descriptor, expectedInfos, classifier) { - descriptor -> createLookupElement(descriptor, classDescriptor) + descriptor -> createLookupElements(descriptor, classDescriptor) } } @@ -102,46 +102,48 @@ class StaticMembers( members.forEach(::processMember) } - private fun createLookupElement(memberDescriptor: DeclarationDescriptor, classDescriptor: ClassDescriptor): LookupElement { - val lookupElement = lookupElementFactory.createLookupElement(memberDescriptor, bindingContext, false) + private fun createLookupElements(memberDescriptor: DeclarationDescriptor, classDescriptor: ClassDescriptor): Collection { + val lookupElements = lookupElementFactory.createLookupElementsInSmartCompletion(memberDescriptor, bindingContext, useReceiverTypes = false) val qualifierPresentation = classDescriptor.getName().asString() val qualifierText = IdeDescriptorRenderers.SOURCE_CODE.renderClassifierName(classDescriptor) - return object: LookupElementDecorator(lookupElement) { - override fun getAllLookupStrings(): Set { - return setOf(lookupElement.getLookupString(), qualifierPresentation) - } - - override fun renderElement(presentation: LookupElementPresentation) { - getDelegate().renderElement(presentation) - - presentation.setItemText(qualifierPresentation + "." + presentation.getItemText()) - - val tailText = " (" + DescriptorUtils.getFqName(classDescriptor.getContainingDeclaration()) + ")" - if (memberDescriptor is FunctionDescriptor) { - presentation.appendTailText(tailText, true) - } - else { - presentation.setTailText(tailText, true) + return lookupElements.map { + object: LookupElementDecorator(it) { + override fun getAllLookupStrings(): Set { + return setOf(it.lookupString, qualifierPresentation) } - if (presentation.getTypeText().isNullOrEmpty()) { - presentation.setTypeText(DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(classDescriptor.getDefaultType())) - } - } + override fun renderElement(presentation: LookupElementPresentation) { + getDelegate().renderElement(presentation) - override fun handleInsert(context: InsertionContext) { - var text = qualifierText + "." + memberDescriptor.getName().render() + presentation.setItemText(qualifierPresentation + "." + presentation.getItemText()) - context.getDocument().replaceString(context.getStartOffset(), context.getTailOffset(), text) - context.setTailOffset(context.getStartOffset() + text.length()) + val tailText = " (" + DescriptorUtils.getFqName(classDescriptor.getContainingDeclaration()) + ")" + if (memberDescriptor is FunctionDescriptor) { + presentation.appendTailText(tailText, true) + } + else { + presentation.setTailText(tailText, true) + } - if (memberDescriptor is FunctionDescriptor) { - getDelegate().handleInsert(context) + if (presentation.getTypeText().isNullOrEmpty()) { + presentation.setTypeText(DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(classDescriptor.getDefaultType())) + } } - shortenReferences(context, context.getStartOffset(), context.getTailOffset()) - } - }.assignSmartCompletionPriority(SmartCompletionItemPriority.STATIC_MEMBER) + override fun handleInsert(context: InsertionContext) { + var text = qualifierText + "." + memberDescriptor.getName().render() + + context.getDocument().replaceString(context.getStartOffset(), context.getTailOffset(), text) + context.setTailOffset(context.getStartOffset() + text.length()) + + if (memberDescriptor is FunctionDescriptor) { + getDelegate().handleInsert(context) + } + + shortenReferences(context, context.getStartOffset(), context.getTailOffset()) + } + }.assignSmartCompletionPriority(SmartCompletionItemPriority.STATIC_MEMBER) + } } } diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/TypeInstantiationItems.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/TypeInstantiationItems.kt index b21e527b31e..68726849675 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/TypeInstantiationItems.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/TypeInstantiationItems.kt @@ -123,7 +123,7 @@ class TypeInstantiationItems( typeArgs: List, tail: Tail? ): LookupElement? { - var lookupElement = lookupElementFactory.createLookupElement(classifier, bindingContext, false) + var lookupElement = lookupElementFactory.createLookupElement(classifier, useReceiverTypes = false) if (DescriptorUtils.isNonCompanionObject(classifier)) { return lookupElement.addTail(tail) @@ -255,10 +255,10 @@ class TypeInstantiationItems( val samConstructor = scope.getFunctions(`class`.name, NoLookupLocation.FROM_IDE) .filterIsInstance() .singleOrNull() ?: return - val lookupElement = lookupElementFactory.createLookupElement(samConstructor, bindingContext, false) - .assignSmartCompletionPriority(SmartCompletionItemPriority.INSTANTIATION) - .addTail(tail) - collection.add(lookupElement) + lookupElementFactory.createLookupElementsInSmartCompletion(samConstructor, bindingContext, useReceiverTypes = false) + .mapTo(collection) { + it.assignSmartCompletionPriority(SmartCompletionItemPriority.INSTANTIATION).addTail(tail) + } } } 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 b63573d36b4..f260e617c50 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 @@ -36,8 +36,7 @@ import org.jetbrains.kotlin.types.TypeSubstitutor import org.jetbrains.kotlin.types.typeUtil.TypeNullability import org.jetbrains.kotlin.types.typeUtil.isNothing import org.jetbrains.kotlin.util.descriptorsEqualWithSubstitution -import java.util.ArrayList -import java.util.HashMap +import java.util.* class ArtificialElementInsertHandler( val textBeforeCaret: String, val textAfterCaret: String, val shortenRefs: Boolean) : InsertHandler{ @@ -155,7 +154,7 @@ fun MutableCollection.addLoo expectedInfos: Collection, infoClassifier: (ExpectedInfo) -> ExpectedInfoClassification, noNameSimilarityForReturnItself: Boolean = false, - lookupElementFactory: (TDescriptor) -> LookupElement? + lookupElementFactory: (TDescriptor) -> Collection ) { class ItemData(val descriptor: TDescriptor, val itemOptions: ItemOptions) { override fun equals(other: Any?) @@ -163,7 +162,7 @@ fun MutableCollection.addLoo override fun hashCode() = if (this.descriptor != null) this.descriptor.getOriginal().hashCode() else 0 } - fun ItemData.createLookupElement() = lookupElementFactory(this.descriptor)?.withOptions(this.itemOptions) + fun ItemData.createLookupElements() = lookupElementFactory(this.descriptor).map { it.withOptions(this.itemOptions) } val matchedInfos = HashMap>() val makeNullableInfos = HashMap>() @@ -179,20 +178,18 @@ fun MutableCollection.addLoo if (!matchedInfos.isEmpty()) { for ((itemData, infos) in matchedInfos) { - val lookupElement = itemData.createLookupElement() - if (lookupElement != null) { - val nameSimilarityInfos = if (noNameSimilarityForReturnItself && descriptor is CallableDescriptor) { - infos.filter { (it.additionalData as? ReturnValueAdditionalData)?.callable != descriptor } // do not calculate name similarity with function itself in its return - } - else - infos - add(lookupElement.addTailAndNameSimilarity(infos, nameSimilarityInfos)) + val lookupElements = itemData.createLookupElements() + val nameSimilarityInfos = if (noNameSimilarityForReturnItself && descriptor is CallableDescriptor) { + infos.filter { (it.additionalData as? ReturnValueAdditionalData)?.callable != descriptor } // do not calculate name similarity with function itself in its return } + else + infos + lookupElements.mapTo(this) { it.addTailAndNameSimilarity(infos, nameSimilarityInfos) } } } else { for ((itemData, infos) in makeNullableInfos) { - addLookupElementsForNullable({ itemData.createLookupElement() }, infos) + addLookupElementsForNullable({ itemData.createLookupElements() }, infos) } } } @@ -204,18 +201,17 @@ private fun T.substituteFixed(substitutor: TypeSubs return this?.substitute(substitutor) as T } -private fun MutableCollection.addLookupElementsForNullable(factory: () -> LookupElement?, matchedInfos: Collection) { - for (element in lookupElementsForNullable(factory)) { - add(element.addTailAndNameSimilarity(matchedInfos)) +private fun MutableCollection.addLookupElementsForNullable(factory: () -> Collection, matchedInfos: Collection) { + fun LookupElement.postProcess(): LookupElement { + var element = this + element = element.suppressAutoInsertion() + element = element.assignSmartCompletionPriority(SmartCompletionItemPriority.NULLABLE) + element = element.addTailAndNameSimilarity(matchedInfos) + return element } -} -private fun lookupElementsForNullable(factory: () -> LookupElement?): Collection { - val result = ArrayList(2) - - var lookupElement = factory() - if (lookupElement != null) { - lookupElement = object: LookupElementDecorator(lookupElement) { + factory().mapTo(this) { + object: LookupElementDecorator(it) { override fun renderElement(presentation: LookupElementPresentation) { super.renderElement(presentation) presentation.setItemText("!! " + presentation.getItemText()) @@ -223,15 +219,11 @@ private fun lookupElementsForNullable(factory: () -> LookupElement?): Collection override fun handleInsert(context: InsertionContext) { WithTailInsertHandler("!!", spaceBefore = false, spaceAfter = false).handleInsert(context, getDelegate()) } - } - lookupElement = lookupElement!!.suppressAutoInsertion() - lookupElement = lookupElement!!.assignSmartCompletionPriority(SmartCompletionItemPriority.NULLABLE) - result.add(lookupElement!!) + }.postProcess() } - lookupElement = factory() - if (lookupElement != null) { - lookupElement = object: LookupElementDecorator(lookupElement!!) { + factory().mapTo(this) { + object: LookupElementDecorator(it) { override fun renderElement(presentation: LookupElementPresentation) { super.renderElement(presentation) presentation.setItemText("?: " + presentation.getItemText()) @@ -239,13 +231,8 @@ private fun lookupElementsForNullable(factory: () -> LookupElement?): Collection override fun handleInsert(context: InsertionContext) { WithTailInsertHandler("?:", spaceBefore = true, spaceAfter = true).handleInsert(context, getDelegate()) //TODO: code style } - } - lookupElement = lookupElement!!.suppressAutoInsertion() - lookupElement = lookupElement!!.assignSmartCompletionPriority(SmartCompletionItemPriority.NULLABLE) - result.add(lookupElement!!) + }.postProcess() } - - return result } fun functionType(function: FunctionDescriptor): JetType? { @@ -274,22 +261,24 @@ fun functionType(function: FunctionDescriptor): JetType? { ) } -fun LookupElementFactory.createLookupElement( +fun LookupElementFactory.createLookupElementsInSmartCompletion( descriptor: DeclarationDescriptor, bindingContext: BindingContext, - boldImmediateMembers: Boolean -): LookupElement { - var element = createLookupElement(descriptor, boldImmediateMembers) + useReceiverTypes: Boolean +): Collection { + return createStandardLookupElementsForDescriptor(descriptor, useReceiverTypes).map { + var element = it - if (descriptor is FunctionDescriptor && descriptor.getValueParameters().isNotEmpty()) { - element = element.keepOldArgumentListOnTab() + if (descriptor is FunctionDescriptor && descriptor.valueParameters.isNotEmpty()) { + element = element.keepOldArgumentListOnTab() + } + + if (descriptor is ValueParameterDescriptor && bindingContext[BindingContext.AUTO_CREATED_IT, descriptor]!!) { + element = element.assignSmartCompletionPriority(SmartCompletionItemPriority.IT) + } + + element } - - if (descriptor is ValueParameterDescriptor && bindingContext[BindingContext.AUTO_CREATED_IT, descriptor]!!) { - element = element.assignSmartCompletionPriority(SmartCompletionItemPriority.IT) - } - - return element } enum class SmartCompletionItemPriority { diff --git a/idea/idea-completion/testData/handlers/smart/ClassObjectMethod4.kt b/idea/idea-completion/testData/handlers/smart/ClassObjectMethod4.kt index a3fed997505..132332416ec 100644 --- a/idea/idea-completion/testData/handlers/smart/ClassObjectMethod4.kt +++ b/idea/idea-completion/testData/handlers/smart/ClassObjectMethod4.kt @@ -11,3 +11,4 @@ fun foo(){ } // ELEMENT: bar +// TAIL_TEXT: "(p: (Int, String) -> Unit) (sample)" diff --git a/idea/idea-completion/testData/handlers/smart/ClassObjectMethod4.kt.after b/idea/idea-completion/testData/handlers/smart/ClassObjectMethod4.kt.after index db9ce3d3363..24595c6d387 100644 --- a/idea/idea-completion/testData/handlers/smart/ClassObjectMethod4.kt.after +++ b/idea/idea-completion/testData/handlers/smart/ClassObjectMethod4.kt.after @@ -11,3 +11,4 @@ fun foo(){ } // ELEMENT: bar +// TAIL_TEXT: "(p: (Int, String) -> Unit) (sample)" diff --git a/idea/idea-completion/testData/smart/HighOrderFunction.kt b/idea/idea-completion/testData/smart/HighOrderFunction.kt new file mode 100644 index 00000000000..35fa6b20919 --- /dev/null +++ b/idea/idea-completion/testData/smart/HighOrderFunction.kt @@ -0,0 +1,6 @@ +fun foo(p: (String, Char) -> Unit): String {} + +fun v: String = fo + +// EXIST: { lookupString:"foo", itemText: "foo", tailText: "(p: (String, Char) -> Unit) ()", typeText:"String" } +// EXIST: { lookupString:"foo", itemText: "foo", tailText: " { String, Char -> ... } (p: (String, Char) -> Unit) ()", typeText:"String" } diff --git a/idea/idea-completion/testData/smart/SAMExpected2.kt b/idea/idea-completion/testData/smart/SAMExpected2.kt new file mode 100644 index 00000000000..9df777f09de --- /dev/null +++ b/idea/idea-completion/testData/smart/SAMExpected2.kt @@ -0,0 +1,7 @@ +import java.util.Comparator + +var v: Comparator = + +// EXIST: { itemText: "object: Comparator{...}" } +// EXIST: { lookupString: "Comparator", itemText: "Comparator", tailText: "(function: (T!, T!) -> Int) (java.util)", typeText: "Comparator" } +// EXIST: { lookupString: "Comparator", itemText: "Comparator", tailText: " { T, T -> ... } (function: (T!, T!) -> Int) (java.util)", typeText: "Comparator" } diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmSmartCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmSmartCompletionTestGenerated.java index ea32cc67f83..bd3f1ee4961 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmSmartCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmSmartCompletionTestGenerated.java @@ -143,6 +143,12 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT doTest(fileName); } + @TestMetadata("HighOrderFunction.kt") + public void testHighOrderFunction() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/HighOrderFunction.kt"); + doTest(fileName); + } + @TestMetadata("IfCondition.kt") public void testIfCondition() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/IfCondition.kt"); @@ -425,6 +431,12 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT doTest(fileName); } + @TestMetadata("SAMExpected2.kt") + public void testSAMExpected2() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/SAMExpected2.kt"); + doTest(fileName); + } + @TestMetadata("SkipDeclarationsOfType.kt") public void testSkipDeclarationsOfType() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/SkipDeclarationsOfType.kt");