[rd debugger] Provide a way to handle mutable XDescriptors

GitOrigin-RevId: 28cebb63cc78d9b73c1003209aa027b07eb6cf5e
This commit is contained in:
Maksim Zuev
2025-08-20 10:54:24 +00:00
committed by intellij-monorepo-bot
parent 8fbb965fca
commit d05e2ad41e
3 changed files with 60 additions and 0 deletions
@@ -10,6 +10,8 @@
<extensionPoints>
<extensionPoint name="xdebugger.customQuickEvaluateActionProvider"
interface="com.intellij.platform.debugger.impl.frontend.actions.CustomQuickEvaluateActionProvider" dynamic="true"/>
<extensionPoint name="xdebugger.descriptorStateManager"
interface="com.intellij.platform.debugger.impl.frontend.FrontendDescriptorStateManagerExtension" dynamic="true"/>
</extensionPoints>
<actions resource-bundle="messages.FrontendDebuggerImplBundle">
<action id="QuickEvaluateExpression" class="com.intellij.platform.debugger.impl.frontend.actions.QuickEvaluateAction"/>
@@ -0,0 +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 com.intellij.platform.debugger.impl.frontend
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.project.Project
import com.intellij.xdebugger.frame.XDescriptor
import fleet.multiplatform.shims.ConcurrentHashMap
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.job
import org.jetbrains.annotations.ApiStatus
/**
* Creates a plugin-specific state for a given [XDescriptor].
* For example, if a [XDescriptor] contains a flow, this extension should create an object which will keep the flow's state.
*/
@ApiStatus.Internal
interface FrontendDescriptorStateManagerExtension {
fun createState(descriptor: XDescriptor, cs: CoroutineScope): Any?
companion object {
private val EP_NAME = ExtensionPointName.create<FrontendDescriptorStateManagerExtension>("com.intellij.xdebugger.descriptorStateManager")
fun createState(descriptor: XDescriptor, cs: CoroutineScope): Any? {
return EP_NAME.extensionList.firstNotNullOfOrNull { it.createState(descriptor, cs) }
}
}
}
@ApiStatus.Internal
@Service(Service.Level.PROJECT)
class FrontendDescriptorStateManager {
private val states = ConcurrentHashMap<XDescriptor, Any>()
internal fun registerDescriptor(descriptor: XDescriptor, cs: CoroutineScope) {
val state = FrontendDescriptorStateManagerExtension.createState(descriptor, cs) ?: return
states[descriptor] = state
cs.coroutineContext.job.invokeOnCompletion { states.remove(descriptor) }
}
fun getState(descriptor: XDescriptor): Any? {
return states[descriptor]
}
companion object {
@JvmStatic
fun getInstance(project: Project): FrontendDescriptorStateManager = project.service()
}
}
@@ -8,6 +8,7 @@ import com.intellij.openapi.components.service
import com.intellij.openapi.diagnostic.thisLogger
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.NlsSafe
import com.intellij.platform.debugger.impl.frontend.FrontendDescriptorStateManager
import com.intellij.platform.debugger.impl.rpc.*
import com.intellij.platform.util.coroutines.childScope
import com.intellij.util.ConcurrencyUtil
@@ -38,6 +39,13 @@ class FrontendXValue private constructor(
private val presentation: StateFlow<XValueSerializedPresentation>,
) : XValue(), XValueTextProvider {
init {
cs.launch {
val descriptor = xValueDto.descriptor?.await() ?: return@launch
FrontendDescriptorStateManager.getInstance(project).registerDescriptor(descriptor, cs)
}
}
@Volatile
private var modifier: XValueModifier? = null