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(); }