mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[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:
committed by
intellij-monorepo-bot
parent
023bbcde39
commit
a5f1d8b396
+13
@@ -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"
|
||||
|
||||
+3
-1
@@ -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
|
||||
}
|
||||
|
||||
|
||||
+35
-162
@@ -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),
|
||||
)
|
||||
}
|
||||
+54
@@ -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) }
|
||||
}
|
||||
}
|
||||
+8
-10
@@ -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>()
|
||||
}
|
||||
}
|
||||
+143
@@ -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),
|
||||
)
|
||||
+61
@@ -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()
|
||||
+19
-9
@@ -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
|
||||
Reference in New Issue
Block a user