[kotlin] KTIJ-36104 Provide EP for build systems to provide their own KCRI storage

This commit splits the current KCRI storage implementation for JPS into
- an API that can also be implemented for other build systems including an EP
- the implementation of KCRI storage for JPS

GitOrigin-RevId: 2f8b83b09b839eb8c73e5130e0dcb2f76dd4c753
This commit is contained in:
Frederik Haselmeier
2025-11-10 19:57:16 +00:00
committed by intellij-monorepo-bot
parent 023bbcde39
commit a5f1d8b396
8 changed files with 336 additions and 182 deletions
@@ -22,6 +22,19 @@
implementation="org.jetbrains.kotlin.idea.search.refIndex.KotlinDirectInheritorProvider"/>
</extensions>
<extensionPoints>
<extensionPoint
qualifiedName="org.jetbrains.kotlin.kotlinCompilerReferenceIndexStorageProvider"
interface="org.jetbrains.kotlin.idea.search.refIndex.KotlinCompilerReferenceIndexStorageProvider"
dynamic="true"/>
</extensionPoints>
<extensions defaultExtensionNs="org.jetbrains.kotlin">
<kotlinCompilerReferenceIndexStorageProvider
implementation="org.jetbrains.kotlin.idea.search.refIndex.jps.JpsKotlinCompilerReferenceIndexStorageProvider"
order="last"/>
</extensions>
<actions>
<action
class="org.jetbrains.kotlin.idea.search.refIndex.KotlinCompilerReferenceIndexVerifierAction"
@@ -219,7 +219,9 @@ class KotlinCompilerReferenceIndexService(private val project: Project) : Dispos
}
private fun closeStorage() {
KotlinCompilerReferenceIndexStorage.close(storage)
storage?.close().let {
LOG.info("KCRI storage is closed" + if (it == null) " (didn't exist)" else "")
}
storage = null
}
@@ -1,177 +1,50 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.kotlin.idea.search.refIndex
import com.intellij.compiler.server.BuildManager
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.util.SmartList
import com.intellij.util.concurrency.AppExecutorUtil
import com.intellij.util.containers.MultiMap
import com.intellij.util.indexing.UnindexedFilesUpdater
import com.intellij.util.io.CorruptedException
import com.intellij.util.io.EnumeratorStringDescriptor
import com.intellij.util.io.PersistentHashMap
import org.jetbrains.annotations.TestOnly
import org.jetbrains.jps.builders.impl.BuildDataPathsImpl
import org.jetbrains.jps.builders.java.JavaModuleBuildTargetType
import org.jetbrains.jps.builders.storage.BuildDataPaths
import org.jetbrains.kotlin.config.SettingConstants
import org.jetbrains.kotlin.incremental.KOTLIN_CACHE_DIRECTORY_NAME
import org.jetbrains.kotlin.incremental.storage.BasicMapsOwner
import org.jetbrains.kotlin.incremental.storage.CollectionExternalizer
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.kotlin.name.FqName
import java.nio.file.Path
import java.util.concurrent.Future
import kotlin.io.path.*
import kotlin.system.measureTimeMillis
class KotlinCompilerReferenceIndexStorage private constructor(
kotlinDataContainerPath: Path,
private val lookupStorageReader: LookupStorageReader,
) {
companion object {
/**
* [org.jetbrains.kotlin.incremental.AbstractIncrementalCache.Companion.SUBTYPES]
*/
private val SUBTYPES_STORAGE_NAME = "subtypes.${BasicMapsOwner.CACHE_EXTENSION}"
private val STORAGE_INDEXING_EXECUTOR = AppExecutorUtil.createBoundedApplicationPoolExecutor(
"Kotlin compiler references indexing", UnindexedFilesUpdater.getMaxNumberOfIndexingThreads()
)
private val LOG = logger<KotlinCompilerReferenceIndexStorage>()
fun open(project: Project, projectPath: String): KotlinCompilerReferenceIndexStorage? {
if (project.isDisposed) return null
val buildDataPaths = project.buildDataPaths
val kotlinDataContainerPath = buildDataPaths.kotlinDataContainer ?: kotlin.run {
LOG.warn("${SettingConstants.KOTLIN_DATA_CONTAINER_ID} is not found")
return null
}
val lookupStorageReader = LookupStorageReader.create(kotlinDataContainerPath, projectPath) ?: kotlin.run {
LOG.warn("LookupStorage not found or corrupted")
return null
}
val storage = KotlinCompilerReferenceIndexStorage(kotlinDataContainerPath, lookupStorageReader)
if (!storage.initialize(buildDataPaths)) return null
return storage
}
fun close(storage: KotlinCompilerReferenceIndexStorage?) {
storage?.close().let {
LOG.info("KCRI storage is closed" + if (it == null) " (didn't exist)" else "")
}
}
fun hasIndex(project: Project): Boolean = LookupStorageReader.hasStorage(project)
@TestOnly
fun initializeForTests(
buildDataPaths: BuildDataPaths,
destination: ClassOneToManyStorage,
) = initializeSubtypeStorage(buildDataPaths, destination)
internal val Project.buildDataPaths: BuildDataPaths
get() = BuildDataPathsImpl(BuildManager.getInstance().getProjectSystemDir(this))
internal val BuildDataPaths.kotlinDataContainer: Path?
get() = targetsDataRoot
.resolve(SettingConstants.KOTLIN_DATA_CONTAINER_ID)
.takeIf { it.exists() && it.isDirectory() }
?.listDirectoryEntries("${SettingConstants.KOTLIN_DATA_CONTAINER_ID}*")
?.firstOrNull()
private fun initializeSubtypeStorage(buildDataPaths: BuildDataPaths, destination: ClassOneToManyStorage): Boolean {
var wasCorrupted = false
val destinationMap = MultiMap.createConcurrentSet<String, String>()
val futures = mutableListOf<Future<*>>()
val timeOfFilling = measureTimeMillis {
visitSubtypeStorages(buildDataPaths) { storagePath ->
futures += STORAGE_INDEXING_EXECUTOR.submit {
try {
initializeStorage(destinationMap, storagePath)
} catch (e: CorruptedException) {
wasCorrupted = true
LOG.warn("KCRI storage was corrupted", e)
}
}
}
try {
for (future in futures) {
future.get()
}
} catch (e: InterruptedException) {
LOG.warn("KCRI initialization was interrupted")
throw e
}
}
if (wasCorrupted) return false
val timeOfFlush = measureTimeMillis {
for ((key, values) in destinationMap.entrySet()) {
destination.put(key, values)
}
}
LOG.info("KCRI storage is opened: took ${timeOfFilling + timeOfFlush} ms for ${futures.size} storages (filling map: $timeOfFilling ms, flush to storage: $timeOfFlush ms)")
return true
}
private fun visitSubtypeStorages(buildDataPaths: BuildDataPaths, processor: (Path) -> Unit) {
for (buildTargetType in JavaModuleBuildTargetType.ALL_TYPES) {
val buildTargetPath = buildDataPaths.getTargetTypeDataRootDir(buildTargetType)
if (buildTargetPath.notExists() || !buildTargetPath.isDirectory()) continue
buildTargetPath.forEachDirectoryEntry { targetDataRoot ->
val workingPath = targetDataRoot.takeIf { it.isDirectory() }
?.resolve(KOTLIN_CACHE_DIRECTORY_NAME)
?.resolve(SUBTYPES_STORAGE_NAME)
?.takeUnless { it.notExists() }
?: return@forEachDirectoryEntry
processor(workingPath)
}
}
}
}
private val subtypesStorage = ClassOneToManyStorage(kotlinDataContainerPath.resolve(SUBTYPES_STORAGE_NAME))
/**
* Interface responsible for managing the storage of the Kotlin Compiler Reference Index (KCRI).
* Implementations can choose how the data is retrieved from the CRI data.
* Typically there should be one implementation for each build system that wants to offer CRI capabilities.
*/
@ApiStatus.Experimental
interface KotlinCompilerReferenceIndexStorage {
/**
* Returns the list of [VirtualFile]s that make use of the given [fqName].
*/
fun getUsages(fqName: FqName): List<VirtualFile>
/**
* @return true if initialization was successful
* Returns subtypes of the class/interface identified by [fqName] stored within this storage.
* When [deep] is true, the returned [Sequence] represents the transitive closure over the subtype relation.
*/
private fun initialize(buildDataPaths: BuildDataPaths): Boolean = initializeSubtypeStorage(buildDataPaths, subtypesStorage)
fun getSubtypesOf(fqName: FqName, deep: Boolean): Sequence<FqName>
private fun close() {
lookupStorageReader.close()
subtypesStorage.closeAndClean()
}
/**
* Closes the storage and releases any held resources.
*/
fun close()
fun getUsages(fqName: FqName): List<VirtualFile> = lookupStorageReader[fqName].mapNotNull { VfsUtil.findFile(it, false) }
companion object {
/**
* Opens a storage for the given [project] by finding the first [KotlinCompilerReferenceIndexStorageProvider]
* that is applicable for the [project].
* Returns null if no provider is applicable or if storage creation fails (e.g. index missing or corrupted).
*/
internal fun open(project: Project, projectPath: String): KotlinCompilerReferenceIndexStorage? {
val provider = KotlinCompilerReferenceIndexStorageProvider.getApplicableProvider(project) ?: return null
return provider.createStorage(project, projectPath)
}
fun getSubtypesOf(fqName: FqName, deep: Boolean): Sequence<FqName> = subtypesStorage[fqName, deep]
}
private fun initializeStorage(destinationMap: MultiMap<String, String>, subtypesSourcePath: Path) {
createKotlinDataReader(subtypesSourcePath).use { source ->
source.processKeys { key ->
source[key]?.let { values ->
destinationMap.putValues(key, values)
}
true
/**
* Returns true if the first applicable [KotlinCompilerReferenceIndexStorageProvider] reports that an index exists for [project].
*/
internal fun hasIndex(project: Project): Boolean {
return KotlinCompilerReferenceIndexStorageProvider.getApplicableProvider(project)?.hasIndex(project) == true
}
}
}
private fun createKotlinDataReader(storagePath: Path): PersistentHashMap<String, Collection<String>> = openReadOnlyPersistentHashMap(
storagePath,
EnumeratorStringDescriptor.INSTANCE,
CollectionExternalizer<String>(EnumeratorStringDescriptor.INSTANCE, ::SmartList),
)
}
@@ -0,0 +1,54 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.kotlin.idea.search.refIndex
import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.project.Project
import org.jetbrains.annotations.ApiStatus
/**
* Provider for [KotlinCompilerReferenceIndexStorage] implementations.
* The provider is responsible for checking whether it is applicable for a given project and, if it is the case,
* for creating a [KotlinCompilerReferenceIndexStorage] instance.
*
* Typically, there should be one implementation per build system that wants to offer Kotlin CRI capabilities.
*
* Implementations can register themselves via the `org.jetbrains.kotlin.kotlinCompilerReferenceIndexStorageProvider` EP.
*/
@ApiStatus.Experimental
interface KotlinCompilerReferenceIndexStorageProvider {
/**
* Returns true if this provider can operate in the given [project] environment.
*
* The method should be fast and side‑effect free. It is called on the UI thread during provider selection.
*/
fun isApplicable(project: Project): Boolean
/**
* Returns true iff the underlying index required by this provider exists for the given [project].
* Implementations should avoid heavy I/O here; a cheap existence/corruption check is sufficient.
*/
fun hasIndex(project: Project): Boolean
/**
* Creates a new storage instance or returns null if storage cannot be created (e.g., index absent/corrupted).
*
* The [projectPath] is a base directory used by implementations that rely on relative paths persisted in the
* index. Implementations should perform any required light‑weight initialization here and delay heavy work if
* possible. The returned storage must be closed by the caller.
*/
fun createStorage(project: Project, projectPath: String): KotlinCompilerReferenceIndexStorage?
companion object {
val EP_NAME: ExtensionPointName<KotlinCompilerReferenceIndexStorageProvider> =
ExtensionPointName.create("org.jetbrains.kotlin.kotlinCompilerReferenceIndexStorageProvider")
/**
* Returns the first provider whose [isApplicable] returns true according to the EP registration order.
*
* To change precedence, adjust the `order` attribute in `intellij.kotlin.compilerReferenceIndex.xml`.
*/
fun getApplicableProvider(project: Project): KotlinCompilerReferenceIndexStorageProvider? =
EP_NAME.extensionList.firstOrNull { it.isApplicable(project) }
}
}
@@ -1,8 +1,7 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.kotlin.idea.search.refIndex
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.kotlin.idea.search.refIndex.jps
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.util.IntellijInternalApi
import com.intellij.util.containers.generateRecursiveSequence
import com.intellij.util.io.EnumeratorStringDescriptor
import com.intellij.util.io.PersistentMapBuilder
@@ -14,8 +13,7 @@ import kotlin.io.path.deleteIfExists
import kotlin.io.path.listDirectoryEntries
import kotlin.io.path.name
@IntellijInternalApi
class ClassOneToManyStorage(storagePath: Path) {
internal class JpsClassOneToManyStorage(storagePath: Path) {
init {
val storageName = storagePath.name
storagePath.parent.listDirectoryEntries("$storageName*").ifNotEmpty {
@@ -25,9 +23,9 @@ class ClassOneToManyStorage(storagePath: Path) {
}
private val storage = PersistentMapBuilder.newBuilder(
storagePath,
EnumeratorStringDescriptor.INSTANCE,
externalizer,
storagePath,
EnumeratorStringDescriptor.INSTANCE,
externalizer,
).build()
fun closeAndClean(): Unit = storage.closeAndClean()
@@ -42,12 +40,12 @@ class ClassOneToManyStorage(storagePath: Path) {
if (!deep) return values
return generateRecursiveSequence(values) {
storage[it]?.asSequence() ?: emptySequence()
storage[it]?.asSequence() ?: emptySequence()
}
}
companion object {
private val externalizer = StringCollectionExternalizer<Collection<String>>(::ArrayList)
private val LOG = logger<ClassOneToManyStorage>()
private val LOG = logger<JpsClassOneToManyStorage>()
}
}
@@ -0,0 +1,143 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.kotlin.idea.search.refIndex.jps
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.util.SmartList
import com.intellij.util.concurrency.AppExecutorUtil
import com.intellij.util.containers.MultiMap
import com.intellij.util.indexing.UnindexedFilesUpdater
import com.intellij.util.io.CorruptedException
import com.intellij.util.io.EnumeratorStringDescriptor
import com.intellij.util.io.PersistentHashMap
import org.jetbrains.annotations.TestOnly
import org.jetbrains.jps.builders.java.JavaModuleBuildTargetType
import org.jetbrains.jps.builders.storage.BuildDataPaths
import org.jetbrains.kotlin.idea.search.refIndex.KotlinCompilerReferenceIndexStorage
import org.jetbrains.kotlin.incremental.KOTLIN_CACHE_DIRECTORY_NAME
import org.jetbrains.kotlin.incremental.storage.BasicMapsOwner
import org.jetbrains.kotlin.incremental.storage.CollectionExternalizer
import org.jetbrains.kotlin.name.FqName
import java.nio.file.Path
import java.util.concurrent.Future
import kotlin.collections.component1
import kotlin.collections.component2
import kotlin.io.path.forEachDirectoryEntry
import kotlin.io.path.isDirectory
import kotlin.io.path.notExists
import kotlin.system.measureTimeMillis
internal class JpsKotlinCompilerReferenceIndexStorageImpl(
private val lookupStorageReader: JpsLookupStorageReader,
private val subtypesStorage: JpsClassOneToManyStorage,
) : KotlinCompilerReferenceIndexStorage {
companion object {
/**
* [org.jetbrains.kotlin.incremental.AbstractIncrementalCache.SUBTYPES]
*/
internal val SUBTYPES_STORAGE_NAME = "subtypes.${BasicMapsOwner.CACHE_EXTENSION}"
private val STORAGE_INDEXING_EXECUTOR = AppExecutorUtil.createBoundedApplicationPoolExecutor(
"Kotlin compiler references indexing", UnindexedFilesUpdater.getMaxNumberOfIndexingThreads()
)
private val LOG = logger<JpsKotlinCompilerReferenceIndexStorageImpl>()
}
override fun getUsages(fqName: FqName): List<VirtualFile> =
lookupStorageReader[fqName].mapNotNull { VfsUtil.findFile(it, false) }
override fun getSubtypesOf(fqName: FqName, deep: Boolean): Sequence<FqName> =
subtypesStorage[fqName, deep]
override fun close() {
lookupStorageReader.close()
subtypesStorage.closeAndClean()
}
private fun visitSubtypeStorages(buildDataPaths: BuildDataPaths, processor: (Path) -> Unit) {
for (buildTargetType in JavaModuleBuildTargetType.ALL_TYPES) {
val buildTargetPath = buildDataPaths.getTargetTypeDataRootDir(buildTargetType)
if (buildTargetPath.notExists() || !buildTargetPath.isDirectory()) continue
buildTargetPath.forEachDirectoryEntry { targetDataRoot ->
val workingPath = targetDataRoot.takeIf { it.isDirectory() }
?.resolve(KOTLIN_CACHE_DIRECTORY_NAME)
?.resolve(SUBTYPES_STORAGE_NAME)
?.takeUnless { it.notExists() }
?: return@forEachDirectoryEntry
processor(workingPath)
}
}
}
private fun initializeSubtypeStorage(buildDataPaths: BuildDataPaths, destination: JpsClassOneToManyStorage): Boolean {
var wasCorrupted = false
val destinationMap = MultiMap.createConcurrentSet<String, String>()
val futures = mutableListOf<Future<*>>()
val timeOfFilling = measureTimeMillis {
visitSubtypeStorages(buildDataPaths) { storagePath ->
futures += STORAGE_INDEXING_EXECUTOR.submit {
try {
initializeStorage(destinationMap, storagePath)
} catch (e: CorruptedException) {
wasCorrupted = true
LOG.warn("KCRI storage was corrupted", e)
}
}
}
try {
for (future in futures) {
future.get()
}
} catch (e: InterruptedException) {
LOG.warn("KCRI initialization was interrupted")
throw e
}
}
if (wasCorrupted) return false
val timeOfFlush = measureTimeMillis {
for ((key, values) in destinationMap.entrySet()) {
destination.put(key, values)
}
}
LOG.info("KCRI storage is opened: took ${timeOfFilling + timeOfFlush} ms for ${futures.size} storages (filling map: $timeOfFilling ms, flush to storage: $timeOfFlush ms)")
return true
}
@TestOnly
fun initializeForTests(
buildDataPaths: BuildDataPaths,
destination: JpsClassOneToManyStorage,
) = initializeSubtypeStorage(buildDataPaths, destination)
/**
* @return true if initialization was successful
*/
internal fun initialize(buildDataPaths: BuildDataPaths): Boolean =
initializeSubtypeStorage(buildDataPaths, subtypesStorage)
}
private fun initializeStorage(destinationMap: MultiMap<String, String>, subtypesSourcePath: Path) {
createKotlinDataReader(subtypesSourcePath).use { source ->
source.processKeys { key ->
source[key]?.let { values ->
destinationMap.putValues(key, values)
}
true
}
}
}
private fun createKotlinDataReader(storagePath: Path): PersistentHashMap<String, Collection<String>> = openReadOnlyPersistentHashMap(
storagePath,
EnumeratorStringDescriptor.INSTANCE,
CollectionExternalizer<String>(EnumeratorStringDescriptor.INSTANCE, ::SmartList),
)
@@ -0,0 +1,61 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.kotlin.idea.search.refIndex.jps
import com.intellij.compiler.server.BuildManager
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.project.Project
import org.jetbrains.jps.builders.impl.BuildDataPathsImpl
import org.jetbrains.jps.builders.storage.BuildDataPaths
import org.jetbrains.kotlin.config.SettingConstants
import org.jetbrains.kotlin.idea.search.refIndex.KotlinCompilerReferenceIndexStorage
import org.jetbrains.kotlin.idea.search.refIndex.KotlinCompilerReferenceIndexStorageProvider
import java.nio.file.Path
import kotlin.io.path.exists
import kotlin.io.path.isDirectory
import kotlin.io.path.listDirectoryEntries
internal class JpsKotlinCompilerReferenceIndexStorageProvider : KotlinCompilerReferenceIndexStorageProvider {
companion object {
private val LOG = logger<JpsKotlinCompilerReferenceIndexStorageProvider>()
}
override fun isApplicable(project: Project): Boolean {
val buildDataPaths = project.buildDataPaths
return buildDataPaths.kotlinDataContainer != null
}
override fun hasIndex(project: Project): Boolean {
return JpsLookupStorageReader.hasStorage(project)
}
override fun createStorage(project: Project, projectPath: String): KotlinCompilerReferenceIndexStorage? {
if (project.isDisposed) return null
val buildDataPaths = project.buildDataPaths
val kotlinDataContainerPath = buildDataPaths.kotlinDataContainer ?: run {
LOG.warn("${SettingConstants.KOTLIN_DATA_CONTAINER_ID} is not found")
return null
}
val lookupStorageReader = JpsLookupStorageReader.create(kotlinDataContainerPath, projectPath) ?: run {
LOG.warn("LookupStorage not found or corrupted")
return null
}
val classOneToManyStorage = JpsClassOneToManyStorage(
kotlinDataContainerPath.resolve(JpsKotlinCompilerReferenceIndexStorageImpl.SUBTYPES_STORAGE_NAME))
val storage = JpsKotlinCompilerReferenceIndexStorageImpl(lookupStorageReader, classOneToManyStorage)
if (!storage.initialize(buildDataPaths)) return null
return storage
}
}
internal val Project.buildDataPaths: BuildDataPaths
get() = BuildDataPathsImpl(BuildManager.getInstance().getProjectSystemDir(this))
internal val BuildDataPaths.kotlinDataContainer: Path?
get() = targetsDataRoot
.resolve(SettingConstants.KOTLIN_DATA_CONTAINER_ID)
.takeIf { it.exists() && it.isDirectory() }
?.listDirectoryEntries("${SettingConstants.KOTLIN_DATA_CONTAINER_ID}*")
?.firstOrNull()
@@ -1,17 +1,27 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.kotlin.idea.search.refIndex
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.kotlin.idea.search.refIndex.jps
import com.intellij.openapi.project.Project
import com.intellij.util.io.*
import org.jetbrains.kotlin.idea.search.refIndex.KotlinCompilerReferenceIndexStorage.Companion.buildDataPaths
import org.jetbrains.kotlin.idea.search.refIndex.KotlinCompilerReferenceIndexStorage.Companion.kotlinDataContainer
import org.jetbrains.kotlin.incremental.storage.*
import com.intellij.util.io.DataExternalizer
import com.intellij.util.io.EnumeratorStringDescriptor
import com.intellij.util.io.ExternalIntegerKeyDescriptor
import com.intellij.util.io.KeyDescriptor
import com.intellij.util.io.PersistentHashMap
import com.intellij.util.io.PersistentMapBuilder
import org.jetbrains.kotlin.incremental.storage.BasicMapsOwner
import org.jetbrains.kotlin.incremental.storage.IntCollectionExternalizer
import org.jetbrains.kotlin.incremental.storage.LookupSymbolKey
import org.jetbrains.kotlin.incremental.storage.LookupSymbolKeyDescriptor
import org.jetbrains.kotlin.incremental.storage.RelativeFileToPathConverter
import org.jetbrains.kotlin.name.FqName
import java.io.File
import java.nio.file.Path
import kotlin.collections.mapNotNull
import kotlin.collections.orEmpty
import kotlin.io.path.exists
class LookupStorageReader private constructor(
internal class JpsLookupStorageReader private constructor(
private val lookupStorage: PersistentHashMap<LookupSymbolKey, Collection<Int>>,
private val idToFileStorage: PersistentHashMap<Int, String>,
projectPath: String,
@@ -33,7 +43,7 @@ class LookupStorageReader private constructor(
}
companion object {
fun create(kotlinDataContainerPath: Path, projectPath: String): LookupStorageReader? {
fun create(kotlinDataContainerPath: Path, projectPath: String): JpsLookupStorageReader? {
val lookupStoragePath = kotlinDataContainerPath.resolve(LOOKUP_STORAGE_NAME).takeIf { it.exists() } ?: return null
val idToFileStoragePath = kotlinDataContainerPath.resolve(ID_TO_FILE_STORAGE_NAME).takeIf { it.exists() } ?: return null
val lookupStorage = openReadOnlyPersistentHashMap(
@@ -48,7 +58,7 @@ class LookupStorageReader private constructor(
EnumeratorStringDescriptor.INSTANCE,
)
return LookupStorageReader(lookupStorage, idToFileStorage, projectPath)
return JpsLookupStorageReader(lookupStorage, idToFileStorage, projectPath)
}
fun hasStorage(project: Project): Boolean = project.buildDataPaths