From 50fdf68bd1325bcc94121876235efc43657fb577 Mon Sep 17 00:00:00 2001 From: Matt Parker <61717342+MattParkerDev@users.noreply.github.com> Date: Tue, 6 Jan 2026 19:15:49 +1000 Subject: [PATCH] refactor creation of debugger process --- .../Features/Debugging/Debugger.cs | 5 +-- .../Debugging/DebuggerProcessStreamHelper.cs | 32 +++++++++++++++++++ .../Features/Debugging/DebuggingService.cs | 26 +++------------ .../Features/Run/DebuggerExecutableInfo.cs | 7 ++++ .../Features/Run/RunService.cs | 4 +-- .../Features/Run/RunMenuItem.cs | 7 +++- 6 files changed, 54 insertions(+), 27 deletions(-) create mode 100644 src/SharpIDE.Application/Features/Debugging/DebuggerProcessStreamHelper.cs create mode 100644 src/SharpIDE.Application/Features/Run/DebuggerExecutableInfo.cs diff --git a/src/SharpIDE.Application/Features/Debugging/Debugger.cs b/src/SharpIDE.Application/Features/Debugging/Debugger.cs index 7b3ba76..cded38e 100644 --- a/src/SharpIDE.Application/Features/Debugging/Debugger.cs +++ b/src/SharpIDE.Application/Features/Debugging/Debugger.cs @@ -1,4 +1,5 @@ using Microsoft.VisualStudio.Shared.VSCodeDebugProtocol.Messages; +using SharpIDE.Application.Features.Run; using SharpIDE.Application.Features.SolutionDiscovery; using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence; @@ -10,9 +11,9 @@ public class Debugger public required SharpIdeProjectModel Project { get; init; } public required int ProcessId { get; init; } private DebuggingService _debuggingService = new DebuggingService(); - public async Task Attach(string? debuggerExecutablePath, Dictionary> breakpointsByFile, SharpIdeProjectModel project, CancellationToken cancellationToken) + public async Task Attach(DebuggerExecutableInfo? debuggerExecutableInfo, Dictionary> breakpointsByFile, SharpIdeProjectModel project, CancellationToken cancellationToken) { - await _debuggingService.Attach(ProcessId, debuggerExecutablePath, breakpointsByFile, project, cancellationToken); + await _debuggingService.Attach(ProcessId, debuggerExecutableInfo, breakpointsByFile, project, cancellationToken); } public async Task SetBreakpointsForFile(SharpIdeFile file, List breakpoints, CancellationToken cancellationToken = default) => await _debuggingService.SetBreakpointsForFile(file, breakpoints, cancellationToken); diff --git a/src/SharpIDE.Application/Features/Debugging/DebuggerProcessStreamHelper.cs b/src/SharpIDE.Application/Features/Debugging/DebuggerProcessStreamHelper.cs new file mode 100644 index 0000000..6e3b1e6 --- /dev/null +++ b/src/SharpIDE.Application/Features/Debugging/DebuggerProcessStreamHelper.cs @@ -0,0 +1,32 @@ +using System.Diagnostics; +using Ardalis.GuardClauses; +using SharpIDE.Application.Features.Run; + +namespace SharpIDE.Application.Features.Debugging; + +public static class DebuggerProcessStreamHelper +{ + public static (Stream Input, Stream Output, bool IsNetCoreDbg) NewDebuggerProcessStreamsForInfo(DebuggerExecutableInfo? debuggerExecutableInfoNullable) + { + if (debuggerExecutableInfoNullable is not {} debuggerExecutableInfo) throw new ArgumentNullException(nameof(debuggerExecutableInfoNullable), "Debugger executable info cannot be null."); + var debuggerExecutablePath = debuggerExecutableInfo.DebuggerExecutablePath; + Guard.Against.NullOrWhiteSpace(debuggerExecutablePath, nameof(debuggerExecutablePath), "Debugger executable path cannot be null or empty."); + var isNetCoreDbg = Path.GetFileNameWithoutExtension(debuggerExecutablePath).Equals("netcoredbg", StringComparison.OrdinalIgnoreCase); + + var process = new Process + { + StartInfo = new ProcessStartInfo + { + //FileName = @"C:\Users\Matthew\Downloads\netcoredbg-win64\netcoredbg\netcoredbg.exe", + FileName = debuggerExecutablePath, + Arguments = "--interpreter=vscode", + RedirectStandardInput = true, + RedirectStandardOutput = true, + UseShellExecute = false, + CreateNoWindow = true + } + }; + process.Start(); + return (process.StandardInput.BaseStream, process.StandardOutput.BaseStream, isNetCoreDbg); + } +} diff --git a/src/SharpIDE.Application/Features/Debugging/DebuggingService.cs b/src/SharpIDE.Application/Features/Debugging/DebuggingService.cs index 18600c7..997bf87 100644 --- a/src/SharpIDE.Application/Features/Debugging/DebuggingService.cs +++ b/src/SharpIDE.Application/Features/Debugging/DebuggingService.cs @@ -7,6 +7,7 @@ using Microsoft.VisualStudio.Shared.VSCodeDebugProtocol.Messages; using Newtonsoft.Json.Linq; using SharpIDE.Application.Features.Debugging.Signing; using SharpIDE.Application.Features.Events; +using SharpIDE.Application.Features.Run; using SharpIDE.Application.Features.SolutionDiscovery; using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence; @@ -16,33 +17,14 @@ namespace SharpIDE.Application.Features.Debugging; public class DebuggingService { private DebugProtocolHost _debugProtocolHost = null!; - public async Task Attach(int debuggeeProcessId, string? debuggerExecutablePath, Dictionary> breakpointsByFile, SharpIdeProjectModel project, CancellationToken cancellationToken = default) + public async Task Attach(int debuggeeProcessId, DebuggerExecutableInfo? debuggerExecutableInfo, Dictionary> breakpointsByFile, SharpIdeProjectModel project, CancellationToken cancellationToken = default) { Guard.Against.NegativeOrZero(debuggeeProcessId, nameof(debuggeeProcessId), "Process ID must be a positive integer."); await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding); - if (string.IsNullOrWhiteSpace(debuggerExecutablePath)) - { - throw new ArgumentNullException(nameof(debuggerExecutablePath), "Debugger executable path cannot be null or empty."); - } - var isNetCoreDbg = Path.GetFileNameWithoutExtension(debuggerExecutablePath).Equals("netcoredbg", StringComparison.OrdinalIgnoreCase); + var (inputStream, outputStream, isNetCoreDbg) = DebuggerProcessStreamHelper.NewDebuggerProcessStreamsForInfo(debuggerExecutableInfo); - var process = new Process - { - StartInfo = new ProcessStartInfo - { - //FileName = @"C:\Users\Matthew\Downloads\netcoredbg-win64\netcoredbg\netcoredbg.exe", - FileName = debuggerExecutablePath, - Arguments = "--interpreter=vscode", - RedirectStandardInput = true, - RedirectStandardOutput = true, - UseShellExecute = false, - CreateNoWindow = true - } - }; - process.Start(); - - var debugProtocolHost = new DebugProtocolHost(process.StandardInput.BaseStream, process.StandardOutput.BaseStream, false); + var debugProtocolHost = new DebugProtocolHost(inputStream, outputStream, false); var initializedEventTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); _debugProtocolHost = debugProtocolHost; debugProtocolHost.LogMessage += (sender, args) => diff --git a/src/SharpIDE.Application/Features/Run/DebuggerExecutableInfo.cs b/src/SharpIDE.Application/Features/Run/DebuggerExecutableInfo.cs new file mode 100644 index 0000000..fb680be --- /dev/null +++ b/src/SharpIDE.Application/Features/Run/DebuggerExecutableInfo.cs @@ -0,0 +1,7 @@ +namespace SharpIDE.Application.Features.Run; + +public readonly record struct DebuggerExecutableInfo +{ + public required bool UseInMemorySharpDbg { get; init; } + public required string? DebuggerExecutablePath { get; init; } +} diff --git a/src/SharpIDE.Application/Features/Run/RunService.cs b/src/SharpIDE.Application/Features/Run/RunService.cs index 1b3c8bc..f75b477 100644 --- a/src/SharpIDE.Application/Features/Run/RunService.cs +++ b/src/SharpIDE.Application/Features/Run/RunService.cs @@ -22,7 +22,7 @@ public partial class RunService(ILogger logger, RoslynAnalysis rosly private readonly ILogger _logger = logger; private readonly RoslynAnalysis _roslynAnalysis = roslynAnalysis; - public async Task RunProject(SharpIdeProjectModel project, bool isDebug = false, string? debuggerExecutablePath = null) + public async Task RunProject(SharpIdeProjectModel project, bool isDebug = false, DebuggerExecutableInfo? debuggerExecutableInfo = null) { Guard.Against.Null(project, nameof(project)); Guard.Against.NullOrWhiteSpace(project.FilePath, nameof(project.FilePath), "Project file path cannot be null or empty."); @@ -93,7 +93,7 @@ public partial class RunService(ILogger logger, RoslynAnalysis rosly // Attach debugger (which internally uses a DiagnosticClient to resume startup) var debugger = new Debugger { Project = project, ProcessId = process.ProcessId }; _debugger = debugger; - await debugger.Attach(debuggerExecutablePath, Breakpoints.ToDictionary(), project, project.RunningCancellationTokenSource.Token).ConfigureAwait(false); + await debugger.Attach(debuggerExecutableInfo, Breakpoints.ToDictionary(), project, project.RunningCancellationTokenSource.Token).ConfigureAwait(false); } project.Running = true; diff --git a/src/SharpIDE.Godot/Features/Run/RunMenuItem.cs b/src/SharpIDE.Godot/Features/Run/RunMenuItem.cs index cb945a2..4f8f48e 100644 --- a/src/SharpIDE.Godot/Features/Run/RunMenuItem.cs +++ b/src/SharpIDE.Godot/Features/Run/RunMenuItem.cs @@ -62,6 +62,11 @@ public partial class RunMenuItem : HBoxContainer private async void OnDebugButtonPressed() { GodotGlobalEvents.Instance.BottomPanelTabExternallySelected.InvokeParallelFireAndForget(BottomPanelType.Debug); - await _runService.RunProject(Project, true, Singletons.AppState.IdeSettings.DebuggerExecutablePath).ConfigureAwait(false); + var debuggerExecutableInfo = new DebuggerExecutableInfo + { + UseInMemorySharpDbg = false, + DebuggerExecutablePath = Singletons.AppState.IdeSettings.DebuggerExecutablePath + }; + await _runService.RunProject(Project, true, debuggerExecutableInfo).ConfigureAwait(false); } } \ No newline at end of file