Performance optimization in completion: search context variables of functional types later

This commit is contained in:
Valentin Kipyatkov
2015-10-30 18:26:12 +03:00
parent 0a5a5a2e36
commit b05d490eeb
19 changed files with 320 additions and 172 deletions
@@ -154,22 +154,30 @@ class BasicCompletionSession(
} }
override fun doComplete() { override fun doComplete() {
if (smartCompletion != null) { fun completeWithSmartCompletion(lookupElementFactory: LookupElementFactory) {
val (additionalItems, @Suppress("UNUSED_VARIABLE") inheritanceSearcher) = smartCompletion.additionalItems(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 // all additional items should have SMART_COMPLETION_ITEM_PRIORITY_KEY to be recognized by SmartCompletionInBasicWeigher
for (item in additionalItems) { for (item in additionalItems) {
if (item.getUserData(SMART_COMPLETION_ITEM_PRIORITY_KEY) == null) { if (item.getUserData(SMART_COMPLETION_ITEM_PRIORITY_KEY) == null) {
item.putUserData(SMART_COMPLETION_ITEM_PRIORITY_KEY, SmartCompletionItemPriority.DEFAULT) item.putUserData(SMART_COMPLETION_ITEM_PRIORITY_KEY, SmartCompletionItemPriority.DEFAULT)
}
} }
}
collector.addElements(additionalItems) collector.addElements(additionalItems)
}
} }
val (imported, notImported) = referenceVariants!! val contextVariableTypesForSmartCompletion = withCollectRequiredContextVariableTypes(::completeWithSmartCompletion)
collector.addDescriptorElements(imported, lookupElementFactory)
collector.addDescriptorElements(notImported, lookupElementFactory, notImported = true) 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() KEYWORDS_ONLY.doComplete()
@@ -188,7 +196,7 @@ class BasicCompletionSession(
} }
} }
packageNames.forEach { collector.addElement(lookupElementFactory.createLookupElementForPackage(it)) } packageNames.forEach { collector.addElement(basicLookupElementFactory.createLookupElementForPackage(it)) }
} }
flushToResultSet() flushToResultSet()
@@ -196,22 +204,35 @@ class BasicCompletionSession(
NamedArgumentCompletion.complete(collector, expectedInfos) NamedArgumentCompletion.complete(collector, expectedInfos)
flushToResultSet() flushToResultSet()
completeNonImported() val contextVariablesProvider = RealContextVariablesProvider(referenceVariantsHelper, position)
flushToResultSet() withContextVariablesProvider(contextVariablesProvider) { lookupElementFactory ->
if (contextVariableTypesForSmartCompletion.any { contextVariablesProvider.functionTypeVariables(it).isNotEmpty() }) {
completeWithSmartCompletion(lookupElementFactory)
}
if (position.containingFile is KtCodeFragment) { if (contextVariableTypesForReferenceVariants.any { contextVariablesProvider.functionTypeVariables(it).isNotEmpty() }) {
val variantsAndFactory = getRuntimeReceiverTypeReferenceVariants() val (imported, notImported) = referenceVariantsWithSingleFunctionTypeParameter()!!
if (variantsAndFactory != null) { collector.addDescriptorElements(imported, lookupElementFactory)
val variants = variantsAndFactory.first collector.addDescriptorElements(notImported, lookupElementFactory, notImported = true)
val lookupElementFactory = variantsAndFactory.second }
collector.addDescriptorElements(variants.imported, lookupElementFactory, withReceiverCast = true)
collector.addDescriptorElements(variants.notImportedExtensions, lookupElementFactory, withReceiverCast = true, notImported = true) completeNonImported(lookupElementFactory)
flushToResultSet() 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()) { if (shouldCompleteTopLevelCallablesFromIndex()) {
collector.addDescriptorElements(getTopLevelCallables(), lookupElementFactory, notImported = true) collector.addDescriptorElements(getTopLevelCallables(), lookupElementFactory, notImported = true)
} }
@@ -292,7 +313,7 @@ class BasicCompletionSession(
"override" -> { "override" -> {
collector.addElement(lookupElement) collector.addElement(lookupElement)
OverridesCompletion(collector, lookupElementFactory).complete(position) OverridesCompletion(collector, basicLookupElementFactory).complete(position)
} }
"class" -> { "class" -> {
@@ -377,7 +398,7 @@ class BasicCompletionSession(
KEYWORDS_ONLY.doComplete() KEYWORDS_ONLY.doComplete()
if (shouldCompleteParameterNameAndType()) { 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) // 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<String>("Prefix ends with uppercase letter") { val prefixPattern = StandardPatterns.string().with(object : PatternCondition<String>("Prefix ends with uppercase letter") {
@@ -470,7 +491,7 @@ class BasicCompletionSession(
} }
superClasses superClasses
.map { lookupElementFactory.createLookupElement(it, useReceiverTypes = false, qualifyNestedClasses = true, includeClassTypeArguments = false) } .map { basicLookupElementFactory.createLookupElement(it, qualifyNestedClasses = true, includeClassTypeArguments = false) }
.forEach { collector.addElement(it) } .forEach { collector.addElement(it) }
} }
} }
@@ -38,7 +38,7 @@ import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor
class BasicLookupElementFactory( class BasicLookupElementFactory(
private val project: Project, private val project: Project,
private val insertHandlerProvider: InsertHandlerProvider val insertHandlerProvider: InsertHandlerProvider
) { ) {
public fun createLookupElement( public fun createLookupElement(
descriptor: DeclarationDescriptor, descriptor: DeclarationDescriptor,
@@ -132,14 +132,16 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
protected val referenceVariantsHelper = ReferenceVariantsHelper(bindingContext, resolutionFacade, isVisibleFilter) protected val referenceVariantsHelper = ReferenceVariantsHelper(bindingContext, resolutionFacade, isVisibleFilter)
protected val callTypeAndReceiver: CallTypeAndReceiver<*, *> protected val callTypeAndReceiver: CallTypeAndReceiver<*, *>
protected val lookupElementFactory: LookupElementFactory protected val receiverTypes: Collection<KotlinType>?
init { init {
val (callTypeAndReceiver, receiverTypes) = detectCallTypeAndReceiverTypes() val (callTypeAndReceiver, receiverTypes) = detectCallTypeAndReceiverTypes()
this.callTypeAndReceiver = callTypeAndReceiver 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 // LookupElementsCollector instantiation is deferred because virtual call to createSorter uses data from derived classes
protected val collector: LookupElementsCollector by lazy(LazyThreadSafetyMode.NONE) { protected val collector: LookupElementsCollector by lazy(LazyThreadSafetyMode.NONE) {
LookupElementsCollector(prefixMatcher, parameters, resultSet, createSorter()) LookupElementsCollector(prefixMatcher, parameters, resultSet, createSorter())
@@ -273,12 +275,13 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
data class ReferenceVariants(val imported: Collection<DeclarationDescriptor>, val notImportedExtensions: Collection<CallableDescriptor>) data class ReferenceVariants(val imported: Collection<DeclarationDescriptor>, val notImportedExtensions: Collection<CallableDescriptor>)
protected val referenceVariants: ReferenceVariants? by lazy { 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( var variants = referenceVariantsHelper.getReferenceVariants(
nameExpression!!, nameExpression,
descriptorKindFilter, descriptorKindFilter,
descriptorNameFilter, descriptorNameFilter,
filterOutJavaGettersAndSetters = false, filterOutJavaGettersAndSetters = false,
@@ -315,8 +318,15 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
return ReferenceVariants(variants, notImportedExtensions) return ReferenceVariants(variants, notImportedExtensions)
} }
protected fun getRuntimeReceiverTypeReferenceVariants(): Pair<ReferenceVariants, LookupElementFactory>? { 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<ReferenceVariants, LookupElementFactory>? {
val evaluator = file.getCopyableUserData(KtCodeFragment.RUNTIME_TYPE_EVALUATOR) ?: return null val evaluator = file.getCopyableUserData(KtCodeFragment.RUNTIME_TYPE_EVALUATOR) ?: return null
val referenceVariants = referenceVariants ?: return null
val explicitReceiver = callTypeAndReceiver.receiver as? KtExpression ?: return null val explicitReceiver = callTypeAndReceiver.receiver as? KtExpression ?: return null
val type = bindingContext.getType(explicitReceiver) ?: return null val type = bindingContext.getType(explicitReceiver) ?: return null
@@ -325,12 +335,12 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
val runtimeType = evaluator(explicitReceiver) val runtimeType = evaluator(explicitReceiver)
if (runtimeType == null || runtimeType == type) return null if (runtimeType == null || runtimeType == type) return null
val (variants, notImportedExtensions) = collectReferenceVariants(descriptorKindFilter!!, ExpressionReceiver(explicitReceiver, runtimeType)) val (variants, notImportedExtensions) = collectReferenceVariants(descriptorKindFilter!!, nameExpression!!, ExpressionReceiver(explicitReceiver, runtimeType))
val filteredVariants = filterVariantsForRuntimeReceiverType(variants, referenceVariants!!.imported) val filteredVariants = filterVariantsForRuntimeReceiverType(variants, referenceVariants.imported)
val filteredNotImportedExtensions = filterVariantsForRuntimeReceiverType(notImportedExtensions, referenceVariants!!.notImportedExtensions) val filteredNotImportedExtensions = filterVariantsForRuntimeReceiverType(notImportedExtensions, referenceVariants.notImportedExtensions)
val referenceVariants = ReferenceVariants(filteredVariants, filteredNotImportedExtensions) val runtimeVariants = ReferenceVariants(filteredVariants, filteredNotImportedExtensions)
return Pair(referenceVariants, lookupElementFactory.copy(receiverTypes = listOf(runtimeType))) return Pair(runtimeVariants, lookupElementFactory.copy(receiverTypes = listOf(runtimeType)))
} }
private fun <TDescriptor : DeclarationDescriptor> filterVariantsForRuntimeReceiverType( private fun <TDescriptor : DeclarationDescriptor> filterVariantsForRuntimeReceiverType(
@@ -373,24 +383,26 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
protected fun addClassesFromIndex(kindFilter: (ClassKind) -> Boolean) { protected fun addClassesFromIndex(kindFilter: (ClassKind) -> Boolean) {
AllClassesCompletion(parameters, indicesHelper, prefixMatcher, resolutionFacade, kindFilter) AllClassesCompletion(parameters, indicesHelper, prefixMatcher, resolutionFacade, kindFilter)
.collect( .collect(
{ descriptor -> collector.addDescriptorElements(descriptor, lookupElementFactory, notImported = true) }, { descriptor -> collector.addElement(basicLookupElementFactory.createLookupElement(descriptor), notImported = true) },
{ javaClass -> collector.addElement(lookupElementFactory.createLookupElementForJavaClass(javaClass), notImported = true) } { javaClass -> collector.addElement(basicLookupElementFactory.createLookupElementForJavaClass(javaClass), notImported = true) }
) )
} }
private fun createLookupElementFactory(callType: CallType<*>?, receiverTypes: Collection<KotlinType>?): LookupElementFactory { protected fun withCollectRequiredContextVariableTypes(action: (LookupElementFactory) -> Unit): Collection<FuzzyType> {
val contextVariablesProvider = { val provider = CollectRequiredTypesContextVariablesProvider()
nameExpression?.let { val lookupElementFactory = createLookupElementFactory(provider)
val descriptorFilter = DescriptorKindFilter.VARIABLES exclude DescriptorKindExclude.Extensions // we exclude extensions by performance reasons action(lookupElementFactory)
referenceVariantsHelper.getReferenceVariants(it, CallTypeAndReceiver.DEFAULT, descriptorFilter, nameFilter = { true }) return provider.requiredTypes
.map { it as VariableDescriptor } }
} ?: emptyList()
}
val insertHandlerProvider = InsertHandlerProvider(callType) { expectedInfos } protected fun withContextVariablesProvider(contextVariablesProvider: ContextVariablesProvider, action: (LookupElementFactory) -> Unit) {
return LookupElementFactory(resolutionFacade, receiverTypes, val lookupElementFactory = createLookupElementFactory(contextVariablesProvider)
callType, expression?.parent is KtSimpleNameStringTemplateEntry, action(lookupElementFactory)
insertHandlerProvider, contextVariablesProvider) }
protected fun createLookupElementFactory(contextVariablesProvider: ContextVariablesProvider): LookupElementFactory {
return LookupElementFactory(basicLookupElementFactory, resolutionFacade, receiverTypes,
callTypeAndReceiver.callType, contextVariablesProvider)
} }
private fun detectCallTypeAndReceiverTypes(): Pair<CallTypeAndReceiver<*, *>, Collection<KotlinType>?> { private fun detectCallTypeAndReceiverTypes(): Pair<CallTypeAndReceiver<*, *>, Collection<KotlinType>?> {
@@ -29,12 +29,8 @@ import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
import org.jetbrains.kotlin.idea.KotlinIcons import org.jetbrains.kotlin.idea.KotlinIcons
import org.jetbrains.kotlin.idea.completion.handlers.CastReceiverInsertHandler import org.jetbrains.kotlin.idea.completion.handlers.CastReceiverInsertHandler
import org.jetbrains.kotlin.idea.completion.handlers.WithTailInsertHandler 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.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.idea.util.*
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.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
@@ -94,22 +90,6 @@ fun LookupElement.withReceiverCast(): LookupElement {
} }
} }
fun LookupElement.withBracesSurrounding(): LookupElement {
return object: LookupElementDecorator<LookupElement>(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<Unit>("KEEP_OLD_ARGUMENT_LIST_ON_TAB_KEY") val KEEP_OLD_ARGUMENT_LIST_ON_TAB_KEY = Key<Unit>("KEEP_OLD_ARGUMENT_LIST_ON_TAB_KEY")
fun LookupElement.keepOldArgumentListOnTab(): LookupElement { fun LookupElement.keepOldArgumentListOnTab(): LookupElement {
@@ -310,7 +290,7 @@ fun breakOrContinueExpressionItems(position: KtElement, breakOrContinue: String)
return result return result
} }
fun LookupElementFactory.createLookupElementForType(type: KotlinType): LookupElement? { fun BasicLookupElementFactory.createLookupElementForType(type: KotlinType): LookupElement? {
if (type.isError()) return null if (type.isError()) return null
if (KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(type)) { if (KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(type)) {
@@ -320,7 +300,7 @@ fun LookupElementFactory.createLookupElementForType(type: KotlinType): LookupEle
} }
else { else {
val classifier = type.getConstructor().getDeclarationDescriptor() ?: return null 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) val itemText = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(type)
@@ -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<Pair<VariableDescriptor, TypeSubstitutor>>
}
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<VariableDescriptor> {
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<Pair<VariableDescriptor, TypeSubstitutor>> {
val result = SmartList<Pair<VariableDescriptor, TypeSubstitutor>>()
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<FuzzyType>()
public val requiredTypes: Set<FuzzyType>
get() = _requiredTypes
override fun functionTypeVariables(requiredType: FuzzyType): Collection<Pair<VariableDescriptor, TypeSubstitutor>> {
_requiredTypes.add(requiredType)
return emptyList()
}
}
@@ -87,7 +87,7 @@ class KDocNameCompletionSession(parameters: CompletionParameters,
.filter { it.getName().asString() !in documentedParameters } .filter { it.getName().asString() !in documentedParameters }
descriptors.forEach { 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 { 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<LookupElement>(element) { collector.addElement(object: LookupElementDecorator<LookupElement>(element) {
override fun handleInsert(context: InsertionContext?) { override fun handleInsert(context: InsertionContext?) {
// insert only plain name here, no qualifier/parentheses/etc. // insert only plain name here, no qualifier/parentheses/etc.
@@ -21,7 +21,6 @@ import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInsight.lookup.LookupElementDecorator import com.intellij.codeInsight.lookup.LookupElementDecorator
import com.intellij.codeInsight.lookup.LookupElementPresentation import com.intellij.codeInsight.lookup.LookupElementPresentation
import com.intellij.codeInsight.lookup.impl.LookupCellRenderer import com.intellij.codeInsight.lookup.impl.LookupCellRenderer
import com.intellij.psi.PsiClass
import com.intellij.util.SmartList import com.intellij.util.SmartList
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.* 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.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.util.CallType import org.jetbrains.kotlin.idea.util.CallType
import org.jetbrains.kotlin.idea.util.FuzzyType 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.DescriptorRenderer
import org.jetbrains.kotlin.renderer.render
import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
import org.jetbrains.kotlin.synthetic.SamAdapterExtensionFunctionDescriptor import org.jetbrains.kotlin.synthetic.SamAdapterExtensionFunctionDescriptor
@@ -44,28 +42,27 @@ import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
data /* we need copy() */ data /* we need copy() */
class LookupElementFactory( class LookupElementFactory(
val basicFactory: BasicLookupElementFactory,
private val resolutionFacade: ResolutionFacade, private val resolutionFacade: ResolutionFacade,
private val receiverTypes: Collection<KotlinType>?, private val receiverTypes: Collection<KotlinType>?,
private val callType: CallType<*>?, private val callType: CallType<*>?,
private val isInStringTemplateAfterDollar: Boolean, private val contextVariablesProvider: ContextVariablesProvider
public val insertHandlerProvider: InsertHandlerProvider,
private val contextVariablesProvider: () -> Collection<VariableDescriptor>
) { ) {
private val basicFactory = BasicLookupElementFactory(resolutionFacade.project, insertHandlerProvider) companion object {
fun hasSingleFunctionTypeParameter(descriptor: FunctionDescriptor): Boolean {
private val functionTypeContextVariables by lazy(LazyThreadSafetyMode.NONE) { val parameter = descriptor.original.valueParameters.singleOrNull() ?: return false
contextVariablesProvider().filter { KotlinBuiltIns.isFunctionOrExtensionFunctionType(it.type) } return KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(parameter.type)
}
} }
val insertHandlerProvider = basicFactory.insertHandlerProvider
public fun createStandardLookupElementsForDescriptor(descriptor: DeclarationDescriptor, useReceiverTypes: Boolean): Collection<LookupElement> { public fun createStandardLookupElementsForDescriptor(descriptor: DeclarationDescriptor, useReceiverTypes: Boolean): Collection<LookupElement> {
val result = SmartList<LookupElement>() val result = SmartList<LookupElement>()
val isNormalCall = callType == CallType.DEFAULT || callType == CallType.DOT || callType == CallType.SAFE val isNormalCall = callType == CallType.DEFAULT || callType == CallType.DOT || callType == CallType.SAFE
var lookupElement = createLookupElement(descriptor, useReceiverTypes, parametersAndTypeGrayed = !isNormalCall && callType != CallType.INFIX) var lookupElement = createLookupElement(descriptor, useReceiverTypes, parametersAndTypeGrayed = !isNormalCall && callType != CallType.INFIX)
if (isInStringTemplateAfterDollar && (descriptor is FunctionDescriptor || descriptor is ClassifierDescriptor)) {
lookupElement = lookupElement.withBracesSurrounding()
}
result.add(lookupElement) result.add(lookupElement)
// add special item for function with one argument of function type with more than one parameter // 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 // check that all parameters except for the last one are optional
val lastParameter = descriptor.valueParameters.lastOrNull() ?: return val lastParameter = descriptor.valueParameters.lastOrNull() ?: return
if (!descriptor.valueParameters.all { it == lastParameter || it.hasDefaultValue() }) 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 // 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) { if (!isSingleParameter || functionParameterCount > 1) {
add(createFunctionCallElementWithLambda(descriptor, parameterType, functionParameterCount > 1, useReceiverTypes)) add(createFunctionCallElementWithLambda(descriptor, parameterType, functionParameterCount > 1, useReceiverTypes))
@@ -94,12 +92,9 @@ class LookupElementFactory(
//TODO: also ::function? at least for local functions //TODO: also ::function? at least for local functions
//TODO: order for them //TODO: order for them
val fuzzyParameterType = FuzzyType(parameterType, descriptor.typeParameters) val fuzzyParameterType = FuzzyType(parameterType, descriptor.typeParameters)
for (variable in functionTypeContextVariables) { for ((variable, substitutor) in contextVariablesProvider.functionTypeVariables(fuzzyParameterType)) {
val substitutor = variable.fuzzyReturnType()?.checkIsSubtypeOf(fuzzyParameterType) val substitutedDescriptor = descriptor.substitute(substitutor)
if (substitutor != null) { add(createFunctionCallElementWithArgument(substitutedDescriptor, variable.name.render(), useReceiverTypes))
val substitutedDescriptor = descriptor.substitute(substitutor) ?: continue
add(createFunctionCallElementWithArgument(substitutedDescriptor, variable.name.asString(), useReceiverTypes))
}
} }
} }
} }
@@ -139,10 +134,6 @@ class LookupElementFactory(
} }
} }
if (isInStringTemplateAfterDollar) {
lookupElement = lookupElement.withBracesSurrounding()
}
return lookupElement return lookupElement
} }
@@ -150,13 +141,7 @@ class LookupElementFactory(
var lookupElement = createLookupElement(descriptor, useReceiverTypes) var lookupElement = createLookupElement(descriptor, useReceiverTypes)
val needTypeArguments = (insertHandlerProvider.insertHandler(descriptor) as KotlinFunctionInsertHandler.Normal).inputTypeArguments val needTypeArguments = (insertHandlerProvider.insertHandler(descriptor) as KotlinFunctionInsertHandler.Normal).inputTypeArguments
lookupElement = FunctionCallWithArgumentLookupElement(lookupElement, descriptor, argumentText, needTypeArguments) return FunctionCallWithArgumentLookupElement(lookupElement, descriptor, argumentText, needTypeArguments)
if (isInStringTemplateAfterDollar) {
lookupElement = lookupElement.withBracesSurrounding()
}
return lookupElement
} }
private inner class FunctionCallWithArgumentLookupElement( private inner class FunctionCallWithArgumentLookupElement(
@@ -283,12 +268,4 @@ class LookupElementFactory(
val typeParameter = receiverParameter.type.constructor.declarationDescriptor as? TypeParameterDescriptor ?: return false val typeParameter = receiverParameter.type.constructor.declarationDescriptor as? TypeParameterDescriptor ?: return false
return typeParameter.containingDeclaration == original 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)
}
} }
@@ -49,7 +49,7 @@ import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
class OverridesCompletion( class OverridesCompletion(
private val collector: LookupElementsCollector, private val collector: LookupElementsCollector,
private val lookupElementFactory: LookupElementFactory private val lookupElementFactory: BasicLookupElementFactory
) { ) {
private val PRESENTATION_RENDERER = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.withOptions { private val PRESENTATION_RENDERER = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.withOptions {
modifiers = emptySet() modifiers = emptySet()
@@ -66,7 +66,7 @@ class OverridesCompletion(
if (isConstructorParameter && memberObject.descriptor !is PropertyDescriptor) continue if (isConstructorParameter && memberObject.descriptor !is PropertyDescriptor) continue
val descriptor = memberObject.descriptor val descriptor = memberObject.descriptor
var lookupElement = lookupElementFactory.createLookupElement(descriptor, useReceiverTypes = false) var lookupElement = lookupElementFactory.createLookupElement(descriptor)
var text = "override " + PRESENTATION_RENDERER.render(descriptor) var text = "override " + PRESENTATION_RENDERER.render(descriptor)
if (descriptor is FunctionDescriptor) { if (descriptor is FunctionDescriptor) {
@@ -48,7 +48,7 @@ import java.util.*
class ParameterNameAndTypeCompletion( class ParameterNameAndTypeCompletion(
private val collector: LookupElementsCollector, private val collector: LookupElementsCollector,
private val lookupElementFactory: LookupElementFactory, private val lookupElementFactory: BasicLookupElementFactory,
private val prefixMatcher: PrefixMatcher, private val prefixMatcher: PrefixMatcher,
private val resolutionFacade: ResolutionFacade private val resolutionFacade: ResolutionFacade
) { ) {
@@ -112,9 +112,9 @@ class ParameterNameAndTypeCompletion(
if (descriptor != null) { if (descriptor != null) {
val parameterType = descriptor.getType() val parameterType = descriptor.getType()
if (parameterType.isVisible(visibilityFilter)) { 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 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) { 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 equals(other: Any?) = other is Type && other.idString == idString
override fun hashCode() = idString.hashCode() override fun hashCode() = idString.hashCode()
} }
private class DescriptorType(private val classifier: ClassifierDescriptor) : Type(IdeDescriptorRenderers.SOURCE_CODE.renderClassifierName(classifier)) { private class DescriptorType(private val classifier: ClassifierDescriptor) : Type(IdeDescriptorRenderers.SOURCE_CODE.renderClassifierName(classifier)) {
override fun createTypeLookupElement(lookupElementFactory: LookupElementFactory) override fun createTypeLookupElement(lookupElementFactory: BasicLookupElementFactory)
= lookupElementFactory.createLookupElement(classifier, useReceiverTypes = false, qualifyNestedClasses = true) = lookupElementFactory.createLookupElement(classifier, qualifyNestedClasses = true)
} }
private class JavaClassType(private val psiClass: PsiClass) : Type(psiClass.getQualifiedName()!!) { 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) = lookupElementFactory.createLookupElementForJavaClass(psiClass, qualifyNestedClasses = true)
} }
private class ArbitraryType(private val type: KotlinType) : Type(IdeDescriptorRenderers.SOURCE_CODE.renderType(type)) { 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) = lookupElementFactory.createLookupElementForType(type)
} }
@@ -187,7 +187,7 @@ class ParameterNameAndTypeCompletion(
) : LookupElementDecorator<LookupElement>(typeLookupElement) { ) : LookupElementDecorator<LookupElement>(typeLookupElement) {
companion object { 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 typeLookupElement = type.createTypeLookupElement(factory) ?: return null
val lookupElement = MyLookupElement(parameterName, type, typeLookupElement) val lookupElement = MyLookupElement(parameterName, type, typeLookupElement)
return lookupElement.suppressAutoInsertion() return lookupElement.suppressAutoInsertion()
@@ -37,6 +37,8 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
object KotlinClassifierInsertHandler : BaseDeclarationInsertHandler() { object KotlinClassifierInsertHandler : BaseDeclarationInsertHandler() {
override fun handleInsert(context: InsertionContext, item: LookupElement) { override fun handleInsert(context: InsertionContext, item: LookupElement) {
surroundWithBracesIfInStringTemplate(context)
super.handleInsert(context, item) super.handleInsert(context, item)
val file = context.getFile() val file = context.getFile()
@@ -58,16 +58,20 @@ sealed class KotlinFunctionInsertHandler : KotlinCallableInsertHandler() {
) = Normal(inputTypeArguments, inputValueArguments, argumentText, lambdaInfo, argumentsOnly) ) = Normal(inputTypeArguments, inputValueArguments, argumentText, lambdaInfo, argumentsOnly)
override fun handleInsert(context: InsertionContext, item: LookupElement) { override fun handleInsert(context: InsertionContext, item: LookupElement) {
val psiDocumentManager = PsiDocumentManager.getInstance(context.project)
val document = context.document
if (!argumentsOnly) { if (!argumentsOnly) {
surroundWithBracesIfInStringTemplate(context)
super.handleInsert(context, item) super.handleInsert(context, item)
} }
val psiDocumentManager = PsiDocumentManager.getInstance(context.project)
psiDocumentManager.commitAllDocuments() psiDocumentManager.commitAllDocuments()
psiDocumentManager.doPostponedOperationsAndUnblockDocument(context.document) psiDocumentManager.doPostponedOperationsAndUnblockDocument(document)
val startOffset = context.getStartOffset() val startOffset = context.startOffset
val element = context.getFile().findElementAt(startOffset) ?: return val element = context.file.findElementAt(startOffset) ?: return
addArguments(context, element) addArguments(context, element)
} }
@@ -16,8 +16,31 @@
package org.jetbrains.kotlin.idea.completion.handlers 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.editor.Document
import com.intellij.openapi.util.TextRange 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? { fun CharSequence.indexOfSkippingSpace(c: Char, startIndex: Int): Int? {
for (i in startIndex..this.length() - 1) { for (i in startIndex..this.length() - 1) {
@@ -22,8 +22,8 @@ import com.intellij.codeInsight.lookup.LookupElementDecorator
import com.intellij.codeInsight.lookup.LookupElementPresentation import com.intellij.codeInsight.lookup.LookupElementPresentation
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor 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.ExpectedInfo
import org.jetbrains.kotlin.idea.completion.LookupElementFactory
import org.jetbrains.kotlin.idea.completion.createLookupElementForType import org.jetbrains.kotlin.idea.completion.createLookupElementForType
import org.jetbrains.kotlin.idea.completion.fuzzyType import org.jetbrains.kotlin.idea.completion.fuzzyType
import org.jetbrains.kotlin.idea.imports.importableFqName import org.jetbrains.kotlin.idea.imports.importableFqName
@@ -36,7 +36,7 @@ object ClassLiteralItems {
public fun addToCollection( public fun addToCollection(
collection: MutableCollection<LookupElement>, collection: MutableCollection<LookupElement>,
expectedInfos: Collection<ExpectedInfo>, expectedInfos: Collection<ExpectedInfo>,
lookupElementFactory: LookupElementFactory, lookupElementFactory: BasicLookupElementFactory,
isJvmModule: Boolean isJvmModule: Boolean
) { ) {
val typeAndSuffixToExpectedInfos = LinkedHashMap<Pair<KotlinType, String>, MutableList<ExpectedInfo>>() val typeAndSuffixToExpectedInfos = LinkedHashMap<Pair<KotlinType, String>, MutableList<ExpectedInfo>>()
@@ -173,7 +173,7 @@ class SmartCompletion(
} }
private fun additionalItemsNoPostProcess(lookupElementFactory: LookupElementFactory): Pair<Collection<LookupElement>, InheritanceItemsSearcher?> { private fun additionalItemsNoPostProcess(lookupElementFactory: LookupElementFactory): Pair<Collection<LookupElement>, InheritanceItemsSearcher?> {
val asTypePositionItems = buildForAsTypePosition(lookupElementFactory) val asTypePositionItems = buildForAsTypePosition(lookupElementFactory.basicFactory)
if (asTypePositionItems != null) { if (asTypePositionItems != null) {
assert(expectedInfos.isEmpty()) assert(expectedInfos.isEmpty())
return Pair(asTypePositionItems, null) return Pair(asTypePositionItems, null)
@@ -209,7 +209,7 @@ class SmartCompletion(
.addToCollection(items, expectedInfos, expression, descriptorsToSkip) .addToCollection(items, expectedInfos, expression, descriptorsToSkip)
} }
ClassLiteralItems.addToCollection(items, expectedInfos, lookupElementFactory, isJvmModule) ClassLiteralItems.addToCollection(items, expectedInfos, lookupElementFactory.basicFactory, isJvmModule)
if (!forBasicCompletion) { if (!forBasicCompletion) {
LambdaItems.addToCollection(items, expectedInfos) LambdaItems.addToCollection(items, expectedInfos)
@@ -368,7 +368,7 @@ class SmartCompletion(
} }
} }
private fun buildForAsTypePosition(lookupElementFactory: LookupElementFactory): Collection<LookupElement>? { private fun buildForAsTypePosition(lookupElementFactory: BasicLookupElementFactory): Collection<LookupElement>? {
val binaryExpression = ((expression.getParent() as? KtUserType) val binaryExpression = ((expression.getParent() as? KtUserType)
?.getParent() as? KtTypeReference) ?.getParent() as? KtTypeReference)
?.getParent() as? KtBinaryExpressionWithTypeRHS ?.getParent() as? KtBinaryExpressionWithTypeRHS
@@ -64,45 +64,68 @@ class SmartCompletionSession(configuration: CompletionSessionConfiguration, para
return return
} }
if (expression != null) { if (expression == null) return
addFunctionLiteralArgumentCompletions()
val (additionalItems, inheritanceSearcher) = smartCompletion!!.additionalItems(lookupElementFactory) addFunctionLiteralArgumentCompletions()
collector.addElements(additionalItems)
if (nameExpression != null) { var inheritanceSearcher: InheritanceItemsSearcher? = null
val filter = smartCompletion!!.descriptorFilter val contextVariableTypesForAdditionalItems = withCollectRequiredContextVariableTypes { lookupElementFactory ->
if (filter != null) { val pair = smartCompletion!!.additionalItems(lookupElementFactory)
val (imported, notImported) = referenceVariants!! collector.addElements(pair.first)
imported.forEach { collector.addElements(filter(it, lookupElementFactory)) } inheritanceSearcher = pair.second
notImported.forEach { collector.addElements(filter(it, lookupElementFactory), notImported = true) } }
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() flushToResultSet()
}
if (shouldCompleteTopLevelCallablesFromIndex()) { if (position.getContainingFile() is KtCodeFragment) {
getTopLevelCallables().forEach { collector.addElements(filter(it, lookupElementFactory), notImported = true) } 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() 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 // special completion for outside parenthesis lambda argument
@@ -0,0 +1,12 @@
class C {
companion object {
fun create(p: (Int) -> Unit) = C()
}
}
val handler: (Int) -> Unit = {}
fun v: C = cr<caret>
// EXIST: { allLookupStrings: "C, create", itemText: "C.create", tailText: " {...} (p: (Int) -> Unit) (<root>)", typeText:"C" }
// EXIST: { allLookupStrings: "C, create", itemText: "C.create", tailText: "(handler) (<root>)", typeText:"C" }
+16 -4
View File
@@ -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<caret> fun foo(p: (String, Char) -> Unit): C {}
// EXIST: { lookupString:"foo", itemText: "foo", tailText: "(p: (String, Char) -> Unit) (<root>)", typeText:"String" } val handler1: (String, Char) -> Unit = {}
// EXIST: { lookupString:"foo", itemText: "foo", tailText: " { String, Char -> ... } (p: (String, Char) -> Unit) (<root>)", typeText:"String" } val handler2: (Int) -> Unit = {}
fun v: C = fo<caret>
// EXIST: { lookupString:"foo", itemText: "foo", tailText: "(p: (String, Char) -> Unit) (<root>)", typeText:"C" }
// EXIST: { lookupString:"foo", itemText: "foo", tailText: " { String, Char -> ... } (p: (String, Char) -> Unit) (<root>)", typeText:"C" }
// EXIST: { lookupString:"foo", itemText: "foo", tailText: "(handler1) (<root>)", typeText:"C" }
// EXIST: { allLookupStrings: "C, foo", itemText: "C.foo", tailText: " {...} (p: (Int) -> Unit) (<root>)", typeText:"C" }
// EXIST: { allLookupStrings: "C, foo", itemText: "C.foo", tailText: "(handler2) (<root>)", typeText:"C" }
@@ -1389,6 +1389,12 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
doTest(fileName); 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") @TestMetadata("ContextVariablesFilter.kt")
public void testContextVariablesFilter() throws Exception { public void testContextVariablesFilter() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/highOrderFunctions/ContextVariablesFilter.kt"); String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/highOrderFunctions/ContextVariablesFilter.kt");
@@ -1389,6 +1389,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
doTest(fileName); 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") @TestMetadata("ContextVariablesFilter.kt")
public void testContextVariablesFilter() throws Exception { public void testContextVariablesFilter() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/highOrderFunctions/ContextVariablesFilter.kt"); String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/highOrderFunctions/ContextVariablesFilter.kt");