[rdct] IJPL-190480: Use Proper API

GitOrigin-RevId: 2245830bae1b1611ae8bed8c0886e308c65b992e
This commit is contained in:
Sergei Kharitontcev-Beglov
2025-06-12 20:28:09 +02:00
committed by intellij-monorepo-bot
parent 7933bb5302
commit 4d6d5776ff
27 changed files with 272 additions and 355 deletions

2
.idea/modules.xml generated
View File

@@ -1164,6 +1164,8 @@
<module fileurl="file://$PROJECT_DIR$/plugins/turboComplete/languages/kotlin/k1/intellij.turboComplete.languages.kotlin.k1.iml" filepath="$PROJECT_DIR$/plugins/turboComplete/languages/kotlin/k1/intellij.turboComplete.languages.kotlin.k1.iml" />
<module fileurl="file://$PROJECT_DIR$/plugins/turboComplete/intellij.turboComplete.tests.iml" filepath="$PROJECT_DIR$/plugins/turboComplete/intellij.turboComplete.tests.iml" />
<module fileurl="file://$PROJECT_DIR$/plugins/ui-designer-core/intellij.uiDesigner.iml" filepath="$PROJECT_DIR$/plugins/ui-designer-core/intellij.uiDesigner.iml" />
<module fileurl="file://$PROJECT_DIR$/platform/managed-cache/util.io.cache/intellij.util.io.cache.iml" filepath="$PROJECT_DIR$/platform/managed-cache/util.io.cache/intellij.util.io.cache.iml" />
<module fileurl="file://$PROJECT_DIR$/platform/managed-cache/util.io.cache.backend/intellij.util.io.cache.backend.iml" filepath="$PROJECT_DIR$/platform/managed-cache/util.io.cache.backend/intellij.util.io.cache.backend.iml" />
<module fileurl="file://$PROJECT_DIR$/plugins/changeReminder/intellij.vcs.changeReminder.iml" filepath="$PROJECT_DIR$/plugins/changeReminder/intellij.vcs.changeReminder.iml" />
<module fileurl="file://$PROJECT_DIR$/plugins/git4idea/intellij.vcs.git.iml" filepath="$PROJECT_DIR$/plugins/git4idea/intellij.vcs.git.iml" />
<module fileurl="file://$PROJECT_DIR$/plugins/git-modal-commit/intellij.vcs.git.commit.modal.iml" filepath="$PROJECT_DIR$/plugins/git-modal-commit/intellij.vcs.git.commit.modal.iml" />

View File

@@ -24,11 +24,6 @@ jvm_library(
"//platform/util/coroutines",
"//platform/kernel/shared:kernel",
"//platform/kernel/pasta",
"//platform/project/shared:project",
"@lib//:kotlinx-serialization-json",
"@lib//:kotlinx-serialization-core",
"//platform/util/concurrency",
"//platform/kernel/backend",
],
runtime_deps = [":backend_resources"]
)

View File

@@ -1,31 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="kotlin-language" name="Kotlin">
<configuration version="5" platform="JVM 17" allPlatforms="JVM [17]" useProjectSettings="false">
<compilerSettings>
<option name="additionalArguments" value="-Xjvm-default=all" />
</compilerSettings>
<compilerArguments>
<stringArguments>
<stringArg name="jvmTarget" arg="17" />
<stringArg name="apiVersion" arg="2.2" />
<stringArg name="languageVersion" arg="2.2" />
</stringArguments>
<arrayArguments>
<arrayArg name="pluginClasspaths">
<args>
<arg>$KOTLIN_BUNDLED$/lib/kotlinx-serialization-compiler-plugin.jar</arg>
<arg>$MAVEN_REPOSITORY$/jetbrains/fleet/rhizomedb-compiler-plugin/2.2.0-RC2-0.1/rhizomedb-compiler-plugin-2.2.0-RC2-0.1.jar</arg>
<arg>$MAVEN_REPOSITORY$/com/jetbrains/fleet/rpc-compiler-plugin/2.2.0-RC2-0.1/rpc-compiler-plugin-2.2.0-RC2-0.1.jar</arg>
</args>
</arrayArg>
<arrayArg name="pluginOptions" />
</arrayArguments>
</compilerArguments>
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
@@ -45,10 +19,5 @@
<orderEntry type="module" module-name="intellij.platform.util.coroutines" />
<orderEntry type="module" module-name="intellij.platform.kernel" />
<orderEntry type="module" module-name="intellij.platform.pasta" />
<orderEntry type="module" module-name="intellij.platform.project" />
<orderEntry type="library" name="kotlinx-serialization-json" level="project" />
<orderEntry type="library" name="kotlinx-serialization-core" level="project" />
<orderEntry type="module" module-name="intellij.platform.concurrency" />
<orderEntry type="module" module-name="intellij.platform.kernel.backend" />
</component>
</module>

