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 fe72e480ed7..a85de500891 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 @@ -37,19 +37,15 @@ import org.jetbrains.kotlin.idea.imports.importableFqName import org.jetbrains.kotlin.idea.project.ProjectStructureUtil import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.idea.util.* -import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf -import org.jetbrains.kotlin.resolve.DataClassDescriptorResolver import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode -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.types.TypeUtils import org.jetbrains.kotlin.types.checker.KotlinTypeChecker import org.jetbrains.kotlin.types.typeUtil.makeNotNullable -import org.jetbrains.kotlin.util.capitalizeDecapitalize.decapitalizeSmart import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance import java.util.* @@ -154,20 +150,6 @@ abstract class CompletionSession( declarationTranslator = { toFromOriginalFileMapper.toSyntheticFile(it) }) } - protected object TopLevelExtensionsExclude : DescriptorKindExclude() { - override fun excludes(descriptor: DeclarationDescriptor): Boolean { - if (descriptor !is CallableMemberDescriptor) return false - if (descriptor.extensionReceiverParameter == null) return false - if (descriptor.kind != CallableMemberDescriptor.Kind.DECLARATION) return false /* do not filter out synthetic extensions */ - val containingPackage = descriptor.containingDeclaration as? PackageFragmentDescriptor ?: return false - if (containingPackage.fqName.asString().startsWith("kotlinx.android.synthetic.")) return false // TODO: temporary solution for Android synthetic extensions - return true - } - - override val fullyExcludedDescriptorKinds: Int - get() = 0 - } - private fun isVisibleDescriptor(descriptor: DeclarationDescriptor, completeNonAccessible: Boolean): Boolean { if (!configuration.javaClassesNotToBeUsed && descriptor is ClassDescriptor) { if (descriptor.importableFqName?.let { isJavaClassNotToBeUsedInKotlin(it) } == true) return false @@ -287,99 +269,20 @@ abstract class CompletionSession( return context } - protected data class ReferenceVariants(val imported: Collection, val notImportedExtensions: Collection) - protected val referenceVariants: ReferenceVariants? by lazy { - if (nameExpression != null && descriptorKindFilter != null) collectReferenceVariants(descriptorKindFilter!!, nameExpression) else null + if (nameExpression != null && descriptorKindFilter != null) + ReferenceVariantsCollector(referenceVariantsHelper, indicesHelper(true), prefixMatcher, + nameExpression, callTypeAndReceiver, resolutionFacade, bindingContext, + importableFqNameClassifier, configuration + ).collectReferenceVariants(descriptorKindFilter!!) + else + null } protected val referenceVariantsWithNonInitializedVarExcluded: ReferenceVariants? by lazy { referenceVariants?.let { ReferenceVariants(referenceVariantsHelper.excludeNonInitializedVariable(it.imported, position), it.notImportedExtensions) } } - private fun collectReferenceVariants( - descriptorKindFilter: DescriptorKindFilter, - nameExpression: KtSimpleNameExpression, - runtimeReceiver: ExpressionReceiver? = null - ): ReferenceVariants { - val completeExtensionsFromIndices = descriptorKindFilter.kindMask.and(DescriptorKindFilter.CALLABLES_MASK) != 0 - && callTypeAndReceiver !is CallTypeAndReceiver.IMPORT_DIRECTIVE - @Suppress("NAME_SHADOWING") - val descriptorKindFilter = if (completeExtensionsFromIndices) - descriptorKindFilter exclude TopLevelExtensionsExclude // handled via indices - else - descriptorKindFilter - - fun getReferenceVariants(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean): Collection { - return referenceVariantsHelper.getReferenceVariants( - nameExpression, - kindFilter, - nameFilter, - filterOutJavaGettersAndSetters = false, - filterOutShadowed = false, - excludeNonInitializedVariable = false, - useReceiverType = runtimeReceiver?.type) - } - - var variants = getReferenceVariants(descriptorKindFilter, descriptorNameFilter.toNameFilter()) - - val getOrSetPrefix = listOf("get", "set", "ge", "se", "g", "s").firstOrNull { prefix.startsWith(it) } - val additionalPropertyNameFilter: ((String) -> Boolean)? = run { - getOrSetPrefix?.let { prefixMatcher.cloneWithPrefix(prefix.removePrefix(getOrSetPrefix).decapitalizeSmart()).asStringNameFilter() } - } - if (additionalPropertyNameFilter != null) { - variants += getReferenceVariants(descriptorKindFilter.intersect(DescriptorKindFilter.VARIABLES), - additionalPropertyNameFilter.toNameFilter()) - variants = variants.distinct() - } - - var notImportedExtensions: Collection = emptyList() - if (completeExtensionsFromIndices) { - val indicesHelper = indicesHelper(true) - val nameFilter = if (additionalPropertyNameFilter != null) - descriptorNameFilter or additionalPropertyNameFilter - else - descriptorNameFilter - val extensions = if (runtimeReceiver != null) - indicesHelper.getCallableTopLevelExtensions(callTypeAndReceiver, listOf(runtimeReceiver.type), nameFilter) - else - indicesHelper.getCallableTopLevelExtensions(callTypeAndReceiver, expression!!, bindingContext, nameFilter) - - val pair = extensions.partition { isImportableDescriptorImported(it) } - variants += pair.first - notImportedExtensions = pair.second - } - - val shadowedDeclarationsFilter = if (runtimeReceiver != null) - ShadowedDeclarationsFilter(bindingContext, resolutionFacade, position, runtimeReceiver) - else - ShadowedDeclarationsFilter.create(bindingContext, resolutionFacade, position, callTypeAndReceiver) - - if (shadowedDeclarationsFilter != null) { - variants = shadowedDeclarationsFilter.filter(variants) - notImportedExtensions = shadowedDeclarationsFilter - .createNonImportedDeclarationsFilter(importedDeclarations = variants) - .invoke(notImportedExtensions) - } - - if (!configuration.javaGettersAndSetters) { - variants = referenceVariantsHelper.filterOutJavaGettersAndSetters(variants) - } - - if (!configuration.dataClassComponentFunctions) { - variants = variants.filter { !isDataClassComponentFunction(it) } - } - - return ReferenceVariants(variants, notImportedExtensions) - } - - private fun isDataClassComponentFunction(descriptor: DeclarationDescriptor): Boolean { - return descriptor is FunctionDescriptor && - descriptor.isOperator && - DataClassDescriptorResolver.isComponentLike(descriptor.name) && - descriptor.kind == CallableMemberDescriptor.Kind.SYNTHESIZED - } - protected fun referenceVariantsWithSingleFunctionTypeParameter(): ReferenceVariants? { val variants = referenceVariants ?: return null val filter: (DeclarationDescriptor) -> Boolean = { it is FunctionDescriptor && LookupElementFactory.hasSingleFunctionTypeParameter(it) } @@ -398,7 +301,11 @@ abstract class CompletionSession( if (runtimeType == null || runtimeType == type) return null val expressionReceiver = ExpressionReceiver.create(explicitReceiver, runtimeType, bindingContext) - val (variants, notImportedExtensions) = collectReferenceVariants(descriptorKindFilter!!, nameExpression!!, expressionReceiver) + val (variants, notImportedExtensions) = ReferenceVariantsCollector( + referenceVariantsHelper, indicesHelper(true), prefixMatcher, + nameExpression!!, callTypeAndReceiver, resolutionFacade, bindingContext, + importableFqNameClassifier, configuration, runtimeReceiver = expressionReceiver + ).collectReferenceVariants(descriptorKindFilter!!) val filteredVariants = filterVariantsForRuntimeReceiverType(variants, referenceVariants.imported) val filteredNotImportedExtensions = filterVariantsForRuntimeReceiverType(notImportedExtensions, referenceVariants.notImportedExtensions) @@ -475,10 +382,4 @@ abstract class CompletionSession( return callTypeAndReceiver to receiverTypes } - - protected fun isImportableDescriptorImported(descriptor: DeclarationDescriptor): Boolean { - val classification = importableFqNameClassifier.classify(descriptor.importableFqName!!, false) - return classification != ImportableFqNameClassifier.Classification.notImported - && classification != ImportableFqNameClassifier.Classification.siblingImported - } } 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 1d9edbd65d0..1873fb99564 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,6 +29,7 @@ import org.jetbrains.kotlin.descriptors.* 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.core.ImportableFqNameClassifier import org.jetbrains.kotlin.idea.core.ShortenReferences import org.jetbrains.kotlin.idea.imports.importableFqName import org.jetbrains.kotlin.idea.resolve.ResolutionFacade @@ -401,3 +402,10 @@ fun LookupElement.decorateAsStaticMember( } } } + +fun ImportableFqNameClassifier.isImportableDescriptorImported(descriptor: DeclarationDescriptor): Boolean { + val classification = classify(descriptor.importableFqName!!, false) + return classification != ImportableFqNameClassifier.Classification.notImported + && classification != ImportableFqNameClassifier.Classification.siblingImported +} + 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 new file mode 100644 index 00000000000..80fdb2e271d --- /dev/null +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ReferenceVariantsCollector.kt @@ -0,0 +1,144 @@ +/* + * Copyright 2010-2016 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.codeInsight.completion.PrefixMatcher +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.idea.codeInsight.ReferenceVariantsHelper +import org.jetbrains.kotlin.idea.core.ImportableFqNameClassifier +import org.jetbrains.kotlin.idea.core.KotlinIndicesHelper +import org.jetbrains.kotlin.idea.resolve.ResolutionFacade +import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver +import org.jetbrains.kotlin.idea.util.ShadowedDeclarationsFilter +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.KtSimpleNameExpression +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.DataClassDescriptorResolver +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 + +data class ReferenceVariants(val imported: Collection, val notImportedExtensions: Collection) + +internal class ReferenceVariantsCollector( + private val referenceVariantsHelper: ReferenceVariantsHelper, + private val indicesHelper: KotlinIndicesHelper, + private val prefixMatcher: PrefixMatcher, + private val nameExpression: KtSimpleNameExpression, + private val callTypeAndReceiver: CallTypeAndReceiver<*, *>, + private val resolutionFacade: ResolutionFacade, + private val bindingContext: BindingContext, + private val importableFqNameClassifier: ImportableFqNameClassifier, + private val configuration: CompletionSessionConfiguration, + private val runtimeReceiver: ExpressionReceiver? = null +) { + private val prefix = prefixMatcher.prefix + private val descriptorNameFilter = prefixMatcher.asStringNameFilter() + + fun collectReferenceVariants(descriptorKindFilter: DescriptorKindFilter): ReferenceVariants { + val completeExtensionsFromIndices = descriptorKindFilter.kindMask.and(DescriptorKindFilter.CALLABLES_MASK) != 0 + && callTypeAndReceiver !is CallTypeAndReceiver.IMPORT_DIRECTIVE + @Suppress("NAME_SHADOWING") + val descriptorKindFilter = if (completeExtensionsFromIndices) + descriptorKindFilter exclude TopLevelExtensionsExclude // handled via indices + else + descriptorKindFilter + + fun getReferenceVariants(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean): Collection { + return referenceVariantsHelper.getReferenceVariants( + nameExpression, + kindFilter, + nameFilter, + filterOutJavaGettersAndSetters = false, + filterOutShadowed = false, + excludeNonInitializedVariable = false, + useReceiverType = runtimeReceiver?.type) + } + + var variants = getReferenceVariants(descriptorKindFilter, descriptorNameFilter.toNameFilter()) + + val getOrSetPrefix = listOf("get", "set", "ge", "se", "g", "s").firstOrNull { prefix.startsWith(it) } + val additionalPropertyNameFilter: ((String) -> Boolean)? = run { + getOrSetPrefix?.let { prefixMatcher.cloneWithPrefix(prefix.removePrefix(getOrSetPrefix).decapitalizeSmart()).asStringNameFilter() } + } + if (additionalPropertyNameFilter != null) { + variants += getReferenceVariants(descriptorKindFilter.intersect(DescriptorKindFilter.VARIABLES), + additionalPropertyNameFilter.toNameFilter()) + variants = variants.distinct() + } + + var notImportedExtensions: Collection = emptyList() + if (completeExtensionsFromIndices) { + val nameFilter = if (additionalPropertyNameFilter != null) + descriptorNameFilter or additionalPropertyNameFilter + else + descriptorNameFilter + val extensions = if (runtimeReceiver != null) + indicesHelper.getCallableTopLevelExtensions(callTypeAndReceiver, listOf(runtimeReceiver.type), nameFilter) + else + indicesHelper.getCallableTopLevelExtensions(callTypeAndReceiver, nameExpression, bindingContext, nameFilter) + + val pair = extensions.partition { importableFqNameClassifier.isImportableDescriptorImported(it) } + variants += pair.first + notImportedExtensions = pair.second + } + + val shadowedDeclarationsFilter = if (runtimeReceiver != null) + ShadowedDeclarationsFilter(bindingContext, resolutionFacade, nameExpression, runtimeReceiver) + else + ShadowedDeclarationsFilter.create(bindingContext, resolutionFacade, nameExpression, callTypeAndReceiver) + + if (shadowedDeclarationsFilter != null) { + variants = shadowedDeclarationsFilter.filter(variants) + notImportedExtensions = shadowedDeclarationsFilter + .createNonImportedDeclarationsFilter(importedDeclarations = variants) + .invoke(notImportedExtensions) + } + + if (!configuration.javaGettersAndSetters) { + variants = referenceVariantsHelper.filterOutJavaGettersAndSetters(variants) + } + + if (!configuration.dataClassComponentFunctions) { + variants = variants.filter { !isDataClassComponentFunction(it) } + } + + return ReferenceVariants(variants, notImportedExtensions) + } + + private object TopLevelExtensionsExclude : DescriptorKindExclude() { + override fun excludes(descriptor: DeclarationDescriptor): Boolean { + if (descriptor !is CallableMemberDescriptor) return false + if (descriptor.extensionReceiverParameter == null) return false + if (descriptor.kind != CallableMemberDescriptor.Kind.DECLARATION) return false /* do not filter out synthetic extensions */ + val containingPackage = descriptor.containingDeclaration as? PackageFragmentDescriptor ?: return false + if (containingPackage.fqName.asString().startsWith("kotlinx.android.synthetic.")) return false // TODO: temporary solution for Android synthetic extensions + return true + } + + override val fullyExcludedDescriptorKinds: Int + get() = 0 + } + + private fun isDataClassComponentFunction(descriptor: DeclarationDescriptor): Boolean { + return descriptor is FunctionDescriptor && + descriptor.isOperator && + DataClassDescriptorResolver.isComponentLike(descriptor.name) && + descriptor.kind == CallableMemberDescriptor.Kind.SYNTHESIZED + } +} \ No newline at end of file