Files
ZuneUIXTools/DebugAdapter/UIX.DebugAdapter.Server/IrisDebugAdapterServer.cs
T

181 lines
6.1 KiB
C#
Raw Normal View History

2025-11-30 13:37:33 -06:00
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Iris.Debug;
2025-12-01 00:45:01 -06:00
using Microsoft.Iris.Debug.Data;
2025-11-30 13:37:33 -06:00
using Microsoft.Iris.DebugAdapter.Server.Handlers;
2025-12-01 00:45:01 -06:00
using OmniSharp.Extensions.DebugAdapter.Protocol.Events;
2025-11-30 13:37:33 -06:00
using OmniSharp.Extensions.DebugAdapter.Server;
using System;
using System.IO;
2025-12-01 00:45:01 -06:00
using System.Threading;
2025-11-30 13:37:33 -06:00
using System.Threading.Tasks;
namespace Microsoft.Iris.DebugAdapter.Server;
2025-12-01 00:45:01 -06:00
public class IrisDebugAdapterServer : IDebuggerServer, IRemoteDebuggerState, IDisposable
2025-11-30 13:37:33 -06:00
{
private readonly TaskCompletionSource<bool> _serverStopped;
2025-12-01 00:45:01 -06:00
private Stream _inputStream, _outputStream;
private InterpreterCommand _debuggerCommand;
2025-11-30 13:37:33 -06:00
2025-12-01 00:45:01 -06:00
public IrisDebugAdapterServer(IrisDebugServerOptions options)
2025-11-30 13:37:33 -06:00
{
_serverStopped = new();
Options = options;
2025-12-01 00:45:01 -06:00
ConnectionString = options.ConnectionString;
2025-11-30 13:37:33 -06:00
}
internal IrisDebugServerOptions Options { get; }
internal DebugAdapterServer? Server { get; private set; }
2025-12-01 00:45:01 -06:00
public InterpreterCommand DebuggerCommand
{
get => _debuggerCommand;
set
{
_debuggerCommand = value;
if (Server is null)
return;
if (value is InterpreterCommand.Continue)
{
Server.SendContinued(new()
{
ThreadId = Environment.CurrentManagedThreadId,
});
}
else if (value is InterpreterCommand.Break)
{
Server.SendStopped(new()
{
Reason = new("unknown"),
ThreadId = Environment.CurrentManagedThreadId,
});
}
}
}
public string ConnectionString { get; }
2025-11-30 13:37:33 -06:00
/// <summary>
/// Start the debug server listening.
/// </summary>
/// <returns>A task that completes when the server is ready.</returns>
public async Task StartAsync()
{
2025-12-01 00:45:01 -06:00
Server = DebugAdapterServer.Create(options =>
2025-11-30 13:37:33 -06:00
{
// We need to let the PowerShell Context Service know that we are in a debug session
// so that it doesn't send the powerShell/startDebugger message.
//_psesHost = ServiceProvider.GetService<PsesInternalHost>();
//_psesHost.DebugContext.IsDebugServerActive = true;
options
.WithInput(_inputStream)
.WithOutput(_outputStream)
.WithServices(serviceCollection =>
serviceCollection
.AddOptions()
2025-12-01 00:45:01 -06:00
.AddIrisDebugServices(this, Options.SymbolDir, Options.SourceDir)
2025-11-30 13:37:33 -06:00
)
// TODO: Consider replacing all WithHandler with AddSingleton
//.WithHandler<AttachHandler>()
//.WithHandler<DisconnectHandler>()
.WithHandler<BreakpointHandler>()
//.WithHandler<ConfigurationDoneHandler>()
//.WithHandler<ThreadsHandler>()
//.WithHandler<StackTraceHandler>()
//.WithHandler<ScopesHandler>()
//.WithHandler<VariablesHandler>()
.WithHandler<ContinueHandler>()
.WithHandler<NextHandler>()
.WithHandler<PauseHandler>()
.WithHandler<StepInHandler>()
//.WithHandler<StepOutHandler>()
//.WithHandler<SourceHandler>()
//.WithHandler<SetVariableHandler>()
//.WithHandler<DebugEvaluateHandler>()
// The OnInitialize delegate gets run when we first receive the _Initialize_ request:
// https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Initialize
.OnInitialize(async (server, _, cancellationToken) =>
{
2025-12-03 14:03:19 -06:00
System.Diagnostics.Debug.WriteLine("SERVER: OnInitialize called");
Console.WriteLine("SERVER: OnInitialize called");
2025-11-30 13:37:33 -06:00
})
.OnInitialized((_, _, response, _) =>
{
2025-12-03 14:03:19 -06:00
System.Diagnostics.Debug.WriteLine("SERVER: OnInitialized called");
Console.WriteLine("SERVER: OnInitialized called");
2025-11-30 13:37:33 -06:00
return Task.CompletedTask;
})
;
2025-12-01 00:45:01 -06:00
});
await Server.Initialize(default).ConfigureAwait(false);
Connected?.Invoke(this, EventArgs.Empty);
2025-12-03 14:03:19 -06:00
await WaitForShutdownAsync().ConfigureAwait(false);
2025-11-30 13:37:33 -06:00
}
public void Dispose()
{
// Note that the lifetime of the DebugContext is longer than the debug server;
// It represents the debugger on the PowerShell process we're in,
// while a new debug server is spun up for every debugging session
//_psesHost.DebugContext.IsDebugServerActive = false;
Server?.Dispose();
_inputStream.Dispose();
_outputStream.Dispose();
_serverStopped.SetResult(true);
}
public async Task WaitForShutdownAsync() => await _serverStopped.Task.ConfigureAwait(false);
public event EventHandler? SessionEnded;
2025-12-01 00:45:01 -06:00
public event Action<IDebuggerState, object> Connected;
2025-11-30 13:37:33 -06:00
internal void OnSessionEnded() => SessionEnded?.Invoke(this, EventArgs.Empty);
2025-12-01 00:45:01 -06:00
public MarkupLineNumberEntry[] OnLineNumberTableRequested(string uri)
{
return [];
}
public void LogInterpreterDecode(object context, InterpreterInstruction instruction)
{
}
public void LogInterpreterExecute(object context, InterpreterEntry entry)
{
}
public void LogDispatcher(string message)
{
}
public void WaitForContinue()
{
while (DebuggerCommand is InterpreterCommand.Break) ;
}
public void Start()
{
2025-12-03 14:03:19 -06:00
ConnectionStringHelper.CreateFromString(ConnectionString, out _inputStream, out _outputStream);
2025-12-01 00:45:01 -06:00
Thread serverThread = new(() =>
{
_ = StartAsync();
//.ContinueWith(t =>
//{
// if (t.Exception is not null)
// System.Diagnostics.Debug.WriteLine(t.Exception);
//});
});
serverThread.IsBackground = true;
serverThread.Start();
}
2025-11-30 13:37:33 -06:00
}