View File

@@ -2,12 +2,10 @@
<dependencies>
<module name="intellij.platform.backend"/>
<module name="intellij.platform.editor"/>
<module name="intellij.platform.kernel.backend"/>
</dependencies>
<extensions defaultExtensionNs="com.intellij">
<editorFactoryListener implementation="com.intellij.platform.editor.backend.BackendEditorFactoryListener"/>
<applicationService serviceImplementation="com.intellij.platform.editor.backend.BackendEditors"
client="remote"/>
<platform.rpc.backend.remoteApiProvider implementation="com.intellij.platform.editor.backend.zombie.RemoteManagedCacheApiImplProvider"/>
</extensions>
</idea-plugin>

View File

@@ -1,88 +0,0 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.platform.editor.backend.zombie
import com.intellij.concurrency.ConcurrentCollectionFactory
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.serviceAsync
import com.intellij.openapi.editor.impl.zombie.necropolisCacheNameAndPath
import com.intellij.openapi.project.Project
import com.intellij.platform.editor.zombie.rpc.CacheId
import com.intellij.platform.editor.zombie.rpc.PrefetchedRemoteCacheValue
import com.intellij.platform.editor.zombie.rpc.RemoteManagedCacheApi
import com.intellij.platform.editor.zombie.rpc.RemoteManagedCacheDto
import com.intellij.platform.project.findProjectOrNull
import com.intellij.util.io.DataExternalizer
import com.intellij.util.io.KeyDescriptor
import com.intellij.util.io.PersistentMapBuilder
import com.intellij.util.io.cache.ManagedCache
import com.intellij.util.io.cache.ManagedPersistentCache
import kotlinx.coroutines.CoroutineScope
import java.io.DataInput
import java.io.DataOutput
@Service(Service.Level.PROJECT)
private class RemoteManagedCacheManager(private val project: Project, private val coroutineScope: CoroutineScope) {
private val storage = ConcurrentCollectionFactory.createConcurrentMap<String, ManagedCache<RemoteManagedCacheDto, RemoteManagedCacheDto>>()
fun get(cacheId: CacheId): ManagedCache<RemoteManagedCacheDto, RemoteManagedCacheDto> {
return storage[cacheId.name]!!
}
suspend fun create(cacheId: CacheId): List<PrefetchedRemoteCacheValue> {
// RD and monolith caches could be handled, since Necropolis is loaded on backend too
val (name, path) = necropolisCacheNameAndPath("${cacheId.name}-backend", project)
val builder = PersistentMapBuilder.newBuilder(
path,
MyKeyDescriptor(),
Externalizer(),
).withVersion(SERDE_VERSION)
val cache = ManagedPersistentCache(name, builder, coroutineScope)
storage[cacheId.name] = cache
return cache.entries().map { (key, value) -> PrefetchedRemoteCacheValue(key, value) }
}
private open class Externalizer : DataExternalizer<RemoteManagedCacheDto> {
override fun save(out: DataOutput, value: RemoteManagedCacheDto) {
out.writeInt(value.data.size)
out.write(value.data)
}
override fun read(`in`: DataInput): RemoteManagedCacheDto {
val dataSize = `in`.readInt()
val data = ByteArray(dataSize)
`in`.readFully(data)
return RemoteManagedCacheDto(data)
}
}
private class MyKeyDescriptor : Externalizer(), KeyDescriptor<RemoteManagedCacheDto> {
override fun getHashCode(value: RemoteManagedCacheDto): Int = value.data.contentHashCode()
override fun isEqual(val1: RemoteManagedCacheDto?, val2: RemoteManagedCacheDto?): Boolean {
return val1?.data?.contentEquals(val2?.data) ?: (val2 == null)
}
}
companion object {
private const val SERDE_VERSION = 1
}
}
internal class RemoteManagedCacheApiImpl: RemoteManagedCacheApi {
private suspend fun CacheId.cache() = projectId.findProjectOrNull()?.serviceAsync<RemoteManagedCacheManager>()?.get(this)
override suspend fun get(cacheId: CacheId, key: RemoteManagedCacheDto): RemoteManagedCacheDto? {
return cacheId.cache()?.get(key)
}
override suspend fun put(cacheId: CacheId, key: RemoteManagedCacheDto, value: RemoteManagedCacheDto?) {
cacheId.cache()?.let {
if (value == null) {
it.remove(key)
} else {
it.put(key, value)
}
}
}
override suspend fun createPrefetchFlow(cacheId: CacheId): List<PrefetchedRemoteCacheValue> {
val project = cacheId.projectId.findProjectOrNull() ?: return emptyList()
return project.serviceAsync<RemoteManagedCacheManager>().create(cacheId)
}
}

