From e7288b32933979a60f6abcb5645d89780256738b Mon Sep 17 00:00:00 2001 From: Ilya Kirillov Date: Sat, 15 May 2021 18:15:56 +0200 Subject: [PATCH] FIR IDE: extract FirCallableCompletionContributor --- .../KotlinFirCompletionContributor.kt | 193 +++--------------- .../FirCallableCompletionContributor.kt | 158 ++++++++++++++ .../FirClassifierCompletionContributor.kt | 5 +- 3 files changed, 184 insertions(+), 172 deletions(-) create mode 100644 idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/contributors/FirCallableCompletionContributor.kt diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirCompletionContributor.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirCompletionContributor.kt index a3dca8ad704..31d63e7130c 100644 --- a/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirCompletionContributor.kt +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirCompletionContributor.kt @@ -16,6 +16,7 @@ import org.jetbrains.kotlin.idea.completion.context.FirBasicCompletionContext import org.jetbrains.kotlin.idea.completion.context.FirNameReferencePositionContext import org.jetbrains.kotlin.idea.completion.context.FirPositionCompletionContextDetector import org.jetbrains.kotlin.idea.completion.context.FirUnknownPositionContext +import org.jetbrains.kotlin.idea.completion.contributors.FirCallableCompletionContributor import org.jetbrains.kotlin.idea.completion.contributors.FirClassifierCompletionContributor import org.jetbrains.kotlin.idea.completion.contributors.FirContextCompletionContributorBase import org.jetbrains.kotlin.idea.completion.contributors.FirKeywordCompletionContributor @@ -57,10 +58,29 @@ private object KotlinFirCompletionProvider : CompletionProvider with( - KotlinWithNameReferenceCompletionProvider(basicContext, indexHelper) - ) { - addCompletions(positionContext) + is FirNameReferencePositionContext -> { + val visibilityChecker = CompletionVisibilityChecker { + parameters.invocationCount > 1 || isVisible( + it, + basicContext.originalKtFile.getFileSymbol(), + positionContext.explicitReceiver, + positionContext.position + ) + } + + with(FirCallableCompletionContributor(basicContext, indexHelper)) { + complete(positionContext, visibilityChecker) + } + + if (positionContext.explicitReceiver == null) { + with(FirClassifierCompletionContributor(basicContext, indexHelper)) { + collectTypesCompletion( + basicContext.originalKtFile.getScopeContextForPosition(positionContext.nameExpression).scopes, + visibilityChecker + ) + } + } + } is FirUnknownPositionContext -> { } @@ -104,168 +124,3 @@ private object KotlinFirCompletionProvider : CompletionProvider 1 || isVisible( - it, - fileSymbol, - positionContext.explicitReceiver, - positionContext.position - ) - } - - when { - nameExpression.parent is KtUserType -> with(FirClassifierCompletionContributor(basicContext, indexHelper)) { - collectTypesCompletion( - scopesContext.scopes, - expectedType, - visibilityChecker - ) - } - explicitReceiver != null -> { - collectDotCompletion( - scopesContext.scopes, - explicitReceiver, - expectedType, - extensionChecker, - visibilityChecker, - ) - } - - else -> collectDefaultCompletion(scopesContext, expectedType, extensionChecker, visibilityChecker) - } - } - - - private fun KtAnalysisSession.collectDotCompletion( - implicitScopes: KtCompositeScope, - explicitReceiver: KtExpression, - expectedType: KtType?, - extensionChecker: ExtensionApplicabilityChecker, - visibilityChecker: CompletionVisibilityChecker, - ) { - val typeOfPossibleReceiver = explicitReceiver.getKtType() - val possibleReceiverScope = typeOfPossibleReceiver.getTypeScope() ?: return - - val nonExtensionMembers = collectNonExtensions(possibleReceiverScope, visibilityChecker) - val extensionNonMembers = collectSuitableExtensions(implicitScopes, extensionChecker, visibilityChecker) - - nonExtensionMembers.forEach { addSymbolToCompletion(expectedType, it) } - extensionNonMembers.forEach { addSymbolToCompletion(expectedType, it) } - - collectTopLevelExtensionsFromIndices(listOf(typeOfPossibleReceiver), extensionChecker, visibilityChecker) - .forEach { addSymbolToCompletion(expectedType, it) } - } - - private fun KtAnalysisSession.collectDefaultCompletion( - implicitScopesContext: KtScopeContext, - expectedType: KtType?, - extensionChecker: ExtensionApplicabilityChecker, - visibilityChecker: CompletionVisibilityChecker, - ) { - val (implicitScopes, implicitReceivers) = implicitScopesContext - val implicitReceiversTypes = implicitReceivers.map { it.type } - - val availableNonExtensions = collectNonExtensions(implicitScopes, visibilityChecker) - val extensionsWhichCanBeCalled = collectSuitableExtensions(implicitScopes, extensionChecker, visibilityChecker) - - availableNonExtensions.forEach { addSymbolToCompletion(expectedType, it) } - extensionsWhichCanBeCalled.forEach { addSymbolToCompletion(expectedType, it) } - - if (shouldCompleteTopLevelCallablesFromIndex) { - val topLevelCallables = indexHelper.getTopLevelCallables(scopeNameFilter) - topLevelCallables.asSequence() - .map { it.getSymbol() as KtCallableSymbol } - .filter { with(visibilityChecker) { isVisible(it) } } - .forEach { addSymbolToCompletion(expectedType, it) } - } - - collectTopLevelExtensionsFromIndices(implicitReceiversTypes, extensionChecker, visibilityChecker) - .forEach { addSymbolToCompletion(expectedType, it) } - - with(FirClassifierCompletionContributor(basicContext, indexHelper)) { - collectTypesCompletion(implicitScopes, expectedType, visibilityChecker) - } - } - - private fun KtAnalysisSession.collectTopLevelExtensionsFromIndices( - receiverTypes: List, - extensionChecker: ExtensionApplicabilityChecker, - visibilityChecker: CompletionVisibilityChecker, - ): Sequence { - val implicitReceiverNames = findAllNamesOfTypes(receiverTypes) - val topLevelExtensions = indexHelper.getTopLevelExtensions(scopeNameFilter, implicitReceiverNames) - - return topLevelExtensions.asSequence() - .map { it.getSymbol() as KtCallableSymbol } - .filter { with(visibilityChecker) { isVisible(it) } } - .filter { with(extensionChecker) { isApplicable(it) } } - } - - private fun KtAnalysisSession.collectNonExtensions(scope: KtScope, visibilityChecker: CompletionVisibilityChecker) = - scope.getCallableSymbols(scopeNameFilter) - .filterNot { it.isExtension } - .filter { with(visibilityChecker) { isVisible(it) } } - - private fun KtAnalysisSession.collectSuitableExtensions( - scope: KtCompositeScope, - hasSuitableExtensionReceiver: ExtensionApplicabilityChecker, - visibilityChecker: CompletionVisibilityChecker, - ): Sequence = - scope.getCallableSymbols(scopeNameFilter) - .filter { it.isExtension } - .filter { with(visibilityChecker) { isVisible(it) } } - .filter { with(hasSuitableExtensionReceiver) { isApplicable(it) } } - - private fun KtAnalysisSession.findAllNamesOfTypes(implicitReceiversTypes: List) = - implicitReceiversTypes.flatMapTo(hashSetOf()) { with(typeNamesProvider) { findAllNames(it) } } -} - -private class TypeNamesProvider(private val indexHelper: IndexHelper) { - fun KtAnalysisSession.findAllNames(type: KtType): Set { - if (type !is KtClassType) return emptySet() - - val typeName = type.classId.shortClassName.let { - if (it.isSpecial) return emptySet() - it.identifier - } - - val result = hashSetOf() - result += typeName - result += indexHelper.getPossibleTypeAliasExpansionNames(typeName) - - val superTypes = (type.classSymbol as? KtClassOrObjectSymbol)?.superTypes - superTypes?.forEach { superType -> - result += findAllNames(superType.type) - } - - return result - } -} \ No newline at end of file diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/contributors/FirCallableCompletionContributor.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/contributors/FirCallableCompletionContributor.kt new file mode 100644 index 00000000000..fe96e82cee0 --- /dev/null +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/contributors/FirCallableCompletionContributor.kt @@ -0,0 +1,158 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.idea.completion.contributors + +import org.jetbrains.kotlin.idea.completion.checkers.CompletionVisibilityChecker +import org.jetbrains.kotlin.idea.completion.checkers.ExtensionApplicabilityChecker +import org.jetbrains.kotlin.idea.completion.context.FirBasicCompletionContext +import org.jetbrains.kotlin.idea.completion.context.FirNameReferencePositionContext +import org.jetbrains.kotlin.idea.fir.low.level.api.IndexHelper +import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession +import org.jetbrains.kotlin.idea.frontend.api.components.KtScopeContext +import org.jetbrains.kotlin.idea.frontend.api.scopes.KtCompositeScope +import org.jetbrains.kotlin.idea.frontend.api.scopes.KtScope +import org.jetbrains.kotlin.idea.frontend.api.symbols.KtCallableSymbol +import org.jetbrains.kotlin.idea.frontend.api.symbols.KtClassOrObjectSymbol +import org.jetbrains.kotlin.idea.frontend.api.types.KtClassType +import org.jetbrains.kotlin.idea.frontend.api.types.KtType +import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.psi.KtUserType + +internal class FirCallableCompletionContributor( + basicContext: FirBasicCompletionContext, + indexHelper: IndexHelper +) : FirContextCompletionContributorBase(basicContext, indexHelper) { + private val typeNamesProvider = TypeNamesProvider(indexHelper) + + private val shouldCompleteTopLevelCallablesFromIndex: Boolean + get() = prefixMatcher.prefix.isNotEmpty() + + fun KtAnalysisSession.complete(positionContext: FirNameReferencePositionContext, visibilityChecker: CompletionVisibilityChecker) = + with(positionContext) { + val expectedType = nameExpression.getExpectedType() + val scopesContext = originalKtFile.getScopeContextForPosition(nameExpression) + + val extensionChecker = ExtensionApplicabilityChecker { + it.checkExtensionIsSuitable(originalKtFile, nameExpression, explicitReceiver) + } + + when { + explicitReceiver != null -> { + collectDotCompletion( + scopesContext.scopes, + explicitReceiver, + expectedType, + extensionChecker, + visibilityChecker, + ) + } + + else -> completeWithoutReceiver(scopesContext, expectedType, extensionChecker, visibilityChecker) + } + } + + + private fun KtAnalysisSession.completeWithoutReceiver( + implicitScopesContext: KtScopeContext, + expectedType: KtType?, + extensionChecker: ExtensionApplicabilityChecker, + visibilityChecker: CompletionVisibilityChecker, + ) { + val (implicitScopes, implicitReceivers) = implicitScopesContext + val implicitReceiversTypes = implicitReceivers.map { it.type } + + val availableNonExtensions = collectNonExtensions(implicitScopes, visibilityChecker) + val extensionsWhichCanBeCalled = collectSuitableExtensions(implicitScopes, extensionChecker, visibilityChecker) + + availableNonExtensions.forEach { addSymbolToCompletion(expectedType, it) } + extensionsWhichCanBeCalled.forEach { addSymbolToCompletion(expectedType, it) } + + if (shouldCompleteTopLevelCallablesFromIndex) { + val topLevelCallables = indexHelper.getTopLevelCallables(scopeNameFilter) + topLevelCallables.asSequence() + .map { it.getSymbol() as KtCallableSymbol } + .filter { with(visibilityChecker) { isVisible(it) } } + .forEach { addSymbolToCompletion(expectedType, it) } + } + + collectTopLevelExtensionsFromIndices(implicitReceiversTypes, extensionChecker, visibilityChecker) + .forEach { addSymbolToCompletion(expectedType, it) } + } + + private fun KtAnalysisSession.collectDotCompletion( + implicitScopes: KtCompositeScope, + explicitReceiver: KtExpression, + expectedType: KtType?, + extensionChecker: ExtensionApplicabilityChecker, + visibilityChecker: CompletionVisibilityChecker, + ) { + val typeOfPossibleReceiver = explicitReceiver.getKtType() + val possibleReceiverScope = typeOfPossibleReceiver.getTypeScope() ?: return + + val nonExtensionMembers = collectNonExtensions(possibleReceiverScope, visibilityChecker) + val extensionNonMembers = collectSuitableExtensions(implicitScopes, extensionChecker, visibilityChecker) + + nonExtensionMembers.forEach { addSymbolToCompletion(expectedType, it) } + extensionNonMembers.forEach { addSymbolToCompletion(expectedType, it) } + + collectTopLevelExtensionsFromIndices(listOf(typeOfPossibleReceiver), extensionChecker, visibilityChecker) + .forEach { addSymbolToCompletion(expectedType, it) } + } + + private fun KtAnalysisSession.collectTopLevelExtensionsFromIndices( + receiverTypes: List, + extensionChecker: ExtensionApplicabilityChecker, + visibilityChecker: CompletionVisibilityChecker, + ): Sequence { + val implicitReceiverNames = findAllNamesOfTypes(receiverTypes) + val topLevelExtensions = indexHelper.getTopLevelExtensions(scopeNameFilter, implicitReceiverNames) + + return topLevelExtensions.asSequence() + .map { it.getSymbol() as KtCallableSymbol } + .filter { with(visibilityChecker) { isVisible(it) } } + .filter { with(extensionChecker) { isApplicable(it) } } + } + + private fun KtAnalysisSession.collectNonExtensions(scope: KtScope, visibilityChecker: CompletionVisibilityChecker) = + scope.getCallableSymbols(scopeNameFilter) + .filterNot { it.isExtension } + .filter { with(visibilityChecker) { isVisible(it) } } + + private fun KtAnalysisSession.collectSuitableExtensions( + scope: KtCompositeScope, + hasSuitableExtensionReceiver: ExtensionApplicabilityChecker, + visibilityChecker: CompletionVisibilityChecker, + ): Sequence = + scope.getCallableSymbols(scopeNameFilter) + .filter { it.isExtension } + .filter { with(visibilityChecker) { isVisible(it) } } + .filter { with(hasSuitableExtensionReceiver) { isApplicable(it) } } + + private fun KtAnalysisSession.findAllNamesOfTypes(implicitReceiversTypes: List) = + implicitReceiversTypes.flatMapTo(hashSetOf()) { with(typeNamesProvider) { findAllNames(it) } } +} + +private class TypeNamesProvider(private val indexHelper: IndexHelper) { + fun KtAnalysisSession.findAllNames(type: KtType): Set { + if (type !is KtClassType) return emptySet() + + val typeName = type.classId.shortClassName.let { + if (it.isSpecial) return emptySet() + it.identifier + } + + val result = hashSetOf() + result += typeName + result += indexHelper.getPossibleTypeAliasExpansionNames(typeName) + + val superTypes = (type.classSymbol as? KtClassOrObjectSymbol)?.superTypes + superTypes?.forEach { superType -> + result += findAllNames(superType.type) + } + + return result + } +} \ No newline at end of file diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/contributors/FirClassifierCompletionContributor.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/contributors/FirClassifierCompletionContributor.kt index 75b81ffd789..4bc3c151917 100644 --- a/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/contributors/FirClassifierCompletionContributor.kt +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/contributors/FirClassifierCompletionContributor.kt @@ -21,19 +21,18 @@ internal class FirClassifierCompletionContributor( fun KtAnalysisSession.collectTypesCompletion( implicitScopes: KtScope, - expectedType: KtType?, visibilityChecker: CompletionVisibilityChecker, ) { val classesFromScopes = implicitScopes .getClassifierSymbols(scopeNameFilter) .filter { with(visibilityChecker) { isVisible(it) } } - classesFromScopes.forEach { addSymbolToCompletion(expectedType, it) } + classesFromScopes.forEach { addSymbolToCompletion(expectedType = null, it) } val kotlinClassesFromIndices = indexHelper.getKotlinClasses(scopeNameFilter, psiFilter = { it !is KtEnumEntry }) kotlinClassesFromIndices.asSequence() .map { it.getSymbol() as KtClassifierSymbol } .filter { with(visibilityChecker) { isVisible(it) } } - .forEach { addSymbolToCompletion(expectedType, it) } + .forEach { addSymbolToCompletion(expectedType = null, it) } } } \ No newline at end of file