From bcf7116955b1b2fe9d798381eedca47d37503e42 Mon Sep 17 00:00:00 2001 From: Ilya Kirillov Date: Sat, 15 May 2021 21:00:40 +0200 Subject: [PATCH] FIR IDE: add type completion with dot --- .../testData/basic/common/InnerClass.kt | 1 + .../NestedClassesOnClassWithDefaultObject.kt | 1 + ...tedClassesOnClassWithNamedDefaultObject.kt | 1 + .../JavaInnerClasses/JavaInnerClasses.kt | 1 + .../KotlinFirLookupElementFactory.kt | 20 +++++---- .../FirClassifierCompletionContributor.kt | 43 ++++++++++++++++--- .../FirCompletionContributorBase.kt | 13 +++++- 7 files changed, 65 insertions(+), 15 deletions(-) diff --git a/idea/idea-completion/testData/basic/common/InnerClass.kt b/idea/idea-completion/testData/basic/common/InnerClass.kt index 46bbd261b98..20f3eb2560b 100644 --- a/idea/idea-completion/testData/basic/common/InnerClass.kt +++ b/idea/idea-completion/testData/basic/common/InnerClass.kt @@ -1,3 +1,4 @@ +// FIR_COMPARISON class AAA { class Nested inner class Inner diff --git a/idea/idea-completion/testData/basic/common/NestedClassesOnClassWithDefaultObject.kt b/idea/idea-completion/testData/basic/common/NestedClassesOnClassWithDefaultObject.kt index 382e6136834..da618d16552 100644 --- a/idea/idea-completion/testData/basic/common/NestedClassesOnClassWithDefaultObject.kt +++ b/idea/idea-completion/testData/basic/common/NestedClassesOnClassWithDefaultObject.kt @@ -1,3 +1,4 @@ +// FIR_COMPARISON class A { class Nested inner class Inner diff --git a/idea/idea-completion/testData/basic/common/NestedClassesOnClassWithNamedDefaultObject.kt b/idea/idea-completion/testData/basic/common/NestedClassesOnClassWithNamedDefaultObject.kt index d5dd47cb135..65d6534f15e 100644 --- a/idea/idea-completion/testData/basic/common/NestedClassesOnClassWithNamedDefaultObject.kt +++ b/idea/idea-completion/testData/basic/common/NestedClassesOnClassWithNamedDefaultObject.kt @@ -1,3 +1,4 @@ +// FIR_COMPARISON class A { class Nested inner class Inner diff --git a/idea/idea-completion/testData/basic/multifile/JavaInnerClasses/JavaInnerClasses.kt b/idea/idea-completion/testData/basic/multifile/JavaInnerClasses/JavaInnerClasses.kt index 891b62bd6da..55cc8efcadb 100644 --- a/idea/idea-completion/testData/basic/multifile/JavaInnerClasses/JavaInnerClasses.kt +++ b/idea/idea-completion/testData/basic/multifile/JavaInnerClasses/JavaInnerClasses.kt @@ -1,3 +1,4 @@ +// FIR_COMPARISON fun main(args: Array) { println(R.) } diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirLookupElementFactory.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirLookupElementFactory.kt index bf073513d7a..529f33874cb 100644 --- a/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirLookupElementFactory.kt +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/KotlinFirLookupElementFactory.kt @@ -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 { 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()) 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 a427f67ebb1..749441c46e5 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 @@ -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(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) } } } \ No newline at end of file diff --git a/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/contributors/FirCompletionContributorBase.kt b/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/contributors/FirCompletionContributorBase.kt index 053d7981b60..37b168a9651 100644 --- a/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/contributors/FirCompletionContributorBase.kt +++ b/idea/idea-fir/src/org/jetbrains/kotlin/idea/completion/contributors/FirCompletionContributorBase.kt @@ -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 createLookupElementForClassLikeSymbol(symbol, insertFqName) + is KtTypeParameterSymbol -> createLookupElement(symbol) + } + } ?: return + result.addElement(lookup) + } + private fun KtAnalysisSession.applyWeighers( lookupElement: LookupElement, symbol: KtSymbol,