From bb6d2796ca9619c6cec0725a65de0fe465fe5edb Mon Sep 17 00:00:00 2001 From: Matt Parker <61717342+MattParkerDev@users.noreply.github.com> Date: Fri, 29 Aug 2025 21:39:17 +1000 Subject: [PATCH] display threads at stop point --- .../Features/Debugging/Debugger.cs | 1 + .../Features/Debugging/DebuggingService.cs | 40 +++++++++++++++++++ .../Debugging/ThreadsStackTraceModel.cs | 31 ++++++++++++++ .../Features/Run/RunService.cs | 4 +- .../Features/Debug_/Tab/DebugPanelTab.cs | 8 ++++ .../Features/Debug_/Tab/DebugPanelTab.tscn | 1 + .../Debug_/Tab/SubTabs/ThreadListItem.tscn | 14 ++----- .../Tab/SubTabs/ThreadsVariablesSubTab.cs | 35 +++++++++++++--- .../Tab/SubTabs/ThreadsVariablesSubTab.tscn | 15 ++++--- 9 files changed, 124 insertions(+), 25 deletions(-) create mode 100644 src/SharpIDE.Application/Features/Debugging/ThreadsStackTraceModel.cs diff --git a/src/SharpIDE.Application/Features/Debugging/Debugger.cs b/src/SharpIDE.Application/Features/Debugging/Debugger.cs index 0c63a48..aa0350a 100644 --- a/src/SharpIDE.Application/Features/Debugging/Debugger.cs +++ b/src/SharpIDE.Application/Features/Debugging/Debugger.cs @@ -15,4 +15,5 @@ public class Debugger } public async Task StepOver(int threadId, CancellationToken cancellationToken = default) => await _debuggingService.StepOver(threadId, cancellationToken); + public async Task GetInfoAtStopPoint() => await _debuggingService.GetInfoAtStopPoint(); } diff --git a/src/SharpIDE.Application/Features/Debugging/DebuggingService.cs b/src/SharpIDE.Application/Features/Debugging/DebuggingService.cs index bb40972..3e74d5d 100644 --- a/src/SharpIDE.Application/Features/Debugging/DebuggingService.cs +++ b/src/SharpIDE.Application/Features/Debugging/DebuggingService.cs @@ -137,4 +137,44 @@ public class DebuggingService var nextRequest = new NextRequest(threadId); _debugProtocolHost.SendRequestSync(nextRequest); } + + public async Task GetInfoAtStopPoint() + { + var model = new ThreadsStackTraceModel(); + try + { + var threads = _debugProtocolHost.SendRequestSync(new ThreadsRequest()); + foreach (var thread in threads.Threads) + { + var threadModel = new ThreadModel { Id = thread.Id, Name = thread.Name }; + model.Threads.Add(threadModel); + var stackTrace = _debugProtocolHost.SendRequestSync(new StackTraceRequest { ThreadId = thread.Id }); + var frame = stackTrace.StackFrames!.FirstOrDefault(); + if (frame == null) continue; + var frameModel = new StackFrameModel + { + Id = frame.Id, + Name = frame.Name, + Line = frame.Line, + Column = frame.Column, + Source = frame.Source?.Path + }; + threadModel.StackFrames.Add(frameModel); + var scopes = _debugProtocolHost.SendRequestSync(new ScopesRequest { FrameId = frame.Id }); + foreach (var scope in scopes.Scopes) + { + var scopeModel = new ScopeModel { Name = scope.Name }; + frameModel.Scopes.Add(scopeModel); + var variablesResponse = _debugProtocolHost.SendRequestSync(new VariablesRequest { VariablesReference = scope.VariablesReference }); + scopeModel.Variables = variablesResponse.Variables; + } + } + } + catch (Exception ex) + { + throw; + } + + return model; + } } diff --git a/src/SharpIDE.Application/Features/Debugging/ThreadsStackTraceModel.cs b/src/SharpIDE.Application/Features/Debugging/ThreadsStackTraceModel.cs new file mode 100644 index 0000000..5528510 --- /dev/null +++ b/src/SharpIDE.Application/Features/Debugging/ThreadsStackTraceModel.cs @@ -0,0 +1,31 @@ +using Microsoft.VisualStudio.Shared.VSCodeDebugProtocol.Messages; + +namespace SharpIDE.Application.Features.Debugging; + +public class ThreadsStackTraceModel +{ + public List Threads { get; set; } = []; +} + +public class ThreadModel +{ + public required int Id { get; set; } + public required string Name { get; set; } + public List StackFrames { get; set; } = []; +} + +public class StackFrameModel +{ + public required int Id { get; set; } + public required string Name { get; set; } + public required int? Line { get; set; } + public required int? Column { get; set; } + public required string? Source { get; set; } + public List Scopes { get; set; } = []; +} + +public class ScopeModel +{ + public required string Name { get; set; } + public List Variables { get; set; } = []; +} diff --git a/src/SharpIDE.Application/Features/Run/RunService.cs b/src/SharpIDE.Application/Features/Run/RunService.cs index a8e4800..72dfbcc 100644 --- a/src/SharpIDE.Application/Features/Run/RunService.cs +++ b/src/SharpIDE.Application/Features/Run/RunService.cs @@ -147,9 +147,9 @@ public class RunService await _debugger!.StepOver(threadId); } - public async Task GetInfoAtStopPoint() + public async Task GetInfoAtStopPoint() { - await _debugger!.GetInfoAtStopPoint(); + return await _debugger!.GetInfoAtStopPoint(); } private string GetRunArguments(SharpIdeProjectModel project) diff --git a/src/SharpIDE.Godot/Features/Debug_/Tab/DebugPanelTab.cs b/src/SharpIDE.Godot/Features/Debug_/Tab/DebugPanelTab.cs index 2bfa10e..fc43471 100644 --- a/src/SharpIDE.Godot/Features/Debug_/Tab/DebugPanelTab.cs +++ b/src/SharpIDE.Godot/Features/Debug_/Tab/DebugPanelTab.cs @@ -1,17 +1,25 @@ using GDExtensionBindgen; using Godot; using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence; +using SharpIDE.Godot.Features.Debug_.Tab.SubTabs; namespace SharpIDE.Godot.Features.Debug_.Tab; public partial class DebugPanelTab : Control { private Terminal _terminal = null!; + private ThreadsVariablesSubTab _threadsVariablesSubTab = null!; private Task _writeTask = Task.CompletedTask; public SharpIdeProjectModel Project { get; set; } = null!; public int TabBarTab { get; set; } + public override void _EnterTree() + { + _threadsVariablesSubTab = GetNode("%ThreadsVariablesSubTab"); + _threadsVariablesSubTab.Project = Project; + } + public override void _Ready() { var terminalControl = GetNode("%Terminal"); diff --git a/src/SharpIDE.Godot/Features/Debug_/Tab/DebugPanelTab.tscn b/src/SharpIDE.Godot/Features/Debug_/Tab/DebugPanelTab.tscn index cdc2f40..2591faf 100644 --- a/src/SharpIDE.Godot/Features/Debug_/Tab/DebugPanelTab.tscn +++ b/src/SharpIDE.Godot/Features/Debug_/Tab/DebugPanelTab.tscn @@ -26,6 +26,7 @@ layout_mode = 2 metadata/_tab_index = 0 [node name="ThreadsVariablesSubTab" parent="TabContainer/Threads & Variables" instance=ExtResource("2_e6ax5")] +unique_name_in_owner = true layout_mode = 1 [node name="Console" type="Control" parent="TabContainer"] diff --git a/src/SharpIDE.Godot/Features/Debug_/Tab/SubTabs/ThreadListItem.tscn b/src/SharpIDE.Godot/Features/Debug_/Tab/SubTabs/ThreadListItem.tscn index b383633..82a21b0 100644 --- a/src/SharpIDE.Godot/Features/Debug_/Tab/SubTabs/ThreadListItem.tscn +++ b/src/SharpIDE.Godot/Features/Debug_/Tab/SubTabs/ThreadListItem.tscn @@ -1,15 +1,9 @@ [gd_scene format=3 uid="uid://1bbofax8nht1"] -[node name="ThreadListItem" type="Control"] -layout_mode = 3 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 +[node name="ThreadListItem" type="MarginContainer"] +offset_right = 40.0 +offset_bottom = 40.0 [node name="Label" type="Label" parent="."] -layout_mode = 0 -offset_right = 40.0 -offset_bottom = 23.0 +layout_mode = 2 text = "Thread Name" diff --git a/src/SharpIDE.Godot/Features/Debug_/Tab/SubTabs/ThreadsVariablesSubTab.cs b/src/SharpIDE.Godot/Features/Debug_/Tab/SubTabs/ThreadsVariablesSubTab.cs index ab16cb4..1f4011b 100644 --- a/src/SharpIDE.Godot/Features/Debug_/Tab/SubTabs/ThreadsVariablesSubTab.cs +++ b/src/SharpIDE.Godot/Features/Debug_/Tab/SubTabs/ThreadsVariablesSubTab.cs @@ -1,13 +1,38 @@ using Godot; +using SharpIDE.Application.Features.Debugging; +using SharpIDE.Application.Features.Events; +using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence; namespace SharpIDE.Godot.Features.Debug_.Tab.SubTabs; public partial class ThreadsVariablesSubTab : Control { - private VBoxContainer _threadsVboxContainer = null!; + private PackedScene _threadListItemScene = GD.Load("res://Features/Debug_/Tab/SubTabs/ThreadListItem.tscn"); + private VBoxContainer _threadsVboxContainer = null!; + public SharpIdeProjectModel Project { get; set; } = null!; - public override void _Ready() - { - _threadsVboxContainer = GetNode("%ThreadsPanel/VBoxContainer"); - } + public override void _Ready() + { + _threadsVboxContainer = GetNode("%ThreadsPanel/VBoxContainer"); + GlobalEvents.DebuggerExecutionStopped += OnDebuggerExecutionStopped; + + } + + private async Task OnDebuggerExecutionStopped(ExecutionStopInfo arg) + { + var result = await Singletons.RunService.GetInfoAtStopPoint(); + var scenes = result.Threads.Select(s => + { + var threadListItem = _threadListItemScene.Instantiate(); + threadListItem.GetNode