Implement the completion using high-level API only
- The indecies are not yet used Co-authored-by: Ilya Kirillov <ilya.kirillov@jetbrains.com>
This commit is contained in:
committed by
Ilya Kirillov
parent
a6dd84a7e4
commit
c3a3ab9e89
+65
-3
@@ -13,8 +13,6 @@ import com.intellij.psi.search.GlobalSearchScope
|
|||||||
import com.intellij.util.ProcessingContext
|
import com.intellij.util.ProcessingContext
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirFile
|
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
|
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
|
||||||
import org.jetbrains.kotlin.fir.psi
|
|
||||||
import org.jetbrains.kotlin.fir.realPsi
|
|
||||||
import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
||||||
import org.jetbrains.kotlin.fir.resolve.scope
|
import org.jetbrains.kotlin.fir.resolve.scope
|
||||||
import org.jetbrains.kotlin.fir.scopes.FirScope
|
import org.jetbrains.kotlin.fir.scopes.FirScope
|
||||||
@@ -28,6 +26,12 @@ import org.jetbrains.kotlin.fir.types.coneTypeUnsafe
|
|||||||
import org.jetbrains.kotlin.idea.fir.getFirOfClosestParent
|
import org.jetbrains.kotlin.idea.fir.getFirOfClosestParent
|
||||||
import org.jetbrains.kotlin.idea.fir.getOrBuildFirSafe
|
import org.jetbrains.kotlin.idea.fir.getOrBuildFirSafe
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.LowLevelFirApiFacade
|
import org.jetbrains.kotlin.idea.fir.low.level.api.LowLevelFirApiFacade
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.getAnalysisSessionFor
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtCallableSymbol
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtNamedSymbol
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtPossibleExtensionSymbol
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.isExtension
|
||||||
|
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
|
||||||
import org.jetbrains.kotlin.idea.references.mainReference
|
import org.jetbrains.kotlin.idea.references.mainReference
|
||||||
import org.jetbrains.kotlin.idea.stubindex.KotlinTopLevelFunctionByPackageIndex
|
import org.jetbrains.kotlin.idea.stubindex.KotlinTopLevelFunctionByPackageIndex
|
||||||
import org.jetbrains.kotlin.idea.stubindex.KotlinTopLevelPropertyByPackageIndex
|
import org.jetbrains.kotlin.idea.stubindex.KotlinTopLevelPropertyByPackageIndex
|
||||||
@@ -35,12 +39,14 @@ import org.jetbrains.kotlin.name.FqName
|
|||||||
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.getNonStrictParentOfType
|
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelector
|
||||||
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
||||||
import org.jetbrains.kotlin.types.model.TypeCheckerProviderContext
|
import org.jetbrains.kotlin.types.model.TypeCheckerProviderContext
|
||||||
|
|
||||||
class KotlinFirCompletionContributor : CompletionContributor() {
|
class KotlinFirCompletionContributor : CompletionContributor() {
|
||||||
init {
|
init {
|
||||||
extend(CompletionType.BASIC, PlatformPatterns.psiElement(), KotlinFirCompletionProvider)
|
// extend(CompletionType.BASIC, PlatformPatterns.psiElement(), KotlinFirCompletionProvider)
|
||||||
|
extend(CompletionType.BASIC, PlatformPatterns.psiElement(), KotlinHighLevelApiContributor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,6 +116,62 @@ private object KotlinFirCompletionProvider : CompletionProvider<CompletionParame
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private object KotlinHighLevelApiContributor : CompletionProvider<CompletionParameters>() {
|
||||||
|
|
||||||
|
override fun addCompletions(parameters: CompletionParameters, context: ProcessingContext, result: CompletionResultSet) {
|
||||||
|
val originalFile = parameters.originalFile as? KtFile ?: return
|
||||||
|
|
||||||
|
val reference = (parameters.position.parent as? KtSimpleNameExpression)?.mainReference ?: return
|
||||||
|
val nameExpression = reference.expression.takeIf { it !is KtLabelReferenceExpression } ?: return
|
||||||
|
|
||||||
|
val possibleReceiver = nameExpression.getQualifiedExpressionForSelector()?.receiverExpression
|
||||||
|
|
||||||
|
val originalSession = getAnalysisSessionFor(originalFile)
|
||||||
|
val sessionForCompletion = originalSession.analyzeInContext()
|
||||||
|
val scopeProvider = sessionForCompletion.scopeProvider
|
||||||
|
|
||||||
|
val (implicitScopes, implicitReceivers) = scopeProvider.getScopeContextForPosition(originalFile, nameExpression)
|
||||||
|
|
||||||
|
val typeOfPossibleReceiver = possibleReceiver?.let { sessionForCompletion.getKtExpressionType(it) }
|
||||||
|
val possibleReceiverScope = typeOfPossibleReceiver?.let { sessionForCompletion.scopeProvider.getScopeForType(it) }
|
||||||
|
|
||||||
|
fun addToCompletion(symbol: KtCallableSymbol) {
|
||||||
|
if (symbol !is KtNamedSymbol) return
|
||||||
|
result.addElement(LookupElementBuilder.create(symbol.name.asString()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (possibleReceiverScope != null) {
|
||||||
|
val nonExtensionMembers = possibleReceiverScope
|
||||||
|
.getCallableSymbols()
|
||||||
|
.filterNot { it.isExtension }
|
||||||
|
.toList()
|
||||||
|
|
||||||
|
val extensionNonMembers = implicitScopes
|
||||||
|
.getCallableSymbols()
|
||||||
|
.filter { it.isExtension && it.canBeCalledWith(listOf(typeOfPossibleReceiver)) }
|
||||||
|
.toList()
|
||||||
|
|
||||||
|
nonExtensionMembers.forEach(::addToCompletion)
|
||||||
|
extensionNonMembers.forEach(::addToCompletion)
|
||||||
|
} else {
|
||||||
|
val extensionNonMembers = implicitScopes
|
||||||
|
.getCallableSymbols()
|
||||||
|
.filter { !it.isExtension || it.canBeCalledWith(implicitReceivers) }
|
||||||
|
|
||||||
|
extensionNonMembers.forEach(::addToCompletion)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtCallableSymbol.canBeCalledWith(implicitReceivers: List<KtType>): Boolean {
|
||||||
|
require(this is KtPossibleExtensionSymbol && this.isExtension) { "This function should be called only on extensions!" }
|
||||||
|
|
||||||
|
val requiredReceiverType = receiverType ?: error("Receiver type should be present")
|
||||||
|
return implicitReceivers.any { it.isSubTypeOf(requiredReceiverType) }
|
||||||
|
}
|
||||||
|
|
||||||
private val FirCallableSymbol<*>.isExtension get() = fir.receiverTypeRef != null
|
private val FirCallableSymbol<*>.isExtension get() = fir.receiverTypeRef != null
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user