FIR IDE: implement completion for callable references

This commit is contained in:
Ilya Kirillov
2021-06-07 19:03:12 +02:00
committed by teamcityserver
parent a42e9c236a
commit 2ffc7ad0a0
21 changed files with 226 additions and 110 deletions
@@ -1,3 +1,4 @@
// FIR_COMPARISON
import java.io.File
val v = File::<caret>
@@ -1,3 +1,4 @@
// FIR_COMPARISON
val v = "a"::<caret>
// EXIST: extFun
@@ -1,3 +1,4 @@
// FIR_COMPARISON
package ppp
class X(p: Int)
@@ -1,3 +1,4 @@
// FIR_COMPARISON
package ppp
class X(p: Int)
@@ -1,3 +1,4 @@
// FIR_COMPARISON
fun globalFun(p: Int) {}
class C {
@@ -1,3 +1,4 @@
// FIR_COMPARISON
fun globalFun(p: Int) {}
class C {
@@ -1,3 +1,4 @@
// FIR_COMPARISON
class C {
fun foo() {
val v = D::<caret>
@@ -1,3 +1,4 @@
// FIR_COMPARISON
class C {
fun foo() {
val v = D::memberFun<caret>
@@ -79,7 +79,9 @@ private object KotlinFirCompletionProvider : CompletionProvider<CompletionParame
) {
val keywordContributor = FirKeywordCompletionContributor(basicContext)
val callableContributor = FirCallableCompletionContributor(basicContext)
val callableReferenceContributor = FirCallableReferenceCompletionContributor(basicContext)
val classifierContributor = FirClassifierCompletionContributor(basicContext)
val classifierReferenceContributor = FirClassifierReferenceCompletionContributor(basicContext)
val annotationsContributor = FirAnnotationCompletionContributor(basicContext)
val packageCompletionContributor = FirPackageCompletionContributor(basicContext)
val importDirectivePackageMembersCompletionContributor = FirImportDirectivePackageMembersCompletionContributor(basicContext)
@@ -137,6 +139,11 @@ private object KotlinFirCompletionProvider : CompletionProvider<CompletionParame
complete(whenWithSubjecConditionContributor, positionContext)
}
is FirCallableReferencePositionContext -> {
complete(callableReferenceContributor, positionContext)
complete(classifierReferenceContributor, positionContext)
}
is FirIncorrectPositionContext -> {
// do nothing, completion is not suposed to be called here
}
@@ -96,6 +96,13 @@ internal class FirWithSubjectEntryPositionContext(
val whenCondition: KtWhenCondition,
) : FirNameReferencePositionContext()
internal class FirCallableReferencePositionContext(
override val position: PsiElement,
override val reference: KtSimpleNameReference,
override val nameExpression: KtSimpleNameExpression,
override val explicitReceiver: KtExpression?
) : FirNameReferencePositionContext()
internal class FirUnknownPositionContext(
override val position: PsiElement
) : FirRawPositionCompletionContext()
@@ -130,6 +137,11 @@ internal object FirPositionCompletionContextDetector {
parent is KtUserType -> {
detectForTypeContext(parent, position, reference, nameExpression, explicitReceiver)
}
parent is KtCallableReferenceExpression -> {
FirCallableReferencePositionContext(
position, reference, nameExpression, parent.receiverExpression
)
}
parent is KtWhenCondition && parent.isConditionOnWhenWithSubject() -> {
FirWithSubjectEntryPositionContext(
position,
@@ -9,22 +9,27 @@ 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.completion.lookups.CallableInsertionStrategy
import org.jetbrains.kotlin.idea.completion.lookups.ImportStrategy
import org.jetbrains.kotlin.idea.fir.HLIndexHelper
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.*
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolWithVisibility
import org.jetbrains.kotlin.idea.frontend.api.types.KtClassType
import org.jetbrains.kotlin.idea.frontend.api.types.KtNonErrorClassType
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
import org.jetbrains.kotlin.psi.KtExpression
internal class FirCallableCompletionContributor(
internal open class FirCallableCompletionContributor(
basicContext: FirBasicCompletionContext,
) : FirContextCompletionContributorBase<FirNameReferencePositionContext>(basicContext) {
private val typeNamesProvider = TypeNamesProvider(indexHelper)
protected open val insertionStrategy: CallableInsertionStrategy = CallableInsertionStrategy.AS_CALL
private val shouldCompleteTopLevelCallablesFromIndex: Boolean
get() = prefixMatcher.prefix.isNotEmpty()
@@ -67,22 +72,22 @@ internal class FirCallableCompletionContributor(
val availableNonExtensions = collectNonExtensions(implicitScopes, visibilityChecker)
val extensionsWhichCanBeCalled = collectSuitableExtensions(implicitScopes, extensionChecker, visibilityChecker)
availableNonExtensions.forEach { addSymbolToCompletion(expectedType, it) }
extensionsWhichCanBeCalled.forEach { addSymbolToCompletion(expectedType, it) }
availableNonExtensions.forEach { addCallableSymbolToCompletion(expectedType, it, insertionStrategy = insertionStrategy) }
extensionsWhichCanBeCalled.forEach { addCallableSymbolToCompletion(expectedType, it, insertionStrategy = insertionStrategy) }
if (shouldCompleteTopLevelCallablesFromIndex) {
val topLevelCallables = indexHelper.getTopLevelCallables(scopeNameFilter)
topLevelCallables.asSequence()
.map { it.getSymbol() as KtCallableSymbol }
.filter { with(visibilityChecker) { isVisible(it) } }
.forEach { addSymbolToCompletion(expectedType, it) }
.forEach { addCallableSymbolToCompletion(expectedType, it, insertionStrategy = insertionStrategy) }
}
collectTopLevelExtensionsFromIndices(implicitReceiversTypes, extensionChecker, visibilityChecker)
.forEach { addSymbolToCompletion(expectedType, it) }
.forEach { addCallableSymbolToCompletion(expectedType, it, insertionStrategy = insertionStrategy) }
}
private fun KtAnalysisSession.collectDotCompletion(
protected open fun KtAnalysisSession.collectDotCompletion(
implicitScopes: KtCompositeScope,
explicitReceiver: KtExpression,
expectedType: KtType?,
@@ -111,11 +116,11 @@ internal class FirCallableCompletionContributor(
.filterNot { it.isExtension }
.filter { with(visibilityChecker) { isVisible(it) } }
.forEach { callable ->
addSymbolToCompletion(expectedType, callable)
addCallableSymbolToCompletion(expectedType, callable, insertionStrategy = insertionStrategy)
}
}
private fun KtAnalysisSession.collectDotCompletionForCallableReceiver(
protected fun KtAnalysisSession.collectDotCompletionForCallableReceiver(
implicitScopes: KtCompositeScope,
explicitReceiver: KtExpression,
expectedType: KtType?,
@@ -128,11 +133,11 @@ internal class FirCallableCompletionContributor(
val nonExtensionMembers = collectNonExtensions(possibleReceiverScope, visibilityChecker)
val extensionNonMembers = collectSuitableExtensions(implicitScopes, extensionChecker, visibilityChecker)
nonExtensionMembers.forEach { addSymbolToCompletion(expectedType, it) }
extensionNonMembers.forEach { addSymbolToCompletion(expectedType, it) }
nonExtensionMembers.forEach { addCallableSymbolToCompletion(expectedType, it, insertionStrategy = insertionStrategy) }
extensionNonMembers.forEach { addCallableSymbolToCompletion(expectedType, it, insertionStrategy = insertionStrategy) }
collectTopLevelExtensionsFromIndices(listOf(typeOfPossibleReceiver), extensionChecker, visibilityChecker)
.forEach { addSymbolToCompletion(expectedType, it) }
.forEach { addCallableSymbolToCompletion(expectedType, it, insertionStrategy = insertionStrategy) }
}
private fun KtAnalysisSession.collectTopLevelExtensionsFromIndices(
@@ -168,6 +173,36 @@ internal class FirCallableCompletionContributor(
implicitReceiversTypes.flatMapTo(hashSetOf()) { with(typeNamesProvider) { findAllNames(it) } }
}
internal class FirCallableReferenceCompletionContributor(
basicContext: FirBasicCompletionContext
) : FirCallableCompletionContributor(basicContext) {
override val insertionStrategy: CallableInsertionStrategy = CallableInsertionStrategy.AS_IDENTIFIER
override fun KtAnalysisSession.collectDotCompletion(
implicitScopes: KtCompositeScope,
explicitReceiver: KtExpression,
expectedType: KtType?,
extensionChecker: ExtensionApplicabilityChecker,
visibilityChecker: CompletionVisibilityChecker
) {
when (val resolved = explicitReceiver.reference()?.resolveToSymbol()) {
is KtPackageSymbol -> return
is KtNamedClassOrObjectSymbol -> {
resolved.getMemberScope()
.getCallableSymbols(scopeNameFilter)
.filter { with(visibilityChecker) { isVisible(it) } }
.forEach { symbol ->
addCallableSymbolToCompletion(expectedType = null, symbol, insertionStrategy = insertionStrategy)
}
}
else -> {
collectDotCompletionForCallableReceiver(implicitScopes, explicitReceiver, expectedType, extensionChecker, visibilityChecker)
}
}
}
}
private class TypeNamesProvider(private val indexHelper: HLIndexHelper) {
fun KtAnalysisSession.findAllNames(type: KtType): Set<String> {
if (type !is KtNonErrorClassType) return emptySet()
@@ -14,6 +14,8 @@ import org.jetbrains.kotlin.idea.frontend.api.symbols.*
import org.jetbrains.kotlin.idea.frontend.api.types.KtClassType
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.idea.completion.contributors.helpers.FirClassifierProvider.getAvailableClassifiersCurrentScope
import org.jetbrains.kotlin.idea.completion.lookups.ImportStrategy
import org.jetbrains.kotlin.idea.completion.lookups.detectImportStrategy
import org.jetbrains.kotlin.idea.frontend.api.types.KtNonErrorClassType
internal open class FirClassifierCompletionContributor(
@@ -22,6 +24,9 @@ internal open class FirClassifierCompletionContributor(
protected open fun KtAnalysisSession.filterClassifiers(classifierSymbol: KtClassifierSymbol): Boolean = true
protected open fun KtAnalysisSession.getImportingStrategy(classifierSymbol: KtClassifierSymbol): ImportStrategy =
detectImportStrategy(classifierSymbol)
override fun KtAnalysisSession.complete(positionContext: FirNameReferencePositionContext) {
val visibilityChecker = CompletionVisibilityChecker.create(basicContext, positionContext)
@@ -45,7 +50,7 @@ internal open class FirClassifierCompletionContributor(
.getClassifierSymbols(scopeNameFilter)
.filter { filterClassifiers(it) }
.filter { with(visibilityChecker) { isVisible(it) } }
.forEach { addClassifierSymbolToCompletion(it, insertFqName = false) }
.forEach { addClassifierSymbolToCompletion(it, ImportStrategy.DoNothing) }
}
private fun KtAnalysisSession.completeWithoutReceiver(
@@ -60,7 +65,9 @@ internal open class FirClassifierCompletionContributor(
visibilityChecker
)
.filter { filterClassifiers(it) }
.forEach { addClassifierSymbolToCompletion(it, insertFqName = true) }
.forEach { classifierSymbol ->
addClassifierSymbolToCompletion(classifierSymbol, getImportingStrategy(classifierSymbol))
}
}
}
@@ -87,4 +94,12 @@ internal class FirAnnotationCompletionContributor(
expendedClass?.let { filterClassifiers(it) } == true
}
}
}
}
internal class FirClassifierReferenceCompletionContributor(
basicContext: FirBasicCompletionContext
) : FirClassifierCompletionContributor(basicContext) {
override fun KtAnalysisSession.getImportingStrategy(classifierSymbol: KtClassifierSymbol): ImportStrategy =
ImportStrategy.DoNothing
}
@@ -6,15 +6,15 @@
package org.jetbrains.kotlin.idea.completion.contributors
import com.intellij.codeInsight.completion.CompletionParameters
import com.intellij.codeInsight.completion.CompletionResultSet
import com.intellij.codeInsight.completion.PrefixMatcher
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.idea.completion.LookupElementSink
import org.jetbrains.kotlin.idea.completion.context.FirBasicCompletionContext
import org.jetbrains.kotlin.idea.completion.context.FirRawPositionCompletionContext
import org.jetbrains.kotlin.idea.completion.lookups.CallableImportStrategy
import org.jetbrains.kotlin.idea.completion.lookups.ImportStrategy
import org.jetbrains.kotlin.idea.completion.lookups.CallableInsertionStrategy
import org.jetbrains.kotlin.idea.completion.lookups.detectImportStrategy
import org.jetbrains.kotlin.idea.completion.lookups.factories.KotlinFirLookupElementFactory
import org.jetbrains.kotlin.idea.completion.weighers.Weighers
import org.jetbrains.kotlin.idea.fir.HLIndexHelper
@@ -50,16 +50,18 @@ internal abstract class FirCompletionContributorBase<C : FirRawPositionCompletio
if (symbol !is KtNamedSymbol) return
with(lookupElementFactory) {
createLookupElement(symbol)
.let { applyWeighers(it, symbol, expectedType) }
.let(sink::addElement)
}
}
protected fun KtAnalysisSession.addClassifierSymbolToCompletion(symbol: KtClassifierSymbol, insertFqName: Boolean) {
protected fun KtAnalysisSession.addClassifierSymbolToCompletion(
symbol: KtClassifierSymbol,
importingStrategy: ImportStrategy = detectImportStrategy(symbol)
) {
if (symbol !is KtNamedSymbol) return
val lookup = with(lookupElementFactory) {
when (symbol) {
is KtClassLikeSymbol -> createLookupElementForClassLikeSymbol(symbol, insertFqName)
is KtClassLikeSymbol -> createLookupElementForClassLikeSymbol(symbol, importingStrategy)
is KtTypeParameterSymbol -> createLookupElement(symbol)
}
} ?: return
@@ -67,14 +69,16 @@ internal abstract class FirCompletionContributorBase<C : FirRawPositionCompletio
}
protected fun KtAnalysisSession.addCallableSymbolToCompletion(
expectedType: KtType?,
symbol: KtCallableSymbol,
importingStrategy: CallableImportStrategy,
importingStrategy: ImportStrategy = detectImportStrategy(symbol),
insertionStrategy: CallableInsertionStrategy,
) {
if (symbol !is KtNamedSymbol) return
val lookup = with(lookupElementFactory) {
createCallableLookupElement(symbol, importingStrategy, insertionStrategy)
} ?: return
}
applyWeighers(lookup, symbol, expectedType)
sink.addElement(lookup)
}
@@ -9,7 +9,7 @@ import org.jetbrains.kotlin.idea.completion.checkers.CompletionVisibilityChecker
import org.jetbrains.kotlin.idea.completion.context.FirBasicCompletionContext
import org.jetbrains.kotlin.idea.completion.context.FirImportDirectivePositionContext
import org.jetbrains.kotlin.idea.completion.contributors.helpers.getStaticScope
import org.jetbrains.kotlin.idea.completion.lookups.CallableImportStrategy
import org.jetbrains.kotlin.idea.completion.lookups.ImportStrategy
import org.jetbrains.kotlin.idea.completion.lookups.CallableInsertionStrategy
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
@@ -23,14 +23,15 @@ internal class FirImportDirectivePackageMembersCompletionContributor(
scope.getClassifierSymbols(scopeNameFilter)
.filter { with(visibilityChecker) { isVisible(it) } }
.forEach { addClassifierSymbolToCompletion(it, insertFqName = false) }
.forEach { addClassifierSymbolToCompletion(it, ImportStrategy.DoNothing) }
scope.getCallableSymbols(scopeNameFilter)
.filter { with(visibilityChecker) { isVisible(it) } }
.forEach {
addCallableSymbolToCompletion(
expectedType = null,
it,
importingStrategy = CallableImportStrategy.DoNothing,
importingStrategy = ImportStrategy.DoNothing,
insertionStrategy = CallableInsertionStrategy.AS_IDENTIFIER
)
}
@@ -1,57 +0,0 @@
/*
* 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.lookups
import org.jetbrains.kotlin.idea.frontend.api.analyse
import org.jetbrains.kotlin.idea.frontend.api.fir.utils.addImportToFile
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtCallableSymbol
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtFunctionSymbol
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtKotlinPropertySymbol
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtPossibleMemberSymbol
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.psi.KtFile
internal sealed class CallableImportStrategy {
object DoNothing : CallableImportStrategy()
data class AddImport(val nameToImport: CallableId) : CallableImportStrategy()
data class InsertFqNameAndShorten(val callableId: CallableId) : CallableImportStrategy()
}
internal fun detectImportStrategy(symbol: KtCallableSymbol): CallableImportStrategy {
if (symbol !is KtPossibleMemberSymbol || symbol.dispatchType != null) return CallableImportStrategy.DoNothing
val propertyId = symbol.callableIdIfNonLocal ?: return CallableImportStrategy.DoNothing
return if (symbol.isExtension) {
CallableImportStrategy.AddImport(propertyId)
} else {
CallableImportStrategy.InsertFqNameAndShorten(propertyId)
}
}
internal fun addCallableImportIfRequired(targetFile: KtFile, nameToImport: CallableId) {
if (!alreadyHasImport(targetFile, nameToImport)) {
addImportToFile(targetFile.project, targetFile, nameToImport)
}
}
private fun alreadyHasImport(file: KtFile, nameToImport: CallableId): Boolean {
if (file.importDirectives.any { it.importPath?.fqName == nameToImport.asSingleFqName() }) return true
withAllowedResolve {
analyse(file) {
val scopes = file.getScopeContextForFile().scopes
if (!scopes.mayContainName(nameToImport.callableName)) return false
return scopes
.getCallableSymbols { it == nameToImport.callableName }
.any {
it is KtKotlinPropertySymbol && it.callableIdIfNonLocal == nameToImport ||
it is KtFunctionSymbol && it.callableIdIfNonLocal == nameToImport
}
}
}
}
@@ -0,0 +1,75 @@
/*
* 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.lookups
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
import org.jetbrains.kotlin.idea.frontend.api.analyse
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.KtPossibleMemberSymbol
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtFile
internal sealed class ImportStrategy {
object DoNothing : ImportStrategy()
data class AddImport(val nameToImport: FqName) : ImportStrategy()
data class InsertFqNameAndShorten(val fqName: FqName) : ImportStrategy()
}
internal fun KtAnalysisSession.detectImportStrategy(symbol: KtSymbol): ImportStrategy = when (symbol) {
is KtCallableSymbol -> detectImportStrategyForCallableSymbol(symbol)
is KtClassLikeSymbol -> detectImportStrategyForClassLikeSymbol(symbol)
else -> ImportStrategy.DoNothing
}
internal fun KtAnalysisSession.detectImportStrategyForCallableSymbol(symbol: KtCallableSymbol): ImportStrategy {
if (symbol !is KtPossibleMemberSymbol || symbol.dispatchType != null) return ImportStrategy.DoNothing
val propertyId = symbol.callableIdIfNonLocal?.asSingleFqName() ?: return ImportStrategy.DoNothing
return if (symbol.isExtension) {
ImportStrategy.AddImport(propertyId)
} else {
ImportStrategy.InsertFqNameAndShorten(propertyId)
}
}
internal fun KtAnalysisSession.detectImportStrategyForClassLikeSymbol(symbol: KtClassLikeSymbol): ImportStrategy {
val classId = symbol.classIdIfNonLocal ?: return ImportStrategy.DoNothing
return ImportStrategy.InsertFqNameAndShorten(classId.asSingleFqName())
}
internal fun addCallableImportIfRequired(targetFile: KtFile, nameToImport: FqName) {
if (!alreadyHasImport(targetFile, nameToImport)) {
addImportToFile(targetFile.project, targetFile, nameToImport)
}
}
private fun alreadyHasImport(file: KtFile, nameToImport: FqName): Boolean {
if (file.importDirectives.any { it.importPath?.fqName == nameToImport }) return true
withAllowedResolve {
analyse(file) {
val scopes = file.getScopeContextForFile().scopes
if (!scopes.mayContainName(nameToImport.shortName())) return false
val anyCallableSymbolMatches = scopes
.getCallableSymbols { it == nameToImport.shortName() }
.any { callable ->
val callableFqName = callable.callableIdIfNonLocal?.asSingleFqName()
callable is KtKotlinPropertySymbol && callableFqName == nameToImport ||
callable is KtFunctionSymbol && callableFqName == nameToImport
}
if (anyCallableSymbolMatches) return true
return scopes.getClassifierSymbols { it == nameToImport.shortName() }.any { classifier ->
val classId = (classifier as? KtClassLikeSymbol)?.classIdIfNonLocal
classId?.asSingleFqName() == nameToImport
}
}
}
}
@@ -10,6 +10,8 @@ import com.intellij.codeInsight.completion.InsertionContext
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInsight.lookup.LookupElementBuilder
import com.intellij.openapi.util.TextRange
import org.jetbrains.kotlin.idea.completion.lookups.*
import org.jetbrains.kotlin.idea.completion.lookups.ImportStrategy
import org.jetbrains.kotlin.idea.completion.lookups.KotlinLookupObject
import org.jetbrains.kotlin.idea.completion.lookups.shortenReferencesForFirCompletion
import org.jetbrains.kotlin.idea.completion.lookups.withSymbolInfo
@@ -21,10 +23,13 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.renderer.render
class ClassLookupElementFactory {
fun KtAnalysisSession.createLookup(symbol: KtClassLikeSymbol, insertFqName: Boolean = true): LookupElementBuilder {
internal class ClassLookupElementFactory {
fun KtAnalysisSession.createLookup(
symbol: KtClassLikeSymbol,
importingStrategy: ImportStrategy = detectImportStrategy(symbol)
): LookupElementBuilder {
val name = symbol.nameOrAnonymous
return LookupElementBuilder.create(ClassifierLookupObject(name, symbol.classIdIfNonLocal, insertFqName), name.asString())
return LookupElementBuilder.create(ClassifierLookupObject(name, importingStrategy), name.asString())
.withInsertHandler(ClassifierInsertionHandler)
.let { withSymbolInfo(symbol, it) }
}
@@ -33,9 +38,8 @@ class ClassLookupElementFactory {
private data class ClassifierLookupObject(
override val shortName: Name,
val classId: ClassId?,
val insertFqName: Boolean
) : KotlinLookupObject {}
val importingStrategy: ImportStrategy
) : KotlinLookupObject
/**
* The simplest implementation of the insertion handler for a classifiers.
@@ -45,8 +49,8 @@ private object ClassifierInsertionHandler : InsertHandler<LookupElement> {
val targetFile = context.file as? KtFile ?: return
val lookupObject = item.`object` as ClassifierLookupObject
if (lookupObject.classId != null && lookupObject.insertFqName) {
val fqName = lookupObject.classId.asSingleFqName()
if (lookupObject.importingStrategy is ImportStrategy.InsertFqNameAndShorten) {
val fqName = lookupObject.importingStrategy.fqName
context.document.replaceString(context.startOffset, context.tailOffset, fqName.render())
context.commitDocument()
@@ -10,18 +10,15 @@ import com.intellij.codeInsight.completion.InsertionContext
import com.intellij.codeInsight.lookup.Lookup
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.util.TextRange
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.completion.lookups.*
import org.jetbrains.kotlin.idea.completion.lookups.CallableImportStrategy
import org.jetbrains.kotlin.idea.completion.lookups.ImportStrategy
import org.jetbrains.kotlin.idea.completion.lookups.CallableInsertionStrategy
import org.jetbrains.kotlin.idea.completion.lookups.CompletionShortNamesRenderer
import org.jetbrains.kotlin.idea.completion.lookups.QuotedNamesAwareInsertionHandler
import org.jetbrains.kotlin.idea.completion.lookups.addCallableImportIfRequired
import org.jetbrains.kotlin.idea.completion.lookups.shortenReferencesForFirCompletion
import org.jetbrains.kotlin.idea.core.asFqNameWithRootPrefixIfNeeded
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
import org.jetbrains.kotlin.idea.frontend.api.symbols.*
import org.jetbrains.kotlin.lexer.KtTokens
@@ -34,11 +31,12 @@ import org.jetbrains.kotlin.idea.completion.lookups.TailTextProvider.getTailText
import org.jetbrains.kotlin.idea.completion.lookups.TailTextProvider.insertLambdaBraces
import org.jetbrains.kotlin.idea.completion.lookups.CompletionShortNamesRenderer.renderFunctionParameters
import org.jetbrains.kotlin.idea.completion.lookups.CompletionShortNamesRenderer.TYPE_RENDERING_OPTIONS
import org.jetbrains.kotlin.idea.core.withRootPrefixIfNeeded
internal class FunctionLookupElementFactory {
fun KtAnalysisSession.createLookup(
symbol: KtFunctionSymbol,
importStrategy: CallableImportStrategy,
importStrategy: ImportStrategy,
insertionStrategy: CallableInsertionStrategy
): LookupElementBuilder {
val lookupObject = FunctionLookupObject(
@@ -68,7 +66,7 @@ internal class FunctionLookupElementFactory {
*/
private data class FunctionLookupObject(
override val shortName: Name,
val importStrategy: CallableImportStrategy,
val importStrategy: ImportStrategy,
val inputValueArguments: Boolean,
val insertEmptyLambda: Boolean,
// for distinction between different overloads
@@ -167,11 +165,11 @@ private object FunctionInsertionHandler : QuotedNamesAwareInsertionHandler() {
val element = context.file.findElementAt(startOffset) ?: return
val importStrategy = lookupObject.importStrategy
if (importStrategy is CallableImportStrategy.InsertFqNameAndShorten) {
if (importStrategy is ImportStrategy.InsertFqNameAndShorten) {
context.document.replaceString(
context.startOffset,
context.tailOffset,
importStrategy.callableId.asFqNameWithRootPrefixIfNeeded().render()
importStrategy.fqName.withRootPrefixIfNeeded().render()
)
context.commitDocument()
@@ -183,7 +181,7 @@ private object FunctionInsertionHandler : QuotedNamesAwareInsertionHandler() {
addArguments(context, element, lookupObject)
context.commitDocument()
if (importStrategy is CallableImportStrategy.AddImport) {
if (importStrategy is ImportStrategy.AddImport) {
addCallableImportIfRequired(targetFile, importStrategy.nameToImport)
}
}
@@ -7,7 +7,7 @@ package org.jetbrains.kotlin.idea.completion.lookups.factories
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInsight.lookup.LookupElementBuilder
import org.jetbrains.kotlin.idea.completion.lookups.CallableImportStrategy
import org.jetbrains.kotlin.idea.completion.lookups.ImportStrategy
import org.jetbrains.kotlin.idea.completion.lookups.CallableInsertionStrategy
import org.jetbrains.kotlin.idea.completion.lookups.detectImportStrategy
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
@@ -37,7 +37,7 @@ internal class KotlinFirLookupElementFactory {
fun KtAnalysisSession.createCallableLookupElement(
symbol: KtCallableSymbol,
importingStrategy: CallableImportStrategy,
importingStrategy: ImportStrategy,
insertionStrategy: CallableInsertionStrategy,
): LookupElementBuilder {
return when (symbol) {
@@ -50,9 +50,12 @@ internal class KotlinFirLookupElementFactory {
fun createPackagePartLookupElement(packagePartFqName: FqName): LookupElement =
packagePartLookupElementFactory.createPackagePartLookupElement(packagePartFqName)
fun KtAnalysisSession.createLookupElementForClassLikeSymbol(symbol: KtClassLikeSymbol, insertFqName: Boolean = true): LookupElement? {
fun KtAnalysisSession.createLookupElementForClassLikeSymbol(
symbol: KtClassLikeSymbol,
importingStrategy: ImportStrategy = detectImportStrategy(symbol)
): LookupElement? {
if (symbol !is KtNamedSymbol) return null
return with(classLookupElementFactory) { createLookup(symbol, insertFqName) }
return with(classLookupElementFactory) { createLookup(symbol, importingStrategy) }
}
}
@@ -11,12 +11,12 @@ import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInsight.lookup.LookupElementBuilder
import com.intellij.openapi.util.TextRange
import org.jetbrains.kotlin.idea.completion.lookups.*
import org.jetbrains.kotlin.idea.completion.lookups.CallableImportStrategy
import org.jetbrains.kotlin.idea.completion.lookups.ImportStrategy
import org.jetbrains.kotlin.idea.completion.lookups.TailTextProvider.getTailText
import org.jetbrains.kotlin.idea.completion.lookups.addCallableImportIfRequired
import org.jetbrains.kotlin.idea.completion.lookups.detectImportStrategy
import org.jetbrains.kotlin.idea.completion.lookups.shortenReferencesForFirCompletion
import org.jetbrains.kotlin.idea.core.asFqNameWithRootPrefixIfNeeded
import org.jetbrains.kotlin.idea.core.withRootPrefixIfNeeded
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
import org.jetbrains.kotlin.idea.frontend.api.components.KtTypeRendererOptions
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtSyntheticJavaPropertySymbol
@@ -28,7 +28,7 @@ import org.jetbrains.kotlin.renderer.render
internal class VariableLookupElementFactory {
fun KtAnalysisSession.createLookup(
symbol: KtVariableLikeSymbol,
importStrategy: CallableImportStrategy = detectImportStrategy(symbol)
importStrategy: ImportStrategy = detectImportStrategy(symbol)
): LookupElementBuilder {
val lookupObject = VariableLookupObject(
symbol.name,
@@ -63,7 +63,7 @@ internal class VariableLookupElementFactory {
*/
private data class VariableLookupObject(
override val shortName: Name,
val importStrategy: CallableImportStrategy,
val importStrategy: ImportStrategy,
override val renderedReceiverType: String?,
) : KotlinCallableLookupObject()
@@ -74,22 +74,22 @@ private object VariableInsertionHandler : InsertHandler<LookupElement> {
val lookupObject = item.`object` as VariableLookupObject
when (val importStrategy = lookupObject.importStrategy) {
is CallableImportStrategy.AddImport -> {
is ImportStrategy.AddImport -> {
addCallableImportIfRequired(targetFile, importStrategy.nameToImport)
}
is CallableImportStrategy.InsertFqNameAndShorten -> {
is ImportStrategy.InsertFqNameAndShorten -> {
context.document.replaceString(
context.startOffset,
context.tailOffset,
importStrategy.callableId.asFqNameWithRootPrefixIfNeeded().render()
importStrategy.fqName.withRootPrefixIfNeeded().render()
)
context.commitDocument()
shortenReferencesForFirCompletion(targetFile, TextRange(context.startOffset, context.tailOffset))
}
is CallableImportStrategy.DoNothing -> {
is ImportStrategy.DoNothing -> {
}
}
}
@@ -47,6 +47,17 @@ interface KtScope : ValidityTokenOwner {
}
}
/**
* Return a sequence of all [KtSymbol] which current scope contain if declaration name matches [nameFilter]
* Constructors are not included here as they have no name
*/
fun getAllNamedSymbols(nameFilter: KtScopeNameFilter = { true }): Sequence<KtSymbol> = withValidityAssertion {
sequence {
yieldAll(getCallableSymbols(nameFilter))
yieldAll(getClassifierSymbols(nameFilter))
}
}
/**
* Return a sequence of [KtCallableSymbol] which current scope contain if declaration name matches [nameFilter]
*/