FIR IDE: add type completion with dot

This commit is contained in:
Ilya Kirillov
2021-05-15 21:00:40 +02:00
committed by TeamCityServer
parent e03916e7fb
commit bcf7116955
7 changed files with 65 additions and 15 deletions
@@ -1,3 +1,4 @@
// FIR_COMPARISON
class AAA {
class Nested
inner class Inner
@@ -1,3 +1,4 @@
// FIR_COMPARISON
class A {
class Nested
inner class Inner
@@ -1,3 +1,4 @@
// FIR_COMPARISON
class A {
class Nested
inner class Inner
@@ -1,3 +1,4 @@
// FIR_COMPARISON
fun main(args: Array<String>) {
println(R.<caret>)
}
@@ -13,15 +13,12 @@ import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInsight.lookup.LookupElementBuilder
import com.intellij.openapi.diagnostic.ControlFlowException
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.editor.Document
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.completion.handlers.isTextAt
import com.intellij.psi.util.PsiUtil
import org.jetbrains.kotlin.idea.core.asFqNameWithRootPrefixIfNeeded
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
import org.jetbrains.kotlin.idea.frontend.api.analyse
import org.jetbrains.kotlin.idea.frontend.api.analyseInDependedAnalysisSession
import org.jetbrains.kotlin.idea.frontend.api.fir.utils.addImportToFile
import org.jetbrains.kotlin.idea.frontend.api.symbols.*
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtNamedSymbol
@@ -54,6 +51,13 @@ internal class KotlinFirLookupElementFactory {
.withPsiElement(symbol.psi) // TODO check if it is a heavy operation and should be postponed
.withIcon(KotlinFirIconProvider.getIconFor(symbol))
}
fun KtAnalysisSession.createLookupElementForClassLikeSymbol(symbol: KtClassLikeSymbol, insertFqName: Boolean = true): LookupElement? {
if (symbol !is KtNamedSymbol) return null
return classLookupElementFactory.createLookup(symbol, insertFqName)
.withPsiElement(symbol.psi) // TODO check if it is a heavy operation and should be postponed
.withIcon(KotlinFirIconProvider.getIconFor(symbol))
}
}
private sealed class CallableImportStrategy {
@@ -71,7 +75,7 @@ private interface KotlinLookupObject {
val shortName: Name
}
private data class ClassifierLookupObject(override val shortName: Name, val classId: ClassId?) : KotlinLookupObject
private data class ClassifierLookupObject(override val shortName: Name, val classId: ClassId?, val insertFqName: Boolean) : KotlinLookupObject
/**
* Simplest lookup object so two lookup elements for the same function will clash.
@@ -93,10 +97,10 @@ private data class VariableLookupObject(
val importStrategy: CallableImportStrategy
) : KotlinLookupObject
private class ClassLookupElementFactory {
fun createLookup(symbol: KtClassLikeSymbol): LookupElementBuilder {
class ClassLookupElementFactory {
fun createLookup(symbol: KtClassLikeSymbol, insertFqName: Boolean = true): LookupElementBuilder {
val name = symbol.nameOrAnonymous
return LookupElementBuilder.create(ClassifierLookupObject(name, symbol.classIdIfNonLocal), name.asString())
return LookupElementBuilder.create(ClassifierLookupObject(name, symbol.classIdIfNonLocal, insertFqName), name.asString())
.withInsertHandler(ClassifierInsertionHandler)
}
}
@@ -202,7 +206,7 @@ private object ClassifierInsertionHandler : InsertHandler<LookupElement> {
val targetFile = context.file as? KtFile ?: return
val lookupObject = item.`object` as ClassifierLookupObject
if (lookupObject.classId != null) {
if (lookupObject.classId != null && lookupObject.insertFqName) {
val fqName = lookupObject.classId.asSingleFqName()
context.document.replaceString(context.startOffset, context.tailOffset, fqName.render())
@@ -8,30 +8,61 @@ package org.jetbrains.kotlin.idea.completion.contributors
import org.jetbrains.kotlin.idea.completion.checkers.CompletionVisibilityChecker
import org.jetbrains.kotlin.idea.completion.context.FirBasicCompletionContext
import org.jetbrains.kotlin.idea.completion.context.FirNameReferenceRawPositionContext
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.scopes.KtScope
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtClassLikeSymbol
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtClassifierSymbol
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtTypeParameterSymbol
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtNamedSymbol
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolWithMembers
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtEnumEntry
import org.jetbrains.kotlin.psi.KtExpression
internal class FirClassifierCompletionContributor(
basicContext: FirBasicCompletionContext,
) : FirContextCompletionContributorBase<FirNameReferenceRawPositionContext>(basicContext) {
override fun KtAnalysisSession.complete(positionContext: FirNameReferenceRawPositionContext) {
val implicitScopes = originalKtFile.getScopeContextForPosition(positionContext.nameExpression).scopes
val visibilityChecker = CompletionVisibilityChecker.create(basicContext, positionContext)
when (val receiver = positionContext.explicitReceiver) {
null -> {
completeWithoutReceiver(positionContext, visibilityChecker)
}
else -> {
completeWithReceiver(receiver, visibilityChecker)
}
}
}
private fun KtAnalysisSession.completeWithReceiver(
receiver: KtExpression,
visibilityChecker: CompletionVisibilityChecker
) {
val reference = receiver.mainReference ?: return
val symbol = reference.resolveToSymbol() as? KtSymbolWithMembers ?: return
symbol.getMemberScope()
.getClassifierSymbols(scopeNameFilter)
.filter { with(visibilityChecker) { isVisible(it) } }
.forEach { addClassifierSymbolToCompletion(it, insertFqName = false) }
}
private fun KtAnalysisSession.completeWithoutReceiver(
positionContext: FirNameReferenceRawPositionContext,
visibilityChecker: CompletionVisibilityChecker
) {
val implicitScopes = originalKtFile.getScopeContextForPosition(positionContext.nameExpression).scopes
val classesFromScopes = implicitScopes
.getClassifierSymbols(scopeNameFilter)
.filter { with(visibilityChecker) { isVisible(it) } }
classesFromScopes.forEach { addSymbolToCompletion(expectedType = null, it) }
classesFromScopes.forEach { addClassifierSymbolToCompletion(it, insertFqName = true) }
val kotlinClassesFromIndices = indexHelper.getKotlinClasses(scopeNameFilter, psiFilter = { it !is KtEnumEntry })
kotlinClassesFromIndices.asSequence()
.map { it.getSymbol() as KtClassifierSymbol }
.filter { with(visibilityChecker) { isVisible(it) } }
.forEach { addSymbolToCompletion(expectedType = null, it) }
.forEach { addClassifierSymbolToCompletion(it, insertFqName = true) }
}
}
@@ -16,7 +16,7 @@ import org.jetbrains.kotlin.idea.completion.context.FirRawPositionCompletionCont
import org.jetbrains.kotlin.idea.completion.weighers.Weighers
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.symbols.KtSymbol
import org.jetbrains.kotlin.idea.frontend.api.symbols.*
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtNamedSymbol
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
import org.jetbrains.kotlin.platform.TargetPlatform
@@ -44,6 +44,17 @@ internal abstract class FirCompletionContributorBase<C : FirRawPositionCompletio
}
}
protected fun KtAnalysisSession.addClassifierSymbolToCompletion(symbol: KtClassifierSymbol, insertFqName: Boolean) {
if (symbol !is KtNamedSymbol) return
val lookup = with(lookupElementFactory) {
when (symbol) {
is KtClassLikeSymbol -> createLookupElementForClassLikeSymbol(symbol, insertFqName)
is KtTypeParameterSymbol -> createLookupElement(symbol)
}
} ?: return
result.addElement(lookup)
}
private fun KtAnalysisSession.applyWeighers(
lookupElement: LookupElement,
symbol: KtSymbol,