[rd debugger] IJPL-219714 Extract AbstractXFetchValueAction to be reused in split and non-split actions

GitOrigin-RevId: 5b803027489bc102fd22b00476f10e8d39a9eaad
This commit is contained in:
Maksim Zuev
2026-02-12 22:52:58 +00:00
committed by intellij-monorepo-bot
parent 8b4badadb1
commit 72ffad9c1f
8 changed files with 242 additions and 212 deletions
@@ -21,14 +21,13 @@ import com.siyeh.ig.psiutils.CollectionUtils;
import com.sun.jdi.Type;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
@ApiStatus.Experimental
public class ShowCollectionHistoryAction extends XFetchValueActionBase {
@Override
public @Nullable ActionRemoteBehavior getBehavior() {
public @NotNull ActionRemoteBehavior getBehavior() {
return ActionRemoteBehavior.Disabled;
}
@@ -207,7 +207,7 @@ com.intellij.xdebugger.impl.ui.XValueTextProvider
- f:getSelectedNodes(com.intellij.openapi.actionSystem.DataContext):java.util.List
- f:getSelectedValue(com.intellij.openapi.actionSystem.DataContext):com.intellij.xdebugger.frame.XValue
*a:com.intellij.xdebugger.impl.ui.tree.actions.XFetchValueSplitActionBase
- com.intellij.xdebugger.impl.ui.tree.actions.XFetchValueActionBase
- com.intellij.xdebugger.impl.ui.tree.actions.AbstractXFetchValueAction
- <init>():V
f:com.intellij.xdebugger.impl.ui.tree.nodes.XValuePresentationUtil
- *s:renderName(java.lang.String,I,java.util.function.Consumer):V
@@ -969,23 +969,6 @@ a:com.intellij.xdebugger.impl.ui.tree.actions.XDebuggerTreeActionBase
- p:isEnabled(com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl,com.intellij.openapi.actionSystem.AnActionEvent):Z
- pa:perform(com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl,java.lang.String,com.intellij.openapi.actionSystem.AnActionEvent):V
- update(com.intellij.openapi.actionSystem.AnActionEvent):V
a:com.intellij.xdebugger.impl.ui.tree.actions.XFetchValueActionBase
- com.intellij.openapi.project.DumbAwareAction
- <init>():V
- actionPerformed(com.intellij.openapi.actionSystem.AnActionEvent):V
- p:addToCollector(java.util.List,com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl,com.intellij.xdebugger.impl.ui.tree.actions.XFetchValueActionBase$ValueCollector):V
- p:createCollector(com.intellij.openapi.actionSystem.AnActionEvent):com.intellij.xdebugger.impl.ui.tree.actions.XFetchValueActionBase$ValueCollector
- p:isEnabled(com.intellij.openapi.actionSystem.AnActionEvent,com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl):Z
- update(com.intellij.openapi.actionSystem.AnActionEvent):V
c:com.intellij.xdebugger.impl.ui.tree.actions.XFetchValueActionBase$ValueCollector
- <init>(com.intellij.xdebugger.impl.ui.tree.actions.XFetchValueActionBase,com.intellij.xdebugger.impl.ui.tree.XDebuggerTree):V
- acquire():I
- add(java.lang.String):V
- add(java.lang.String,I):V
- evaluationComplete(I,java.lang.String):V
- finish():V
- getTree():com.intellij.xdebugger.impl.ui.tree.XDebuggerTree
- handleInCollector(com.intellij.openapi.project.Project,java.lang.String,com.intellij.xdebugger.impl.ui.tree.XDebuggerTree):V
c:com.intellij.xdebugger.impl.ui.tree.nodes.EvaluatingExpressionRootNode
- com.intellij.xdebugger.impl.ui.tree.nodes.XValueContainerNode
- <init>(com.intellij.xdebugger.impl.evaluate.XDebuggerEvaluationDialog,com.intellij.xdebugger.impl.ui.tree.XDebuggerTree):V
+24 -3
View File
@@ -80,6 +80,26 @@ com.intellij.xdebugger.impl.ui.attach.dialog.AttachHostItem
- a:getPresentation():java.lang.String
c:com.intellij.xdebugger.impl.ui.tree.XDebuggerTree
- rebuild():V
a:com.intellij.xdebugger.impl.ui.tree.actions.AbstractXFetchValueAction
- com.intellij.openapi.project.DumbAwareAction
- <init>():V
- actionPerformed(com.intellij.openapi.actionSystem.AnActionEvent):V
- p:addToCollector(java.util.List,com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl,com.intellij.xdebugger.impl.ui.tree.actions.AbstractXFetchValueAction$ValueCollector):V
- p:createCollector(com.intellij.openapi.actionSystem.AnActionEvent):com.intellij.xdebugger.impl.ui.tree.actions.AbstractXFetchValueAction$ValueCollector
- p:handle(com.intellij.openapi.project.Project,java.lang.String):V
- p:handle(com.intellij.openapi.project.Project,java.lang.String,com.intellij.xdebugger.impl.ui.tree.XDebuggerTree):V
- p:isEnabled(com.intellij.openapi.actionSystem.AnActionEvent,com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl):Z
- update(com.intellij.openapi.actionSystem.AnActionEvent):V
c:com.intellij.xdebugger.impl.ui.tree.actions.AbstractXFetchValueAction$ValueCollector
- <init>(com.intellij.xdebugger.impl.ui.tree.actions.AbstractXFetchValueAction,com.intellij.openapi.project.Project):V
- acquire():I
- add(java.lang.String):V
- add(java.lang.String,I):V
- evaluationComplete(I,java.lang.String):V
- finish():V
- getTree():com.intellij.xdebugger.impl.ui.tree.XDebuggerTree
- handleInCollector(com.intellij.openapi.project.Project,java.lang.String):V
- handleInCollector(com.intellij.openapi.project.Project,java.lang.String,com.intellij.xdebugger.impl.ui.tree.XDebuggerTree):V
c:com.intellij.xdebugger.impl.ui.tree.actions.ShowReferringObjectsAction
- com.intellij.xdebugger.impl.ui.tree.actions.XDebuggerTreeSplitActionBase
a:com.intellij.xdebugger.impl.ui.tree.actions.XDebuggerTreeActionBase
@@ -92,11 +112,12 @@ f:com.intellij.xdebugger.impl.ui.tree.actions.XDebuggerTreeActionBase$Companion
- f:getSelectedNodes(com.intellij.openapi.actionSystem.DataContext):java.util.List
- f:getSelectedValue(com.intellij.openapi.actionSystem.DataContext):com.intellij.xdebugger.frame.XValue
a:com.intellij.xdebugger.impl.ui.tree.actions.XFetchValueActionBase
- p:handle(com.intellij.openapi.project.Project,java.lang.String):V
- p:handle(com.intellij.openapi.project.Project,java.lang.String,com.intellij.xdebugger.impl.ui.tree.XDebuggerTree):V
- com.intellij.xdebugger.impl.ui.tree.actions.AbstractXFetchValueAction
- <init>():V
c:com.intellij.xdebugger.impl.ui.tree.actions.XFetchValueActionBase$ValueCollector
- com.intellij.xdebugger.impl.ui.tree.actions.AbstractXFetchValueAction$ValueCollector
- <init>(com.intellij.xdebugger.impl.ui.tree.actions.XFetchValueActionBase,com.intellij.openapi.project.Project):V
- handleInCollector(com.intellij.openapi.project.Project,java.lang.String):V
- <init>(com.intellij.xdebugger.impl.ui.tree.actions.XFetchValueActionBase,com.intellij.xdebugger.impl.ui.tree.XDebuggerTree):V
com.intellij.xdebugger.impl.ui.tree.nodes.WatchNode
- com.intellij.xdebugger.Obsolescent
- javax.swing.tree.TreeNode
@@ -0,0 +1,194 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.xdebugger.impl.ui.tree.actions;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.remoting.ActionRemoteBehaviorSpecification;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.AppUIUtil;
import com.intellij.util.SmartList;
import com.intellij.xdebugger.frame.XFullValueEvaluator;
import com.intellij.xdebugger.impl.ui.DebuggerUIUtil;
import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree;
import com.intellij.xdebugger.impl.ui.tree.nodes.HeadlessValueEvaluationCallback;
import com.intellij.xdebugger.impl.ui.tree.nodes.WatchNodeImpl;
import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public abstract class AbstractXFetchValueAction extends DumbAwareAction implements ActionRemoteBehaviorSpecification {
@Override
public void update(@NotNull AnActionEvent e) {
for (XValueNodeImpl node : getNodes(e)) {
if (isEnabled(e, node)) {
return;
}
}
e.getPresentation().setEnabled(false);
}
@ApiStatus.Internal
protected abstract @NotNull List<@NotNull XValueNodeImpl> getNodes(@NotNull AnActionEvent e);
protected boolean isEnabled(@NotNull AnActionEvent event, @NotNull XValueNodeImpl node) {
if (node instanceof WatchNodeImpl || node.isComputed()) {
event.getPresentation().setEnabled(true);
return true;
}
return false;
}
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
List<XValueNodeImpl> nodes = getNodes(e);
if (nodes.isEmpty()) {
return;
}
ValueCollector valueCollector = createCollector(e);
for (XValueNodeImpl node : nodes) {
addToCollector(nodes, node, valueCollector);
}
valueCollector.processed = true;
valueCollector.finish();
}
protected void addToCollector(@NotNull List<XValueNodeImpl> paths, @NotNull XValueNodeImpl valueNode, @NotNull ValueCollector valueCollector) {
if (paths.size() > 1) { // multiselection - copy the whole node text, see IDEA-136722
valueCollector.add(valueNode.getText().toString(), valueNode.getPath().getPathCount());
}
else {
XFullValueEvaluator fullValueEvaluator = valueNode.getFullValueEvaluator();
if (fullValueEvaluator == null || !fullValueEvaluator.isShowValuePopup()) {
valueCollector.add(StringUtil.notNullize(DebuggerUIUtil.getNodeRawValue(valueNode)));
}
else {
new CopyValueEvaluationCallback(valueNode, valueCollector).startFetchingValue(fullValueEvaluator);
}
}
}
protected @NotNull ValueCollector createCollector(@NotNull AnActionEvent e) {
XDebuggerTree tree = XDebuggerTree.getTree(e.getDataContext());
return new ValueCollector(tree, e.getProject());
}
public class ValueCollector {
private final List<String> values = new SmartList<>();
private final Int2IntMap indents = new Int2IntOpenHashMap();
private final Project myProject;
private volatile boolean processed;
private XDebuggerTree myTree;
@ApiStatus.Internal
protected ValueCollector(@Nullable XDebuggerTree tree, Project project) {
this(project);
myTree = tree;
}
public ValueCollector(Project project) {
indents.defaultReturnValue(-1);
myProject = project;
}
public void add(@NotNull String value) {
values.add(value);
}
/**
* @deprecated Do not use. Override {@link AbstractXFetchValueAction#createCollector(AnActionEvent)} to access required state instead.
*/
@Deprecated
public @Nullable XDebuggerTree getTree() {
return myTree;
}
public void add(@NotNull String value, int indent) {
values.add(value);
indents.put(values.size() - 1, indent);
}
public void finish() {
if (processed && !values.contains(null) && !myProject.isDisposed()) {
int minIndent = Integer.MAX_VALUE;
for (int indent : indents.values()) {
minIndent = Math.min(minIndent, indent);
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < values.size(); i++) {
if (i > 0) {
sb.append("\n");
}
int indent = indents.get(i);
if (indent > 0) {
StringUtil.repeatSymbol(sb, ' ', indent - minIndent);
}
sb.append(values.get(i));
}
handleInCollector(myProject, sb.toString(), myTree);
}
}
/**
* @deprecated Use {@link #handleInCollector(Project, String)} instead
*/
@SuppressWarnings("DeprecatedIsStillUsed")
@Deprecated
public void handleInCollector(final Project project, final String value, XDebuggerTree ignoredTree) {
handleInCollector(project, value);
}
public void handleInCollector(final Project project, final String value) {
handle(project, value, myTree);
}
public int acquire() {
int index = values.size();
values.add(null);
return index;
}
public void evaluationComplete(final int index, final @NotNull String value) {
AppUIUtil.invokeOnEdt(() -> {
values.set(index, value);
finish();
});
}
}
/**
* @deprecated Use {@link #handle(Project, String)} instead
*/
@Deprecated
protected void handle(final Project project, final String value, XDebuggerTree ignoredTree) {
handle(project, value);
}
protected void handle(final Project project, final String value) {
throw new AbstractMethodError();
}
private static final class CopyValueEvaluationCallback extends HeadlessValueEvaluationCallback {
private final int myValueIndex;
private final ValueCollector myValueCollector;
CopyValueEvaluationCallback(@NotNull XValueNodeImpl node, @NotNull ValueCollector valueCollector) {
super(node);
myValueCollector = valueCollector;
myValueIndex = valueCollector.acquire();
}
@Override
protected void evaluationComplete(@NotNull String value, @NotNull Project project) {
myValueCollector.evaluationComplete(myValueIndex, value);
}
}
}
@@ -1,27 +1,15 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.xdebugger.impl.ui.tree.actions;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.remoting.ActionRemoteBehavior;
import com.intellij.openapi.actionSystem.remoting.ActionRemoteBehaviorSpecification;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.AppUIUtil;
import com.intellij.util.SmartList;
import com.intellij.xdebugger.XDebugProcess;
import com.intellij.xdebugger.frame.XFullValueEvaluator;
import com.intellij.xdebugger.frame.XValue;
import com.intellij.xdebugger.impl.ui.DebuggerUIUtil;
import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree;
import com.intellij.xdebugger.impl.ui.tree.nodes.HeadlessValueEvaluationCallback;
import com.intellij.xdebugger.impl.ui.tree.nodes.WatchNodeImpl;
import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
@@ -38,190 +26,34 @@ import java.util.List;
* <p>
* For the action which should operate on the frontend use {@link XFetchValueSplitActionBase} instead.
*/
public abstract class XFetchValueActionBase extends DumbAwareAction implements ActionRemoteBehaviorSpecification {
@ApiStatus.Internal
@Override
public @Nullable ActionRemoteBehavior getBehavior() {
return ActionRemoteBehavior.BackendOnly;
}
@Override
public void update(@NotNull AnActionEvent e) {
for (XValueNodeImpl node : getNodes(e)) {
if (isEnabled(e, node)) {
return;
}
public abstract class XFetchValueActionBase extends AbstractXFetchValueAction {
public class ValueCollector extends AbstractXFetchValueAction.ValueCollector {
public ValueCollector(Project project) {
super(project);
}
e.getPresentation().setEnabled(false);
}
/**
* @return {@link XValueNodeImpl} node instances with corresponding backend {@link XValue}s.
*/
@NotNull List<@NotNull XValueNodeImpl> getNodes(@NotNull AnActionEvent e) {
return XDebuggerTreeActionBase.getSelectedNodes(e.getDataContext());
}
protected boolean isEnabled(@NotNull AnActionEvent event, @NotNull XValueNodeImpl node) {
if (node instanceof WatchNodeImpl || node.isComputed()) {
event.getPresentation().setEnabled(true);
return true;
}
return false;
}
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
List<XValueNodeImpl> nodes = getNodes(e);
if (nodes.isEmpty()) {
return;
}
ValueCollector valueCollector = createCollector(e);
for (XValueNodeImpl node : nodes) {
addToCollector(nodes, node, valueCollector);
}
valueCollector.processed = true;
valueCollector.finish();
}
protected void addToCollector(@NotNull List<XValueNodeImpl> paths, @NotNull XValueNodeImpl valueNode, @NotNull ValueCollector valueCollector) {
if (paths.size() > 1) { // multiselection - copy the whole node text, see IDEA-136722
valueCollector.add(valueNode.getText().toString(), valueNode.getPath().getPathCount());
}
else {
XFullValueEvaluator fullValueEvaluator = valueNode.getFullValueEvaluator();
if (fullValueEvaluator == null || !fullValueEvaluator.isShowValuePopup()) {
valueCollector.add(StringUtil.notNullize(DebuggerUIUtil.getNodeRawValue(valueNode)));
}
else {
new CopyValueEvaluationCallback(valueNode, valueCollector).startFetchingValue(fullValueEvaluator);
}
}
}
protected @NotNull ValueCollector createCollector(@NotNull AnActionEvent e) {
XDebuggerTree tree = XDebuggerTree.getTree(e.getDataContext());
return new ValueCollector(tree, e.getProject());
}
public class ValueCollector {
private final List<String> values = new SmartList<>();
private final Int2IntMap indents = new Int2IntOpenHashMap();
private final Project myProject;
private volatile boolean processed;
private XDebuggerTree myTree;
/**
* @deprecated Use {@link #ValueCollector(Project)} instead
*/
@Deprecated
public ValueCollector(@NotNull XDebuggerTree tree) {
this(tree.getProject());
myTree = tree;
super(tree, tree.getProject());
}
}
ValueCollector(@Nullable XDebuggerTree tree, Project project) {
this(project);
myTree = tree;
}
public ValueCollector(Project project) {
indents.defaultReturnValue(-1);
myProject = project;
}
public void add(@NotNull String value) {
values.add(value);
}
/**
* @deprecated Do not use. Override {@link XFetchValueActionBase#createCollector(AnActionEvent)} to access required state instead.
*/
@Deprecated
public @Nullable XDebuggerTree getTree() {
return myTree;
}
public void add(@NotNull String value, int indent) {
values.add(value);
indents.put(values.size() - 1, indent);
}
public void finish() {
if (processed && !values.contains(null) && !myProject.isDisposed()) {
int minIndent = Integer.MAX_VALUE;
for (int indent : indents.values()) {
minIndent = Math.min(minIndent, indent);
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < values.size(); i++) {
if (i > 0) {
sb.append("\n");
}
int indent = indents.get(i);
if (indent > 0) {
StringUtil.repeatSymbol(sb, ' ', indent - minIndent);
}
sb.append(values.get(i));
}
handleInCollector(myProject, sb.toString(), myTree);
}
}
/**
* @deprecated Use {@link #handleInCollector(Project, String)} instead
*/
@SuppressWarnings("DeprecatedIsStillUsed")
@Deprecated
public void handleInCollector(final Project project, final String value, XDebuggerTree ignoredTree) {
handleInCollector(project, value);
}
public void handleInCollector(final Project project, final String value) {
handle(project, value, myTree);
}
public int acquire() {
int index = values.size();
values.add(null);
return index;
}
public void evaluationComplete(final int index, final @NotNull String value) {
AppUIUtil.invokeOnEdt(() -> {
values.set(index, value);
finish();
});
}
@ApiStatus.Internal
@Override
public @NotNull ActionRemoteBehavior getBehavior() {
return ActionRemoteBehavior.BackendOnly;
}
/**
* @deprecated Use {@link #handle(Project, String)} instead
* @return {@link XValueNodeImpl} node instances with corresponding backend {@link XValue}s.
*/
@Deprecated
protected void handle(final Project project, final String value, XDebuggerTree ignoredTree) {
handle(project, value);
}
protected void handle(final Project project, final String value) {
throw new AbstractMethodError();
}
private static final class CopyValueEvaluationCallback extends HeadlessValueEvaluationCallback {
private final int myValueIndex;
private final ValueCollector myValueCollector;
CopyValueEvaluationCallback(@NotNull XValueNodeImpl node, @NotNull ValueCollector valueCollector) {
super(node);
myValueCollector = valueCollector;
myValueIndex = valueCollector.acquire();
}
@Override
protected void evaluationComplete(@NotNull String value, @NotNull Project project) {
myValueCollector.evaluationComplete(myValueIndex, value);
}
@ApiStatus.Internal
@Override
@NotNull
protected final List<@NotNull XValueNodeImpl> getNodes(@NotNull AnActionEvent e) {
return XDebuggerTreeActionBase.getSelectedNodes(e.getDataContext());
}
}
@@ -22,8 +22,9 @@ import org.jetbrains.annotations.ApiStatus
* For backend only Monolith actions use [XFetchValueActionBase] instead, which works with backend [XValue] instances.
*/
@ApiStatus.Experimental
abstract class XFetchValueSplitActionBase : XFetchValueActionBase() {
internal final override fun getNodes(e: AnActionEvent): List<XValueNodeImpl> {
abstract class XFetchValueSplitActionBase : AbstractXFetchValueAction(), SplitDebuggerAction {
@ApiStatus.Internal
protected final override fun getNodes(e: AnActionEvent): List<XValueNodeImpl> {
return XDebuggerTreeSplitActionBase.getSelectedNodes(e.dataContext)
}
@@ -2,12 +2,12 @@
package com.jetbrains.python.debugger
import com.intellij.openapi.project.Project
import com.intellij.xdebugger.impl.ui.tree.actions.XFetchValueActionBase
import com.intellij.xdebugger.impl.ui.tree.actions.AbstractXFetchValueAction
import com.intellij.xdebugger.impl.ui.tree.nodes.HeadlessValueEvaluationCallback
import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl
class PyCopyValueEvaluationCallback(node: XValueNodeImpl,
private val valueCollector: XFetchValueActionBase.ValueCollector,
private val valueCollector: AbstractXFetchValueAction.ValueCollector,
private val currentPolicy: QuotingPolicy) : HeadlessValueEvaluationCallback(node) {
private val valueIndex = valueCollector.acquire()