[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:
+23
@@ -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>()
|
||||||
|
}
|
||||||
+122
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
-2
@@ -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.LLFirResolvableModuleSession
|
||||||
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSessionInvalidator
|
import org.jetbrains.kotlin.analysis.low.level.api.fir.sessions.LLFirSessionInvalidator
|
||||||
import org.jetbrains.kotlin.analysis.project.structure.KtNotUnderContentRootModule
|
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.providers.createPackageProvider
|
||||||
import org.jetbrains.kotlin.analysis.utils.caches.SoftCachedMap
|
import org.jetbrains.kotlin.analysis.utils.caches.SoftCachedMap
|
||||||
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
|
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.session.*
|
||||||
import org.jetbrains.kotlin.fir.symbols.FirLazyDeclarationResolver
|
import org.jetbrains.kotlin.fir.symbols.FirLazyDeclarationResolver
|
||||||
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
|
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleResolver
|
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleResolver
|
||||||
|
|
||||||
internal class LLFirNonUnderContentRootSessionFactory(private val project: Project) {
|
internal class LLFirNonUnderContentRootSessionFactory(private val project: Project) {
|
||||||
@@ -75,10 +75,12 @@ internal class LLFirNonUnderContentRootSessionFactory(private val project: Proje
|
|||||||
registerResolveComponents()
|
registerResolveComponents()
|
||||||
registerJavaSpecificResolveComponents()
|
registerJavaSpecificResolveComponents()
|
||||||
|
|
||||||
|
val ktFile = module.file as? KtFile
|
||||||
|
|
||||||
val provider = LLFirProvider(
|
val provider = LLFirProvider(
|
||||||
this,
|
this,
|
||||||
components,
|
components,
|
||||||
project.createDeclarationProvider(contentScope),
|
if (ktFile != null) FileBasedKotlinDeclarationProvider(ktFile) else EmptyKotlinDeclarationProvider,
|
||||||
project.createPackageProvider(contentScope),
|
project.createPackageProvider(contentScope),
|
||||||
canContainKotlinPackage = true,
|
canContainKotlinPackage = true,
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user