IJPL-195558 [debugger] Collection visualizer as a split bundled plugin

GitOrigin-RevId: 0ee6b4828ac757727c155c8f3eca0e57d14be3b4
This commit is contained in:
Alexander Kuznetsov
2025-08-29 15:58:45 +00:00
committed by intellij-monorepo-bot
parent a1566e6956
commit 67f992941a
8 changed files with 72 additions and 67 deletions
@@ -272,8 +272,6 @@ user.renderers.configurable.display.name=Java Type Renderers
async.stacktraces.configurable.display.name=Async Stack Traces
label.user.renderers.configurable.renderer.name=Renderer name:
text.user.renderers.configurable.no.renderers=No renderers
debugger.collection.visualizer.title=Collection Presentation
debugger.collection.visualizer.title.0=Collection Presentation: {0}
button.copy=Copy
user.renderers.configurable.button.description.copy=Copy selected renderer
label.generic.debugger.parameters.patcher.configurable.shmem=Shared &memory
+3 -1
View File
@@ -79,7 +79,6 @@
- name: intellij.java.performancePlugin
- name: intellij.java.terminal
- name: intellij.java.debugger.impl.backend
- name: intellij.debugger.collections.visualizer.jvm
- name: intellij.jvm.dfa.analysis.ui
- name: lib/javac2.jar
modules:
@@ -143,6 +142,9 @@
module: intellij.java.aetherDependencyResolver
files:
- name: $MAVEN_REPOSITORY$/org/apache/maven/resolver/maven-resolver-transport-http/1/maven-resolver-transport-http-1.jar
- name: lib/modules/intellij.debugger.collections.visualizer.jvm.backend.jar
contentModules:
- name: intellij.debugger.collections.visualizer.jvm.backend
- name: lib/modules/intellij.java.debugger.impl.frontend.jar
contentModules:
- name: intellij.java.debugger.impl.frontend
@@ -59,7 +59,6 @@
<module name="intellij.java.langInjection"/>
<module name="intellij.java.langInjection.jps"/>
<module name="intellij.debugger.collections.visualizer.jvm"/>
<module name="intellij.profiler.ultimate"/>
<module name="intellij.jvm.dfa.analysis.ui"/>
</content>
@@ -9,29 +9,19 @@ import com.intellij.openapi.application.readAction
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.NlsContexts
import com.intellij.platform.debugger.impl.rpc.*
import com.intellij.platform.debugger.impl.rpc.XFullValueEvaluatorDto.FullValueEvaluatorLinkAttributes
import com.intellij.xdebugger.XDebuggerBundle
import com.intellij.xdebugger.evaluation.ExpressionInfo
import com.intellij.xdebugger.evaluation.XDebuggerEvaluator
import com.intellij.xdebugger.evaluation.XDebuggerEvaluator.XEvaluationCallback
import com.intellij.xdebugger.impl.evaluate.XEvaluationOrigin
import com.intellij.xdebugger.frame.XNamedValue
import com.intellij.xdebugger.frame.XValue
import com.intellij.xdebugger.impl.XDebugSessionImpl
import com.intellij.xdebugger.impl.evaluate.XEvaluationOrigin
import com.intellij.xdebugger.impl.evaluate.quick.XDebuggerDocumentOffsetEvaluator
import com.intellij.xdebugger.impl.evaluate.quick.common.ValueHintType
import com.intellij.xdebugger.impl.pinned.items.PinToTopValue
import com.intellij.xdebugger.impl.rpc.XStackFrameId
import com.intellij.xdebugger.impl.rpc.models.*
import com.intellij.xdebugger.impl.ui.XValueTextProvider
import com.intellij.xdebugger.impl.ui.tree.nodes.XEvaluationCallbackWithOrigin
import fleet.rpc.core.RpcFlow
import fleet.rpc.core.toRpc
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.future.asDeferred
import kotlinx.coroutines.future.await
import org.jetbrains.concurrency.asDeferred
@@ -111,36 +101,6 @@ internal class BackendXDebuggerEvaluatorApi : XDebuggerEvaluatorApi {
}
}
internal suspend fun BackendXValueModel.toXValueDto(): XValueDto {
val xValueModel = this
val xValue = this.xValue
val valueMarkupFlow: RpcFlow<XValueMarkerDto?> = xValueModel.marker.toRpc()
val textProvider = (xValue as? XValueTextProvider)
?.let {
xValueModel.presentation.map {
XValueTextProviderDto(
xValue.shouldShowTextValue(),
xValue.valueText,
)
}.distinctUntilChanged()
}
return XValueDto(
xValueModel.id,
xValue.xValueDescriptorAsync?.asDeferred(),
canNavigateToSource = xValue.canNavigateToSource(),
canNavigateToTypeSource = xValue.canNavigateToTypeSourceAsync().asDeferred(),
canBeModified = xValue.modifierAsync.thenApply { modifier -> modifier != null }.asDeferred(),
valueMarkupFlow,
xValueModel.presentation.toRpc(),
xValueModel.getEvaluatorDtoFlow().toRpc(),
(xValue as? XNamedValue)?.name,
textProvider?.toRpc(),
(xValue as? PinToTopValue)?.pinToTopDataFuture?.asDeferred(),
)
}
internal fun BackendXValueGroupModel.toXValueGroupDto(): XValueGroupDto {
return XValueGroupDto(
id,
@@ -153,22 +113,6 @@ internal fun BackendXValueGroupModel.toXValueGroupDto(): XValueGroupDto {
)
}
private fun BackendXValueModel.getEvaluatorDtoFlow(): Flow<XFullValueEvaluatorDto?> {
return fullValueEvaluator.map {
if (it == null) {
return@map null
}
XFullValueEvaluatorDto(
it.linkText,
it.isEnabled,
it.isShowValuePopup,
it.linkAttributes?.let { attributes ->
FullValueEvaluatorLinkAttributes(attributes.linkIcon?.rpcId(), attributes.linkTooltipText, attributes.shortcutSupplier?.get())
}
)
}
}
internal fun newXValueModel(
stackFrameModel: XStackFrameModel,
xValue: XValue,
@@ -17,6 +17,7 @@ import com.intellij.xdebugger.impl.rpc.XValueId
import com.intellij.xdebugger.impl.rpc.models.BackendXValueModel
import com.intellij.xdebugger.impl.rpc.models.findValue
import com.intellij.xdebugger.impl.rpc.models.getOrStoreGlobally
import com.intellij.xdebugger.impl.rpc.models.toXValueDto
import fleet.rpc.core.toRpc
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
@@ -4,7 +4,6 @@ package com.intellij.xdebugger.impl.collection.visualizer
import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.project.Project
import com.intellij.xdebugger.frame.XDebuggerTreeNodeHyperlink
import com.intellij.xdebugger.impl.XDebuggerSuspendScopeProvider
import com.intellij.xdebugger.impl.frame.XDebugView
import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl
import kotlinx.coroutines.CoroutineScope
@@ -23,9 +22,6 @@ interface XDebuggerNodeLinkActionProvider {
/**
* Compute a hyperlink for the given [node].
*
* Passed [CoroutineScope] restricts the lifetime of the link action to a current debugger session state.
* Refer to [XDebuggerSuspendScopeProvider.provideSuspendScope] for more details.
*
* @see [XDebuggerNodeLinkActionProvider.computeHyperlink]
*/
suspend fun CoroutineScope.provideHyperlink(project: Project, node: XValueNodeImpl): XDebuggerTreeNodeHyperlink?
@@ -37,7 +33,8 @@ interface XDebuggerNodeLinkActionProvider {
fun computeHyperlink(project: Project, node: XValueNodeImpl) {
if (node.hasLinks()) return
val scope = XDebugView.getSessionProxy(node.tree)?.currentSuspendContextCoroutineScope?: return
val sessionProxy = XDebugView.getSessionProxy(node.tree) ?: return
val scope = sessionProxy.currentSuspendContextCoroutineScope ?: return
scope.launch(Dispatchers.Default) {
for (provider in EP_NAME.extensionList) {
@@ -1,11 +1,16 @@
// 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.xdebugger.impl.rpc.models
import com.intellij.ide.ui.icons.rpcId
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project
import com.intellij.platform.debugger.impl.rpc.XFullValueEvaluatorDto
import com.intellij.platform.debugger.impl.rpc.XFullValueEvaluatorDto.FullValueEvaluatorLinkAttributes
import com.intellij.platform.debugger.impl.rpc.XValueDto
import com.intellij.platform.debugger.impl.rpc.XValueMarkerDto
import com.intellij.platform.debugger.impl.rpc.XValueSerializedPresentation
import com.intellij.platform.debugger.impl.rpc.XValueTextProviderDto
import com.intellij.platform.kernel.ids.BackendValueIdType
import com.intellij.platform.kernel.ids.deleteValueById
import com.intellij.platform.kernel.ids.findValueById
@@ -13,16 +18,24 @@ import com.intellij.platform.kernel.ids.storeValueGlobally
import com.intellij.util.AwaitCancellationAndInvoke
import com.intellij.util.awaitCancellationAndInvoke
import com.intellij.xdebugger.frame.XFullValueEvaluator
import com.intellij.xdebugger.frame.XNamedValue
import com.intellij.xdebugger.frame.XValue
import com.intellij.xdebugger.frame.XValuePlace
import com.intellij.xdebugger.impl.XDebugSessionImpl
import com.intellij.xdebugger.impl.pinned.items.PinToTopValue
import com.intellij.xdebugger.impl.rpc.XValueId
import com.intellij.xdebugger.impl.ui.XValueTextProvider
import com.intellij.xdebugger.impl.ui.tree.ValueMarkup
import fleet.rpc.core.RpcFlow
import fleet.rpc.core.toRpc
import kotlinx.collections.immutable.toImmutableSet
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.future.asDeferred
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.concurrency.asDeferred
/**
* Model which holds reference to the [XValue].
@@ -112,6 +125,57 @@ class BackendXValueModel internal constructor(
}
}
@ApiStatus.Internal
suspend fun BackendXValueModel.toXValueDto(): XValueDto {
val xValueModel = this
val xValue = this.xValue
val valueMarkupFlow: RpcFlow<XValueMarkerDto?> = xValueModel.marker.toRpc()
val textProvider = getTextProviderFlow(xValue, xValueModel)
return XValueDto(
xValueModel.id,
xValue.xValueDescriptorAsync?.asDeferred(),
canNavigateToSource = xValue.canNavigateToSource(),
canNavigateToTypeSource = xValue.canNavigateToTypeSourceAsync().asDeferred(),
canBeModified = xValue.modifierAsync.thenApply { modifier -> modifier != null }.asDeferred(),
valueMarkupFlow,
xValueModel.presentation.toRpc(),
xValueModel.getEvaluatorDtoFlow().toRpc(),
(xValue as? XNamedValue)?.name,
textProvider?.toRpc(),
(xValue as? PinToTopValue)?.pinToTopDataFuture?.asDeferred(),
)
}
private fun BackendXValueModel.getEvaluatorDtoFlow(): Flow<XFullValueEvaluatorDto?> {
return fullValueEvaluator.map {
if (it == null) {
return@map null
}
XFullValueEvaluatorDto(
it.linkText,
it.isEnabled,
it.isShowValuePopup,
it.linkAttributes?.let { attributes ->
FullValueEvaluatorLinkAttributes(attributes.linkIcon?.rpcId(), attributes.linkTooltipText, attributes.shortcutSupplier?.get())
}
)
}
}
private fun getTextProviderFlow(
xValue: XValue,
xValueModel: BackendXValueModel,
): Flow<XValueTextProviderDto>? = (xValue as? XValueTextProvider)
?.let {
xValueModel.presentation.map {
XValueTextProviderDto(
xValue.shouldShowTextValue(),
xValue.valueText,
)
}.distinctUntilChanged()
}
@ApiStatus.Internal
@Service(Service.Level.PROJECT)
@@ -173,7 +173,7 @@ public class XValueNodeImpl extends XValueContainerNode<XValue> implements XValu
public void addAdditionalHyperlink(@NotNull XDebuggerTreeNodeHyperlink link) {
invokeNodeUpdate(() -> {
if (!myAdditionalHyperLink.compareAndSet(null, link)) {
LOG.warn("Additional hyperlink already set; having more than one is discouraged");
LOG.warn("Additional hyperlink already set; having more than one is not supported");
}
fireNodeChanged();
});