View File

@@ -20,7 +20,6 @@ jvm_library(
"//platform/kernel/shared:kernel",
"//platform/platform-impl:ide-impl",
"//platform/kernel/pasta",
"//platform/project/shared:project",
],
runtime_deps = [":editor_resources"]
)

View File

@@ -1,31 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="kotlin-language" name="Kotlin">
<configuration version="5" platform="JVM 17" allPlatforms="JVM [17]" useProjectSettings="false">
<compilerSettings>
<option name="additionalArguments" value="-Xjvm-default=all" />
</compilerSettings>
<compilerArguments>
<stringArguments>
<stringArg name="jvmTarget" arg="17" />
<stringArg name="apiVersion" arg="2.2" />
<stringArg name="languageVersion" arg="2.2" />
</stringArguments>
<arrayArguments>
<arrayArg name="pluginClasspaths">
<args>
<arg>$KOTLIN_BUNDLED$/lib/kotlinx-serialization-compiler-plugin.jar</arg>
<arg>$MAVEN_REPOSITORY$/jetbrains/fleet/rhizomedb-compiler-plugin/2.2.0-RC2-0.1/rhizomedb-compiler-plugin-2.2.0-RC2-0.1.jar</arg>
<arg>$MAVEN_REPOSITORY$/com/jetbrains/fleet/rpc-compiler-plugin/2.2.0-RC2-0.1/rpc-compiler-plugin-2.2.0-RC2-0.1.jar</arg>
</args>
</arrayArg>
<arrayArg name="pluginOptions" />
</arrayArguments>
</compilerArguments>
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
@@ -41,6 +15,5 @@
<orderEntry type="module" module-name="intellij.platform.kernel" />
<orderEntry type="module" module-name="intellij.platform.ide.impl" />
<orderEntry type="module" module-name="intellij.platform.pasta" />
<orderEntry type="module" module-name="intellij.platform.project" />
</component>
</module>

View File

