FIR IDE: introduce import statement completion
This commit is contained in:
committed by
TeamCityServer
parent
c998d0f026
commit
943ae108f5
@@ -8,14 +8,40 @@ package org.jetbrains.kotlin.idea.stubindex
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.newvfs.persistent.PersistentFS
|
||||
import com.intellij.openapi.vfs.newvfs.persistent.PersistentFSImpl
|
||||
import com.intellij.psi.JavaPsiFacade
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.stubs.StubIndex
|
||||
import org.jetbrains.kotlin.idea.search.getKotlinFqName
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.platform.TargetPlatform
|
||||
import org.jetbrains.kotlin.platform.jvm.isJvm
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
|
||||
object PackageIndexUtil {
|
||||
fun getJavaAndKotlinSubPackageFqNames(
|
||||
packageFqName: FqName,
|
||||
scope: GlobalSearchScope,
|
||||
project: Project,
|
||||
targetPlatform: TargetPlatform,
|
||||
nameFilter: (Name) -> Boolean
|
||||
) = sequence {
|
||||
yieldAll(getSubPackageFqNames(packageFqName, scope, project, nameFilter))
|
||||
|
||||
if (targetPlatform.isJvm()) {
|
||||
val javaPackage = JavaPsiFacade.getInstance(project).findPackage(packageFqName.asString())
|
||||
if (javaPackage != null) {
|
||||
for (psiPackage in javaPackage.getSubPackages(scope)) {
|
||||
val fqName = psiPackage.getKotlinFqName() ?: continue
|
||||
if (nameFilter(fqName.shortName())) {
|
||||
yield(fqName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getSubPackageFqNames(
|
||||
packageFqName: FqName,
|
||||
|
||||
+6
-3
@@ -10,8 +10,6 @@ import com.intellij.openapi.components.service
|
||||
import com.intellij.patterns.PlatformPatterns
|
||||
import com.intellij.patterns.PsiJavaPatterns
|
||||
import com.intellij.util.ProcessingContext
|
||||
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
|
||||
import org.jetbrains.kotlin.idea.completion.checkers.CompletionVisibilityChecker
|
||||
import org.jetbrains.kotlin.idea.completion.context.*
|
||||
import org.jetbrains.kotlin.idea.completion.context.FirBasicCompletionContext
|
||||
import org.jetbrains.kotlin.idea.completion.context.FirExpressionNameReferencePositionContext
|
||||
@@ -25,7 +23,6 @@ import org.jetbrains.kotlin.idea.completion.contributors.FirClassifierCompletion
|
||||
import org.jetbrains.kotlin.idea.completion.contributors.FirKeywordCompletionContributor
|
||||
import org.jetbrains.kotlin.idea.completion.contributors.complete
|
||||
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.util.originalKtFile
|
||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
@@ -70,6 +67,7 @@ private object KotlinFirCompletionProvider : CompletionProvider<CompletionParame
|
||||
val classifierContributor = FirClassifierCompletionContributor(basicContext)
|
||||
val annotationsContributor = FirAnnotationCompletionContributor(basicContext)
|
||||
val packageCompletionContributor = FirPackageCompletionContributor(basicContext)
|
||||
val importDirectivePackageMembersCompletionContributor = FirImportDirectivePackageMembersCompletionContributor(basicContext)
|
||||
|
||||
when (positionContext) {
|
||||
is FirExpressionNameReferencePositionContext -> {
|
||||
@@ -95,6 +93,11 @@ private object KotlinFirCompletionProvider : CompletionProvider<CompletionParame
|
||||
complete(FirSuperEntryContributor(basicContext), positionContext)
|
||||
}
|
||||
|
||||
is FirImportDirectivePositionContext -> {
|
||||
complete(packageCompletionContributor, positionContext)
|
||||
complete(importDirectivePackageMembersCompletionContributor, positionContext)
|
||||
}
|
||||
|
||||
is FirUnknownPositionContext -> {
|
||||
complete(keywordContributor, positionContext)
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.*
|
||||
import javax.swing.Icon
|
||||
|
||||
internal object KotlinFirIconProvider {
|
||||
fun getIconFor(symbol: KtNamedSymbol): Icon? {
|
||||
fun getIconFor(symbol: KtSymbol): Icon? {
|
||||
if (symbol is KtFunctionSymbol) {
|
||||
val isAbstract = symbol.modality == Modality.ABSTRACT
|
||||
|
||||
|
||||
+91
-57
@@ -11,10 +11,12 @@ 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.icons.AllIcons
|
||||
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.contributors.helpers.addDotAndInvokeCompletion
|
||||
import org.jetbrains.kotlin.idea.completion.handlers.isTextAt
|
||||
import org.jetbrains.kotlin.idea.core.asFqNameWithRootPrefixIfNeeded
|
||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||
@@ -27,6 +29,7 @@ import org.jetbrains.kotlin.idea.frontend.api.tokens.HackToForceAllowRunningAnal
|
||||
import org.jetbrains.kotlin.idea.frontend.api.tokens.hackyAllowRunningOnEdt
|
||||
import org.jetbrains.kotlin.idea.frontend.api.types.KtFunctionalType
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.miniStdLib.letIf
|
||||
import org.jetbrains.kotlin.name.*
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtTypeArgumentList
|
||||
@@ -40,38 +43,50 @@ internal class KotlinFirLookupElementFactory {
|
||||
private val typeParameterLookupElementFactory = TypeParameterLookupElementFactory()
|
||||
|
||||
fun KtAnalysisSession.createLookupElement(symbol: KtNamedSymbol): LookupElement? {
|
||||
val elementBuilder = when (symbol) {
|
||||
is KtFunctionSymbol -> with(functionLookupElementFactory) { createLookup(symbol) }
|
||||
is KtVariableLikeSymbol -> with(variableLookupElementFactory) { createLookup(symbol) }
|
||||
is KtClassLikeSymbol -> classLookupElementFactory.createLookup(symbol)
|
||||
is KtTypeParameterSymbol -> typeParameterLookupElementFactory.createLookup(symbol)
|
||||
return when (symbol) {
|
||||
is KtCallableSymbol -> createCallableLookupElement(symbol, importingStrategy = detectImportStrategy(symbol), CallableInsertionStrategy.AS_CALL)
|
||||
is KtClassLikeSymbol -> with(classLookupElementFactory) { createLookup(symbol) }
|
||||
is KtTypeParameterSymbol -> with(typeParameterLookupElementFactory) { createLookup(symbol) }
|
||||
else -> throw IllegalArgumentException("Cannot create a lookup element for $symbol")
|
||||
} ?: return null
|
||||
|
||||
return elementBuilder
|
||||
.withPsiElement(symbol.psi) // TODO check if it is a heavy operation and should be postponed
|
||||
.withIcon(KotlinFirIconProvider.getIconFor(symbol))
|
||||
}
|
||||
}
|
||||
|
||||
fun createPackageLookupElement(packageFqName: FqName): LookupElement {
|
||||
return LookupElementBuilder.create(PackageLookupObject(packageFqName), packageFqName.shortName().asString())
|
||||
.withInsertHandler(QuotedNamesAwareInsertionHandler())
|
||||
.let {
|
||||
if (!packageFqName.parent().isRoot) {
|
||||
it.appendTailText(" (${packageFqName.asString()})", true)
|
||||
} else it
|
||||
fun KtAnalysisSession.createCallableLookupElement(
|
||||
symbol: KtCallableSymbol,
|
||||
importingStrategy: CallableImportStrategy,
|
||||
insertionStrategy: CallableInsertionStrategy,
|
||||
): LookupElementBuilder? {
|
||||
return when (symbol) {
|
||||
is KtFunctionSymbol -> with(functionLookupElementFactory) { createLookup(symbol, importingStrategy, insertionStrategy) }
|
||||
is KtVariableLikeSymbol -> with(variableLookupElementFactory) { createLookup(symbol, importingStrategy) }
|
||||
else -> throw IllegalArgumentException("Cannot create a lookup element for $symbol")
|
||||
}
|
||||
}
|
||||
|
||||
fun createPackagePartLookupElement(packagePartFqName: FqName): LookupElement {
|
||||
val shortName = packagePartFqName.shortName()
|
||||
return LookupElementBuilder.create(PackagePartLookupObject(shortName), "${shortName.render()}.")
|
||||
.withInsertHandler(PackagePartInsertionHandler)
|
||||
.withIcon(AllIcons.Nodes.Package)
|
||||
.letIf(!packagePartFqName.parent().isRoot) {
|
||||
it.appendTailText("(${packagePartFqName.asString()})", true)
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
return with(classLookupElementFactory) { createLookup(symbol, insertFqName) }
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class CallableImportStrategy {
|
||||
private fun KtAnalysisSession.withSymbolInfo(
|
||||
symbol: KtSymbol,
|
||||
elementBuilder: LookupElementBuilder
|
||||
): LookupElementBuilder = elementBuilder
|
||||
.withPsiElement(symbol.psi) // TODO check if it is a heavy operation and should be postponed
|
||||
.withIcon(KotlinFirIconProvider.getIconFor(symbol))
|
||||
|
||||
internal sealed class CallableImportStrategy {
|
||||
object DoNothing : CallableImportStrategy()
|
||||
data class AddImport(val nameToImport: CallableId) : CallableImportStrategy()
|
||||
data class InsertFqNameAndShorten(val callableId: CallableId) : CallableImportStrategy()
|
||||
@@ -86,14 +101,12 @@ private interface KotlinLookupObject {
|
||||
val shortName: Name
|
||||
}
|
||||
|
||||
private data class PackageLookupObject(
|
||||
val packageFqName: FqName,
|
||||
) : KotlinLookupObject {
|
||||
override val shortName: Name = packageFqName.shortName()
|
||||
}
|
||||
private data class PackagePartLookupObject(
|
||||
override val shortName: Name,
|
||||
) : KotlinLookupObject
|
||||
|
||||
|
||||
private data class ClassifierLookupObject(override val shortName: Name, val classId: ClassId?, val insertFqName: Boolean) : 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.
|
||||
@@ -116,30 +129,37 @@ private data class VariableLookupObject(
|
||||
) : KotlinLookupObject
|
||||
|
||||
class ClassLookupElementFactory {
|
||||
fun createLookup(symbol: KtClassLikeSymbol, insertFqName: Boolean = true): LookupElementBuilder {
|
||||
fun KtAnalysisSession.createLookup(symbol: KtClassLikeSymbol, insertFqName: Boolean = true): LookupElementBuilder {
|
||||
val name = symbol.nameOrAnonymous
|
||||
return LookupElementBuilder.create(ClassifierLookupObject(name, symbol.classIdIfNonLocal, insertFqName), name.asString())
|
||||
.withInsertHandler(ClassifierInsertionHandler)
|
||||
.let { withSymbolInfo(symbol, it) }
|
||||
}
|
||||
}
|
||||
|
||||
private class TypeParameterLookupElementFactory {
|
||||
fun createLookup(symbol: KtTypeParameterSymbol): LookupElementBuilder {
|
||||
return LookupElementBuilder.create(UniqueLookupObject(), symbol.name.asString())
|
||||
fun KtAnalysisSession.createLookup(symbol: KtTypeParameterSymbol): LookupElementBuilder {
|
||||
return LookupElementBuilder
|
||||
.create(UniqueLookupObject(), symbol.name.asString())
|
||||
.let { withSymbolInfo(symbol, it) }
|
||||
}
|
||||
}
|
||||
|
||||
private class VariableLookupElementFactory {
|
||||
fun KtAnalysisSession.createLookup(symbol: KtVariableLikeSymbol): LookupElementBuilder {
|
||||
fun KtAnalysisSession.createLookup(
|
||||
symbol: KtVariableLikeSymbol,
|
||||
importStrategy: CallableImportStrategy = detectImportStrategy(symbol)
|
||||
): LookupElementBuilder {
|
||||
val lookupObject = VariableLookupObject(
|
||||
symbol.name,
|
||||
importStrategy = detectImportStrategy(symbol)
|
||||
importStrategy = importStrategy
|
||||
)
|
||||
|
||||
return LookupElementBuilder.create(lookupObject, symbol.name.asString())
|
||||
.withTypeText(symbol.annotatedType.type.render(WITH_TYPE_RENDERING_OPTIONS))
|
||||
.markIfSyntheticJavaProperty(symbol)
|
||||
.withInsertHandler(VariableInsertionHandler)
|
||||
.let { withSymbolInfo(symbol, it) }
|
||||
}
|
||||
|
||||
private fun LookupElementBuilder.markIfSyntheticJavaProperty(symbol: KtVariableLikeSymbol): LookupElementBuilder = when (symbol) {
|
||||
@@ -154,35 +174,50 @@ private class VariableLookupElementFactory {
|
||||
|
||||
private fun buildSyntheticPropertyTailText(getterName: String, setterName: String?): String =
|
||||
if (setterName != null) "$getterName()/$setterName()" else "$getterName()"
|
||||
}
|
||||
|
||||
private fun detectImportStrategy(symbol: KtVariableLikeSymbol): CallableImportStrategy {
|
||||
if (symbol !is KtKotlinPropertySymbol || symbol.dispatchType != null) return CallableImportStrategy.DoNothing
|
||||
private fun detectImportStrategy(symbol: KtCallableSymbol): CallableImportStrategy {
|
||||
if (symbol !is KtKotlinPropertySymbol || symbol.dispatchType != null) return CallableImportStrategy.DoNothing
|
||||
|
||||
val propertyId = symbol.callableIdIfNonLocal ?: return CallableImportStrategy.DoNothing
|
||||
val propertyId = symbol.callableIdIfNonLocal ?: return CallableImportStrategy.DoNothing
|
||||
|
||||
return if (symbol.isExtension) {
|
||||
CallableImportStrategy.AddImport(propertyId)
|
||||
} else {
|
||||
CallableImportStrategy.InsertFqNameAndShorten(propertyId)
|
||||
}
|
||||
return if (symbol.isExtension) {
|
||||
CallableImportStrategy.AddImport(propertyId)
|
||||
} else {
|
||||
CallableImportStrategy.InsertFqNameAndShorten(propertyId)
|
||||
}
|
||||
}
|
||||
|
||||
internal enum class CallableInsertionStrategy {
|
||||
AS_CALL,
|
||||
AS_IDENTIFIER
|
||||
}
|
||||
|
||||
private class FunctionLookupElementFactory {
|
||||
fun KtAnalysisSession.createLookup(symbol: KtFunctionSymbol): LookupElementBuilder? {
|
||||
fun KtAnalysisSession.createLookup(
|
||||
symbol: KtFunctionSymbol,
|
||||
importStrategy: CallableImportStrategy,
|
||||
insertionStrategy: CallableInsertionStrategy
|
||||
): LookupElementBuilder? {
|
||||
val lookupObject = FunctionLookupObject(
|
||||
symbol.name,
|
||||
importStrategy = detectImportStrategy(symbol),
|
||||
importStrategy = importStrategy,
|
||||
inputValueArguments = symbol.valueParameters.isNotEmpty(),
|
||||
insertEmptyLambda = insertLambdaBraces(symbol),
|
||||
renderedFunctionParameters = with(ShortNamesRenderer) { renderFunctionParameters(symbol) }
|
||||
)
|
||||
|
||||
val insertionHandler = when (insertionStrategy) {
|
||||
CallableInsertionStrategy.AS_CALL -> FunctionInsertionHandler
|
||||
CallableInsertionStrategy.AS_IDENTIFIER -> QuotedNamesAwareInsertionHandler()
|
||||
}
|
||||
|
||||
return try {
|
||||
LookupElementBuilder.create(lookupObject, symbol.name.asString())
|
||||
.withTailText(getTailText(symbol), true)
|
||||
.withTypeText(symbol.annotatedType.type.render(WITH_TYPE_RENDERING_OPTIONS))
|
||||
.withInsertHandler(FunctionInsertionHandler)
|
||||
.withInsertHandler(insertionHandler)
|
||||
.let { withSymbolInfo(symbol, it) }
|
||||
} catch (e: Throwable) {
|
||||
if (e is ControlFlowException) throw e
|
||||
LOG.error(e)
|
||||
@@ -190,18 +225,6 @@ private class FunctionLookupElementFactory {
|
||||
}
|
||||
}
|
||||
|
||||
private fun detectImportStrategy(symbol: KtFunctionSymbol): CallableImportStrategy {
|
||||
if (symbol.dispatchType != null) return CallableImportStrategy.DoNothing
|
||||
|
||||
val functionFqName = symbol.callableIdIfNonLocal ?: return CallableImportStrategy.DoNothing
|
||||
|
||||
return if (symbol.isExtension) {
|
||||
CallableImportStrategy.AddImport(functionFqName)
|
||||
} else {
|
||||
CallableImportStrategy.InsertFqNameAndShorten(functionFqName)
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtAnalysisSession.getTailText(symbol: KtFunctionSymbol): String {
|
||||
return if (insertLambdaBraces(symbol)) " {...}" else with(ShortNamesRenderer) { renderFunctionParameters(symbol) }
|
||||
}
|
||||
@@ -371,11 +394,22 @@ private object VariableInsertionHandler : InsertHandler<LookupElement> {
|
||||
shortenReferencesForFirCompletion(targetFile, TextRange(context.startOffset, context.tailOffset))
|
||||
}
|
||||
|
||||
is CallableImportStrategy.DoNothing -> {}
|
||||
is CallableImportStrategy.DoNothing -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private object PackagePartInsertionHandler : InsertHandler<LookupElement> {
|
||||
override fun handleInsert(context: InsertionContext, item: LookupElement) {
|
||||
val lookupElement = item.`object` as PackagePartLookupObject
|
||||
val name = lookupElement.shortName.render()
|
||||
context.document.replaceString(context.startOffset, context.tailOffset, name)
|
||||
context.commitDocument()
|
||||
context.addDotAndInvokeCompletion()
|
||||
}
|
||||
}
|
||||
|
||||
private open class QuotedNamesAwareInsertionHandler : InsertHandler<LookupElement> {
|
||||
override fun handleInsert(context: InsertionContext, item: LookupElement) {
|
||||
val lookupElement = item.`object` as KotlinLookupObject
|
||||
|
||||
+2
@@ -9,6 +9,7 @@ import com.intellij.codeInsight.completion.CompletionParameters
|
||||
import com.intellij.codeInsight.completion.CompletionResultSet
|
||||
import com.intellij.codeInsight.completion.PrefixMatcher
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.idea.caches.project.IdeaModuleInfo
|
||||
import org.jetbrains.kotlin.idea.caches.project.getModuleInfo
|
||||
import org.jetbrains.kotlin.idea.completion.KotlinFirLookupElementFactory
|
||||
import org.jetbrains.kotlin.idea.fir.low.level.api.IndexHelper
|
||||
@@ -29,6 +30,7 @@ internal class FirBasicCompletionContext(
|
||||
val lookupElementFactory: KotlinFirLookupElementFactory = KotlinFirLookupElementFactory(),
|
||||
) {
|
||||
val visibleScope = KotlinSourceFilterScope.projectSourceAndClassFiles(originalKtFile.resolveScope, project)
|
||||
val moduleInfo: IdeaModuleInfo = originalKtFile.getModuleInfo()
|
||||
|
||||
companion object {
|
||||
fun createFromParameters(parameters: CompletionParameters, result: CompletionResultSet): FirBasicCompletionContext? {
|
||||
|
||||
+32
-3
@@ -6,6 +6,7 @@
|
||||
package org.jetbrains.kotlin.idea.completion.context
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.util.parentOfType
|
||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.frontend.api.analyse
|
||||
import org.jetbrains.kotlin.idea.frontend.api.analyseInDependedAnalysisSession
|
||||
@@ -24,6 +25,15 @@ internal sealed class FirNameReferencePositionContext : FirRawPositionCompletion
|
||||
abstract val explicitReceiver: KtExpression?
|
||||
}
|
||||
|
||||
internal class FirImportDirectivePositionContext(
|
||||
override val position: PsiElement,
|
||||
override val reference: KtSimpleNameReference,
|
||||
override val nameExpression: KtSimpleNameExpression,
|
||||
override val explicitReceiver: KtExpression?,
|
||||
val importDirective: KtImportDirective,
|
||||
) : FirNameReferencePositionContext()
|
||||
|
||||
|
||||
internal class FirTypeNameReferencePositionContext(
|
||||
override val position: PsiElement,
|
||||
override val reference: KtSimpleNameReference,
|
||||
@@ -68,17 +78,36 @@ internal object FirPositionCompletionContextDetector {
|
||||
val nameExpression = reference.expression.takeIf { it !is KtLabelReferenceExpression }
|
||||
?: return FirUnknownPositionContext(position)
|
||||
val explicitReceiver = nameExpression.getReceiverExpression()
|
||||
val parent = nameExpression.parent
|
||||
|
||||
return when (val parent = nameExpression.parent) {
|
||||
is KtUserType -> {
|
||||
return when {
|
||||
parent is KtUserType -> {
|
||||
detectForTypeContext(parent, position, reference, nameExpression, explicitReceiver)
|
||||
}
|
||||
nameExpression.isReferenceExpressionInImportDirective() -> {
|
||||
FirImportDirectivePositionContext(
|
||||
position,
|
||||
reference,
|
||||
nameExpression,
|
||||
explicitReceiver,
|
||||
parent.parentOfType(withSelf = true)!!
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
FirExpressionNameReferencePositionContext(position, reference, nameExpression, explicitReceiver)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtExpression.isReferenceExpressionInImportDirective() = when (val parent = parent) {
|
||||
is KtImportDirective -> parent.importedReference == this
|
||||
is KtDotQualifiedExpression -> {
|
||||
val importDirective = parent.parent as? KtImportDirective
|
||||
importDirective?.importedReference == parent
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
|
||||
private fun detectForTypeContext(
|
||||
userType: KtUserType,
|
||||
position: PsiElement,
|
||||
@@ -116,7 +145,7 @@ internal object FirPositionCompletionContextDetector {
|
||||
positionContext.nameExpression,
|
||||
action
|
||||
)
|
||||
is FirUnknownPositionContext -> {
|
||||
is FirUnknownPositionContext, is FirImportDirectivePositionContext -> {
|
||||
analyse(basicContext.originalKtFile, action)
|
||||
}
|
||||
}
|
||||
|
||||
+2
-9
@@ -8,13 +8,10 @@ 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.completion.contributors.helpers.getStaticScope
|
||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||
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.KtSymbolWithMembers
|
||||
import org.jetbrains.kotlin.idea.frontend.api.types.KtClassType
|
||||
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
|
||||
|
||||
@@ -42,11 +39,7 @@ internal open class FirClassifierCompletionContributor(
|
||||
visibilityChecker: CompletionVisibilityChecker
|
||||
) {
|
||||
val reference = receiver.reference() ?: return
|
||||
val scope = when (val symbol = reference.resolveToSymbol()) {
|
||||
is KtSymbolWithMembers -> symbol.getMemberScope()
|
||||
is KtPackageSymbol -> symbol.getPackageScope()
|
||||
else -> return
|
||||
}
|
||||
val scope = getStaticScope(reference) ?: return
|
||||
scope
|
||||
.getClassifierSymbols(scopeNameFilter)
|
||||
.filter { filterClassifiers(it) }
|
||||
|
||||
+14
-1
@@ -10,6 +10,8 @@ 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.CallableImportStrategy
|
||||
import org.jetbrains.kotlin.idea.completion.CallableInsertionStrategy
|
||||
import org.jetbrains.kotlin.idea.completion.KotlinFirLookupElementFactory
|
||||
import org.jetbrains.kotlin.idea.completion.context.FirBasicCompletionContext
|
||||
import org.jetbrains.kotlin.idea.completion.context.FirRawPositionCompletionContext
|
||||
@@ -21,7 +23,6 @@ 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.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope
|
||||
import org.jetbrains.kotlin.platform.TargetPlatform
|
||||
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
@@ -64,6 +65,18 @@ internal abstract class FirCompletionContributorBase<C : FirRawPositionCompletio
|
||||
result.addElement(lookup)
|
||||
}
|
||||
|
||||
protected fun KtAnalysisSession.addCallableSymbolToCompletion(
|
||||
symbol: KtCallableSymbol,
|
||||
importingStrategy: CallableImportStrategy,
|
||||
insertionStrategy: CallableInsertionStrategy,
|
||||
) {
|
||||
if (symbol !is KtNamedSymbol) return
|
||||
val lookup = with(lookupElementFactory) {
|
||||
createCallableLookupElement(symbol, importingStrategy, insertionStrategy)
|
||||
} ?: return
|
||||
result.addElement(lookup)
|
||||
}
|
||||
|
||||
protected fun KtExpression.reference() = when (this) {
|
||||
is KtDotQualifiedExpression -> selectorExpression?.mainReference
|
||||
else -> mainReference
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.contributors
|
||||
|
||||
import org.jetbrains.kotlin.idea.completion.CallableImportStrategy
|
||||
import org.jetbrains.kotlin.idea.completion.CallableInsertionStrategy
|
||||
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.frontend.api.KtAnalysisSession
|
||||
|
||||
internal class FirImportDirectivePackageMembersCompletionContributor(
|
||||
basicContext: FirBasicCompletionContext
|
||||
) : FirCompletionContributorBase<FirImportDirectivePositionContext>(basicContext) {
|
||||
override fun KtAnalysisSession.complete(positionContext: FirImportDirectivePositionContext) {
|
||||
val reference = positionContext.explicitReceiver?.reference() ?: return
|
||||
val scope = getStaticScope(reference) ?: return
|
||||
val visibilityChecker = CompletionVisibilityChecker.create(basicContext, positionContext)
|
||||
|
||||
scope.getClassifierSymbols(scopeNameFilter)
|
||||
.filter { with(visibilityChecker) { isVisible(it) } }
|
||||
.forEach { addClassifierSymbolToCompletion(it, insertFqName = false) }
|
||||
|
||||
scope.getCallableSymbols(scopeNameFilter)
|
||||
.filter { with(visibilityChecker) { isVisible(it) } }
|
||||
.forEach {
|
||||
addCallableSymbolToCompletion(
|
||||
it,
|
||||
importingStrategy = CallableImportStrategy.DoNothing,
|
||||
insertionStrategy = CallableInsertionStrategy.AS_IDENTIFIER
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-8
@@ -5,7 +5,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.completion.contributors
|
||||
|
||||
import com.intellij.psi.JavaPsiFacade
|
||||
import org.jetbrains.kotlin.idea.completion.context.FirBasicCompletionContext
|
||||
import org.jetbrains.kotlin.idea.completion.context.FirNameReferenceRawPositionContext
|
||||
import org.jetbrains.kotlin.idea.completion.context.FirRawPositionCompletionContext
|
||||
@@ -13,12 +12,6 @@ import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtPackageSymbol
|
||||
import org.jetbrains.kotlin.idea.isExcludedFromAutoImport
|
||||
import org.jetbrains.kotlin.idea.project.languageVersionSettings
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.idea.stubindex.PackageIndexUtil
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.platform.jvm.isJvm
|
||||
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
|
||||
internal class FirPackageCompletionContributor(
|
||||
basicContext: FirBasicCompletionContext,
|
||||
@@ -36,7 +29,7 @@ internal class FirPackageCompletionContributor(
|
||||
packageName.fqName.isExcludedFromAutoImport(project, originalKtFile, originalKtFile.languageVersionSettings)
|
||||
}
|
||||
.forEach { packageSymbol ->
|
||||
result.addElement(lookupElementFactory.createPackageLookupElement(packageSymbol.fqName))
|
||||
result.addElement(lookupElementFactory.createPackagePartLookupElement(packageSymbol.fqName))
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-16
@@ -37,15 +37,14 @@ internal object SuperCallInsertionHandler : InsertHandler<LookupElement> {
|
||||
val lookupObject = item.`object` as SuperCallLookupObject
|
||||
|
||||
replaceWithClassIdAndShorten(lookupObject, context)
|
||||
addDot(context)
|
||||
invokeCompletion(context)
|
||||
context.addDotAndInvokeCompletion()
|
||||
}
|
||||
|
||||
private fun replaceWithClassIdAndShorten(
|
||||
lookupObject: SuperCallLookupObject,
|
||||
context: InsertionContext
|
||||
) {
|
||||
val replaceTo = lookupObject.replaceTo ?: return
|
||||
val replaceTo = lookupObject.replaceTo ?: return
|
||||
context.document.replaceString(context.startOffset, context.tailOffset, replaceTo)
|
||||
context.commitDocument()
|
||||
|
||||
@@ -54,19 +53,6 @@ internal object SuperCallInsertionHandler : InsertHandler<LookupElement> {
|
||||
shortenReferencesForFirCompletion(targetFile, TextRange(context.startOffset, context.tailOffset))
|
||||
}
|
||||
}
|
||||
|
||||
private fun invokeCompletion(context: InsertionContext) {
|
||||
ApplicationManager.getApplication().invokeLater {
|
||||
CodeCompletionHandlerBase(CompletionType.BASIC, true, false, true)
|
||||
.invokeCompletion(context.project, context.editor)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addDot(context: InsertionContext) {
|
||||
context.document.insertString(context.tailOffset, ".")
|
||||
context.commitDocument()
|
||||
context.editor.caretModel.moveToOffset(context.tailOffset)
|
||||
}
|
||||
}
|
||||
|
||||
internal interface SuperCallLookupObject {
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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.contributors.helpers
|
||||
|
||||
import com.intellij.codeInsight.completion.CodeCompletionHandlerBase
|
||||
import com.intellij.codeInsight.completion.CompletionType
|
||||
import com.intellij.codeInsight.completion.InsertionContext
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
|
||||
internal fun InsertionContext.addDotAndInvokeCompletion() {
|
||||
addDotToCompletion(this)
|
||||
invokeCompletion(this)
|
||||
}
|
||||
|
||||
private fun invokeCompletion(context: InsertionContext) {
|
||||
ApplicationManager.getApplication().invokeLater {
|
||||
CodeCompletionHandlerBase(CompletionType.BASIC, true, false, true)
|
||||
.invokeCompletion(context.project, context.editor)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addDotToCompletion(context: InsertionContext) {
|
||||
context.document.insertString(context.tailOffset, ".")
|
||||
context.commitDocument()
|
||||
context.editor.caretModel.moveToOffset(context.tailOffset)
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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.contributors.helpers
|
||||
|
||||
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.KtPackageSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolWithMembers
|
||||
import org.jetbrains.kotlin.idea.references.KtReference
|
||||
|
||||
internal fun KtAnalysisSession.getStaticScope(reference: KtReference): KtScope? =
|
||||
when (val symbol = reference.resolveToSymbol()) {
|
||||
is KtSymbolWithMembers -> symbol.getStaticMemberScope()
|
||||
is KtPackageSymbol -> symbol.getPackageScope()
|
||||
else -> null
|
||||
}
|
||||
+5
@@ -19,6 +19,8 @@ import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
abstract class KtScopeProvider : KtAnalysisSessionComponent() {
|
||||
abstract fun getMemberScope(classSymbol: KtSymbolWithMembers): KtMemberScope
|
||||
abstract fun getStaticMemberScope(symbol: KtSymbolWithMembers): KtScope
|
||||
|
||||
abstract fun getDeclaredMemberScope(classSymbol: KtSymbolWithMembers): KtDeclaredMemberScope
|
||||
abstract fun getFileScope(fileSymbol: KtFileSymbol): KtDeclarationScope<KtSymbolWithDeclarations>
|
||||
abstract fun getPackageScope(packageSymbol: KtPackageSymbol): KtPackageScope
|
||||
@@ -39,6 +41,9 @@ interface KtScopeProviderMixIn : KtAnalysisSessionMixIn {
|
||||
fun KtSymbolWithMembers.getDeclaredMemberScope(): KtDeclaredMemberScope =
|
||||
analysisSession.scopeProvider.getDeclaredMemberScope(this)
|
||||
|
||||
fun KtSymbolWithMembers.getStaticMemberScope(): KtScope =
|
||||
analysisSession.scopeProvider.getStaticMemberScope(this)
|
||||
|
||||
fun KtFileSymbol.getFileScope(): KtDeclarationScope<KtSymbolWithDeclarations> =
|
||||
analysisSession.scopeProvider.getFileScope(this)
|
||||
|
||||
|
||||
+9
@@ -86,6 +86,15 @@ internal class KtFirScopeProvider(
|
||||
}
|
||||
}
|
||||
|
||||
override fun getStaticMemberScope(symbol: KtSymbolWithMembers): KtScope {
|
||||
val firScope = symbol.withFirForScope { fir ->
|
||||
fir.scopeProvider.getStaticScope(fir, analysisSession.rootModuleSession, ScopeSession())
|
||||
} ?: return KtFirEmptyMemberScope(symbol)
|
||||
firScopeStorage.register(firScope)
|
||||
check(firScope is FirContainingNamesAwareScope)
|
||||
return KtFirDelegatingScopeImpl(firScope, builder, token)
|
||||
}
|
||||
|
||||
override fun getDeclaredMemberScope(classSymbol: KtSymbolWithMembers): KtDeclaredMemberScope = withValidityAssertion {
|
||||
declaredMemberScopeCache.getOrPut(classSymbol) {
|
||||
val firScope = classSymbol.withFirForScope {
|
||||
|
||||
+7
@@ -79,6 +79,13 @@ internal fun FirScope.getCallableSymbols(callableNames: Collection<Name>, builde
|
||||
}
|
||||
}
|
||||
|
||||
internal class KtFirDelegatingScopeImpl<S>(
|
||||
override val firScope: S,
|
||||
builder: KtSymbolByFirBuilder,
|
||||
token: ValidityToken
|
||||
) : KtFirDelegatingScope<S>(builder, token) where S : FirContainingNamesAwareScope, S : FirScope
|
||||
|
||||
|
||||
internal fun FirScope.getClassifierSymbols(classLikeNames: Collection<Name>, builder: KtSymbolByFirBuilder): Sequence<KtClassifierSymbol> =
|
||||
sequence {
|
||||
classLikeNames.forEach { name ->
|
||||
|
||||
+7
-16
@@ -62,6 +62,11 @@ internal class KtFirPackageScope(
|
||||
|
||||
KotlinTopLevelTypeAliasByPackageIndex.getInstance()[fqName.asString(), project, searchScope]
|
||||
.mapNotNullTo(this) { it.nameAsName }
|
||||
|
||||
JavaPsiFacade.getInstance(project)
|
||||
.findPackage(fqName.asString())
|
||||
?.getClasses(searchScope)
|
||||
?.mapNotNullTo(this) { it.name?.let(Name::identifier) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,21 +79,7 @@ internal class KtFirPackageScope(
|
||||
}
|
||||
|
||||
override fun getPackageSymbols(nameFilter: KtScopeNameFilter): Sequence<KtPackageSymbol> = withValidityAssertion {
|
||||
sequence {
|
||||
PackageIndexUtil
|
||||
.getSubPackageFqNames(fqName, searchScope, project, nameFilter)
|
||||
.forEach {
|
||||
yield(builder.createPackageSymbol(it))
|
||||
}
|
||||
|
||||
if (targetPlatform.isJvm()) {
|
||||
JavaPsiFacade.getInstance(project).findPackage(fqName.asString())?.getSubPackages(searchScope)?.forEach { psiPackage ->
|
||||
val fqName = psiPackage.getKotlinFqName() ?: return@forEach
|
||||
if (nameFilter(fqName.shortName())) {
|
||||
yield(builder.createPackageSymbol(fqName))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
PackageIndexUtil.getJavaAndKotlinSubPackageFqNames(fqName, searchScope, project, targetPlatform, nameFilter)
|
||||
.map { builder.createPackageSymbol(it) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* 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.miniStdLib
|
||||
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
inline fun <T : R, R> T.letIf(condition: Boolean, block: (T) -> R): R {
|
||||
contract {
|
||||
callsInPlace(block, InvocationKind.AT_MOST_ONCE)
|
||||
}
|
||||
return if (condition) block(this) else this
|
||||
}
|
||||
Reference in New Issue
Block a user