From 23c22984e810c35f8164d907fe3b50d2e80965d2 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Thu, 27 Oct 2016 19:48:33 +0300 Subject: [PATCH] Optimization: look for callables at first when prefix starts uppercase and the opposite otherwise --- .../kotlin/resolve/scopes/MemberScope.kt | 8 ++- .../idea/completion/BasicCompletionSession.kt | 53 +++++++++++++++---- .../idea/completion/CompletionSession.kt | 27 +++++----- .../completion/ReferenceVariantsCollector.kt | 32 +++++++++-- .../smart/SmartCompletionSession.kt | 12 +++-- 5 files changed, 97 insertions(+), 35 deletions(-) diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/scopes/MemberScope.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/scopes/MemberScope.kt index 196145c01ed..14b5f193e91 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/scopes/MemberScope.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/scopes/MemberScope.kt @@ -175,6 +175,10 @@ class DescriptorKindFilter( abstract class DescriptorKindExclude { abstract fun excludes(descriptor: DeclarationDescriptor): Boolean + /** + * Bit-mask of descriptor kind's that are fully excluded by this [DescriptorKindExclude]. + * That is, [excludes] returns true for all descriptor of these kinds. + */ abstract val fullyExcludedDescriptorKinds: Int override fun toString() = this.javaClass.simpleName @@ -190,8 +194,8 @@ abstract class DescriptorKindExclude { override fun excludes(descriptor: DeclarationDescriptor) = descriptor !is CallableDescriptor || descriptor.extensionReceiverParameter == null - override val fullyExcludedDescriptorKinds: Int - get() = DescriptorKindFilter.ALL_KINDS_MASK and (DescriptorKindFilter.FUNCTIONS_MASK or DescriptorKindFilter.VARIABLES_MASK).inv() + override val fullyExcludedDescriptorKinds + = DescriptorKindFilter.ALL_KINDS_MASK and (DescriptorKindFilter.FUNCTIONS_MASK or DescriptorKindFilter.VARIABLES_MASK).inv() } object EnumEntry : DescriptorKindExclude() { 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 f283bcb6b36..80c7b88f8d8 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 @@ -26,9 +26,7 @@ import com.intellij.patterns.StandardPatterns import com.intellij.psi.* import com.intellij.psi.search.GlobalSearchScope import com.intellij.util.ProcessingContext -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.ClassKind -import org.jetbrains.kotlin.descriptors.ClassifierDescriptorWithTypeParameters +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.idea.completion.handlers.createKeywordConstructLookupElement import org.jetbrains.kotlin.idea.completion.smart.ExpectedInfoMatch import org.jetbrains.kotlin.idea.completion.smart.SMART_COMPLETION_ITEM_PRIORITY_KEY @@ -39,6 +37,8 @@ import org.jetbrains.kotlin.idea.project.ProjectStructureUtil import org.jetbrains.kotlin.idea.stubindex.PackageIndexUtil import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.load.java.descriptors.SamConstructorDescriptor +import org.jetbrains.kotlin.load.java.descriptors.SamConstructorDescriptorKindExclude import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* @@ -158,7 +158,7 @@ class BasicCompletionSession( } private val ALL = object : CompletionKind { - override val descriptorKindFilter: DescriptorKindFilter? by lazy { + override val descriptorKindFilter: DescriptorKindFilter by lazy { var filter = callTypeAndReceiver.callType.descriptorKindFilter if (filter.kindMask.and(DescriptorKindFilter.PACKAGES_MASK) != 0) { @@ -201,9 +201,20 @@ class BasicCompletionSession( val contextVariableTypesForSmartCompletion = withCollectRequiredContextVariableTypes(::completeWithSmartCompletion) val contextVariableTypesForReferenceVariants = withCollectRequiredContextVariableTypes { lookupElementFactory -> - val (imported, notImported) = referenceVariantsWithNonInitializedVarExcluded!! - collector.addDescriptorElements(imported, lookupElementFactory) - collector.addDescriptorElements(notImported, lookupElementFactory, notImported = true) + if (prefix.isEmpty() || callTypeAndReceiver.receiver != null) { + addReferenceVariantElements(lookupElementFactory, descriptorKindFilter) + } + else if (prefix[0].isLowerCase()) { + addReferenceVariantElements(lookupElementFactory, USUALLY_START_LOWER_CASE.intersect(descriptorKindFilter)) + flushToResultSet() + addReferenceVariantElements(lookupElementFactory, USUALLY_START_UPPER_CASE.intersect(descriptorKindFilter)) + } + else { + addReferenceVariantElements(lookupElementFactory, USUALLY_START_UPPER_CASE.intersect(descriptorKindFilter)) + flushToResultSet() + addReferenceVariantElements(lookupElementFactory, USUALLY_START_LOWER_CASE.intersect(descriptorKindFilter)) + } + referenceVariantsCollector!!.collectingFinished() } KEYWORDS_ONLY.doComplete() @@ -253,7 +264,11 @@ class BasicCompletionSession( } val staticMembersCompletion = StaticMembersCompletion( - prefixMatcher, resolutionFacade, lookupElementFactory, referenceVariants!!.imported, isJvmModule) + prefixMatcher, + resolutionFacade, + lookupElementFactory, + referenceVariantsCollector!!.allCollected.imported, + isJvmModule) if (callTypeAndReceiver is CallTypeAndReceiver.DEFAULT) { staticMembersCompletion.completeFromImports(file, collector) } @@ -526,7 +541,7 @@ class BasicCompletionSession( } private val SUPER_QUALIFIER = object : CompletionKind { - override val descriptorKindFilter: DescriptorKindFilter? + override val descriptorKindFilter: DescriptorKindFilter get() = DescriptorKindFilter.NON_SINGLETON_CLASSIFIERS override fun doComplete() { @@ -536,7 +551,7 @@ class BasicCompletionSession( .mapNotNull { it.constructor.declarationDescriptor as? ClassDescriptor } if (callTypeAndReceiver.receiver != null) { - val referenceVariantsSet = referenceVariants!!.imported.toSet() + val referenceVariantsSet = referenceVariantsCollector!!.collectReferenceVariants(descriptorKindFilter).imported.toSet() superClasses = superClasses.filter { it in referenceVariantsSet } } @@ -594,4 +609,20 @@ class BasicCompletionSession( ).collect(classifierDescriptorCollector, javaClassCollector) } -} \ No newline at end of file + private fun addReferenceVariantElements(lookupElementFactory: LookupElementFactory, descriptorKindFilter: DescriptorKindFilter) { + val (imported, notImported) = referenceVariantsCollector!!.collectReferenceVariants(descriptorKindFilter).excludeNonInitializedVariable() + collector.addDescriptorElements(imported, lookupElementFactory) + collector.addDescriptorElements(notImported, lookupElementFactory, notImported = true) + } +} + +private val USUALLY_START_UPPER_CASE = DescriptorKindFilter(DescriptorKindFilter.CLASSIFIERS_MASK or DescriptorKindFilter.FUNCTIONS_MASK, + listOf(NonSamConstructorFunctionExclude, DescriptorKindExclude.Extensions /* needed for faster getReferenceVariants */)) +private val USUALLY_START_LOWER_CASE = DescriptorKindFilter(DescriptorKindFilter.CALLABLES_MASK or DescriptorKindFilter.PACKAGES_MASK, + listOf(SamConstructorDescriptorKindExclude)) + +private object NonSamConstructorFunctionExclude : DescriptorKindExclude() { + override fun excludes(descriptor: DeclarationDescriptor) = descriptor is FunctionDescriptor && descriptor !is SamConstructorDescriptor + + override val fullyExcludedDescriptorKinds: Int get() = 0 +} 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 a85de500891..39b04bc661a 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 @@ -269,29 +269,28 @@ abstract class CompletionSession( return context } - protected val referenceVariants: ReferenceVariants? by lazy { - if (nameExpression != null && descriptorKindFilter != null) - ReferenceVariantsCollector(referenceVariantsHelper, indicesHelper(true), prefixMatcher, - nameExpression, callTypeAndReceiver, resolutionFacade, bindingContext, - importableFqNameClassifier, configuration - ).collectReferenceVariants(descriptorKindFilter!!) - else - null + protected val referenceVariantsCollector = if (nameExpression != null) { + ReferenceVariantsCollector(referenceVariantsHelper, indicesHelper(true), prefixMatcher, + nameExpression, callTypeAndReceiver, resolutionFacade, bindingContext, + importableFqNameClassifier, configuration) + } + else { + null } - protected val referenceVariantsWithNonInitializedVarExcluded: ReferenceVariants? by lazy { - referenceVariants?.let { ReferenceVariants(referenceVariantsHelper.excludeNonInitializedVariable(it.imported, position), it.notImportedExtensions) } + protected fun ReferenceVariants.excludeNonInitializedVariable(): ReferenceVariants { + return ReferenceVariants(referenceVariantsHelper.excludeNonInitializedVariable(imported, position), notImportedExtensions) } protected fun referenceVariantsWithSingleFunctionTypeParameter(): ReferenceVariants? { - val variants = referenceVariants ?: return null - val filter: (DeclarationDescriptor) -> Boolean = { it is FunctionDescriptor && LookupElementFactory.hasSingleFunctionTypeParameter(it) } + val variants = referenceVariantsCollector?.allCollected ?: return null + val filter = { descriptor: DeclarationDescriptor -> descriptor is FunctionDescriptor && LookupElementFactory.hasSingleFunctionTypeParameter(descriptor) } 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 referenceVariants = referenceVariantsCollector?.allCollected ?: return null val explicitReceiver = callTypeAndReceiver.receiver as? KtExpression ?: return null val type = bindingContext.getType(explicitReceiver) ?: return null @@ -337,7 +336,7 @@ abstract class CompletionSession( protected fun processTopLevelCallables(processor: (CallableDescriptor) -> Unit) { val shadowedFilter = ShadowedDeclarationsFilter.create(bindingContext, resolutionFacade, nameExpression!!, callTypeAndReceiver) - ?.createNonImportedDeclarationsFilter(referenceVariants!!.imported) + ?.createNonImportedDeclarationsFilter(referenceVariantsCollector!!.allCollected.imported) indicesHelper(true).processTopLevelCallables({ prefixMatcher.prefixMatches(it) }) { if (shadowedFilter != null) { shadowedFilter(listOf(it)).singleOrNull()?.let(processor) diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ReferenceVariantsCollector.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ReferenceVariantsCollector.kt index 80fdb2e271d..08f6c1acc24 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ReferenceVariantsCollector.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ReferenceVariantsCollector.kt @@ -32,10 +32,11 @@ import org.jetbrains.kotlin.resolve.scopes.DescriptorKindExclude import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver import org.jetbrains.kotlin.util.capitalizeDecapitalize.decapitalizeSmart +import java.util.* data class ReferenceVariants(val imported: Collection, val notImportedExtensions: Collection) -internal class ReferenceVariantsCollector( +class ReferenceVariantsCollector( private val referenceVariantsHelper: ReferenceVariantsHelper, private val indicesHelper: KotlinIndicesHelper, private val prefixMatcher: PrefixMatcher, @@ -50,8 +51,32 @@ internal class ReferenceVariantsCollector( private val prefix = prefixMatcher.prefix private val descriptorNameFilter = prefixMatcher.asStringNameFilter() + private val collectedImported = LinkedHashSet() + private val collectedNotImportedExtensions = LinkedHashSet() + private var isCollectingFinished = false + + val allCollected: ReferenceVariants + get() { + assert(isCollectingFinished) + return ReferenceVariants(collectedImported, collectedNotImportedExtensions) + } + + fun collectingFinished() { + assert(!isCollectingFinished) + isCollectingFinished = true + } + fun collectReferenceVariants(descriptorKindFilter: DescriptorKindFilter): ReferenceVariants { + assert(!isCollectingFinished) + val variants = doCollectReferenceVariants(descriptorKindFilter) + collectedImported.addAll(variants.imported) + collectedNotImportedExtensions.addAll(variants.notImportedExtensions) + return variants + } + + private fun doCollectReferenceVariants(descriptorKindFilter: DescriptorKindFilter): ReferenceVariants { val completeExtensionsFromIndices = descriptorKindFilter.kindMask.and(DescriptorKindFilter.CALLABLES_MASK) != 0 + && DescriptorKindExclude.Extensions !in descriptorKindFilter.excludes && callTypeAndReceiver !is CallTypeAndReceiver.IMPORT_DIRECTIVE @Suppress("NAME_SHADOWING") val descriptorKindFilter = if (completeExtensionsFromIndices) @@ -131,8 +156,7 @@ internal class ReferenceVariantsCollector( return true } - override val fullyExcludedDescriptorKinds: Int - get() = 0 + override val fullyExcludedDescriptorKinds: Int get() = 0 } private fun isDataClassComponentFunction(descriptor: DeclarationDescriptor): Boolean { @@ -141,4 +165,4 @@ internal class ReferenceVariantsCollector( DataClassDescriptorResolver.isComponentLike(descriptor.name) && descriptor.kind == CallableMemberDescriptor.Kind.SYNTHESIZED } -} \ No newline at end of file +} 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 2f1cd1b59a4..d1d8d563254 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 @@ -86,9 +86,12 @@ class SmartCompletionSession( val filter = smartCompletion!!.descriptorFilter val contextVariableTypesForReferenceVariants = filter?.let { withCollectRequiredContextVariableTypes { lookupElementFactory -> - val (imported, notImported) = referenceVariantsWithNonInitializedVarExcluded ?: return@withCollectRequiredContextVariableTypes - imported.forEach { collector.addElements(filter(it, lookupElementFactory)) } - notImported.forEach { collector.addElements(filter(it, lookupElementFactory), notImported = true) } + if (referenceVariantsCollector != null) { + val (imported, notImported) = referenceVariantsCollector.collectReferenceVariants(descriptorKindFilter).excludeNonInitializedVariable() + imported.forEach { collector.addElements(filter(it, lookupElementFactory)) } + notImported.forEach { collector.addElements(filter(it, lookupElementFactory), notImported = true) } + referenceVariantsCollector.collectingFinished() + } } } @@ -120,7 +123,8 @@ class SmartCompletionSession( if (filter != null) { val staticMembersCompletion: StaticMembersCompletion? if (callTypeAndReceiver is CallTypeAndReceiver.DEFAULT) { - staticMembersCompletion = StaticMembersCompletion(prefixMatcher, resolutionFacade, lookupElementFactory, referenceVariants!!.imported, isJvmModule) + val alreadyCollected = referenceVariantsCollector!!.allCollected.imported + staticMembersCompletion = StaticMembersCompletion(prefixMatcher, resolutionFacade, lookupElementFactory, alreadyCollected, isJvmModule) val decoratedFactory = staticMembersCompletion.decoratedLookupElementFactory(ItemPriority.STATIC_MEMBER_FROM_IMPORTS) staticMembersCompletion.membersFromImports(file) .flatMap { filter(it, decoratedFactory) }