[rd debugger] Convert ViewAsGroup to Kotlin

GitOrigin-RevId: 5c96c7ee669763cbb68aaf6d7d53801154e38aa4
This commit is contained in:
Maksim Zuev
2025-08-20 10:54:25 +00:00
committed by intellij-monorepo-bot
parent a830f04615
commit 02dd01217a
@@ -1,211 +1,181 @@
// 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.debugger.actions;
package com.intellij.debugger.actions
import com.intellij.debugger.engine.DebugProcessImpl;
import com.intellij.debugger.engine.DebuggerManagerThreadImpl;
import com.intellij.debugger.engine.JavaValue;
import com.intellij.debugger.engine.SuspendContextImpl;
import com.intellij.debugger.engine.events.DebuggerContextCommandImpl;
import com.intellij.debugger.impl.DebuggerContextImpl;
import com.intellij.debugger.impl.DebuggerUtilsAsync;
import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl;
import com.intellij.debugger.ui.tree.render.NodeRenderer;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.util.ProgressIndicatorUtils;
import com.intellij.openapi.project.DumbAware;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.xdebugger.frame.XValue;
import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree;
import com.intellij.xdebugger.impl.ui.tree.actions.XDebuggerTreeActionBase;
import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import com.intellij.debugger.engine.JavaValue
import com.intellij.debugger.engine.SuspendContextImpl
import com.intellij.debugger.engine.events.DebuggerContextCommandImpl
import com.intellij.debugger.impl.DebuggerUtilsAsync
import com.intellij.debugger.ui.tree.render.NodeRenderer
import com.intellij.openapi.actionSystem.*
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.progress.util.ProgressIndicatorUtils
import com.intellij.openapi.project.DumbAware
import com.intellij.util.containers.ContainerUtil
import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree
import com.intellij.xdebugger.impl.ui.tree.actions.XDebuggerTreeActionBase
import one.util.streamex.StreamEx
import org.jetbrains.annotations.ApiStatus
import java.util.concurrent.CompletableFuture
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
public class ViewAsGroup extends ActionGroup implements DumbAware {
private static final Logger LOG = Logger.getInstance(ViewAsGroup.class);
public ViewAsGroup() {
super(Presentation.NULL_STRING, true);
class ViewAsGroup : ActionGroup(Presentation.NULL_STRING, true), DumbAware {
override fun getActionUpdateThread(): ActionUpdateThread {
return ActionUpdateThread.BGT
}
@Override
public @NotNull ActionUpdateThread getActionUpdateThread() {
return ActionUpdateThread.BGT;
}
private static class RendererAction extends ToggleAction {
private final NodeRenderer myNodeRenderer;
RendererAction(NodeRenderer nodeRenderer) {
super(nodeRenderer.getName());
myNodeRenderer = nodeRenderer;
getTemplatePresentation().setKeepPopupOnPerform(KeepPopupOnPerform.IfRequested);
private class RendererAction(private val nodeRenderer: NodeRenderer) : ToggleAction(nodeRenderer.getName()) {
init {
getTemplatePresentation().setKeepPopupOnPerform(KeepPopupOnPerform.IfRequested)
}
@Override
public boolean isSelected(@NotNull AnActionEvent e) {
List<JavaValue> values = getSelectedValues(e);
override fun isSelected(e: AnActionEvent): Boolean {
val values = getSelectedValues(e)
if (values.isEmpty()) {
return false;
return false
}
for (JavaValue value : values) {
if (value.getDescriptor().getLastRenderer() != myNodeRenderer) {
return false;
for (value in values) {
if (value.descriptor.lastRenderer !== nodeRenderer) {
return false
}
}
return true;
return true
}
@Override
public @NotNull ActionUpdateThread getActionUpdateThread() {
return ActionUpdateThread.EDT;
override fun getActionUpdateThread(): ActionUpdateThread {
return ActionUpdateThread.EDT
}
@Override
public void setSelected(final @NotNull AnActionEvent e, final boolean state) {
if (!state) return;
override fun setSelected(e: AnActionEvent, state: Boolean) {
if (!state) return
final DebuggerContextImpl debuggerContext = DebuggerAction.getDebuggerContext(e.getDataContext());
final List<JavaValue> values = getSelectedValues(e);
final List<XValueNodeImpl> selectedNodes = XDebuggerTreeActionBase.getSelectedNodes(e.getDataContext());
val debuggerContext = DebuggerAction.getDebuggerContext(e.dataContext)
val values = getSelectedValues(e)
val selectedNodes = XDebuggerTreeActionBase.getSelectedNodes(e.dataContext)
LOG.assertTrue(!values.isEmpty());
LOG.assertTrue(!values.isEmpty())
DebuggerManagerThreadImpl managerThread = debuggerContext.getManagerThread();
if (managerThread == null) {
return;
}
val managerThread = debuggerContext.getManagerThread() ?: return
managerThread.schedule(new DebuggerContextCommandImpl(debuggerContext) {
@Override
public void threadAction(@NotNull SuspendContextImpl suspendContext) {
for (XValueNodeImpl node : selectedNodes) {
XValue container = node.getValueContainer();
if (container instanceof JavaValue javaValue) {
javaValue.setRenderer(myNodeRenderer, node);
managerThread.schedule(object : DebuggerContextCommandImpl(debuggerContext) {
override fun threadAction(suspendContext: SuspendContextImpl) {
for (node in selectedNodes) {
val container = node.valueContainer
if (container is JavaValue) {
container.setRenderer(nodeRenderer, node)
}
}
}
});
})
}
}
@Override
public AnAction @NotNull [] getChildren(final @Nullable AnActionEvent e) {
override fun getChildren(e: AnActionEvent?): Array<AnAction> {
if (e == null) {
return EMPTY_ARRAY;
return EMPTY_ARRAY
}
DebuggerContextImpl debuggerContext = DebuggerAction.getDebuggerContext(e.getDataContext());
DebugProcessImpl process = debuggerContext.getDebugProcess();
if (process == null) {
return EMPTY_ARRAY;
val debuggerContext = DebuggerAction.getDebuggerContext(e.dataContext)
if (debuggerContext.debugProcess == null) {
return EMPTY_ARRAY
}
List<JavaValue> values = getSelectedValues(e);
val values = getSelectedValues(e)
if (!values.isEmpty()) {
CompletableFuture<List<NodeRenderer>> future = new CompletableFuture<>();
boolean scheduled = Objects.requireNonNull(debuggerContext.getManagerThread()).schedule(new DebuggerContextCommandImpl(debuggerContext) {
@Override
public void threadAction(@NotNull SuspendContextImpl suspendContext) {
getApplicableRenderers(values, process)
.whenComplete((renderers, throwable) -> DebuggerUtilsAsync.completeFuture(renderers, throwable, future));
val future = CompletableFuture<List<NodeRenderer>>()
val scheduled = debuggerContext.getManagerThread()!!.schedule(object : DebuggerContextCommandImpl(debuggerContext) {
override fun threadAction(suspendContext: SuspendContextImpl) {
getApplicableRenderers(values)
.whenComplete { renderers, throwable ->
DebuggerUtilsAsync.completeFuture(renderers, throwable, future)
}
}
@Override
protected void commandCancelled() {
future.cancel(false);
override fun commandCancelled() {
future.cancel(false)
}
});
})
if (scheduled) {
List<NodeRenderer> rs = ProgressIndicatorUtils.awaitWithCheckCanceled(future);
val rs = ProgressIndicatorUtils.awaitWithCheckCanceled(future)
if (ContainerUtil.isEmpty(rs)) {
return EMPTY_ARRAY;
return EMPTY_ARRAY
}
List<AnAction> children = new ArrayList<>();
ActionManager actionManager = ActionManager.getInstance();
AnAction[] viewAsActions = ((DefaultActionGroup)actionManager.getAction("Debugger.Representation"))
.getChildren(actionManager);
for (AnAction viewAsAction : viewAsActions) {
if (viewAsAction instanceof AutoRendererAction) {
if (rs.size() > 1) {
children.add(viewAsAction);
val children = mutableListOf<AnAction>()
val actionManager = ActionManager.getInstance()
val viewAsActions = (actionManager.getAction("Debugger.Representation") as DefaultActionGroup)
.getChildren(actionManager)
for (viewAsAction in viewAsActions) {
if (viewAsAction is AutoRendererAction) {
if (rs.size > 1) {
children.add(viewAsAction)
}
}
else {
children.add(viewAsAction);
children.add(viewAsAction)
}
}
if (!children.isEmpty()) {
children.add(Separator.getInstance());
children.add(Separator.getInstance())
}
children.addAll(ContainerUtil.map(rs, RendererAction::new));
return children.toArray(EMPTY_ARRAY);
children.addAll(rs.map { nodeRenderer -> RendererAction(nodeRenderer) })
return children.toTypedArray()
}
}
return EMPTY_ARRAY;
return EMPTY_ARRAY
}
private static @NotNull CompletableFuture<List<NodeRenderer>> getApplicableRenderers(List<JavaValue> values, DebugProcessImpl process) {
List<CompletableFuture<List<NodeRenderer>>> futures = new ArrayList<>(values.size());
for (JavaValue value : values) {
CompletableFuture<List<NodeRenderer>> completedFuture = getApplicableNodeRenderers(value);
futures.add(completedFuture);
override fun update(event: AnActionEvent) {
val debuggerContext = DebuggerAction.getDebuggerContext(event.dataContext)
if (getSelectedValues(event).isEmpty() || debuggerContext.debugProcess == null) {
event.presentation.setEnabledAndVisible(false)
return
}
event.presentation.setEnabledAndVisible(true)
}
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).thenApply(__ -> {
List<NodeRenderer> res = null;
for (CompletableFuture<List<NodeRenderer>> future : futures) {
List<NodeRenderer> list = future.join();
if (res == null) {
res = list;
}
else {
res.retainAll(list);
}
companion object {
private val LOG = Logger.getInstance(ViewAsGroup::class.java)
private fun getApplicableRenderers(values: List<JavaValue>): CompletableFuture<List<NodeRenderer>> {
val futures = mutableListOf<CompletableFuture<List<NodeRenderer>>>()
for (value in values) {
val completedFuture = getApplicableNodeRenderers(value)
futures.add(completedFuture)
}
return ContainerUtil.notNullize(res);
});
}
@ApiStatus.Internal
public static CompletableFuture<List<NodeRenderer>> getApplicableNodeRenderers(JavaValue value) {
if (value instanceof JavaReferringObjectsValue) { // disable for any referrers at all
return CompletableFuture.completedFuture(Collections.emptyList());
return CompletableFuture.allOf(*futures.toTypedArray()).thenApply {
var res: MutableList<NodeRenderer>? = null
for (future in futures) {
val list = future.join()
if (res == null) {
res = list.toMutableList()
}
else {
res.retainAll(list)
}
}
res ?: emptyList()
}
}
ValueDescriptorImpl valueDescriptor = value.getDescriptor();
if (!valueDescriptor.isValueValid()) {
return CompletableFuture.completedFuture(Collections.emptyList());
}
DebugProcessImpl process = value.getEvaluationContext().getDebugProcess();
return process.getApplicableRenderers(valueDescriptor.getType());
}
@Override
public void update(final @NotNull AnActionEvent event) {
DebuggerContextImpl debuggerContext = DebuggerAction.getDebuggerContext(event.getDataContext());
if (getSelectedValues(event).isEmpty() || debuggerContext.getDebugProcess() == null) {
event.getPresentation().setEnabledAndVisible(false);
return;
@ApiStatus.Internal
fun getApplicableNodeRenderers(value: JavaValue): CompletableFuture<List<NodeRenderer>> {
if (value is JavaReferringObjectsValue) { // disable for any referrers at all
return CompletableFuture.completedFuture(emptyList())
}
val valueDescriptor = value.descriptor
if (!valueDescriptor.isValueValid) {
return CompletableFuture.completedFuture(emptyList())
}
val process = value.evaluationContext.debugProcess
return process.getApplicableRenderers(valueDescriptor.getType())
}
event.getPresentation().setEnabledAndVisible(true);
}
public static @NotNull List<JavaValue> getSelectedValues(@NotNull AnActionEvent event) {
List<XValueNodeImpl> selectedNodes = XDebuggerTree.getSelectedNodes(event.getDataContext());
return StreamEx.of(selectedNodes)
.map(XValueNodeImpl::getValueContainer)
.select(JavaValue.class)
.toList();
@JvmStatic
fun getSelectedValues(event: AnActionEvent): List<JavaValue> {
val selectedNodes = XDebuggerTree.getSelectedNodes(event.dataContext)
return StreamEx.of(selectedNodes)
.map { obj -> obj.valueContainer }
.select(JavaValue::class.java)
.toList()
}
}
}