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 d320e458acf..dcd630243d0 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 @@ -154,22 +154,30 @@ class BasicCompletionSession( } override fun doComplete() { - if (smartCompletion != null) { - val (additionalItems, @Suppress("UNUSED_VARIABLE") inheritanceSearcher) = smartCompletion.additionalItems(lookupElementFactory) + fun completeWithSmartCompletion(lookupElementFactory: LookupElementFactory) { + if (smartCompletion != null) { + val (additionalItems, @Suppress("UNUSED_VARIABLE") inheritanceSearcher) = smartCompletion.additionalItems(lookupElementFactory) - // 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) + // 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.addElements(additionalItems) + } } - val (imported, notImported) = referenceVariants!! - collector.addDescriptorElements(imported, lookupElementFactory) - collector.addDescriptorElements(notImported, lookupElementFactory, notImported = true) + val contextVariableTypesForSmartCompletion = withCollectRequiredContextVariableTypes(::completeWithSmartCompletion) + + fun completeReferenceVariants(lookupElementFactory: LookupElementFactory) { + val (imported, notImported) = referenceVariants!! + collector.addDescriptorElements(imported, lookupElementFactory) + collector.addDescriptorElements(notImported, lookupElementFactory, notImported = true) + } + + val contextVariableTypesForReferenceVariants = withCollectRequiredContextVariableTypes(::completeReferenceVariants) KEYWORDS_ONLY.doComplete() @@ -188,7 +196,7 @@ class BasicCompletionSession( } } - packageNames.forEach { collector.addElement(lookupElementFactory.createLookupElementForPackage(it)) } + packageNames.forEach { collector.addElement(basicLookupElementFactory.createLookupElementForPackage(it)) } } flushToResultSet() @@ -196,22 +204,35 @@ class BasicCompletionSession( NamedArgumentCompletion.complete(collector, expectedInfos) flushToResultSet() - completeNonImported() - flushToResultSet() + val contextVariablesProvider = RealContextVariablesProvider(referenceVariantsHelper, position) + withContextVariablesProvider(contextVariablesProvider) { lookupElementFactory -> + if (contextVariableTypesForSmartCompletion.any { contextVariablesProvider.functionTypeVariables(it).isNotEmpty() }) { + completeWithSmartCompletion(lookupElementFactory) + } - if (position.containingFile is KtCodeFragment) { - val variantsAndFactory = getRuntimeReceiverTypeReferenceVariants() - if (variantsAndFactory != null) { - val variants = variantsAndFactory.first - val lookupElementFactory = variantsAndFactory.second - collector.addDescriptorElements(variants.imported, lookupElementFactory, withReceiverCast = true) - collector.addDescriptorElements(variants.notImportedExtensions, lookupElementFactory, withReceiverCast = true, notImported = true) - flushToResultSet() + if (contextVariableTypesForReferenceVariants.any { contextVariablesProvider.functionTypeVariables(it).isNotEmpty() }) { + val (imported, notImported) = referenceVariantsWithSingleFunctionTypeParameter()!! + collector.addDescriptorElements(imported, lookupElementFactory) + collector.addDescriptorElements(notImported, lookupElementFactory, notImported = true) + } + + completeNonImported(lookupElementFactory) + flushToResultSet() + + if (position.containingFile is KtCodeFragment) { + val variantsAndFactory = getRuntimeReceiverTypeReferenceVariants(lookupElementFactory) + if (variantsAndFactory != null) { + val variants = variantsAndFactory.first + @Suppress("NAME_SHADOWING") val lookupElementFactory = variantsAndFactory.second + collector.addDescriptorElements(variants.imported, lookupElementFactory, withReceiverCast = true) + collector.addDescriptorElements(variants.notImportedExtensions, lookupElementFactory, withReceiverCast = true, notImported = true) + flushToResultSet() + } } } } - private fun completeNonImported() { + private fun completeNonImported(lookupElementFactory: LookupElementFactory) { if (shouldCompleteTopLevelCallablesFromIndex()) { collector.addDescriptorElements(getTopLevelCallables(), lookupElementFactory, notImported = true) } @@ -292,7 +313,7 @@ class BasicCompletionSession( "override" -> { collector.addElement(lookupElement) - OverridesCompletion(collector, lookupElementFactory).complete(position) + OverridesCompletion(collector, basicLookupElementFactory).complete(position) } "class" -> { @@ -377,7 +398,7 @@ class BasicCompletionSession( KEYWORDS_ONLY.doComplete() if (shouldCompleteParameterNameAndType()) { - val parameterNameAndTypeCompletion = ParameterNameAndTypeCompletion(collector, lookupElementFactory, prefixMatcher, resolutionFacade) + val parameterNameAndTypeCompletion = ParameterNameAndTypeCompletion(collector, basicLookupElementFactory, prefixMatcher, resolutionFacade) // if we are typing parameter name, restart completion each time we type an upper case letter because new suggestions will appear (previous words can be used as user prefix) val prefixPattern = StandardPatterns.string().with(object : PatternCondition("Prefix ends with uppercase letter") { @@ -470,7 +491,7 @@ class BasicCompletionSession( } superClasses - .map { lookupElementFactory.createLookupElement(it, useReceiverTypes = false, qualifyNestedClasses = true, includeClassTypeArguments = false) } + .map { basicLookupElementFactory.createLookupElement(it, qualifyNestedClasses = true, includeClassTypeArguments = false) } .forEach { collector.addElement(it) } } } diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/BasicLookupElementFactory.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/BasicLookupElementFactory.kt index d89fa25dffb..69616158cab 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/BasicLookupElementFactory.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/BasicLookupElementFactory.kt @@ -38,7 +38,7 @@ import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor class BasicLookupElementFactory( private val project: Project, - private val insertHandlerProvider: InsertHandlerProvider + val insertHandlerProvider: InsertHandlerProvider ) { public fun createLookupElement( descriptor: DeclarationDescriptor, 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 928dcf16bd5..40b2369b7cd 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 @@ -132,14 +132,16 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC protected val referenceVariantsHelper = ReferenceVariantsHelper(bindingContext, resolutionFacade, isVisibleFilter) protected val callTypeAndReceiver: CallTypeAndReceiver<*, *> - protected val lookupElementFactory: LookupElementFactory + protected val receiverTypes: Collection? init { val (callTypeAndReceiver, receiverTypes) = detectCallTypeAndReceiverTypes() this.callTypeAndReceiver = callTypeAndReceiver - this.lookupElementFactory = createLookupElementFactory(callTypeAndReceiver.callType, receiverTypes) + this.receiverTypes = receiverTypes } + protected val basicLookupElementFactory = BasicLookupElementFactory(project, InsertHandlerProvider(callTypeAndReceiver.callType) { expectedInfos }) + // LookupElementsCollector instantiation is deferred because virtual call to createSorter uses data from derived classes protected val collector: LookupElementsCollector by lazy(LazyThreadSafetyMode.NONE) { LookupElementsCollector(prefixMatcher, parameters, resultSet, createSorter()) @@ -273,12 +275,13 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC data class ReferenceVariants(val imported: Collection, val notImportedExtensions: Collection) protected val referenceVariants: ReferenceVariants? by lazy { - descriptorKindFilter?.let { collectReferenceVariants(it) } + if (nameExpression != null && descriptorKindFilter != null) collectReferenceVariants(descriptorKindFilter!!, nameExpression) else null } - private fun collectReferenceVariants(descriptorKindFilter: DescriptorKindFilter, runtimeReceiver: ExpressionReceiver? = null): ReferenceVariants { + + private fun collectReferenceVariants(descriptorKindFilter: DescriptorKindFilter, nameExpression: KtSimpleNameExpression, runtimeReceiver: ExpressionReceiver? = null): ReferenceVariants { var variants = referenceVariantsHelper.getReferenceVariants( - nameExpression!!, + nameExpression, descriptorKindFilter, descriptorNameFilter, filterOutJavaGettersAndSetters = false, @@ -315,8 +318,15 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC return ReferenceVariants(variants, notImportedExtensions) } - protected fun getRuntimeReceiverTypeReferenceVariants(): Pair? { + protected fun referenceVariantsWithSingleFunctionTypeParameter(): ReferenceVariants? { + val variants = referenceVariants ?: return null + val filter: (DeclarationDescriptor) -> Boolean = { it is FunctionDescriptor && LookupElementFactory.hasSingleFunctionTypeParameter(it) } + return ReferenceVariants(variants.imported.filter(filter), variants.notImportedExtensions.filter(filter)) + } + + protected fun getRuntimeReceiverTypeReferenceVariants(lookupElementFactory: LookupElementFactory): Pair? { val evaluator = file.getCopyableUserData(KtCodeFragment.RUNTIME_TYPE_EVALUATOR) ?: return null + val referenceVariants = referenceVariants ?: return null val explicitReceiver = callTypeAndReceiver.receiver as? KtExpression ?: return null val type = bindingContext.getType(explicitReceiver) ?: return null @@ -325,12 +335,12 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC val runtimeType = evaluator(explicitReceiver) if (runtimeType == null || runtimeType == type) return null - val (variants, notImportedExtensions) = collectReferenceVariants(descriptorKindFilter!!, ExpressionReceiver(explicitReceiver, runtimeType)) - val filteredVariants = filterVariantsForRuntimeReceiverType(variants, referenceVariants!!.imported) - val filteredNotImportedExtensions = filterVariantsForRuntimeReceiverType(notImportedExtensions, referenceVariants!!.notImportedExtensions) + val (variants, notImportedExtensions) = collectReferenceVariants(descriptorKindFilter!!, nameExpression!!, ExpressionReceiver(explicitReceiver, runtimeType)) + val filteredVariants = filterVariantsForRuntimeReceiverType(variants, referenceVariants.imported) + val filteredNotImportedExtensions = filterVariantsForRuntimeReceiverType(notImportedExtensions, referenceVariants.notImportedExtensions) - val referenceVariants = ReferenceVariants(filteredVariants, filteredNotImportedExtensions) - return Pair(referenceVariants, lookupElementFactory.copy(receiverTypes = listOf(runtimeType))) + val runtimeVariants = ReferenceVariants(filteredVariants, filteredNotImportedExtensions) + return Pair(runtimeVariants, lookupElementFactory.copy(receiverTypes = listOf(runtimeType))) } private fun filterVariantsForRuntimeReceiverType( @@ -373,24 +383,26 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC protected fun addClassesFromIndex(kindFilter: (ClassKind) -> Boolean) { AllClassesCompletion(parameters, indicesHelper, prefixMatcher, resolutionFacade, kindFilter) .collect( - { descriptor -> collector.addDescriptorElements(descriptor, lookupElementFactory, notImported = true) }, - { javaClass -> collector.addElement(lookupElementFactory.createLookupElementForJavaClass(javaClass), notImported = true) } + { descriptor -> collector.addElement(basicLookupElementFactory.createLookupElement(descriptor), notImported = true) }, + { javaClass -> collector.addElement(basicLookupElementFactory.createLookupElementForJavaClass(javaClass), notImported = true) } ) } - private fun createLookupElementFactory(callType: CallType<*>?, receiverTypes: Collection?): LookupElementFactory { - val contextVariablesProvider = { - nameExpression?.let { - val descriptorFilter = DescriptorKindFilter.VARIABLES exclude DescriptorKindExclude.Extensions // we exclude extensions by performance reasons - referenceVariantsHelper.getReferenceVariants(it, CallTypeAndReceiver.DEFAULT, descriptorFilter, nameFilter = { true }) - .map { it as VariableDescriptor } - } ?: emptyList() - } + protected fun withCollectRequiredContextVariableTypes(action: (LookupElementFactory) -> Unit): Collection { + val provider = CollectRequiredTypesContextVariablesProvider() + val lookupElementFactory = createLookupElementFactory(provider) + action(lookupElementFactory) + return provider.requiredTypes + } - val insertHandlerProvider = InsertHandlerProvider(callType) { expectedInfos } - return LookupElementFactory(resolutionFacade, receiverTypes, - callType, expression?.parent is KtSimpleNameStringTemplateEntry, - insertHandlerProvider, contextVariablesProvider) + protected fun withContextVariablesProvider(contextVariablesProvider: ContextVariablesProvider, action: (LookupElementFactory) -> Unit) { + val lookupElementFactory = createLookupElementFactory(contextVariablesProvider) + action(lookupElementFactory) + } + + protected fun createLookupElementFactory(contextVariablesProvider: ContextVariablesProvider): LookupElementFactory { + return LookupElementFactory(basicLookupElementFactory, resolutionFacade, receiverTypes, + callTypeAndReceiver.callType, contextVariablesProvider) } private fun detectCallTypeAndReceiverTypes(): Pair, Collection?> { 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 bb4083137ca..35a06ab506a 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 @@ -29,12 +29,8 @@ import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor import org.jetbrains.kotlin.idea.KotlinIcons import org.jetbrains.kotlin.idea.completion.handlers.CastReceiverInsertHandler import org.jetbrains.kotlin.idea.completion.handlers.WithTailInsertHandler -import org.jetbrains.kotlin.idea.util.getResolutionScope import org.jetbrains.kotlin.idea.resolve.ResolutionFacade -import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers -import org.jetbrains.kotlin.idea.util.ShortenReferences -import org.jetbrains.kotlin.idea.util.findLabelAndCall -import org.jetbrains.kotlin.idea.util.getImplicitReceiversWithInstanceToExpression +import org.jetbrains.kotlin.idea.util.* import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf @@ -94,22 +90,6 @@ fun LookupElement.withReceiverCast(): LookupElement { } } -fun LookupElement.withBracesSurrounding(): LookupElement { - return object: LookupElementDecorator(this) { - override fun handleInsert(context: InsertionContext) { - val startOffset = context.getStartOffset() - context.getDocument().insertString(startOffset, "{") - context.getOffsetMap().addOffset(CompletionInitializationContext.START_OFFSET, startOffset + 1) - - val tailOffset = context.getTailOffset() - context.getDocument().insertString(tailOffset, "}") - context.setTailOffset(tailOffset) - - super.handleInsert(context) - } - } -} - val KEEP_OLD_ARGUMENT_LIST_ON_TAB_KEY = Key("KEEP_OLD_ARGUMENT_LIST_ON_TAB_KEY") fun LookupElement.keepOldArgumentListOnTab(): LookupElement { @@ -310,7 +290,7 @@ fun breakOrContinueExpressionItems(position: KtElement, breakOrContinue: String) return result } -fun LookupElementFactory.createLookupElementForType(type: KotlinType): LookupElement? { +fun BasicLookupElementFactory.createLookupElementForType(type: KotlinType): LookupElement? { if (type.isError()) return null if (KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(type)) { @@ -320,7 +300,7 @@ fun LookupElementFactory.createLookupElementForType(type: KotlinType): LookupEle } else { val classifier = type.getConstructor().getDeclarationDescriptor() ?: return null - val baseLookupElement = createLookupElement(classifier, useReceiverTypes = false, qualifyNestedClasses = true, includeClassTypeArguments = false) + val baseLookupElement = createLookupElement(classifier, 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/ContextVariablesProvider.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ContextVariablesProvider.kt new file mode 100644 index 00000000000..1d443870c66 --- /dev/null +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ContextVariablesProvider.kt @@ -0,0 +1,70 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.completion + +import com.intellij.psi.PsiElement +import com.intellij.util.SmartList +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.VariableDescriptor +import org.jetbrains.kotlin.idea.codeInsight.ReferenceVariantsHelper +import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver +import org.jetbrains.kotlin.idea.util.FuzzyType +import org.jetbrains.kotlin.idea.util.fuzzyReturnType +import org.jetbrains.kotlin.resolve.scopes.DescriptorKindExclude +import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter +import org.jetbrains.kotlin.types.TypeSubstitutor +import java.util.* + +interface ContextVariablesProvider { + fun functionTypeVariables(requiredType: FuzzyType): Collection> +} + +class RealContextVariablesProvider( + private val referenceVariantsHelper: ReferenceVariantsHelper, + private val contextElement: PsiElement +) : ContextVariablesProvider { + private val functionTypeVariables by lazy { + collectVariables().filter { KotlinBuiltIns.isFunctionOrExtensionFunctionType(it.type) } + } + + private fun collectVariables(): Collection { + val descriptorFilter = DescriptorKindFilter.VARIABLES exclude DescriptorKindExclude.Extensions // we exclude extensions by performance reasons + return referenceVariantsHelper.getReferenceVariants(contextElement, CallTypeAndReceiver.DEFAULT, descriptorFilter, nameFilter = { true }) + .map { it as VariableDescriptor } + } + + override fun functionTypeVariables(requiredType: FuzzyType): Collection> { + val result = SmartList>() + for (variable in functionTypeVariables) { + val substitutor = variable.fuzzyReturnType()?.checkIsSubtypeOf(requiredType) ?: continue + result.add(variable to substitutor) + } + return result + } +} + +class CollectRequiredTypesContextVariablesProvider : ContextVariablesProvider { + private val _requiredTypes = HashSet() + + public val requiredTypes: Set + get() = _requiredTypes + + override fun functionTypeVariables(requiredType: FuzzyType): Collection> { + _requiredTypes.add(requiredType) + return emptyList() + } +} \ No newline at end of file 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 c5746ce214a..18f20a7720b 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 @@ -87,7 +87,7 @@ class KDocNameCompletionSession(parameters: CompletionParameters, .filter { it.getName().asString() !in documentedParameters } descriptors.forEach { - collector.addElement(lookupElementFactory.createLookupElement(it, useReceiverTypes = false, parametersAndTypeGrayed = true)) + collector.addElement(basicLookupElementFactory.createLookupElement(it, parametersAndTypeGrayed = true)) } } @@ -108,7 +108,7 @@ class KDocNameCompletionSession(parameters: CompletionParameters, } scope.collectDescriptorsFiltered(nameFilter = descriptorNameFilter).filter(::isApplicable).forEach { - val element = lookupElementFactory.createLookupElement(it, useReceiverTypes = false, parametersAndTypeGrayed = true) + val element = basicLookupElementFactory.createLookupElement(it, parametersAndTypeGrayed = true) 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 f077f55e5fd..229f1e68b0b 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 @@ -21,7 +21,6 @@ import com.intellij.codeInsight.lookup.LookupElement import com.intellij.codeInsight.lookup.LookupElementDecorator import com.intellij.codeInsight.lookup.LookupElementPresentation import com.intellij.codeInsight.lookup.impl.LookupCellRenderer -import com.intellij.psi.PsiClass import com.intellij.util.SmartList import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.* @@ -31,9 +30,8 @@ import org.jetbrains.kotlin.idea.completion.handlers.lambdaPresentation import org.jetbrains.kotlin.idea.resolve.ResolutionFacade import org.jetbrains.kotlin.idea.util.CallType import org.jetbrains.kotlin.idea.util.FuzzyType -import org.jetbrains.kotlin.idea.util.fuzzyReturnType -import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.renderer.DescriptorRenderer +import org.jetbrains.kotlin.renderer.render import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension import org.jetbrains.kotlin.synthetic.SamAdapterExtensionFunctionDescriptor @@ -44,28 +42,27 @@ import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf data /* we need copy() */ class LookupElementFactory( + val basicFactory: BasicLookupElementFactory, private val resolutionFacade: ResolutionFacade, private val receiverTypes: Collection?, private val callType: CallType<*>?, - private val isInStringTemplateAfterDollar: Boolean, - public val insertHandlerProvider: InsertHandlerProvider, - private val contextVariablesProvider: () -> Collection + private val contextVariablesProvider: ContextVariablesProvider ) { - private val basicFactory = BasicLookupElementFactory(resolutionFacade.project, insertHandlerProvider) - - private val functionTypeContextVariables by lazy(LazyThreadSafetyMode.NONE) { - contextVariablesProvider().filter { KotlinBuiltIns.isFunctionOrExtensionFunctionType(it.type) } + companion object { + fun hasSingleFunctionTypeParameter(descriptor: FunctionDescriptor): Boolean { + val parameter = descriptor.original.valueParameters.singleOrNull() ?: return false + return KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(parameter.type) + } } + val insertHandlerProvider = basicFactory.insertHandlerProvider + public fun createStandardLookupElementsForDescriptor(descriptor: DeclarationDescriptor, useReceiverTypes: Boolean): Collection { val result = SmartList() val isNormalCall = callType == CallType.DEFAULT || callType == CallType.DOT || callType == CallType.SAFE var lookupElement = createLookupElement(descriptor, useReceiverTypes, parametersAndTypeGrayed = !isNormalCall && callType != CallType.INFIX) - if (isInStringTemplateAfterDollar && (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 @@ -80,11 +77,12 @@ class LookupElementFactory( // check that all parameters except for the last one are optional val lastParameter = descriptor.valueParameters.lastOrNull() ?: return if (!descriptor.valueParameters.all { it == lastParameter || it.hasDefaultValue() }) return - val parameterType = lastParameter.type - if (KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(parameterType)) { - val isSingleParameter = descriptor.valueParameters.size() == 1 - val functionParameterCount = KotlinBuiltIns.getParameterTypeProjectionsFromFunctionType(parameterType).size() + if (KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(lastParameter.original.type)) { + val isSingleParameter = descriptor.valueParameters.size == 1 + + val parameterType = lastParameter.type + val functionParameterCount = KotlinBuiltIns.getParameterTypeProjectionsFromFunctionType(parameterType).size // we don't need special item inserting lambda for single functional parameter that does not need multiple arguments because the default item will be special in this case if (!isSingleParameter || functionParameterCount > 1) { add(createFunctionCallElementWithLambda(descriptor, parameterType, functionParameterCount > 1, useReceiverTypes)) @@ -94,12 +92,9 @@ class LookupElementFactory( //TODO: also ::function? at least for local functions //TODO: order for them val fuzzyParameterType = FuzzyType(parameterType, descriptor.typeParameters) - for (variable in functionTypeContextVariables) { - val substitutor = variable.fuzzyReturnType()?.checkIsSubtypeOf(fuzzyParameterType) - if (substitutor != null) { - val substitutedDescriptor = descriptor.substitute(substitutor) ?: continue - add(createFunctionCallElementWithArgument(substitutedDescriptor, variable.name.asString(), useReceiverTypes)) - } + for ((variable, substitutor) in contextVariablesProvider.functionTypeVariables(fuzzyParameterType)) { + val substitutedDescriptor = descriptor.substitute(substitutor) + add(createFunctionCallElementWithArgument(substitutedDescriptor, variable.name.render(), useReceiverTypes)) } } } @@ -139,10 +134,6 @@ class LookupElementFactory( } } - if (isInStringTemplateAfterDollar) { - lookupElement = lookupElement.withBracesSurrounding() - } - return lookupElement } @@ -150,13 +141,7 @@ class LookupElementFactory( var lookupElement = createLookupElement(descriptor, useReceiverTypes) val needTypeArguments = (insertHandlerProvider.insertHandler(descriptor) as KotlinFunctionInsertHandler.Normal).inputTypeArguments - lookupElement = FunctionCallWithArgumentLookupElement(lookupElement, descriptor, argumentText, needTypeArguments) - - if (isInStringTemplateAfterDollar) { - lookupElement = lookupElement.withBracesSurrounding() - } - - return lookupElement + return FunctionCallWithArgumentLookupElement(lookupElement, descriptor, argumentText, needTypeArguments) } private inner class FunctionCallWithArgumentLookupElement( @@ -283,12 +268,4 @@ class LookupElementFactory( val typeParameter = receiverParameter.type.constructor.declarationDescriptor as? TypeParameterDescriptor ?: return false return typeParameter.containingDeclaration == original } - - public fun createLookupElementForJavaClass(psiClass: PsiClass, qualifyNestedClasses: Boolean = false, includeClassTypeArguments: Boolean = true): LookupElement { - return basicFactory.createLookupElementForJavaClass(psiClass, qualifyNestedClasses, includeClassTypeArguments) - } - - public fun createLookupElementForPackage(name: FqName): LookupElement { - return basicFactory.createLookupElementForPackage(name) - } } diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/OverridesCompletion.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/OverridesCompletion.kt index b6aa5b8444d..e2f446c216e 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/OverridesCompletion.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/OverridesCompletion.kt @@ -49,7 +49,7 @@ import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType class OverridesCompletion( private val collector: LookupElementsCollector, - private val lookupElementFactory: LookupElementFactory + private val lookupElementFactory: BasicLookupElementFactory ) { private val PRESENTATION_RENDERER = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.withOptions { modifiers = emptySet() @@ -66,7 +66,7 @@ class OverridesCompletion( if (isConstructorParameter && memberObject.descriptor !is PropertyDescriptor) continue val descriptor = memberObject.descriptor - var lookupElement = lookupElementFactory.createLookupElement(descriptor, useReceiverTypes = false) + var lookupElement = lookupElementFactory.createLookupElement(descriptor) var text = "override " + PRESENTATION_RENDERER.render(descriptor) if (descriptor is FunctionDescriptor) { 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 1d148a5a267..1245f09efb1 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 @@ -48,7 +48,7 @@ import java.util.* class ParameterNameAndTypeCompletion( private val collector: LookupElementsCollector, - private val lookupElementFactory: LookupElementFactory, + private val lookupElementFactory: BasicLookupElementFactory, private val prefixMatcher: PrefixMatcher, private val resolutionFacade: ResolutionFacade ) { @@ -112,9 +112,9 @@ class ParameterNameAndTypeCompletion( if (descriptor != null) { val parameterType = descriptor.getType() if (parameterType.isVisible(visibilityFilter)) { - val lookupElement = MyLookupElement.create(name, ArbitraryType(parameterType), lookupElementFactory) + val lookupElement = MyLookupElement.create(name, ArbitraryType(parameterType), lookupElementFactory)!! val count = lookupElementToCount[lookupElement] ?: 0 - lookupElementToCount[lookupElement!!] = count + 1 + lookupElementToCount[lookupElement] = count + 1 } } } @@ -159,24 +159,24 @@ class ParameterNameAndTypeCompletion( } private abstract class Type(private val idString: String) { - abstract fun createTypeLookupElement(lookupElementFactory: LookupElementFactory): LookupElement? + abstract fun createTypeLookupElement(lookupElementFactory: BasicLookupElementFactory): LookupElement? override fun equals(other: Any?) = other is Type && other.idString == idString override fun hashCode() = idString.hashCode() } private class DescriptorType(private val classifier: ClassifierDescriptor) : Type(IdeDescriptorRenderers.SOURCE_CODE.renderClassifierName(classifier)) { - override fun createTypeLookupElement(lookupElementFactory: LookupElementFactory) - = lookupElementFactory.createLookupElement(classifier, useReceiverTypes = false, qualifyNestedClasses = true) + override fun createTypeLookupElement(lookupElementFactory: BasicLookupElementFactory) + = lookupElementFactory.createLookupElement(classifier, qualifyNestedClasses = true) } private class JavaClassType(private val psiClass: PsiClass) : Type(psiClass.getQualifiedName()!!) { - override fun createTypeLookupElement(lookupElementFactory: LookupElementFactory) + override fun createTypeLookupElement(lookupElementFactory: BasicLookupElementFactory) = lookupElementFactory.createLookupElementForJavaClass(psiClass, qualifyNestedClasses = true) } private class ArbitraryType(private val type: KotlinType) : Type(IdeDescriptorRenderers.SOURCE_CODE.renderType(type)) { - override fun createTypeLookupElement(lookupElementFactory: LookupElementFactory) + override fun createTypeLookupElement(lookupElementFactory: BasicLookupElementFactory) = lookupElementFactory.createLookupElementForType(type) } @@ -187,7 +187,7 @@ class ParameterNameAndTypeCompletion( ) : LookupElementDecorator(typeLookupElement) { companion object { - fun create(parameterName: String, type: Type, factory: LookupElementFactory): LookupElement? { + fun create(parameterName: String, type: Type, factory: BasicLookupElementFactory): LookupElement? { val typeLookupElement = type.createTypeLookupElement(factory) ?: return null val lookupElement = MyLookupElement(parameterName, type, typeLookupElement) return lookupElement.suppressAutoInsertion() diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/KotlinClassifierInsertHandler.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/KotlinClassifierInsertHandler.kt index a2670b39e39..8c91ae5ceb0 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/KotlinClassifierInsertHandler.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/KotlinClassifierInsertHandler.kt @@ -37,6 +37,8 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode object KotlinClassifierInsertHandler : BaseDeclarationInsertHandler() { override fun handleInsert(context: InsertionContext, item: LookupElement) { + surroundWithBracesIfInStringTemplate(context) + super.handleInsert(context, item) val file = context.getFile() diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/KotlinFunctionInsertHandler.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/KotlinFunctionInsertHandler.kt index 40da122fe10..49cf7d71b22 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/KotlinFunctionInsertHandler.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/KotlinFunctionInsertHandler.kt @@ -58,16 +58,20 @@ sealed class KotlinFunctionInsertHandler : KotlinCallableInsertHandler() { ) = Normal(inputTypeArguments, inputValueArguments, argumentText, lambdaInfo, argumentsOnly) override fun handleInsert(context: InsertionContext, item: LookupElement) { + val psiDocumentManager = PsiDocumentManager.getInstance(context.project) + val document = context.document + if (!argumentsOnly) { + surroundWithBracesIfInStringTemplate(context) + super.handleInsert(context, item) } - val psiDocumentManager = PsiDocumentManager.getInstance(context.project) psiDocumentManager.commitAllDocuments() - psiDocumentManager.doPostponedOperationsAndUnblockDocument(context.document) + psiDocumentManager.doPostponedOperationsAndUnblockDocument(document) - val startOffset = context.getStartOffset() - val element = context.getFile().findElementAt(startOffset) ?: return + val startOffset = context.startOffset + val element = context.file.findElementAt(startOffset) ?: return addArguments(context, element) } diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/handlerUtils.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/handlerUtils.kt index 6d3810d3f89..3cb1950fdb9 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/handlerUtils.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/handlers/handlerUtils.kt @@ -16,8 +16,31 @@ package org.jetbrains.kotlin.idea.completion.handlers +import com.intellij.codeInsight.completion.CompletionInitializationContext +import com.intellij.codeInsight.completion.InsertionContext import com.intellij.openapi.editor.Document import com.intellij.openapi.util.TextRange +import com.intellij.psi.PsiDocumentManager +import org.jetbrains.kotlin.lexer.KtTokens + +fun surroundWithBracesIfInStringTemplate(context: InsertionContext) { + val startOffset = context.startOffset + val document = context.document + if (startOffset > 0 && document.charsSequence[startOffset - 1] == '$') { + val psiDocumentManager = PsiDocumentManager.getInstance(context.project) + psiDocumentManager.commitAllDocuments() + psiDocumentManager.doPostponedOperationsAndUnblockDocument(document) + + if (context.file.findElementAt(startOffset - 1)?.node?.elementType == KtTokens.SHORT_TEMPLATE_ENTRY_START) { + document.insertString(startOffset, "{") + context.offsetMap.addOffset(CompletionInitializationContext.START_OFFSET, startOffset + 1) + + val tailOffset = context.tailOffset + document.insertString(tailOffset, "}") + context.tailOffset = tailOffset + } + } +} fun CharSequence.indexOfSkippingSpace(c: Char, startIndex: Int): Int? { for (i in startIndex..this.length() - 1) { diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/ClassLiteralItems.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/ClassLiteralItems.kt index 8c92c4a9728..54b29204c2e 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/ClassLiteralItems.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/ClassLiteralItems.kt @@ -22,8 +22,8 @@ import com.intellij.codeInsight.lookup.LookupElementDecorator import com.intellij.codeInsight.lookup.LookupElementPresentation import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.idea.completion.BasicLookupElementFactory import org.jetbrains.kotlin.idea.completion.ExpectedInfo -import org.jetbrains.kotlin.idea.completion.LookupElementFactory import org.jetbrains.kotlin.idea.completion.createLookupElementForType import org.jetbrains.kotlin.idea.completion.fuzzyType import org.jetbrains.kotlin.idea.imports.importableFqName @@ -36,7 +36,7 @@ object ClassLiteralItems { public fun addToCollection( collection: MutableCollection, expectedInfos: Collection, - lookupElementFactory: LookupElementFactory, + lookupElementFactory: BasicLookupElementFactory, isJvmModule: Boolean ) { val typeAndSuffixToExpectedInfos = LinkedHashMap, MutableList>() 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 cb942a1ede8..3dffc8e9f65 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 @@ -173,7 +173,7 @@ class SmartCompletion( } private fun additionalItemsNoPostProcess(lookupElementFactory: LookupElementFactory): Pair, InheritanceItemsSearcher?> { - val asTypePositionItems = buildForAsTypePosition(lookupElementFactory) + val asTypePositionItems = buildForAsTypePosition(lookupElementFactory.basicFactory) if (asTypePositionItems != null) { assert(expectedInfos.isEmpty()) return Pair(asTypePositionItems, null) @@ -209,7 +209,7 @@ class SmartCompletion( .addToCollection(items, expectedInfos, expression, descriptorsToSkip) } - ClassLiteralItems.addToCollection(items, expectedInfos, lookupElementFactory, isJvmModule) + ClassLiteralItems.addToCollection(items, expectedInfos, lookupElementFactory.basicFactory, isJvmModule) if (!forBasicCompletion) { LambdaItems.addToCollection(items, expectedInfos) @@ -368,7 +368,7 @@ class SmartCompletion( } } - private fun buildForAsTypePosition(lookupElementFactory: LookupElementFactory): Collection? { + private fun buildForAsTypePosition(lookupElementFactory: BasicLookupElementFactory): Collection? { val binaryExpression = ((expression.getParent() as? KtUserType) ?.getParent() as? KtTypeReference) ?.getParent() as? KtBinaryExpressionWithTypeRHS diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletionSession.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletionSession.kt index 4a0fcf2606a..4b7ce0b7898 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletionSession.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletionSession.kt @@ -64,45 +64,68 @@ class SmartCompletionSession(configuration: CompletionSessionConfiguration, para return } - if (expression != null) { - addFunctionLiteralArgumentCompletions() + if (expression == null) return - val (additionalItems, inheritanceSearcher) = smartCompletion!!.additionalItems(lookupElementFactory) - collector.addElements(additionalItems) + addFunctionLiteralArgumentCompletions() - if (nameExpression != null) { - val filter = smartCompletion!!.descriptorFilter - if (filter != null) { - val (imported, notImported) = referenceVariants!! - imported.forEach { collector.addElements(filter(it, lookupElementFactory)) } - notImported.forEach { collector.addElements(filter(it, lookupElementFactory), notImported = true) } + var inheritanceSearcher: InheritanceItemsSearcher? = null + val contextVariableTypesForAdditionalItems = withCollectRequiredContextVariableTypes { lookupElementFactory -> + val pair = smartCompletion!!.additionalItems(lookupElementFactory) + collector.addElements(pair.first) + inheritanceSearcher = pair.second + } + val filter = smartCompletion!!.descriptorFilter + var contextVariableTypesForReferenceVariants = filter?.let { + withCollectRequiredContextVariableTypes { lookupElementFactory -> + val (imported, notImported) = referenceVariants ?: return@withCollectRequiredContextVariableTypes + imported.forEach { collector.addElements(filter(it, lookupElementFactory)) } + notImported.forEach { collector.addElements(filter(it, lookupElementFactory), notImported = true) } + } + } + + flushToResultSet() + + val contextVariablesProvider = RealContextVariablesProvider(referenceVariantsHelper, position) + withContextVariablesProvider(contextVariablesProvider) { lookupElementFactory -> + if (contextVariableTypesForAdditionalItems.any { contextVariablesProvider.functionTypeVariables(it).isNotEmpty() }) { + val additionalItems = smartCompletion!!.additionalItems(lookupElementFactory).first + collector.addElements(additionalItems) + } + + if (filter != null && contextVariableTypesForReferenceVariants!!.any { contextVariablesProvider.functionTypeVariables(it).isNotEmpty() }) { + val (imported, notImported) = referenceVariantsWithSingleFunctionTypeParameter()!! + imported.forEach { collector.addElements(filter(it, lookupElementFactory)) } + notImported.forEach { collector.addElements(filter(it, lookupElementFactory), notImported = true) } + } + + flushToResultSet() + + if (filter != null) { + if (shouldCompleteTopLevelCallablesFromIndex()) { + getTopLevelCallables().forEach { collector.addElements(filter(it, lookupElementFactory), notImported = true) } flushToResultSet() + } - if (shouldCompleteTopLevelCallablesFromIndex()) { - getTopLevelCallables().forEach { collector.addElements(filter(it, lookupElementFactory), notImported = true) } + if (position.getContainingFile() is KtCodeFragment) { + val variantsAndFactory = getRuntimeReceiverTypeReferenceVariants(lookupElementFactory) + if (variantsAndFactory != null) { + val variants = variantsAndFactory.first + @Suppress("NAME_SHADOWING") val lookupElementFactory = variantsAndFactory.second + variants.imported.forEach { collector.addElements(filter(it, lookupElementFactory).map { it.withReceiverCast() }) } + variants.notImportedExtensions.forEach { collector.addElements(filter(it, lookupElementFactory).map { it.withReceiverCast() }, notImported = true) } flushToResultSet() } - - if (position.getContainingFile() is KtCodeFragment) { - val variantsAndFactory = getRuntimeReceiverTypeReferenceVariants() - if (variantsAndFactory != null) { - val variants = variantsAndFactory.first - val lookupElementFactory = variantsAndFactory.second - variants.imported.forEach { collector.addElements(filter(it, lookupElementFactory).map { it.withReceiverCast() }) } - variants.notImportedExtensions.forEach { collector.addElements(filter(it, lookupElementFactory).map { it.withReceiverCast() }, notImported = true) } - flushToResultSet() - } - } - } - - // it makes no sense to search inheritors if there is no reference because it means that we have prefix like "this@" - inheritanceSearcher?.search({ prefixMatcher.prefixMatches(it) }) { - collector.addElement(it) - flushToResultSet() } } } + + + // it makes no sense to search inheritors if there is no reference because it means that we have prefix like "this@" + inheritanceSearcher?.search({ prefixMatcher.prefixMatches(it) }) { + collector.addElement(it) + flushToResultSet() + } } // special completion for outside parenthesis lambda argument diff --git a/idea/idea-completion/testData/basic/common/highOrderFunctions/ContextVariables3.kt b/idea/idea-completion/testData/basic/common/highOrderFunctions/ContextVariables3.kt new file mode 100644 index 00000000000..6a75e9312b6 --- /dev/null +++ b/idea/idea-completion/testData/basic/common/highOrderFunctions/ContextVariables3.kt @@ -0,0 +1,12 @@ +class C { + companion object { + fun create(p: (Int) -> Unit) = C() + } +} + +val handler: (Int) -> Unit = {} + +fun v: C = cr + +// EXIST: { allLookupStrings: "C, create", itemText: "C.create", tailText: " {...} (p: (Int) -> Unit) ()", typeText:"C" } +// EXIST: { allLookupStrings: "C, create", itemText: "C.create", tailText: "(handler) ()", typeText:"C" } diff --git a/idea/idea-completion/testData/smart/HighOrderFunction.kt b/idea/idea-completion/testData/smart/HighOrderFunction.kt index 35fa6b20919..9245ed358be 100644 --- a/idea/idea-completion/testData/smart/HighOrderFunction.kt +++ b/idea/idea-completion/testData/smart/HighOrderFunction.kt @@ -1,6 +1,18 @@ -fun foo(p: (String, Char) -> Unit): String {} +class C { + companion object { + fun foo(p: (Int) -> Unit) = C() + } +} -fun v: String = fo +fun foo(p: (String, Char) -> Unit): C {} -// 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" } +val handler1: (String, Char) -> Unit = {} +val handler2: (Int) -> Unit = {} + +fun v: C = fo + +// EXIST: { lookupString:"foo", itemText: "foo", tailText: "(p: (String, Char) -> Unit) ()", typeText:"C" } +// EXIST: { lookupString:"foo", itemText: "foo", tailText: " { String, Char -> ... } (p: (String, Char) -> Unit) ()", typeText:"C" } +// EXIST: { lookupString:"foo", itemText: "foo", tailText: "(handler1) ()", typeText:"C" } +// EXIST: { allLookupStrings: "C, foo", itemText: "C.foo", tailText: " {...} (p: (Int) -> Unit) ()", typeText:"C" } +// EXIST: { allLookupStrings: "C, foo", itemText: "C.foo", tailText: "(handler2) ()", typeText:"C" } 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 1fec628203a..15873a2d71c 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 @@ -1389,6 +1389,12 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes doTest(fileName); } + @TestMetadata("ContextVariables3.kt") + public void testContextVariables3() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/highOrderFunctions/ContextVariables3.kt"); + doTest(fileName); + } + @TestMetadata("ContextVariablesFilter.kt") public void testContextVariablesFilter() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/highOrderFunctions/ContextVariablesFilter.kt"); 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 5a43dd1cbcb..6ed68194804 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 @@ -1389,6 +1389,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT doTest(fileName); } + @TestMetadata("ContextVariables3.kt") + public void testContextVariables3() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/highOrderFunctions/ContextVariables3.kt"); + doTest(fileName); + } + @TestMetadata("ContextVariablesFilter.kt") public void testContextVariablesFilter() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/highOrderFunctions/ContextVariablesFilter.kt");