From 7d2044249148b8a54ad035aa22a807a7a3bebd7e Mon Sep 17 00:00:00 2001 From: Maksim Zuev Date: Fri, 14 Nov 2025 14:07:12 +0100 Subject: [PATCH] [rd debugger] IJPL-215857 Refactor: introduce XContainerId to unify computeChildren processing IJ-CR-181826 GitOrigin-RevId: 8da907315334878315bae09eedc855b253e14610 --- .../impl/backend/BackendXExecutionStackApi.kt | 5 -- .../debugger/impl/backend/BackendXValueApi.kt | 74 ++++++++----------- .../frontend/evaluate/quick/FrontendXValue.kt | 4 +- .../evaluate/quick/FrontendXValueContainer.kt | 9 +-- .../evaluate/quick/FrontendXValueGroup.kt | 5 +- .../frontend/frame/FrontendXStackFrame.kt | 4 +- .../frontend/frame/VariablesPreloadManager.kt | 9 +-- .../debugger/impl/rpc/XExecutionStackApi.kt | 4 +- .../platform/debugger/impl/rpc/XValueApi.kt | 35 +++------ 9 files changed, 52 insertions(+), 97 deletions(-) diff --git a/platform/xdebugger-impl/backend/src/com/intellij/platform/debugger/impl/backend/BackendXExecutionStackApi.kt b/platform/xdebugger-impl/backend/src/com/intellij/platform/debugger/impl/backend/BackendXExecutionStackApi.kt index 646b8ee62a6d..46557fe920c7 100644 --- a/platform/xdebugger-impl/backend/src/com/intellij/platform/debugger/impl/backend/BackendXExecutionStackApi.kt +++ b/platform/xdebugger-impl/backend/src/com/intellij/platform/debugger/impl/backend/BackendXExecutionStackApi.kt @@ -62,11 +62,6 @@ internal class BackendXExecutionStackApi : XExecutionStackApi { }.buffer(Channel.UNLIMITED) } - override fun computeVariables(xStackFrameId: XStackFrameId): Flow { - val stackFrameModel = xStackFrameId.findValue() ?: return emptyFlow() - return computeContainerChildren(stackFrameModel.coroutineScope, stackFrameModel.stackFrame, stackFrameModel.session) - } - override suspend fun canDrop(sessionId: XDebugSessionId, stackFrameId: XStackFrameId): Boolean { val session = sessionId.findValue() ?: return false val stack = stackFrameId.findValue() ?: return false diff --git a/platform/xdebugger-impl/backend/src/com/intellij/platform/debugger/impl/backend/BackendXValueApi.kt b/platform/xdebugger-impl/backend/src/com/intellij/platform/debugger/impl/backend/BackendXValueApi.kt index bfeaaebf63f6..c710aaa41749 100644 --- a/platform/xdebugger-impl/backend/src/com/intellij/platform/debugger/impl/backend/BackendXValueApi.kt +++ b/platform/xdebugger-impl/backend/src/com/intellij/platform/debugger/impl/backend/BackendXValueApi.kt @@ -31,19 +31,13 @@ internal class BackendXValueApi : XValueApi { return xValueModel.computeTooltipPresentation() } - override fun computeChildren(xValueId: XValueId): Flow { - val xValueModel = BackendXValueModel.findById(xValueId) ?: return emptyFlow() - return computeContainerChildren(xValueModel.cs, xValueModel.xValue, xValueModel.session) - } - - override fun computeXValueGroupChildren(xValueGroupId: XValueGroupId): Flow { - val xValueModel = xValueGroupId.findValue() ?: return emptyFlow() - return computeContainerChildren(xValueModel.cs, xValueModel.xValueGroup, xValueModel.session) + override fun computeChildren(id: XContainerId): Flow { + return computeChildrenInternal(id) } override fun computeExpandedChildren(frameId: XStackFrameId, root: XDebuggerTreeExpandedNode): Flow { return channelFlow { - processExpandedChildren(XContainer.Frame(frameId), this, root) + processExpandedChildren(frameId, this, root) } } @@ -177,7 +171,7 @@ private class AddNextChildrenCallbackHandler(cs: CoroutineScope) { } @Suppress("OPT_IN_USAGE") -internal fun computeContainerChildren( +private fun computeContainerChildren( parentCs: CoroutineScope, xValueContainer: XValueContainer, session: XDebugSessionImpl, @@ -326,48 +320,42 @@ private sealed interface RawComputeChildrenEvent { } } -private sealed interface XContainer { - fun createExpandEvent(event: XValueComputeChildrenEvent): PreloadChildrenEvent.ExpandedChildrenEvent - fun createPreloadEvent(): PreloadChildrenEvent.ToBePreloaded - suspend fun computeChildren(): Flow - - data class Value(val id: XValueId) : XContainer { - override fun createExpandEvent(event: XValueComputeChildrenEvent) = PreloadChildrenEvent.ExpandedChildrenEvent.Value(id, event) - override fun createPreloadEvent() = PreloadChildrenEvent.ToBePreloaded.Value(id) - override suspend fun computeChildren() = XValueApi.getInstance().computeChildren(id) - } - - data class Group(val id: XValueGroupId) : XContainer { - override fun createExpandEvent(event: XValueComputeChildrenEvent) = PreloadChildrenEvent.ExpandedChildrenEvent.Group(id, event) - override fun createPreloadEvent() = PreloadChildrenEvent.ToBePreloaded.Group(id) - override suspend fun computeChildren() = XValueApi.getInstance().computeXValueGroupChildren(id) - } - - data class Frame(val id: XStackFrameId) : XContainer { - override fun createExpandEvent(event: XValueComputeChildrenEvent) = PreloadChildrenEvent.ExpandedChildrenEvent.Frame(id, event) - override fun createPreloadEvent() = error("Frame preload event is implicit") - override suspend fun computeChildren() = XExecutionStackApi.getInstance().computeVariables(id) +private fun computeChildrenInternal(containerId: XContainerId): Flow { + val (container, scope, session) = when (containerId) { + is XStackFrameId -> { + val stackFrameModel = containerId.findValue() ?: return emptyFlow() + Triple(stackFrameModel.stackFrame, stackFrameModel.coroutineScope, stackFrameModel.session) + } + is XValueGroupId -> { + val xGroupModel = containerId.findValue() ?: return emptyFlow() + Triple(xGroupModel.xValueGroup, xGroupModel.cs, xGroupModel.session) + } + is XValueId -> { + val xValueModel = BackendXValueModel.findById(containerId) ?: return emptyFlow() + Triple(xValueModel.xValue, xValueModel.cs, xValueModel.session) + } } + return computeContainerChildren(scope, container, session) } /** - * Match [XContainer] children with [XDebuggerTreeExpandedNode] children, and start child computation if matched. + * Match [XContainerId] children with [XDebuggerTreeExpandedNode] children, and start child computation if matched. */ -private suspend fun processExpandedChildren(id: XContainer, producerScope: ProducerScope, root: XDebuggerTreeExpandedNode) { +private suspend fun processExpandedChildren(id: XContainerId, producerScope: ProducerScope, root: XDebuggerTreeExpandedNode) { val name2Child = MultiMap() for (child in root.children) { name2Child.putValue(child.name, child) } - val childrenEventsFlow = id.computeChildren() + val childrenEventsFlow = computeChildrenInternal(id) childrenEventsFlow.collect { event -> val childrenToLoad = collectChildrenToLoad(event, name2Child) // notify which children are going to be preloaded for ((childId, _) in childrenToLoad) { - producerScope.send(childId.createPreloadEvent()) + producerScope.send(PreloadChildrenEvent.ToBePreloaded(childId)) } // then pass the original event - producerScope.send(id.createExpandEvent(event)) + producerScope.send(PreloadChildrenEvent.ExpandedChildrenEvent(id, event)) // and then start children loading for ((childId, node) in childrenToLoad) { producerScope.launch { @@ -380,9 +368,9 @@ private suspend fun processExpandedChildren(id: XContainer, producerScope: Produ private fun collectChildrenToLoad( event: XValueComputeChildrenEvent, name2Child: MultiMap, -): List> { +): List> { if (event !is XValueComputeChildrenEvent.AddChildren) return emptyList() - val children = mutableListOf>() + val children = mutableListOf>() val namedContainers = collectNamedChildren(event) for ((name, childId) in namedContainers) { @@ -395,17 +383,17 @@ private fun collectChildrenToLoad( return children } -private fun collectNamedChildren(event: XValueComputeChildrenEvent.AddChildren): List> { - val nameAndId = mutableListOf>() +private fun collectNamedChildren(event: XValueComputeChildrenEvent.AddChildren): List> { + val nameAndId = mutableListOf>() for (group in event.topGroups) { - nameAndId += group.groupName to XContainer.Group(group.id) + nameAndId += group.groupName to group.id } for (value in event.topValues + event.children) { val name = value.name ?: continue - nameAndId += name to XContainer.Value(value.id) + nameAndId += name to value.id } for (group in event.bottomGroups) { - nameAndId += group.groupName to XContainer.Group(group.id) + nameAndId += group.groupName to group.id } return nameAndId } diff --git a/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/evaluate/quick/FrontendXValue.kt b/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/evaluate/quick/FrontendXValue.kt index 806251ffa2c8..ea3feaf8b368 100644 --- a/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/evaluate/quick/FrontendXValue.kt +++ b/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/evaluate/quick/FrontendXValue.kt @@ -68,9 +68,7 @@ class FrontendXValue private constructor( var canMarkValue: Boolean = false private set - private val xValueContainer = FrontendXValueContainer(project, cs, hasParentValue, xValueDto.id) { - XValueApi.getInstance().computeChildren(xValueDto.id) - } + private val xValueContainer = FrontendXValueContainer(project, cs, hasParentValue, xValueDto.id) private val fullValueEvaluator = fullValueEvaluatorFlow.map { evaluatorDto -> if (evaluatorDto == null) { diff --git a/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/evaluate/quick/FrontendXValueContainer.kt b/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/evaluate/quick/FrontendXValueContainer.kt index 54d0648ba189..9f58d3008f0b 100644 --- a/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/evaluate/quick/FrontendXValueContainer.kt +++ b/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/evaluate/quick/FrontendXValueContainer.kt @@ -5,10 +5,11 @@ import com.intellij.ide.ui.icons.icon import com.intellij.openapi.application.EDT import com.intellij.openapi.project.Project import com.intellij.platform.debugger.impl.frontend.frame.PreloadManagerContainer +import com.intellij.platform.debugger.impl.rpc.XContainerId +import com.intellij.platform.debugger.impl.rpc.XValueApi import com.intellij.platform.debugger.impl.rpc.XValueComputeChildrenEvent import com.intellij.platform.debugger.impl.rpc.XValueGroupDto import com.intellij.platform.debugger.impl.shared.XValuesPresentationBuilder -import com.intellij.platform.rpc.Id import com.intellij.ui.SimpleTextAttributes import com.intellij.xdebugger.frame.XCompositeNode import com.intellij.xdebugger.frame.XNamedValue @@ -16,20 +17,18 @@ import com.intellij.xdebugger.frame.XValueChildrenList import com.intellij.xdebugger.frame.XValueContainer import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.launch internal class FrontendXValueContainer( private val project: Project, private val cs: CoroutineScope, private val hasParentValue: Boolean, - private val id: Id, - private val childrenComputation: suspend () -> Flow, + private val id: XContainerId, ) : XValueContainer() { override fun computeChildren(node: XCompositeNode) { val preloadManager = cs.coroutineContext[PreloadManagerContainer]?.manager node.childCoroutineScope(parentScope = cs, "FrontendXValueContainer#computeChildren").launch(Dispatchers.EDT) { - val flow = preloadManager?.getChildrenEventsFlow(id) ?: childrenComputation() + val flow = preloadManager?.getChildrenEventsFlow(id) ?: XValueApi.getInstance().computeChildren(id) val builder = XValuesPresentationBuilder() flow.collect { event -> when (event) { diff --git a/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/evaluate/quick/FrontendXValueGroup.kt b/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/evaluate/quick/FrontendXValueGroup.kt index 2ab14cbc3988..0b1583555f15 100644 --- a/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/evaluate/quick/FrontendXValueGroup.kt +++ b/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/evaluate/quick/FrontendXValueGroup.kt @@ -4,7 +4,6 @@ package com.intellij.platform.debugger.impl.frontend.evaluate.quick import com.intellij.ide.ui.icons.icon import com.intellij.openapi.project.Project import com.intellij.openapi.util.NlsSafe -import com.intellij.platform.debugger.impl.rpc.XValueApi import com.intellij.platform.debugger.impl.rpc.XValueGroupDto import com.intellij.xdebugger.frame.XCompositeNode import com.intellij.xdebugger.frame.XValueGroup @@ -18,9 +17,7 @@ internal class FrontendXValueGroup( hasParentValue: Boolean, ) : XValueGroup(xValueGroupDto.groupName) { - private val xValueContainer = FrontendXValueContainer(project, cs, hasParentValue, xValueGroupDto.id) { - XValueApi.getInstance().computeXValueGroupChildren(xValueGroupDto.id) - } + private val xValueContainer = FrontendXValueContainer(project, cs, hasParentValue, xValueGroupDto.id) override fun computeChildren(node: XCompositeNode) { xValueContainer.computeChildren(node) diff --git a/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/frame/FrontendXStackFrame.kt b/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/frame/FrontendXStackFrame.kt index 3ed58abb0100..d50b8644cf8f 100644 --- a/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/frame/FrontendXStackFrame.kt +++ b/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/frame/FrontendXStackFrame.kt @@ -43,9 +43,7 @@ internal class FrontendXStackFrame( createFrontendXDebuggerEvaluator(project, suspendContextLifetimeScope, evaluatorDto, id) } - private val xValueContainer = FrontendXValueContainer(project, cs, false, id) { - XExecutionStackApi.getInstance().computeVariables(id) - } + private val xValueContainer = FrontendXValueContainer(project, cs, false, id) val backgroundColor: Color? get() = bgColor?.colorId?.color() diff --git a/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/frame/VariablesPreloadManager.kt b/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/frame/VariablesPreloadManager.kt index 595a2c5530f4..adb5e1526064 100644 --- a/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/frame/VariablesPreloadManager.kt +++ b/platform/xdebugger-impl/frontend/src/com/intellij/platform/debugger/impl/frontend/frame/VariablesPreloadManager.kt @@ -3,11 +3,9 @@ package com.intellij.platform.debugger.impl.frontend.frame import com.intellij.openapi.diagnostic.fileLogger import com.intellij.platform.debugger.impl.rpc.* -import com.intellij.platform.rpc.Id import com.intellij.platform.util.coroutines.childScope import com.intellij.util.AwaitCancellationAndInvoke import com.intellij.xdebugger.impl.ui.tree.XDebuggerTreeState -import fleet.multiplatform.shims.ConcurrentHashMap import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.cancel import kotlinx.coroutines.channels.Channel @@ -17,6 +15,7 @@ import kotlinx.coroutines.flow.channelFlow import kotlinx.coroutines.launch import org.jetbrains.annotations.ApiStatus import org.jetbrains.annotations.VisibleForTesting +import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.atomic.AtomicReference import kotlin.coroutines.AbstractCoroutineContextElement import kotlin.coroutines.CoroutineContext @@ -50,7 +49,7 @@ class VariablesPreloadManager( frameId: XStackFrameId, ) { private val cs = parentScope.childScope("VariablesPreloadManager") - private val preloadedEvents = ConcurrentHashMap>() + private val preloadedEvents = ConcurrentHashMap>() init { markToBeLoaded(frameId) @@ -72,14 +71,14 @@ class VariablesPreloadManager( } } - fun getChildrenEventsFlow(entityId: Id): Flow? { + fun getChildrenEventsFlow(entityId: XContainerId): Flow? { val eventsChannel = preloadedEvents[entityId] ?: return null return channelFlow { eventsChannel.consumeEach { send(it) } } } - private fun markToBeLoaded(id: Id) { + private fun markToBeLoaded(id: XContainerId) { val old = preloadedEvents.put(id, Channel(capacity = Channel.UNLIMITED)) assert(old == null) { "Channel for $id was already registered" } } diff --git a/platform/xdebugger-impl/rpc/src/com/intellij/platform/debugger/impl/rpc/XExecutionStackApi.kt b/platform/xdebugger-impl/rpc/src/com/intellij/platform/debugger/impl/rpc/XExecutionStackApi.kt index 8b0c369404d3..839675e6eeef 100644 --- a/platform/xdebugger-impl/rpc/src/com/intellij/platform/debugger/impl/rpc/XExecutionStackApi.kt +++ b/platform/xdebugger-impl/rpc/src/com/intellij/platform/debugger/impl/rpc/XExecutionStackApi.kt @@ -17,8 +17,6 @@ interface XExecutionStackApi : RemoteApi { suspend fun computeStackFrames(executionStackId: XExecutionStackId, firstFrameIndex: Int): Flow - fun computeVariables(xStackFrameId: XStackFrameId): Flow - suspend fun canDrop(sessionId: XDebugSessionId, stackFrameId: XStackFrameId): Boolean suspend fun dropFrame(sessionId: XDebugSessionId, stackFrameId: XStackFrameId) @@ -42,7 +40,7 @@ data class XExecutionStackId(override val uid: UID) : Id */ @ApiStatus.Internal @Serializable -data class XStackFrameId(override val uid: UID) : Id +data class XStackFrameId(override val uid: UID) : XContainerId @ApiStatus.Internal @Serializable diff --git a/platform/xdebugger-impl/rpc/src/com/intellij/platform/debugger/impl/rpc/XValueApi.kt b/platform/xdebugger-impl/rpc/src/com/intellij/platform/debugger/impl/rpc/XValueApi.kt index f514385b6596..fa4e00ed583c 100644 --- a/platform/xdebugger-impl/rpc/src/com/intellij/platform/debugger/impl/rpc/XValueApi.kt +++ b/platform/xdebugger-impl/rpc/src/com/intellij/platform/debugger/impl/rpc/XValueApi.kt @@ -18,8 +18,7 @@ import org.jetbrains.annotations.ApiStatus interface XValueApi : RemoteApi { suspend fun computeTooltipPresentation(xValueId: XValueId): Flow - fun computeChildren(xValueId: XValueId): Flow - fun computeXValueGroupChildren(xValueGroupId: XValueGroupId): Flow + fun computeChildren(id: XContainerId): Flow fun computeExpandedChildren(frameId: XStackFrameId, root: XDebuggerTreeExpandedNode): Flow suspend fun disposeXValue(xValueId: XValueId) @@ -40,17 +39,21 @@ interface XValueApi : RemoteApi { } } +@ApiStatus.Internal +@Serializable +sealed interface XContainerId : Id + /** * @see com.intellij.xdebugger.impl.rpc.models.BackendXValueModel */ @ApiStatus.Internal @Serializable -data class XValueId(override val uid: UID) : Id +data class XValueId(override val uid: UID) : XContainerId /** @see com.intellij.xdebugger.impl.rpc.models.BackendXValueGroupModel */ @ApiStatus.Internal @Serializable -data class XValueGroupId(override val uid: UID) : Id +data class XValueGroupId(override val uid: UID) : XContainerId @ApiStatus.Internal @Serializable @@ -70,28 +73,8 @@ data class XDebuggerTreeExpandedNode( @Serializable sealed interface PreloadChildrenEvent { @Serializable - sealed interface ToBePreloaded : PreloadChildrenEvent { - val id: Id - - @Serializable - data class Value(override val id: XValueId) : ToBePreloaded - - @Serializable - data class Group(override val id: XValueGroupId) : ToBePreloaded - } + data class ToBePreloaded(val id: XContainerId) : PreloadChildrenEvent @Serializable - sealed interface ExpandedChildrenEvent : PreloadChildrenEvent { - val id: Id - val event: XValueComputeChildrenEvent - - @Serializable - data class Frame(override val id: XStackFrameId, override val event: XValueComputeChildrenEvent) : ExpandedChildrenEvent - - @Serializable - data class Value(override val id: XValueId, override val event: XValueComputeChildrenEvent) : ExpandedChildrenEvent - - @Serializable - data class Group(override val id: XValueGroupId, override val event: XValueComputeChildrenEvent) : ExpandedChildrenEvent - } + data class ExpandedChildrenEvent(val id: XContainerId, val event: XValueComputeChildrenEvent) : PreloadChildrenEvent }