From ef46c94b3018dae590f3e4a91f735805343fd592 Mon Sep 17 00:00:00 2001 From: Matt Parker <61717342+MattParkerDev@users.noreply.github.com> Date: Mon, 25 Aug 2025 21:26:53 +1000 Subject: [PATCH] set initial breakpoints --- .../Features/Debugging/Breakpoint.cs | 6 +++++ .../Features/Debugging/Debugger.cs | 5 ++-- .../Features/Debugging/DebuggingService.cs | 19 +++++++++------ .../Features/Run/RunService.cs | 4 +++- src/SharpIDE.Godot/SharpIdeCodeEdit.cs | 23 +++++++++++++++++++ 5 files changed, 47 insertions(+), 10 deletions(-) create mode 100644 src/SharpIDE.Application/Features/Debugging/Breakpoint.cs diff --git a/src/SharpIDE.Application/Features/Debugging/Breakpoint.cs b/src/SharpIDE.Application/Features/Debugging/Breakpoint.cs new file mode 100644 index 0000000..0ea33d4 --- /dev/null +++ b/src/SharpIDE.Application/Features/Debugging/Breakpoint.cs @@ -0,0 +1,6 @@ +namespace SharpIDE.Application.Features.Debugging; + +public class Breakpoint +{ + public required int Line { get; init; } +} diff --git a/src/SharpIDE.Application/Features/Debugging/Debugger.cs b/src/SharpIDE.Application/Features/Debugging/Debugger.cs index 3f94949..475eb40 100644 --- a/src/SharpIDE.Application/Features/Debugging/Debugger.cs +++ b/src/SharpIDE.Application/Features/Debugging/Debugger.cs @@ -1,4 +1,5 @@ using SharpIDE.Application.Features.Debugging.Experimental; +using SharpIDE.Application.Features.SolutionDiscovery; using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence; namespace SharpIDE.Application.Features.Debugging; @@ -8,8 +9,8 @@ public class Debugger public required SharpIdeProjectModel Project { get; init; } public required int ProcessId { get; init; } private DebuggingService _debuggingService = new DebuggingService(); - public async Task Attach(CancellationToken cancellationToken) + public async Task Attach(CancellationToken cancellationToken, Dictionary> breakpointsByFile) { - await _debuggingService.Attach(ProcessId, cancellationToken); + await _debuggingService.Attach(ProcessId, breakpointsByFile, cancellationToken); } } diff --git a/src/SharpIDE.Application/Features/Debugging/DebuggingService.cs b/src/SharpIDE.Application/Features/Debugging/DebuggingService.cs index 27b8bfa..4ab303e 100644 --- a/src/SharpIDE.Application/Features/Debugging/DebuggingService.cs +++ b/src/SharpIDE.Application/Features/Debugging/DebuggingService.cs @@ -6,13 +6,14 @@ using Microsoft.VisualStudio.Shared.VSCodeDebugProtocol.Messages; using Newtonsoft.Json.Linq; using SharpIDE.Application.Features.Debugging.Experimental; using SharpIDE.Application.Features.Debugging.Experimental.VsDbg; +using SharpIDE.Application.Features.SolutionDiscovery; namespace SharpIDE.Application.Features.Debugging; public class DebuggingService { private DebugProtocolHost _debugProtocolHost = null!; - public async Task Attach(int debuggeeProcessId, CancellationToken cancellationToken = default) + public async Task Attach(int debuggeeProcessId, Dictionary> breakpointsByFile, CancellationToken cancellationToken = default) { Guard.Against.NegativeOrZero(debuggeeProcessId, nameof(debuggeeProcessId), "Process ID must be a positive integer."); await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding); @@ -100,12 +101,16 @@ public class DebuggingService }; debugProtocolHost.SendRequestSync(attachRequest); - // var breakpointRequest = new SetBreakpointsRequest - // { - // Source = new Source { Path = @"C:\Users\Matthew\Documents\Git\BlazorCodeBreaker\src\WebApi\Program.cs" }, - // Breakpoints = [new SourceBreakpoint { Line = 7 }] - // }; - // var breakpointsResponse = debugProtocolHost.SendRequestSync(breakpointRequest); + foreach (var breakpoint in breakpointsByFile) + { + var setBreakpointsRequest = new SetBreakpointsRequest + { + Source = new Source { Path = breakpoint.Key.Path }, + Breakpoints = breakpoint.Value.Select(b => new SourceBreakpoint { Line = b.Line }).ToList() + }; + var breakpointsResponse = debugProtocolHost.SendRequestSync(setBreakpointsRequest); + } + new DiagnosticsClient(debuggeeProcessId).ResumeRuntime(); var configurationDoneRequest = new ConfigurationDoneRequest(); debugProtocolHost.SendRequestSync(configurationDoneRequest); diff --git a/src/SharpIDE.Application/Features/Run/RunService.cs b/src/SharpIDE.Application/Features/Run/RunService.cs index ed3fc4b..7a5e94e 100644 --- a/src/SharpIDE.Application/Features/Run/RunService.cs +++ b/src/SharpIDE.Application/Features/Run/RunService.cs @@ -5,6 +5,7 @@ using AsyncReadProcess; using SharpIDE.Application.Features.Debugging; using SharpIDE.Application.Features.Evaluation; using SharpIDE.Application.Features.Events; +using SharpIDE.Application.Features.SolutionDiscovery; using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence; namespace SharpIDE.Application.Features.Run; @@ -12,6 +13,7 @@ namespace SharpIDE.Application.Features.Run; public class RunService { private readonly ConcurrentDictionary _projectLocks = []; + public ConcurrentDictionary> Breakpoints { get; } = []; public async Task RunProject(SharpIdeProjectModel project) { var isDebug = true; @@ -83,7 +85,7 @@ public class RunService { // Attach debugger (which internally uses a DiagnosticClient to resume startup) var debuggingService = new Debugger { Project = project, ProcessId = process.ProcessId }; - await debuggingService.Attach(project.RunningCancellationTokenSource.Token).ConfigureAwait(false); + await debuggingService.Attach(project.RunningCancellationTokenSource.Token, Breakpoints.ToDictionary()).ConfigureAwait(false); } project.Running = true; diff --git a/src/SharpIDE.Godot/SharpIdeCodeEdit.cs b/src/SharpIDE.Godot/SharpIdeCodeEdit.cs index 53524d1..25e7017 100644 --- a/src/SharpIDE.Godot/SharpIdeCodeEdit.cs +++ b/src/SharpIDE.Godot/SharpIdeCodeEdit.cs @@ -4,12 +4,14 @@ using System.Collections.Immutable; using System.IO; using System.Linq; using System.Threading; +using Ardalis.GuardClauses; using Godot; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Text; using SharpIDE.Application.Features.Analysis; +using SharpIDE.Application.Features.Debugging; using SharpIDE.Application.Features.SolutionDiscovery; using Task = System.Threading.Tasks.Task; @@ -34,11 +36,13 @@ public partial class SharpIdeCodeEdit : CodeEdit public override void _Ready() { + SyntaxHighlighter = _syntaxHighlighter; _popupMenu = GetNode("CodeFixesMenu"); _popupMenu.IdPressed += OnCodeFixSelected; CodeCompletionRequested += OnCodeCompletionRequested; CodeFixesRequested += OnCodeFixesRequested; + BreakpointToggled += OnBreakpointToggled; CaretChanged += OnCaretChanged; TextChanged += OnTextChanged; SymbolHovered += OnSymbolHovered; @@ -46,6 +50,25 @@ public partial class SharpIdeCodeEdit : CodeEdit SymbolLookup += OnSymbolLookup; } + private void OnBreakpointToggled(long line) + { + var lineInt = (int)line; + var breakpointAdded = IsLineBreakpointed(lineInt); + lineInt++; // Godot is 0-indexed, Debugging is 1-indexed + Guard.Against.Negative(lineInt, nameof(lineInt)); + var breakpoints = Singletons.RunService.Breakpoints.GetOrAdd(_currentFile, []); + if (breakpointAdded) + { + breakpoints.Add(new Breakpoint { Line = lineInt } ); + } + else + { + var breakpoint = breakpoints.Single(b => b.Line == lineInt); + breakpoints.Remove(breakpoint); + } + GD.Print($"Breakpoint {(breakpointAdded ? "added" : "removed")} at line {lineInt}"); + } + private void OnSymbolLookup(string symbol, long line, long column) { GD.Print($"Symbol lookup requested: {symbol} at line {line}, column {column}");