diff --git a/platform/platform-resources-en/src/messages/ActionsBundle.properties b/platform/platform-resources-en/src/messages/ActionsBundle.properties
index 4632c4e92383..1ebcddf33b35 100644
--- a/platform/platform-resources-en/src/messages/ActionsBundle.properties
+++ b/platform/platform-resources-en/src/messages/ActionsBundle.properties
@@ -1350,6 +1350,8 @@ action.XDebugger.EditWatch.text=Edit\u2026
action.XDebugger.CopyWatch.text=Duplicate Watch
action.XDebugger.MoveWatchUp.text=Move Watch Up
action.XDebugger.MoveWatchDown.text=Move Watch Down
+action.XDebugger.PauseWatch.text=Pause Watch
+action.XDebugger.ResumeWatch.text=Resume Watch
action.XDebugger.SeparateWatches.text=Separate Watches
action.XDebugger.ToggleEvaluateExpressionField.text=Show Evaluate Expression Field
action.XDebugger.PreviewTab.text=Open Files in Preview Tab
diff --git a/platform/platform-resources/src/idea/XDebuggerActions.xml b/platform/platform-resources/src/idea/XDebuggerActions.xml
index 8376f0eaf6ab..d9999340eb11 100644
--- a/platform/platform-resources/src/idea/XDebuggerActions.xml
+++ b/platform/platform-resources/src/idea/XDebuggerActions.xml
@@ -26,6 +26,7 @@
+
+
diff --git a/platform/xdebugger-api/resources/messages/XDebuggerBundle.properties b/platform/xdebugger-api/resources/messages/XDebuggerBundle.properties
index 407ca6dc12f0..a828f4ae7a4f 100644
--- a/platform/xdebugger-api/resources/messages/XDebuggerBundle.properties
+++ b/platform/xdebugger-api/resources/messages/XDebuggerBundle.properties
@@ -296,6 +296,8 @@ memory.view.instances.warning.not.all.loaded=Not all instances will be loaded (o
debugger.inline.watches.group.name=Inline Watches
debugger.inline.watches.edit.watch.expression.text=Edit Watch Expression
debugger.inline.watches.popup.action.add.as.inline.watch=Add as Inline Watch
+xdebugger.evaluate.paused.watch=\u2026 Evaluate
+xdebugger.watch.is.paused.message=The watch is paused
debug.toolwindow.empty.text.0=To debug your code, do one of the following:
debug.toolwindow.empty.text.1=\u2013 Click the Run icon in the editor gutter
diff --git a/platform/xdebugger-impl/api-dump-unreviewed.txt b/platform/xdebugger-impl/api-dump-unreviewed.txt
index faf0b26d440c..d8e5090ea841 100644
--- a/platform/xdebugger-impl/api-dump-unreviewed.txt
+++ b/platform/xdebugger-impl/api-dump-unreviewed.txt
@@ -1583,6 +1583,7 @@ c:com.intellij.xdebugger.impl.ui.tree.nodes.WatchNodeImpl
- getEvaluationOrigin():com.intellij.xdebugger.impl.ui.tree.nodes.XEvaluationOrigin
- getExpression():com.intellij.xdebugger.XExpression
- getValueContainer():com.intellij.xdebugger.frame.XValue
+- getXWatch():com.intellij.xdebugger.impl.XWatch
- ps:renderName(com.intellij.xdebugger.XExpression):java.lang.String
- p:shouldUpdateInlineDebuggerData():Z
c:com.intellij.xdebugger.impl.ui.tree.nodes.WatchesRootNode
diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/actions/XPauseWatchAction.kt b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/actions/XPauseWatchAction.kt
new file mode 100644
index 000000000000..030d27b3ee1b
--- /dev/null
+++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/actions/XPauseWatchAction.kt
@@ -0,0 +1,52 @@
+// 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.frame.actions
+
+import com.intellij.icons.AllIcons
+import com.intellij.idea.ActionsBundle
+import com.intellij.openapi.actionSystem.ActionUpdateThread
+import com.intellij.openapi.actionSystem.AnActionEvent
+import com.intellij.xdebugger.frame.XValuePlace
+import com.intellij.xdebugger.impl.frame.XWatchesView
+import com.intellij.xdebugger.impl.frame.actions.XWatchesTreeActionBase.getSelectedNodes
+import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree
+import com.intellij.xdebugger.impl.ui.tree.nodes.WatchNodeImpl
+
+private class XPauseWatchAction : XWatchesTreeActionBase() {
+ override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.EDT
+
+ override fun isEnabled(e: AnActionEvent, tree: XDebuggerTree): Boolean {
+ val selectedNode = getSelectedWatchNode(tree) ?: return false
+ return selectedNode.xWatch.canBePaused
+ }
+
+ override fun update(e: AnActionEvent) {
+ super.update(e)
+ e.presentation.isVisible = e.presentation.isEnabled
+ if (e.presentation.isEnabled) {
+ val tree = XDebuggerTree.getTree(e) ?: return
+ val node = getSelectedWatchNode(tree) ?: return
+ val paused = node.xWatch.isPaused
+ e.presentation.text =
+ if (paused) ActionsBundle.message("action.XDebugger.ResumeWatch.text")
+ else ActionsBundle.message("action.XDebugger.PauseWatch.text")
+ e.presentation.icon = if (paused) AllIcons.Toolwindows.ToolWindowRun else AllIcons.Actions.Pause
+ }
+ }
+
+ override fun perform(e: AnActionEvent, tree: XDebuggerTree, watchesView: XWatchesView) {
+ val node = getSelectedWatchNode(tree) ?: return
+ val paused = node.xWatch.isPaused
+ node.xWatch.isPaused = !paused
+ if (!paused) {
+ // only update icon, keep calculated value
+ node.setPresentation(AllIcons.Actions.Pause, node.valuePresentation!!, !node.isLeaf)
+ }
+ else {
+ // resume watch, trigger value evaluation
+ node.valueContainer.computePresentation(node, XValuePlace.TREE)
+ }
+ }
+}
+
+private fun getSelectedWatchNode(tree: XDebuggerTree): WatchNodeImpl? =
+ getSelectedNodes(tree, WatchNodeImpl::class.java).singleOrNull()
diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/nodes/WatchNodeImpl.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/nodes/WatchNodeImpl.java
index 60536c2abe63..244706654ca5 100644
--- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/nodes/WatchNodeImpl.java
+++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/nodes/WatchNodeImpl.java
@@ -5,6 +5,7 @@ import com.intellij.icons.AllIcons;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.util.ThreeState;
import com.intellij.xdebugger.Obsolescent;
+import com.intellij.xdebugger.XDebuggerBundle;
import com.intellij.xdebugger.XExpression;
import com.intellij.xdebugger.evaluation.XDebuggerEvaluator;
import com.intellij.xdebugger.evaluation.XInstanceEvaluator;
@@ -15,11 +16,14 @@ import com.intellij.xdebugger.impl.XAlwaysEvaluatedWatch;
import com.intellij.xdebugger.impl.XWatch;
import com.intellij.xdebugger.impl.ui.XDebuggerUIConstants;
import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree;
+import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.concurrency.Promise;
import org.jetbrains.concurrency.Promises;
+import java.util.concurrent.atomic.AtomicBoolean;
+
public class WatchNodeImpl extends XValueNodeImpl implements WatchNode {
private final XWatch myWatch;
@@ -46,7 +50,7 @@ public class WatchNodeImpl extends XValueNodeImpl implements WatchNode {
this(tree, parent, new XAlwaysEvaluatedWatch(expression), null, null, value);
}
- XWatch getWatch() {
+ public XWatch getXWatch() {
return myWatch;
}
@@ -99,6 +103,7 @@ public class WatchNodeImpl extends XValueNodeImpl implements WatchNode {
private final XDebuggerTree myTree;
private final XStackFrame myStackFrame;
private volatile XValue myValue;
+ private final AtomicBoolean myEvaluateRequested = new AtomicBoolean(false);
XWatchValue(XWatch watch, XDebuggerTree tree, XStackFrame stackFrame) {
super(watch.getExpression().getExpression());
@@ -116,6 +121,11 @@ public class WatchNodeImpl extends XValueNodeImpl implements WatchNode {
@Override
public void computePresentation(@NotNull XValueNode node, @NotNull XValuePlace place) {
+ clearPausedLink(node);
+ if (myWatch.isPaused() && !myEvaluateRequested.getAndSet(false)) {
+ showPausedPresentation(node, place);
+ return;
+ }
if (myStackFrame != null) {
if (myTree.isShowing() || ApplicationManager.getApplication().isUnitTestMode()) {
XDebuggerEvaluator evaluator = myStackFrame.getEvaluator();
@@ -132,6 +142,18 @@ public class WatchNodeImpl extends XValueNodeImpl implements WatchNode {
node.setPresentation(AllIcons.Debugger.Db_watch, EMPTY_PRESENTATION, false);
}
+ private void clearPausedLink(@NotNull XValueNode node) {
+ if (node instanceof XValueNodeImpl xValueNode
+ && xValueNode.getFullValueEvaluator() instanceof EvaluateLink) {
+ xValueNode.clearFullValueEvaluator();
+ }
+ }
+
+ private void showPausedPresentation(@NotNull XValueNode node, @NotNull XValuePlace place) {
+ node.setPresentation(AllIcons.Actions.Pause, PAUSED_PRESENTATION, false);
+ node.setFullValueEvaluator(new EvaluateLink(node, place));
+ }
+
private class MyEvaluationCallback extends XEvaluationCallbackBase implements XEvaluationCallbackWithOrigin, Obsolescent {
@NotNull private final XValueNode myNode;
@NotNull private final XValuePlace myPlace;
@@ -240,5 +262,38 @@ public class WatchNodeImpl extends XValueNodeImpl implements WatchNode {
public XReferrersProvider getReferrersProvider() {
return myValue != null ? myValue.getReferrersProvider() : null;
}
+
+ private class EvaluateLink extends XFullValueEvaluator {
+ private final @NotNull XValueNode myNode;
+ private final @NotNull XValuePlace myPlace;
+
+ private EvaluateLink(@NotNull XValueNode node, @NotNull XValuePlace place) {
+ super(XDebuggerBundle.message("xdebugger.evaluate.paused.watch"));
+ myNode = node;
+ myPlace = place;
+ setShowValuePopup(false);
+ }
+
+ @Override
+ public void startEvaluation(@NotNull XFullValueEvaluationCallback callback) {
+ callback.evaluated("");
+ myEvaluateRequested.set(true);
+ computePresentation(myNode, myPlace);
+ }
+ }
}
+
+ @ApiStatus.Internal
+ public static final XValuePresentation PAUSED_PRESENTATION = new XValuePresentation() {
+ @NotNull
+ @Override
+ public String getSeparator() {
+ return " ";
+ }
+
+ @Override
+ public void renderValue(@NotNull XValueTextRenderer renderer) {
+ renderer.renderComment(XDebuggerBundle.message("xdebugger.watch.is.paused.message"));
+ }
+ };
}
\ No newline at end of file
diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/nodes/WatchesRootNode.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/nodes/WatchesRootNode.java
index 767edd72bebe..a1f33e405b67 100644
--- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/nodes/WatchesRootNode.java
+++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/nodes/WatchesRootNode.java
@@ -165,7 +165,7 @@ public class WatchesRootNode extends XValueContainerNode {
return StreamEx.of(getWatchChildren())
.select(WatchNodeImpl.class)
.filter(node -> !(node instanceof ResultNode))
- .map(WatchNodeImpl::getWatch)
+ .map(WatchNodeImpl::getXWatch)
.toList();
}