diff --git a/src/SharpIDE.Application/Features/Debugging/Debugger.cs b/src/SharpIDE.Application/Features/Debugging/Debugger.cs index abe6b99..408681e 100644 --- a/src/SharpIDE.Application/Features/Debugging/Debugger.cs +++ b/src/SharpIDE.Application/Features/Debugging/Debugger.cs @@ -14,6 +14,7 @@ public class Debugger { await _debuggingService.Attach(ProcessId, debuggerExecutablePath, breakpointsByFile, project, cancellationToken); } + public async Task SetBreakpointsForFile(SharpIdeFile file, List breakpoints, CancellationToken cancellationToken = default) => await _debuggingService.SetBreakpointsForFile(file, breakpoints, cancellationToken); public async Task StepOver(int threadId, CancellationToken cancellationToken = default) => await _debuggingService.StepOver(threadId, cancellationToken); public async Task StepInto(int threadId, CancellationToken cancellationToken = default) => await _debuggingService.StepInto(threadId, cancellationToken); diff --git a/src/SharpIDE.Application/Features/Debugging/DebuggingService.cs b/src/SharpIDE.Application/Features/Debugging/DebuggingService.cs index 9039ae5..20e19e6 100644 --- a/src/SharpIDE.Application/Features/Debugging/DebuggingService.cs +++ b/src/SharpIDE.Application/Features/Debugging/DebuggingService.cs @@ -157,6 +157,16 @@ public class DebuggingService } // Typically you would do attachRequest, setBreakpointsRequest, configurationDoneRequest, then ResumeRuntime. But netcoredbg blows up on configurationDoneRequest if ResumeRuntime hasn't been called yet. + public async Task SetBreakpointsForFile(SharpIdeFile file, List breakpoints, CancellationToken cancellationToken = default) + { + var setBreakpointsRequest = new SetBreakpointsRequest + { + Source = new Source { Path = file.Path }, + Breakpoints = breakpoints.Select(b => new SourceBreakpoint { Line = b.Line }).ToList() + }; + var breakpointsResponse = _debugProtocolHost.SendRequestSync(setBreakpointsRequest); + } + public async Task StepOver(int threadId, CancellationToken cancellationToken) { await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding); diff --git a/src/SharpIDE.Application/Features/Run/RunService.cs b/src/SharpIDE.Application/Features/Run/RunService.cs index 03b51e7..5f77d86 100644 --- a/src/SharpIDE.Application/Features/Run/RunService.cs +++ b/src/SharpIDE.Application/Features/Run/RunService.cs @@ -14,10 +14,9 @@ using Breakpoint = SharpIDE.Application.Features.Debugging.Breakpoint; namespace SharpIDE.Application.Features.Run; -public class RunService(ILogger logger, RoslynAnalysis roslynAnalysis) +public partial class RunService(ILogger logger, RoslynAnalysis roslynAnalysis) { private readonly ConcurrentDictionary _projectLocks = []; - public ConcurrentDictionary> Breakpoints { get; } = []; private Debugger? _debugger; // TODO: Support multiple debuggers for multiple running projects private readonly ILogger _logger = logger; diff --git a/src/SharpIDE.Application/Features/Run/RunService_Breakpoints.cs b/src/SharpIDE.Application/Features/Run/RunService_Breakpoints.cs new file mode 100644 index 0000000..9e4dc40 --- /dev/null +++ b/src/SharpIDE.Application/Features/Run/RunService_Breakpoints.cs @@ -0,0 +1,35 @@ +using System.Collections.Concurrent; +using Ardalis.GuardClauses; +using SharpIDE.Application.Features.Debugging; +using SharpIDE.Application.Features.SolutionDiscovery; + +namespace SharpIDE.Application.Features.Run; + +public partial class RunService +{ + public ConcurrentDictionary> Breakpoints { get; } = []; + public async Task AddBreakpointForFile(SharpIdeFile file, int line) + { + Guard.Against.Null(file); + + var breakpoints = Breakpoints.GetOrAdd(file, []); + var breakpoint = new Breakpoint { Line = line }; + breakpoints.Add(breakpoint); + if (_debugger is not null) + { + await _debugger.SetBreakpointsForFile(file, breakpoints); + } + } + + public async Task RemoveBreakpointForFile(SharpIdeFile file, int line) + { + Guard.Against.Null(file); + var breakpoints = Breakpoints.GetOrAdd(file, []); + var breakpoint = breakpoints.Single(b => b.Line == line); + breakpoints.Remove(breakpoint); + if (_debugger is not null) + { + await _debugger.SetBreakpointsForFile(file, breakpoints); + } + } +} diff --git a/src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit.cs b/src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit.cs index 9fbf99e..24581a6 100644 --- a/src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit.cs +++ b/src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit.cs @@ -176,21 +176,19 @@ public partial class SharpIdeCodeEdit : CodeEdit if (_currentFile is not null) _openTabsFileManager.CloseFile(_currentFile); } - private void OnBreakpointToggled(long line) + private async void OnBreakpointToggled(long line) { if (_fileChangingSuppressBreakpointToggleEvent) return; var lineInt = (int)line; var breakpointAdded = IsLineBreakpointed(lineInt); var lineForDebugger = lineInt + 1; // Godot is 0-indexed, Debugging is 1-indexed - var breakpoints = _runService.Breakpoints.GetOrAdd(_currentFile, []); if (breakpointAdded) { - breakpoints.Add(new Breakpoint { Line = lineForDebugger } ); + await _runService.AddBreakpointForFile(_currentFile, lineForDebugger); } else { - var breakpoint = breakpoints.Single(b => b.Line == lineForDebugger); - breakpoints.Remove(breakpoint); + await _runService.RemoveBreakpointForFile(_currentFile, lineForDebugger); } SetLineColour(lineInt); GD.Print($"Breakpoint {(breakpointAdded ? "added" : "removed")} at line {lineForDebugger}");