FIR IDE: add type completion with dot
This commit is contained in:
committed by
TeamCityServer
parent
e03916e7fb
commit
bcf7116955
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_COMPARISON
|
||||||
class AAA {
|
class AAA {
|
||||||
class Nested
|
class Nested
|
||||||
inner class Inner
|
inner class Inner
|
||||||
|
|||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_COMPARISON
|
||||||
class A {
|
class A {
|
||||||
class Nested
|
class Nested
|
||||||
inner class Inner
|
inner class Inner
|
||||||
|
|||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_COMPARISON
|
||||||
class A {
|
class A {
|
||||||
class Nested
|
class Nested
|
||||||
inner class Inner
|
inner class Inner
|
||||||
|
|||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_COMPARISON
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
println(R.<caret>)
|
println(R.<caret>)
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-8
@@ -13,15 +13,12 @@ import com.intellij.codeInsight.lookup.LookupElement
|
|||||||
import com.intellij.codeInsight.lookup.LookupElementBuilder
|
import com.intellij.codeInsight.lookup.LookupElementBuilder
|
||||||
import com.intellij.openapi.diagnostic.ControlFlowException
|
import com.intellij.openapi.diagnostic.ControlFlowException
|
||||||
import com.intellij.openapi.diagnostic.logger
|
import com.intellij.openapi.diagnostic.logger
|
||||||
import com.intellij.openapi.editor.Document
|
|
||||||
import com.intellij.openapi.util.TextRange
|
import com.intellij.openapi.util.TextRange
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import org.jetbrains.kotlin.idea.completion.handlers.isTextAt
|
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.core.asFqNameWithRootPrefixIfNeeded
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.analyse
|
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.fir.utils.addImportToFile
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.*
|
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.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
|
.withPsiElement(symbol.psi) // TODO check if it is a heavy operation and should be postponed
|
||||||
.withIcon(KotlinFirIconProvider.getIconFor(symbol))
|
.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 {
|
private sealed class CallableImportStrategy {
|
||||||
@@ -71,7 +75,7 @@ private interface KotlinLookupObject {
|
|||||||
val shortName: Name
|
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.
|
* Simplest lookup object so two lookup elements for the same function will clash.
|
||||||
@@ -93,10 +97,10 @@ private data class VariableLookupObject(
|
|||||||
val importStrategy: CallableImportStrategy
|
val importStrategy: CallableImportStrategy
|
||||||
) : KotlinLookupObject
|
) : KotlinLookupObject
|
||||||
|
|
||||||
private class ClassLookupElementFactory {
|
class ClassLookupElementFactory {
|
||||||
fun createLookup(symbol: KtClassLikeSymbol): LookupElementBuilder {
|
fun createLookup(symbol: KtClassLikeSymbol, insertFqName: Boolean = true): LookupElementBuilder {
|
||||||
val name = symbol.nameOrAnonymous
|
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)
|
.withInsertHandler(ClassifierInsertionHandler)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -202,7 +206,7 @@ private object ClassifierInsertionHandler : InsertHandler<LookupElement> {
|
|||||||
val targetFile = context.file as? KtFile ?: return
|
val targetFile = context.file as? KtFile ?: return
|
||||||
val lookupObject = item.`object` as ClassifierLookupObject
|
val lookupObject = item.`object` as ClassifierLookupObject
|
||||||
|
|
||||||
if (lookupObject.classId != null) {
|
if (lookupObject.classId != null && lookupObject.insertFqName) {
|
||||||
val fqName = lookupObject.classId.asSingleFqName()
|
val fqName = lookupObject.classId.asSingleFqName()
|
||||||
|
|
||||||
context.document.replaceString(context.startOffset, context.tailOffset, fqName.render())
|
context.document.replaceString(context.startOffset, context.tailOffset, fqName.render())
|
||||||
|
|||||||
+37
-6
@@ -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.checkers.CompletionVisibilityChecker
|
||||||
import org.jetbrains.kotlin.idea.completion.context.FirBasicCompletionContext
|
import org.jetbrains.kotlin.idea.completion.context.FirBasicCompletionContext
|
||||||
import org.jetbrains.kotlin.idea.completion.context.FirNameReferenceRawPositionContext
|
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.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.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.KtEnumEntry
|
||||||
|
import org.jetbrains.kotlin.psi.KtExpression
|
||||||
|
|
||||||
internal class FirClassifierCompletionContributor(
|
internal class FirClassifierCompletionContributor(
|
||||||
basicContext: FirBasicCompletionContext,
|
basicContext: FirBasicCompletionContext,
|
||||||
) : FirContextCompletionContributorBase<FirNameReferenceRawPositionContext>(basicContext) {
|
) : FirContextCompletionContributorBase<FirNameReferenceRawPositionContext>(basicContext) {
|
||||||
|
|
||||||
override fun KtAnalysisSession.complete(positionContext: FirNameReferenceRawPositionContext) {
|
override fun KtAnalysisSession.complete(positionContext: FirNameReferenceRawPositionContext) {
|
||||||
val implicitScopes = originalKtFile.getScopeContextForPosition(positionContext.nameExpression).scopes
|
|
||||||
val visibilityChecker = CompletionVisibilityChecker.create(basicContext, positionContext)
|
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
|
val classesFromScopes = implicitScopes
|
||||||
.getClassifierSymbols(scopeNameFilter)
|
.getClassifierSymbols(scopeNameFilter)
|
||||||
.filter { with(visibilityChecker) { isVisible(it) } }
|
.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 })
|
val kotlinClassesFromIndices = indexHelper.getKotlinClasses(scopeNameFilter, psiFilter = { it !is KtEnumEntry })
|
||||||
kotlinClassesFromIndices.asSequence()
|
kotlinClassesFromIndices.asSequence()
|
||||||
.map { it.getSymbol() as KtClassifierSymbol }
|
.map { it.getSymbol() as KtClassifierSymbol }
|
||||||
.filter { with(visibilityChecker) { isVisible(it) } }
|
.filter { with(visibilityChecker) { isVisible(it) } }
|
||||||
.forEach { addSymbolToCompletion(expectedType = null, it) }
|
.forEach { addClassifierSymbolToCompletion(it, insertFqName = true) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+12
-1
@@ -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.completion.weighers.Weighers
|
||||||
import org.jetbrains.kotlin.idea.fir.low.level.api.IndexHelper
|
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.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.symbols.markers.KtNamedSymbol
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
|
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
|
||||||
import org.jetbrains.kotlin.platform.TargetPlatform
|
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(
|
private fun KtAnalysisSession.applyWeighers(
|
||||||
lookupElement: LookupElement,
|
lookupElement: LookupElement,
|
||||||
symbol: KtSymbol,
|
symbol: KtSymbol,
|
||||||
|
|||||||
Reference in New Issue
Block a user