[LL API] Add PSI-based declaration provider for files outside roots

Indices are not available in the IDE for files outside content roots,
so a new provider acts as a single-file replacement.

^KTIJ-23937 Fixed
This commit is contained in:
Yan Zhulanow
2022-12-14 01:24:39 +09:00
committed by Space Team
parent 00a266fd88
commit e9af55fdee
3 changed files with 149 additions and 2 deletions
@@ -0,0 +1,23 @@
/*
* Copyright 2010-2023 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.analysis.low.level.api.fir.project.structure
import org.jetbrains.kotlin.analysis.providers.KotlinDeclarationProvider
import org.jetbrains.kotlin.name.*
import org.jetbrains.kotlin.psi.*
internal object EmptyKotlinDeclarationProvider : KotlinDeclarationProvider() {
override fun getClassLikeDeclarationByClassId(classId: ClassId) = null
override fun getAllClassesByClassId(classId: ClassId) = emptyList<KtClassOrObject>()
override fun getAllTypeAliasesByClassId(classId: ClassId) = emptyList<KtTypeAlias>()
override fun getTopLevelKotlinClassLikeDeclarationNamesInPackage(packageFqName: FqName) = emptySet<Name>()
override fun getTopLevelProperties(callableId: CallableId) = emptyList<KtProperty>()
override fun getTopLevelFunctions(callableId: CallableId) = emptyList<KtNamedFunction>()
override fun getTopLevelCallableFiles(callableId: CallableId) = emptyList<KtFile>()
override fun getTopLevelCallableNamesInPackage(packageFqName: FqName) = emptySet<Name>()
override fun findFilesForFacadeByPackage(packageFqName: FqName) = emptyList<KtFile>()
override fun findFilesForFacade(facadeFqName: FqName) = emptyList<KtFile>()
}
@@ -0,0 +1,122 @@
/*
* Copyright 2010-2022 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.analysis.low.level.api.fir.project.structure
import org.jetbrains.kotlin.analysis.providers.KotlinDeclarationProvider
import org.jetbrains.kotlin.name.*
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.utils.addIfNotNull
internal class FileBasedKotlinDeclarationProvider(private val kotlinFile: KtFile) : KotlinDeclarationProvider() {
override fun getClassLikeDeclarationByClassId(classId: ClassId): KtClassLikeDeclaration? {
if (classId.isLocal) {
return null
}
if (kotlinFile.packageFqName != classId.packageFqName) {
return null
}
var current: KtElement = kotlinFile
nextSegment@ for (segment in classId.relativeClassName.pathSegments()) {
val container = current as? KtDeclarationContainer ?: return null
for (child in container.declarations) {
if (child is KtClassLikeDeclaration && child.nameAsName == segment) {
current = child
continue@nextSegment
}
}
return null
}
return current as? KtClassLikeDeclaration
}
override fun getAllClassesByClassId(classId: ClassId): Collection<KtClassOrObject> {
return listOfNotNull(getClassLikeDeclarationByClassId(classId) as? KtClassOrObject)
}
override fun getAllTypeAliasesByClassId(classId: ClassId): Collection<KtTypeAlias> {
val name = classId.relativeClassName.pathSegments().singleOrNull() ?: return emptyList()
return getTopLevelDeclarations(classId.packageFqName, name)
}
override fun getTopLevelKotlinClassLikeDeclarationNamesInPackage(packageFqName: FqName): Set<Name> {
return getTopLevelDeclarationNames<KtClassLikeDeclaration>(packageFqName)
}
override fun getTopLevelProperties(callableId: CallableId): Collection<KtProperty> {
return getTopLevelCallables(callableId)
}
override fun getTopLevelFunctions(callableId: CallableId): Collection<KtNamedFunction> {
return getTopLevelCallables(callableId)
}
override fun getTopLevelCallableFiles(callableId: CallableId): Collection<KtFile> {
return buildSet {
getTopLevelProperties(callableId).mapTo(this) { it.containingKtFile }
getTopLevelFunctions(callableId).mapTo(this) { it.containingKtFile }
}
}
override fun getTopLevelCallableNamesInPackage(packageFqName: FqName): Set<Name> {
return getTopLevelDeclarationNames<KtCallableDeclaration>(packageFqName)
}
override fun findFilesForFacadeByPackage(packageFqName: FqName): Collection<KtFile> {
if (kotlinFile.packageFqName != packageFqName) {
return emptyList()
}
return listOf(kotlinFile)
}
override fun findFilesForFacade(facadeFqName: FqName): Collection<KtFile> {
for (declaration in kotlinFile.declarations) {
if (declaration !is KtClassLikeDeclaration) {
return listOf(kotlinFile)
}
}
return emptyList()
}
private inline fun <reified T : KtCallableDeclaration> getTopLevelCallables(callableId: CallableId): Collection<T> {
require(callableId.classId == null)
return getTopLevelDeclarations(callableId.packageName, callableId.callableName)
}
private inline fun <reified T : KtNamedDeclaration> getTopLevelDeclarations(packageFqName: FqName, name: Name): Collection<T> {
if (kotlinFile.packageFqName != packageFqName) {
return emptyList()
}
return buildList {
for (declaration in kotlinFile.declarations) {
if (declaration is T && declaration.nameAsName == name) {
add(declaration)
}
}
}
}
private inline fun <reified T : KtNamedDeclaration> getTopLevelDeclarationNames(packageFqName: FqName): Set<Name> {
if (kotlinFile.packageFqName != packageFqName) {
return emptySet()
}
return buildSet {
for (declaration in kotlinFile.declarations) {
if (declaration is T) {
addIfNotNull(declaration.nameAsName)
}
}
}
}
}
@@ -17,7 +17,6 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirNonUnderCon
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirResolvableModuleSession
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSessionInvalidator
import org.jetbrains.kotlin.analysis.project.structure.KtNotUnderContentRootModule
import org.jetbrains.kotlin.analysis.providers.createDeclarationProvider
import org.jetbrains.kotlin.analysis.providers.createPackageProvider
import org.jetbrains.kotlin.analysis.utils.caches.SoftCachedMap
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
@@ -35,6 +34,7 @@ import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider
import org.jetbrains.kotlin.fir.session.*
import org.jetbrains.kotlin.fir.symbols.FirLazyDeclarationResolver
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleResolver
internal class LLFirNonUnderContentRootSessionFactory(private val project: Project) {
@@ -75,10 +75,12 @@ internal class LLFirNonUnderContentRootSessionFactory(private val project: Proje
registerResolveComponents()
registerJavaSpecificResolveComponents()
val ktFile = module.file as? KtFile
val provider = LLFirProvider(
this,
components,
project.createDeclarationProvider(contentScope),
if (ktFile != null) FileBasedKotlinDeclarationProvider(ktFile) else EmptyKotlinDeclarationProvider,
project.createPackageProvider(contentScope),
canContainKotlinPackage = true,
)