@@ -20,7 +20,6 @@ data class EditorEntity(override val eid: EID) : Entity {
val id: EditorId by idAttr
val clientId: ClientId by clientIdAttr
@Internal
companion object : DurableEntityType<EditorEntity>(
EditorEntity::class.java.name,
"com.intellij.platform.editor",

View File

@@ -1,29 +0,0 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.platform.editor.zombie.rpc
import com.intellij.util.io.DataExternalizer
import kotlinx.serialization.Serializable
import org.jetbrains.annotations.ApiStatus
import java.io.*
@Serializable
@ApiStatus.Internal
class RemoteManagedCacheDto(
val data: ByteArray,
) {
fun <V> toValue(externalizer: DataExternalizer<V>): V = ByteArrayInputStream(data).use {
DataInputStream(it).use { dataInput ->
externalizer.read(dataInput)
}
}
companion object {
fun<V> V.fromValue(externalizer: DataExternalizer<V>): RemoteManagedCacheDto {
return ByteArrayOutputStream().use {bao ->
DataOutputStream(bao).use { dataOutput: DataOutput ->
externalizer.save(dataOutput, this)
RemoteManagedCacheDto(bao.toByteArray())
}
}
}
}
}

View File

@@ -0,0 +1,30 @@
### auto-generated section `build intellij.util.io.cache.backend` start
load("@rules_jvm//:jvm.bzl", "jvm_library", "jvm_resources")
jvm_resources(
name = "util.io.cache.backend_resources",
files = glob(["resources/**/*"]),
strip_prefix = "resources"
)
jvm_library(
name = "util.io.cache.backend",
module_name = "intellij.util.io.cache.backend",
visibility = ["//visibility:public"],
srcs = glob(["src/**/*.kt", "src/**/*.java"], allow_empty = True),
deps = [
"@lib//:kotlin-stdlib",
"@lib//:jetbrains-annotations",
"//platform/kernel/backend",
"@lib//:kotlinx-serialization-core",
"@lib//:kotlinx-serialization-json",
"@lib//:kotlinx-coroutines-core",
"//platform/project/shared:project",
"//platform/util",
"//platform/platform-impl:ide-impl",
"//platform/managed-cache/util.io.cache",
"//platform/util/concurrency",
],
runtime_deps = [":util.io.cache.backend_resources"]
)
### auto-generated section `build intellij.util.io.cache.backend` end

View File

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="kotlin-language" name="Kotlin">
<configuration version="5" platform="JVM 17" allPlatforms="JVM [17]" useProjectSettings="false">
<compilerSettings>
<option name="additionalArguments" value="-Xjvm-default=all" />
</compilerSettings>
<compilerArguments>
<stringArguments>
<stringArg name="jvmTarget" arg="17" />
<stringArg name="apiVersion" arg="2.2" />
<stringArg name="languageVersion" arg="2.2" />
</stringArguments>
<arrayArguments>
<arrayArg name="pluginClasspaths">
<args>
<arg>$KOTLIN_BUNDLED$/lib/kotlinx-serialization-compiler-plugin.jar</arg>
<arg>$MAVEN_REPOSITORY$/jetbrains/fleet/rhizomedb-compiler-plugin/2.2.0-RC2-0.1/rhizomedb-compiler-plugin-2.2.0-RC2-0.1.jar</arg>
<arg>$MAVEN_REPOSITORY$/com/jetbrains/fleet/rpc-compiler-plugin/2.2.0-RC2-0.1/rpc-compiler-plugin-2.2.0-RC2-0.1.jar</arg>
</args>
</arrayArg>
<arrayArg name="pluginOptions" />
</arrayArguments>
</compilerArguments>
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" packagePrefix="com.intellij.util.io.cache.backend" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="kotlin-stdlib" level="project" />
<orderEntry type="library" name="jetbrains-annotations" level="project" />
<orderEntry type="module" module-name="intellij.platform.kernel.backend" />
<orderEntry type="library" name="kotlinx-serialization-core" level="project" />
<orderEntry type="library" name="kotlinx-serialization-json" level="project" />
<orderEntry type="library" name="kotlinx-coroutines-core" level="project" />
<orderEntry type="module" module-name="intellij.platform.project" />
<orderEntry type="module" module-name="intellij.platform.util" />
<orderEntry type="module" module-name="intellij.platform.ide.impl" />
<orderEntry type="module" module-name="intellij.util.io.cache" />
<orderEntry type="module" module-name="intellij.platform.concurrency" />
</component>
</module>

View File

@@ -0,0 +1,9 @@
<idea-plugin>
<dependencies>
<module name="intellij.platform.kernel.backend"/>
<module name="intellij.platform.backend"/>
</dependencies>
<extensions defaultExtensionNs="com.intellij">
<platform.rpc.backend.remoteApiProvider implementation="com.intellij.util.io.cache.backend.RemoteManagedCacheApiImplProvider"/>
</extensions>
</idea-plugin>

View File

@@ -0,0 +1,89 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.util.io.cache.backend
import com.intellij.concurrency.ConcurrentCollectionFactory
import com.intellij.openapi.application.PathManager
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.serviceAsync
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.getProjectCacheFileName
import com.intellij.platform.project.findProjectOrNull
import com.intellij.util.io.DataExternalizer
import com.intellij.util.io.KeyDescriptor
import com.intellij.util.io.PersistentMapBuilder
import com.intellij.util.io.cache.*
import kotlinx.coroutines.CoroutineScope
import java.io.DataInput
import java.io.DataOutput
import java.nio.file.Path
private fun remoteCacheLocation(): Path {
return PathManager.getSystemDir().resolve("remote-cache")
}
@Service(Service.Level.PROJECT)
private class RemoteManagedCacheManager(private val project: Project, private val coroutineScope: CoroutineScope) {
private val storage = ConcurrentCollectionFactory.createConcurrentMap<String, ManagedCache<ByteArray, ByteArray>>()
fun get(cacheId: CacheId): ManagedCache<ByteArray, ByteArray> {
return storage[cacheId.name]!!
}
suspend fun create(cacheId: CacheId, buildParams: RemoteManagedCacheBuildParams): List<PrefetchedRemoteCacheValue> {
val path = getCacheDir(cacheId.name, project)
val builder = PersistentMapBuilder.newBuilder(
path,
MyKeyDescriptor(),
Externalizer(),
).withVersion(buildParams.serdeVersion)
val cache = ManagedPersistentCache(cacheId.name, builder, coroutineScope)
storage[cacheId.name] = cache
return cache.entries().map { (key, value) -> PrefetchedRemoteCacheValue(key, value) }
}
private fun getCacheDir(cacheName: String, project: Project): Path {
val projectPart = project.getProjectCacheFileName(hashSeparator = "")
return remoteCacheLocation().resolve("$cacheName-$projectPart").resolve(cacheName)
}
private open class Externalizer : DataExternalizer<ByteArray> {
override fun save(out: DataOutput, value: ByteArray) {
out.writeInt(value.size)
out.write(value)
}
override fun read(`in`: DataInput): ByteArray {
val dataSize = `in`.readInt()
val data = ByteArray(dataSize)
`in`.readFully(data)
return data
}
}
private class MyKeyDescriptor : Externalizer(), KeyDescriptor<ByteArray> {
override fun getHashCode(value: ByteArray): Int = value.contentHashCode()
override fun isEqual(val1: ByteArray?, val2: ByteArray?): Boolean {
return val1?.contentEquals(val2) ?: (val2 == null)
}
}
}
internal class RemoteManagedCacheApiImpl: RemoteManagedCacheApi {
private suspend fun CacheId.cache() = projectId.findProjectOrNull()?.serviceAsync<RemoteManagedCacheManager>()?.get(this)
override suspend fun get(cacheId: CacheId, key: ByteArray): ByteArray? {
return cacheId.cache()?.get(key)
}
override suspend fun put(cacheId: CacheId, key: ByteArray, value: ByteArray?) {
cacheId.cache()?.let {
if (value == null) {
it.remove(key)
} else {
it.put(key, value)
}
}
}
override suspend fun createCacheAndProvideEntries(cacheId: CacheId, buildParams: RemoteManagedCacheBuildParams): List<PrefetchedRemoteCacheValue> {
val project = cacheId.projectId.findProjectOrNull() ?: return emptyList()
return project.serviceAsync<RemoteManagedCacheManager>().create(cacheId, buildParams)
}
}

View File

@@ -1,8 +1,8 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.platform.editor.backend.zombie
package com.intellij.util.io.cache.backend
import com.intellij.platform.editor.zombie.rpc.RemoteManagedCacheApi
import com.intellij.platform.rpc.backend.RemoteApiProvider
import com.intellij.util.io.cache.RemoteManagedCacheApi
import fleet.rpc.remoteApiDescriptor
private class RemoteManagedCacheApiImplProvider : RemoteApiProvider {

View File

@@ -0,0 +1,26 @@
### auto-generated section `build intellij.util.io.cache` start
load("@rules_jvm//:jvm.bzl", "jvm_library", "jvm_resources")
jvm_resources(
name = "util.io.cache_resources",
files = glob(["resources/**/*"]),
strip_prefix = "resources"
)
jvm_library(
name = "util.io.cache",
module_name = "intellij.util.io.cache",
visibility = ["//visibility:public"],
srcs = glob(["src/**/*.kt", "src/**/*.java"], allow_empty = True),
deps = [
"@lib//:kotlin-stdlib",
"@lib//:jetbrains-annotations",
"//platform/kernel/shared:kernel",
"@lib//:kotlinx-serialization-core",
"@lib//:kotlinx-serialization-json",
"@lib//:kotlinx-coroutines-core",
"//platform/project/shared:project",
],
runtime_deps = [":util.io.cache_resources"]
)
### auto-generated section `build intellij.util.io.cache` end

View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="kotlin-language" name="Kotlin">
<configuration version="5" platform="JVM 17" allPlatforms="JVM [17]" useProjectSettings="false">
<compilerSettings>
<option name="additionalArguments" value="-Xjvm-default=all" />
</compilerSettings>
<compilerArguments>
<stringArguments>
<stringArg name="jvmTarget" arg="17" />
<stringArg name="apiVersion" arg="2.2" />
<stringArg name="languageVersion" arg="2.2" />
</stringArguments>
<arrayArguments>
<arrayArg name="pluginClasspaths">
<args>
<arg>$KOTLIN_BUNDLED$/lib/kotlinx-serialization-compiler-plugin.jar</arg>
<arg>$MAVEN_REPOSITORY$/jetbrains/fleet/rhizomedb-compiler-plugin/2.2.0-RC2-0.1/rhizomedb-compiler-plugin-2.2.0-RC2-0.1.jar</arg>
<arg>$MAVEN_REPOSITORY$/com/jetbrains/fleet/rpc-compiler-plugin/2.2.0-RC2-0.1/rpc-compiler-plugin-2.2.0-RC2-0.1.jar</arg>
</args>
</arrayArg>
<arrayArg name="pluginOptions" />
</arrayArguments>
</compilerArguments>
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$/">
<sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" packagePrefix="com.intellij.util.io.cache" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="kotlin-stdlib" level="project" />
<orderEntry type="library" name="jetbrains-annotations" level="project" />
<orderEntry type="module" module-name="intellij.platform.kernel" />
<orderEntry type="library" name="kotlinx-serialization-core" level="project" />
<orderEntry type="library" name="kotlinx-serialization-json" level="project" />
<orderEntry type="library" name="kotlinx-coroutines-core" level="project" />
<orderEntry type="module" module-name="intellij.platform.project" />
</component>
</module>

View File

@@ -0,0 +1,2 @@
<idea-plugin>
</idea-plugin>

View File

@@ -1,5 +1,5 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.platform.editor.zombie.rpc
package com.intellij.util.io.cache
import com.intellij.platform.project.ProjectId
import kotlinx.serialization.Serializable

View File

@@ -1,9 +1,9 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.platform.editor.zombie.rpc
package com.intellij.util.io.cache
import kotlinx.serialization.Serializable
import org.jetbrains.annotations.ApiStatus
@ApiStatus.Internal
@Serializable
class PrefetchedRemoteCacheValue(val key: RemoteManagedCacheDto, val value: RemoteManagedCacheDto)
class PrefetchedRemoteCacheValue(val key: ByteArray, val value: ByteArray)

View File

@@ -1,5 +1,5 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.platform.editor.zombie.rpc
package com.intellij.util.io.cache
import com.intellij.platform.rpc.RemoteApiProviderService
import fleet.rpc.RemoteApi
@@ -10,10 +10,10 @@ import org.jetbrains.annotations.ApiStatus
@ApiStatus.Internal
@Rpc
interface RemoteManagedCacheApi : RemoteApi<Unit> {
suspend fun get(cacheId: CacheId, key: RemoteManagedCacheDto): RemoteManagedCacheDto?
suspend fun put(cacheId: CacheId, key: RemoteManagedCacheDto, value: RemoteManagedCacheDto?)
// Used for linearization on creation & pre-fetching
suspend fun createPrefetchFlow(cacheId: CacheId): List<PrefetchedRemoteCacheValue>
suspend fun get(cacheId: CacheId, key: ByteArray): ByteArray?
suspend fun put(cacheId: CacheId, key: ByteArray, value: ByteArray?)
// Used for linearization on creation and pre-fetching
suspend fun createCacheAndProvideEntries(cacheId: CacheId, buildParams: RemoteManagedCacheBuildParams): List<PrefetchedRemoteCacheValue>
companion object {
suspend fun getInstance(): RemoteManagedCacheApi {

View File

@@ -1,13 +1,9 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.util.io.cache
import kotlinx.coroutines.Deferred
import kotlinx.serialization.Serializable
import org.jetbrains.annotations.ApiStatus
@ApiStatus.Internal
interface RemoteManagedCache<K, V>: ManagedCache<K, V> {
/**
* A deferred content that delivered from remote source
*/
val preloadedContent: Deferred<List<Map.Entry<K, V>>>
}
@Serializable
data class RemoteManagedCacheBuildParams(val serdeVersion: Int)

View File

@@ -21,10 +21,8 @@ import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.FileIdAdapter
import com.intellij.openapi.project.getProjectCacheFileName
import com.intellij.openapi.vfs.VirtualFile
import kotlinx.coroutines.*
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.annotations.TestOnly
import java.nio.file.Path
import java.util.concurrent.CancellationException
@@ -34,21 +32,10 @@ private val LOG: Logger = logger<Necropolis>()
private val NECROMANCER_EP = ExtensionPointName<NecromancerAwaker<Zombie>>("com.intellij.textEditorNecromancerAwaker")
@ApiStatus.Internal
fun necropolisPath(): Path {
internal fun necropolisPath(): Path {
return PathManager.getSystemDir().resolve("editor")
}
@ApiStatus.Internal
fun necropolisCacheNameAndPath(graveName: String, project: Project): Pair<String, Path> {
// IJPL-157893 the cache should survive project renaming
val projectName = project.getProjectCacheFileName(hashSeparator="-")
val projectPath = necropolisPath().resolve(projectName)
val cacheName = "$graveName-$projectName" // name should be unique across the application
val cachePath = projectPath.resolve(graveName).resolve(graveName)
return cacheName to cachePath
}
/**
* Service managing all necromancers.
*
@@ -74,7 +61,6 @@ class Necropolis(private val project: Project, private val coroutineScope: Corou
}
}
suspend fun spawnZombies(
project: Project,
file: VirtualFile,

View File

@@ -1,31 +0,0 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.openapi.editor.impl.zombie
import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.project.Project
import com.intellij.util.io.DataExternalizer
import com.intellij.util.io.cache.RemoteManagedCache
import kotlinx.coroutines.CoroutineScope
interface RemoteCacheFactory {
fun<K, V> createCache(
cacheName: String,
keyExternalizer: DataExternalizer<K>,
valueExternalizer: DataExternalizer<V>,
project: Project,
coroutineScope: CoroutineScope
): RemoteManagedCache<K, V>
companion object {
val EP: ExtensionPointName<RemoteCacheFactory> = ExtensionPointName<RemoteCacheFactory>("com.intellij.remoteManagedCacheFactory")
fun<K, V> tryCreateCache(
cacheName: String,
keyExternalizer: DataExternalizer<K>,
valueExternalizer: DataExternalizer<V>,
project: Project,
coroutineScope: CoroutineScope
): RemoteManagedCache<K, V>? {
val remoteCacheFactory = EP.findFirstSafe { true }
return remoteCacheFactory?.createCache(cacheName, keyExternalizer, valueExternalizer, project, coroutineScope)
}
}
}

View File

@@ -1,104 +0,0 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.openapi.editor.impl.zombie
import com.intellij.openapi.project.Project
import com.intellij.util.io.DataExternalizer
import com.intellij.util.io.DataInputOutputUtil.readLONG
import com.intellij.util.io.DataInputOutputUtil.writeLONG
import com.intellij.util.io.EnumeratorIntegerDescriptor
import com.intellij.util.io.PersistentMapBuilder
import com.intellij.util.io.cache.ManagedPersistentCache
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import java.io.DataInput
import java.io.DataOutput
internal class RemoteLocalCache<Z: Zombie>(
private val localCache: Cache<Z>,
private val remoteCache: RemoteCache<Z>?
) : Cache<Z> {
override suspend fun put(key: Int, value: FingerprintedZombie<Z>) {
coroutineScope {
async { localCache.put(key, value) }
async { remoteCache?.put(key, value) }
}
}
suspend fun prefetch(id: Int) {
if (localCache.get(id) != null) return
remoteCache?.get(id)?.let { localCache.put(id, it) }
}
override suspend fun get(key: Int): FingerprintedZombie<Z>? {
return localCache.get(key) ?: remoteCache?.get(key)?.also { localCache.put(key, it) }
}
override suspend fun remove(key: Int) {
coroutineScope {
async { localCache.remove(key) }
async { remoteCache?.remove(key) }
}
}
companion object {
@Suppress("UNCHECKED_CAST")
fun <Z: Zombie> create(
cacheName: String,
necromancy: Necromancy<Z>,
project: Project,
coroutineScope: CoroutineScope
): RemoteLocalCache<Z> {
val localCache = createLocalCache(cacheName, necromancy, project, coroutineScope)
val remoteCache = RemoteCacheFactory
.tryCreateCache<Int, FingerprintedZombie<Z>>(
cacheName,
EnumeratorIntegerDescriptor.INSTANCE,
FingerprintedExternalizer(necromancy),
project,
coroutineScope
)
remoteCache?.preload(coroutineScope, localCache)
return RemoteLocalCache(
localCache = localCache,
remoteCache = remoteCache,
)
}
private fun<Z: Zombie> RemoteCache<Z>.preload(coroutineScope: CoroutineScope, localCache: Cache<Z>) = coroutineScope.launch {
val preloadedFromReload = preloadedContent.await()
preloadedFromReload.forEach { (key, value) -> localCache.put(key, value) }
}
private fun<Z: Zombie> createLocalCache(cacheName: String, necromancy: Necromancy<Z>, project: Project, coroutineScope: CoroutineScope): Cache<Z> {
val (cacheName, cachePath) = necropolisCacheNameAndPath(cacheName, project)
val builder = PersistentMapBuilder.newBuilder(
cachePath,
EnumeratorIntegerDescriptor.INSTANCE,
FingerprintedExternalizer(necromancy),
).withVersion(necromancy.spellLevel())
return if (necromancy.isDeepBury()) {
ManagedPersistentCache(cacheName, builder, coroutineScope)
} else {
// TODO: heap implementation
ManagedPersistentCache(cacheName, builder, coroutineScope)
}
}
}
}
private class FingerprintedExternalizer<Z : Zombie>(
private val necromancy: Necromancy<Z>,
) : DataExternalizer<FingerprintedZombie<Z>> {
override fun read(input: DataInput): FingerprintedZombie<Z> {
val fingerprint = readLONG(input)
val zombie = necromancy.exhumeZombie(input)
return FingerprintedZombieImpl(fingerprint, zombie)
}
override fun save(output: DataOutput, value: FingerprintedZombie<Z>) {
writeLONG(output, value.fingerprint())
necromancy.buryZombie(output, value.zombie())
}
}

View File

@@ -45,16 +45,17 @@ class ManagedPersistentCache<K, V> @OptIn(ExperimentalCoroutinesApi::class) cons
withPersistentMap(opName="put") { map ->
map.put(key, value)
}
forceAsync()
}
suspend fun entries(): List<Map.Entry<K, V>> = withPersistentMap(opName = "entries") { map ->
suspend fun entries(): List<Map.Entry<K, V>> {
return withPersistentMap(opName = "entries") { map ->
val list = buildList {
map.processExistingKeys { key -> add(Entry<K, V>(key, map.get(key)!!)) }
}
list
}.orEmpty()
}
override suspend fun get(key: K): V? {
return withPersistentMap(opName="get") { map ->

View File

@@ -579,7 +579,6 @@
<extensionPoint name="mac.dockMenuActions" interface="com.intellij.ui.mac.MacDockMenuActions" dynamic="false"/>
<extensionPoint name="textEditorNecromancerAwaker" interface="com.intellij.openapi.editor.impl.zombie.NecromancerAwaker" dynamic="true"/>
<extensionPoint name="remoteManagedCacheFactory" interface="com.intellij.openapi.editor.impl.zombie.RemoteCacheFactory" dynamic="true"/>
<extensionPoint name="toolbarQuickAction" beanClass="com.intellij.ide.ui.customization.ToolbarAddQuickActionInfoBean" dynamic="true">
<with attribute="implementationClass" implements="com.intellij.ide.ui.customization.ToolbarAddQuickActionInfo"/>

View File

@@ -38,9 +38,11 @@
-->
<module name="intellij.platform.find" loading="embedded"/>
<module name="intellij.platform.find.backend"/>
<module name="intellij.platform.editor" loading="embedded"/>
<module name="intellij.platform.editor"/>
<module name="intellij.platform.editor.backend"/>
<module name="intellij.platform.editor.frontend"/>
<module name="intellij.util.io.cache" loading="embedded"/>
<module name="intellij.util.io.cache.backend"/>
<module name="intellij.platform.debugger.impl.frontend"/>
<module name="intellij.platform.debugger.impl.backend"/>