From 0dd6061a5fb1e6665bd328615737c8ceaecf51c3 Mon Sep 17 00:00:00 2001 From: Matt Parker <61717342+MattParkerDev@users.noreply.github.com> Date: Sun, 14 Dec 2025 18:30:12 +1000 Subject: [PATCH] Debugger - expand variables with variablesReference --- .../Features/Debugging/Debugger.cs | 1 + .../Features/Debugging/DebuggingService.cs | 7 +++ .../Features/Run/RunService.cs | 4 ++ .../Tab/SubTabs/ThreadsVariablesSubTab.cs | 46 ++++++++++++++++++- 4 files changed, 56 insertions(+), 2 deletions(-) diff --git a/src/SharpIDE.Application/Features/Debugging/Debugger.cs b/src/SharpIDE.Application/Features/Debugging/Debugger.cs index 408681e..7b3ba76 100644 --- a/src/SharpIDE.Application/Features/Debugging/Debugger.cs +++ b/src/SharpIDE.Application/Features/Debugging/Debugger.cs @@ -23,4 +23,5 @@ public class Debugger public async Task> GetThreadsAtStopPoint() => await _debuggingService.GetThreadsAtStopPoint(); public async Task> GetStackFramesForThread(int threadId) => await _debuggingService.GetStackFramesForThread(threadId); public async Task> GetVariablesForStackFrame(int frameId) => await _debuggingService.GetVariablesForStackFrame(frameId); + public async Task> GetVariablesForVariablesReference(int variablesReferenceId) => await _debuggingService.GetVariablesForVariablesReference(variablesReferenceId); } diff --git a/src/SharpIDE.Application/Features/Debugging/DebuggingService.cs b/src/SharpIDE.Application/Features/Debugging/DebuggingService.cs index 20e19e6..25cb8a5 100644 --- a/src/SharpIDE.Application/Features/Debugging/DebuggingService.cs +++ b/src/SharpIDE.Application/Features/Debugging/DebuggingService.cs @@ -242,6 +242,13 @@ public class DebuggingService return allVariables; } + public async Task> GetVariablesForVariablesReference(int variablesReference) + { + var variablesRequest = new VariablesRequest { VariablesReference = variablesReference }; + var variablesResponse = _debugProtocolHost.SendRequestSync(variablesRequest); + return variablesResponse.Variables; + } + // netcoredbg does not provide the stack frame name in this format, so don't use this if using netcoredbg private static ManagedStackFrameInfo? ParseStackFrameName(string name) { diff --git a/src/SharpIDE.Application/Features/Run/RunService.cs b/src/SharpIDE.Application/Features/Run/RunService.cs index 5f77d86..1b3c8bc 100644 --- a/src/SharpIDE.Application/Features/Run/RunService.cs +++ b/src/SharpIDE.Application/Features/Run/RunService.cs @@ -166,6 +166,10 @@ public partial class RunService(ILogger logger, RoslynAnalysis rosly { return await _debugger!.GetVariablesForStackFrame(frameId); } + public async Task> GetVariablesForVariablesReference(int variablesReferenceId) + { + return await _debugger!.GetVariablesForVariablesReference(variablesReferenceId); + } private async Task GetRunArguments(SharpIdeProjectModel project) { diff --git a/src/SharpIDE.Godot/Features/Debug_/Tab/SubTabs/ThreadsVariablesSubTab.cs b/src/SharpIDE.Godot/Features/Debug_/Tab/SubTabs/ThreadsVariablesSubTab.cs index 554aa8f..b9db4c4 100644 --- a/src/SharpIDE.Godot/Features/Debug_/Tab/SubTabs/ThreadsVariablesSubTab.cs +++ b/src/SharpIDE.Godot/Features/Debug_/Tab/SubTabs/ThreadsVariablesSubTab.cs @@ -1,5 +1,6 @@ using Ardalis.GuardClauses; using Godot; +using Microsoft.VisualStudio.Shared.VSCodeDebugProtocol.Messages; using SharpIDE.Application.Features.Debugging; using SharpIDE.Application.Features.Events; using SharpIDE.Application.Features.Run; @@ -28,9 +29,38 @@ public partial class ThreadsVariablesSubTab : Control GlobalEvents.Instance.DebuggerExecutionStopped.Subscribe(OnDebuggerExecutionStopped); _threadsTree.ItemSelected += OnThreadSelected; _stackFramesTree.ItemSelected += OnStackFrameSelected; + _variablesTree.ItemCollapsed += OnVariablesItemExpandedOrCollapsed; Project.ProjectStoppedRunning.Subscribe(ProjectStoppedRunning); } + private void OnVariablesItemExpandedOrCollapsed(TreeItem item) + { + var wasExpanded = item.IsCollapsed() is false; + var metadata = item.GetMetadata(0).AsVector2I(); + var alreadyRetrievedChildren = metadata.X == 1; + if (wasExpanded && alreadyRetrievedChildren is false) + { + // retrieve children + var variablesReferenceId = metadata.Y; + _ = Task.GodotRun(async () => + { + var variables = await _runService.GetVariablesForVariablesReference(variablesReferenceId); + await this.InvokeAsync(() => + { + var firstChild = item.GetFirstChild(); + Guard.Against.Null(firstChild); + item.RemoveChild(firstChild); + foreach (var variable in variables) + { + AddVariableToTreeItem(item, variable); + } + // mark as retrieved + item.SetMetadata(0, new Vector2I(1, variablesReferenceId)); + }); + }); + } + } + public override void _ExitTree() { GlobalEvents.Instance.DebuggerExecutionStopped.Unsubscribe(OnDebuggerExecutionStopped); @@ -90,11 +120,23 @@ public partial class ThreadsVariablesSubTab : Control var root = _variablesTree.CreateItem(); foreach (var variable in variables) { - var variableItem = _variablesTree.CreateItem(root); - variableItem.SetText(0, $$"""{{variable.Name}} = {{{variable.Type}}} {{variable.Value}}"""); + AddVariableToTreeItem(root, variable); } }); } + + private void AddVariableToTreeItem(TreeItem parentItem, Variable variable) + { + var variableItem = _variablesTree.CreateItem(parentItem); + variableItem.SetText(0, $$"""{{variable.Name}} = {{{variable.Type}}} {{variable.Value}}"""); + variableItem.SetMetadata(0, new Vector2I(0, variable.VariablesReference)); + if (variable.VariablesReference is not 0) + { + var placeHolderItem = _variablesTree.CreateItem(variableItem); + placeHolderItem.SetText(0, "Loading..."); + variableItem.Collapsed = true; + } + } private async Task OnDebuggerExecutionStopped(ExecutionStopInfo stopInfo)