From ae1e6eef6f1d6f7d88788d507c85927098de3c90 Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Mon, 12 Oct 2020 10:21:48 -0700 Subject: [PATCH 01/13] raname --- src/PowerShellEditorServices/PowerShellEditorServices.csproj | 4 ++-- src/PowerShellEditorServices/Server/PsesDebugServer.cs | 1 + .../Services/DebugAdapter/Handlers/InitializeHandler.cs | 2 +- .../PowerShellEditorServices.Test.E2E.csproj | 2 +- .../PowerShellEditorServices.Test.csproj | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/PowerShellEditorServices/PowerShellEditorServices.csproj b/src/PowerShellEditorServices/PowerShellEditorServices.csproj index d490dee74..8aff14b55 100644 --- a/src/PowerShellEditorServices/PowerShellEditorServices.csproj +++ b/src/PowerShellEditorServices/PowerShellEditorServices.csproj @@ -39,8 +39,8 @@ - - + + diff --git a/src/PowerShellEditorServices/Server/PsesDebugServer.cs b/src/PowerShellEditorServices/Server/PsesDebugServer.cs index 3e74221a5..66bd8e31a 100644 --- a/src/PowerShellEditorServices/Server/PsesDebugServer.cs +++ b/src/PowerShellEditorServices/Server/PsesDebugServer.cs @@ -13,6 +13,7 @@ using Microsoft.PowerShell.EditorServices.Handlers; using Microsoft.PowerShell.EditorServices.Services; using Microsoft.PowerShell.EditorServices.Utility; +using OmniSharp.Extensions.DebugAdapter.Protocol; using OmniSharp.Extensions.DebugAdapter.Protocol.Serialization; using OmniSharp.Extensions.JsonRpc; using OmniSharp.Extensions.LanguageServer.Server; diff --git a/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/InitializeHandler.cs b/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/InitializeHandler.cs index 4fb305090..92d684ef1 100644 --- a/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/InitializeHandler.cs +++ b/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/InitializeHandler.cs @@ -11,7 +11,7 @@ namespace Microsoft.PowerShell.EditorServices.Handlers { - internal class InitializeHandler : IInitializeHandler + internal class InitializeHandler : IDebugAdapterInitializeHandler { private readonly ILogger _logger; private readonly DebugService _debugService; diff --git a/test/PowerShellEditorServices.Test.E2E/PowerShellEditorServices.Test.E2E.csproj b/test/PowerShellEditorServices.Test.E2E/PowerShellEditorServices.Test.E2E.csproj index 8d28f6f0d..ab1f5b5ed 100644 --- a/test/PowerShellEditorServices.Test.E2E/PowerShellEditorServices.Test.E2E.csproj +++ b/test/PowerShellEditorServices.Test.E2E/PowerShellEditorServices.Test.E2E.csproj @@ -10,7 +10,7 @@ - + diff --git a/test/PowerShellEditorServices.Test/PowerShellEditorServices.Test.csproj b/test/PowerShellEditorServices.Test/PowerShellEditorServices.Test.csproj index 8823c88f6..60d37b876 100644 --- a/test/PowerShellEditorServices.Test/PowerShellEditorServices.Test.csproj +++ b/test/PowerShellEditorServices.Test/PowerShellEditorServices.Test.csproj @@ -30,7 +30,7 @@ - + From 7a858102d52451712ead3d5bd695a58b3714da0f Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Mon, 26 Oct 2020 19:32:04 -0700 Subject: [PATCH 02/13] move to 0.18.1 --- .../Services/TextDocument/Handlers/CodeLensHandlers.cs | 2 ++ .../PowerShellEditorServices.Test.E2E.csproj | 2 +- .../PowerShellEditorServices.Test.csproj | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/PowerShellEditorServices/Services/TextDocument/Handlers/CodeLensHandlers.cs b/src/PowerShellEditorServices/Services/TextDocument/Handlers/CodeLensHandlers.cs index a43dac341..4bcd45a91 100644 --- a/src/PowerShellEditorServices/Services/TextDocument/Handlers/CodeLensHandlers.cs +++ b/src/PowerShellEditorServices/Services/TextDocument/Handlers/CodeLensHandlers.cs @@ -24,11 +24,13 @@ namespace Microsoft.PowerShell.EditorServices.Handlers { internal class PsesCodeLensHandlers : ICodeLensHandler, ICodeLensResolveHandler { + private readonly Guid _id = new Guid(); private readonly ILogger _logger; private readonly SymbolsService _symbolsService; private readonly WorkspaceService _workspaceService; private CodeLensCapability _capability; + public Guid Id => _id; public PsesCodeLensHandlers(ILoggerFactory factory, SymbolsService symbolsService, WorkspaceService workspaceService, ConfigurationService configurationService) { diff --git a/test/PowerShellEditorServices.Test.E2E/PowerShellEditorServices.Test.E2E.csproj b/test/PowerShellEditorServices.Test.E2E/PowerShellEditorServices.Test.E2E.csproj index ab1f5b5ed..e0631b3cf 100644 --- a/test/PowerShellEditorServices.Test.E2E/PowerShellEditorServices.Test.E2E.csproj +++ b/test/PowerShellEditorServices.Test.E2E/PowerShellEditorServices.Test.E2E.csproj @@ -10,7 +10,7 @@ - + diff --git a/test/PowerShellEditorServices.Test/PowerShellEditorServices.Test.csproj b/test/PowerShellEditorServices.Test/PowerShellEditorServices.Test.csproj index 60d37b876..f808f7a70 100644 --- a/test/PowerShellEditorServices.Test/PowerShellEditorServices.Test.csproj +++ b/test/PowerShellEditorServices.Test/PowerShellEditorServices.Test.csproj @@ -30,7 +30,7 @@ - + From 03572f6a2753f65c0c6023fbc36c4474a3aa9e83 Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Mon, 26 Oct 2020 21:09:48 -0700 Subject: [PATCH 03/13] almost working --- .../Extensions/Api/EditorContextService.cs | 4 +-- .../Api/EditorExtensionServiceProvider.cs | 8 +++--- .../Extensions/Api/EditorUIService.cs | 4 +-- .../Extensions/Api/LanguageServerService.cs | 4 +-- .../Hosting/EditorServicesServerFactory.cs | 4 +-- .../Server/PsesDebugServer.cs | 26 ++++++++++++++----- .../Server/PsesServiceCollectionExtensions.cs | 4 +-- .../Services/Analysis/AnalysisService.cs | 4 +-- .../EditorOperationsService.cs | 4 +-- .../PowerShellContext/ExtensionService.cs | 4 +-- .../Handlers/GetVersionHandler.cs | 4 +-- .../PowerShellContextService.cs | 6 ++--- .../Session/Host/PromptHandlers.cs | 8 +++--- .../Host/ProtocolPSHostUserInterface.cs | 4 +-- 14 files changed, 51 insertions(+), 37 deletions(-) diff --git a/src/PowerShellEditorServices/Extensions/Api/EditorContextService.cs b/src/PowerShellEditorServices/Extensions/Api/EditorContextService.cs index e9c3cab87..40482fc3f 100644 --- a/src/PowerShellEditorServices/Extensions/Api/EditorContextService.cs +++ b/src/PowerShellEditorServices/Extensions/Api/EditorContextService.cs @@ -84,10 +84,10 @@ public interface IEditorContextService internal class EditorContextService : IEditorContextService { - private readonly ILanguageServer _languageServer; + private readonly ILanguageServerFacade _languageServer; internal EditorContextService( - ILanguageServer languageServer) + ILanguageServerFacade languageServer) { _languageServer = languageServer; } diff --git a/src/PowerShellEditorServices/Extensions/Api/EditorExtensionServiceProvider.cs b/src/PowerShellEditorServices/Extensions/Api/EditorExtensionServiceProvider.cs index da3cf0629..582543663 100644 --- a/src/PowerShellEditorServices/Extensions/Api/EditorExtensionServiceProvider.cs +++ b/src/PowerShellEditorServices/Extensions/Api/EditorExtensionServiceProvider.cs @@ -43,12 +43,12 @@ public class EditorExtensionServiceProvider internal EditorExtensionServiceProvider(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; - LanguageServer = new LanguageServerService(_serviceProvider.GetService()); + LanguageServer = new LanguageServerService(_serviceProvider.GetService()); //DocumentSymbols = new DocumentSymbolService(_serviceProvider.GetService()); ExtensionCommands = new ExtensionCommandService(_serviceProvider.GetService()); Workspace = new WorkspaceService(_serviceProvider.GetService()); - EditorContext = new EditorContextService(_serviceProvider.GetService()); - EditorUI = new EditorUIService(_serviceProvider.GetService()); + EditorContext = new EditorContextService(_serviceProvider.GetService()); + EditorUI = new EditorUIService(_serviceProvider.GetService()); } /// @@ -143,7 +143,7 @@ public object GetServiceByAssemblyQualifiedName(string asmQualifiedTypeName) /// /// This method is intended as a trapdoor and should not be used in the first instance. /// Consider using the public extension services if possible. - /// + /// /// Also note that services in PSES may live in a separate assembly load context, /// meaning that a type of the seemingly correct name may fail to fetch to a service /// that is known under a type of the same name but loaded in a different context. diff --git a/src/PowerShellEditorServices/Extensions/Api/EditorUIService.cs b/src/PowerShellEditorServices/Extensions/Api/EditorUIService.cs index 0e58d7d64..1dd3e8738 100644 --- a/src/PowerShellEditorServices/Extensions/Api/EditorUIService.cs +++ b/src/PowerShellEditorServices/Extensions/Api/EditorUIService.cs @@ -103,9 +103,9 @@ internal class EditorUIService : IEditorUIService { private static string[] s_choiceResponseLabelSeparators = new[] { ", " }; - private readonly ILanguageServer _languageServer; + private readonly ILanguageServerFacade _languageServer; - public EditorUIService(ILanguageServer languageServer) + public EditorUIService(ILanguageServerFacade languageServer) { _languageServer = languageServer; } diff --git a/src/PowerShellEditorServices/Extensions/Api/LanguageServerService.cs b/src/PowerShellEditorServices/Extensions/Api/LanguageServerService.cs index 0e7be9043..0d6e9a98a 100644 --- a/src/PowerShellEditorServices/Extensions/Api/LanguageServerService.cs +++ b/src/PowerShellEditorServices/Extensions/Api/LanguageServerService.cs @@ -66,9 +66,9 @@ public interface ILanguageServerService internal class LanguageServerService : ILanguageServerService { - private readonly ILanguageServer _languageServer; + private readonly ILanguageServerFacade _languageServer; - internal LanguageServerService(ILanguageServer languageServer) + internal LanguageServerService(ILanguageServerFacade languageServer) { _languageServer = languageServer; } diff --git a/src/PowerShellEditorServices/Hosting/EditorServicesServerFactory.cs b/src/PowerShellEditorServices/Hosting/EditorServicesServerFactory.cs index cfb49699b..e15a78940 100644 --- a/src/PowerShellEditorServices/Hosting/EditorServicesServerFactory.cs +++ b/src/PowerShellEditorServices/Hosting/EditorServicesServerFactory.cs @@ -122,11 +122,11 @@ public PsesDebugServer CreateDebugServerForTempSession(Stream inputStream, Strea .ClearProviders() .AddSerilog() .SetMinimumLevel(LogLevel.Trace)) - .AddSingleton(provider => null) + .AddSingleton(provider => null) .AddPsesLanguageServices(hostStartupInfo) // For a Temp session, there is no LanguageServer so just set it to null .AddSingleton( - typeof(ILanguageServer), + typeof(ILanguageServerFacade), _ => null) .BuildServiceProvider(); diff --git a/src/PowerShellEditorServices/Server/PsesDebugServer.cs b/src/PowerShellEditorServices/Server/PsesDebugServer.cs index 66bd8e31a..7a0af8aae 100644 --- a/src/PowerShellEditorServices/Server/PsesDebugServer.cs +++ b/src/PowerShellEditorServices/Server/PsesDebugServer.cs @@ -15,6 +15,7 @@ using Microsoft.PowerShell.EditorServices.Utility; using OmniSharp.Extensions.DebugAdapter.Protocol; using OmniSharp.Extensions.DebugAdapter.Protocol.Serialization; +using OmniSharp.Extensions.DebugAdapter.Server; using OmniSharp.Extensions.JsonRpc; using OmniSharp.Extensions.LanguageServer.Server; @@ -42,7 +43,7 @@ internal class PsesDebugServer : IDisposable private readonly bool _usePSReadLine; private readonly TaskCompletionSource _serverStopped; - private IJsonRpcServer _jsonRpcServer; + private DebugAdapterServer _debugAdapterServer; private PowerShellContextService _powerShellContextService; protected readonly ILoggerFactory _loggerFactory; @@ -72,10 +73,9 @@ public PsesDebugServer( /// A task that completes when the server is ready. public async Task StartAsync() { - _jsonRpcServer = await JsonRpcServer.From(options => + _debugAdapterServer = await DebugAdapterServer.From(options => { - options.Serializer = new DapProtocolSerializer(); - options.Receiver = new DapReceiver(); + // options.Serializer = new DapProtocolSerializer(); options.LoggerFactory = _loggerFactory; ILogger logger = options.LoggerFactory.CreateLogger("DebugOptionsStartup"); @@ -99,6 +99,7 @@ public async Task StartAsync() } options.Services = new ServiceCollection() + .AddOptions() .AddPsesDebugServices(ServiceProvider, this, _useTempSession); options @@ -127,7 +128,20 @@ public async Task StartAsync() .WithHandler() .WithHandler() .WithHandler() - .WithHandler(); + .WithHandler() + .OnInitialize(async (server, request, cancellationToken) => { + var breakpointService = server.GetService(); + // Clear any existing breakpoints before proceeding + await breakpointService.RemoveAllBreakpointsAsync().ConfigureAwait(false); + }) + .OnInitialized(async (server, request, response, cancellationToken) => { + response.SupportsConditionalBreakpoints = true; + response.SupportsConfigurationDoneRequest = true; + response.SupportsFunctionBreakpoints = true; + response.SupportsHitConditionalBreakpoints = true; + response.SupportsLogPoints = true; + response.SupportsSetVariable = true; + }); logger.LogInformation("Handlers added"); }).ConfigureAwait(false); @@ -136,7 +150,7 @@ public async Task StartAsync() public void Dispose() { _powerShellContextService.IsDebugServerActive = false; - _jsonRpcServer.Dispose(); + _debugAdapterServer.Dispose(); _serverStopped.SetResult(true); } diff --git a/src/PowerShellEditorServices/Server/PsesServiceCollectionExtensions.cs b/src/PowerShellEditorServices/Server/PsesServiceCollectionExtensions.cs index 63d30070d..11b196cd4 100644 --- a/src/PowerShellEditorServices/Server/PsesServiceCollectionExtensions.cs +++ b/src/PowerShellEditorServices/Server/PsesServiceCollectionExtensions.cs @@ -26,7 +26,7 @@ public static IServiceCollection AddPsesLanguageServices( (provider) => PowerShellContextService.Create( provider.GetService(), - provider.GetService(), + provider.GetService(), hostStartupInfo)) .AddSingleton() .AddSingleton() @@ -36,7 +36,7 @@ public static IServiceCollection AddPsesLanguageServices( { var extensionService = new ExtensionService( provider.GetService(), - provider.GetService()); + provider.GetService()); extensionService.InitializeAsync( serviceProvider: provider, editorOperations: provider.GetService()) diff --git a/src/PowerShellEditorServices/Services/Analysis/AnalysisService.cs b/src/PowerShellEditorServices/Services/Analysis/AnalysisService.cs index de4bcf01e..439d85b34 100644 --- a/src/PowerShellEditorServices/Services/Analysis/AnalysisService.cs +++ b/src/PowerShellEditorServices/Services/Analysis/AnalysisService.cs @@ -84,7 +84,7 @@ internal static string GetUniqueIdFromDiagnostic(Diagnostic diagnostic) private readonly ILogger _logger; - private readonly ILanguageServer _languageServer; + private readonly ILanguageServerFacade _languageServer; private readonly ConfigurationService _configurationService; @@ -109,7 +109,7 @@ internal static string GetUniqueIdFromDiagnostic(Diagnostic diagnostic) /// The workspace service for file handling within a workspace. public AnalysisService( ILoggerFactory loggerFactory, - ILanguageServer languageServer, + ILanguageServerFacade languageServer, ConfigurationService configurationService, WorkspaceService workspaceService) { diff --git a/src/PowerShellEditorServices/Services/PowerShellContext/EditorOperationsService.cs b/src/PowerShellEditorServices/Services/PowerShellContext/EditorOperationsService.cs index 78e907acb..e6dc46c1d 100644 --- a/src/PowerShellEditorServices/Services/PowerShellContext/EditorOperationsService.cs +++ b/src/PowerShellEditorServices/Services/PowerShellContext/EditorOperationsService.cs @@ -19,12 +19,12 @@ internal class EditorOperationsService : IEditorOperations private readonly WorkspaceService _workspaceService; private readonly PowerShellContextService _powerShellContextService; - private readonly ILanguageServer _languageServer; + private readonly ILanguageServerFacade _languageServer; public EditorOperationsService( WorkspaceService workspaceService, PowerShellContextService powerShellContextService, - ILanguageServer languageServer) + ILanguageServerFacade languageServer) { _workspaceService = workspaceService; _powerShellContextService = powerShellContextService; diff --git a/src/PowerShellEditorServices/Services/PowerShellContext/ExtensionService.cs b/src/PowerShellEditorServices/Services/PowerShellContext/ExtensionService.cs index 28cd3e2a1..b70c39c48 100644 --- a/src/PowerShellEditorServices/Services/PowerShellContext/ExtensionService.cs +++ b/src/PowerShellEditorServices/Services/PowerShellContext/ExtensionService.cs @@ -25,7 +25,7 @@ internal sealed class ExtensionService private readonly Dictionary editorCommands = new Dictionary(); - private readonly ILanguageServer _languageServer; + private readonly ILanguageServerFacade _languageServer; #endregion @@ -57,7 +57,7 @@ internal sealed class ExtensionService /// PowerShellContext for loading and executing extension code. /// /// A PowerShellContext used to execute extension code. - internal ExtensionService(PowerShellContextService powerShellContext, ILanguageServer languageServer) + internal ExtensionService(PowerShellContextService powerShellContext, ILanguageServerFacade languageServer) { this.PowerShellContext = powerShellContext; _languageServer = languageServer; diff --git a/src/PowerShellEditorServices/Services/PowerShellContext/Handlers/GetVersionHandler.cs b/src/PowerShellEditorServices/Services/PowerShellContext/Handlers/GetVersionHandler.cs index b61e32798..4d5897359 100644 --- a/src/PowerShellEditorServices/Services/PowerShellContext/Handlers/GetVersionHandler.cs +++ b/src/PowerShellEditorServices/Services/PowerShellContext/Handlers/GetVersionHandler.cs @@ -23,13 +23,13 @@ internal class GetVersionHandler : IGetVersionHandler private readonly ILogger _logger; private readonly PowerShellContextService _powerShellContextService; - private readonly ILanguageServer _languageServer; + private readonly ILanguageServerFacade _languageServer; private readonly ConfigurationService _configurationService; public GetVersionHandler( ILoggerFactory factory, PowerShellContextService powerShellContextService, - ILanguageServer languageServer, + ILanguageServerFacade languageServer, ConfigurationService configurationService) { _logger = factory.CreateLogger(); diff --git a/src/PowerShellEditorServices/Services/PowerShellContext/PowerShellContextService.cs b/src/PowerShellEditorServices/Services/PowerShellContext/PowerShellContextService.cs index 26ae9bd93..556d86a6c 100644 --- a/src/PowerShellEditorServices/Services/PowerShellContext/PowerShellContextService.cs +++ b/src/PowerShellEditorServices/Services/PowerShellContext/PowerShellContextService.cs @@ -69,7 +69,7 @@ static PowerShellContextService() private readonly SemaphoreSlim resumeRequestHandle = AsyncUtils.CreateSimpleLockingSemaphore(); private readonly SessionStateLock sessionStateLock = new SessionStateLock(); - private readonly OmniSharp.Extensions.LanguageServer.Protocol.Server.ILanguageServer _languageServer; + private readonly OmniSharp.Extensions.LanguageServer.Protocol.Server.ILanguageServerFacade _languageServer; private readonly bool isPSReadLineEnabled; private readonly ILogger logger; @@ -173,7 +173,7 @@ public RunspaceDetails CurrentRunspace /// public PowerShellContextService( ILogger logger, - OmniSharp.Extensions.LanguageServer.Protocol.Server.ILanguageServer languageServer, + OmniSharp.Extensions.LanguageServer.Protocol.Server.ILanguageServerFacade languageServer, bool isPSReadLineEnabled) { _languageServer = languageServer; @@ -187,7 +187,7 @@ public PowerShellContextService( [SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "Checked by Validate call")] public static PowerShellContextService Create( ILoggerFactory factory, - OmniSharp.Extensions.LanguageServer.Protocol.Server.ILanguageServer languageServer, + OmniSharp.Extensions.LanguageServer.Protocol.Server.ILanguageServerFacade languageServer, HostStartupInfo hostStartupInfo) { Validate.IsNotNull(nameof(hostStartupInfo), hostStartupInfo); diff --git a/src/PowerShellEditorServices/Services/PowerShellContext/Session/Host/PromptHandlers.cs b/src/PowerShellEditorServices/Services/PowerShellContext/Session/Host/PromptHandlers.cs index 0fbfa9636..dc1a08168 100644 --- a/src/PowerShellEditorServices/Services/PowerShellContext/Session/Host/PromptHandlers.cs +++ b/src/PowerShellEditorServices/Services/PowerShellContext/Session/Host/PromptHandlers.cs @@ -14,12 +14,12 @@ namespace Microsoft.PowerShell.EditorServices.Services.PowerShellContext { internal class ProtocolChoicePromptHandler : ConsoleChoicePromptHandler { - private readonly ILanguageServer _languageServer; + private readonly ILanguageServerFacade _languageServer; private readonly IHostInput _hostInput; private TaskCompletionSource _readLineTask; public ProtocolChoicePromptHandler( - ILanguageServer languageServer, + ILanguageServerFacade languageServer, IHostInput hostInput, IHostOutput hostOutput, ILogger logger) @@ -96,12 +96,12 @@ private void HandlePromptResponse( internal class ProtocolInputPromptHandler : ConsoleInputPromptHandler { - private readonly ILanguageServer _languageServer; + private readonly ILanguageServerFacade _languageServer; private readonly IHostInput hostInput; private TaskCompletionSource readLineTask; public ProtocolInputPromptHandler( - ILanguageServer languageServer, + ILanguageServerFacade languageServer, IHostInput hostInput, IHostOutput hostOutput, ILogger logger) diff --git a/src/PowerShellEditorServices/Services/PowerShellContext/Session/Host/ProtocolPSHostUserInterface.cs b/src/PowerShellEditorServices/Services/PowerShellContext/Session/Host/ProtocolPSHostUserInterface.cs index b45f0d4a4..e8e051cbd 100644 --- a/src/PowerShellEditorServices/Services/PowerShellContext/Session/Host/ProtocolPSHostUserInterface.cs +++ b/src/PowerShellEditorServices/Services/PowerShellContext/Session/Host/ProtocolPSHostUserInterface.cs @@ -15,7 +15,7 @@ internal class ProtocolPSHostUserInterface : EditorServicesPSHostUserInterface { #region Private Fields - private readonly ILanguageServer _languageServer; + private readonly ILanguageServerFacade _languageServer; #endregion @@ -27,7 +27,7 @@ internal class ProtocolPSHostUserInterface : EditorServicesPSHostUserInterface /// /// public ProtocolPSHostUserInterface( - ILanguageServer languageServer, + ILanguageServerFacade languageServer, PowerShellContextService powerShellContext, ILogger logger) : base ( From 36c53e77e9dfeec67fd300d36cb926a1eb3d70c9 Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Tue, 27 Oct 2020 15:13:29 -0700 Subject: [PATCH 04/13] merge launch and attach and handle onstarted --- .../Server/PsesDebugServer.cs | 28 ++--- .../DebugAdapter/DebugEventHandlerService.cs | 20 ++-- .../DebugAdapter/DebugStateService.cs | 2 + .../Handlers/ConfigurationDoneHandler.cs | 10 +- .../Handlers/LaunchAndAttachHandler.cs | 112 +++++++----------- 5 files changed, 67 insertions(+), 105 deletions(-) diff --git a/src/PowerShellEditorServices/Server/PsesDebugServer.cs b/src/PowerShellEditorServices/Server/PsesDebugServer.cs index 7a0af8aae..ebac16897 100644 --- a/src/PowerShellEditorServices/Server/PsesDebugServer.cs +++ b/src/PowerShellEditorServices/Server/PsesDebugServer.cs @@ -75,10 +75,6 @@ public async Task StartAsync() { _debugAdapterServer = await DebugAdapterServer.From(options => { - // options.Serializer = new DapProtocolSerializer(); - options.LoggerFactory = _loggerFactory; - ILogger logger = options.LoggerFactory.CreateLogger("DebugOptionsStartup"); - // 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. _powerShellContextService = ServiceProvider.GetService(); @@ -98,20 +94,14 @@ public async Task StartAsync() .GetResult(); } - options.Services = new ServiceCollection() - .AddOptions() - .AddPsesDebugServices(ServiceProvider, this, _useTempSession); - options .WithInput(_inputStream) - .WithOutput(_outputStream); - - logger.LogInformation("Adding handlers"); - - options - .WithHandler() - .WithHandler() - .WithHandler() + .WithOutput(_outputStream) + .WithServices(serviceCollection => serviceCollection + .AddLogging() + .AddOptions() + .AddPsesDebugServices(ServiceProvider, this, _useTempSession)) + .WithHandler() .WithHandler() .WithHandler() .WithHandler() @@ -134,16 +124,16 @@ public async Task StartAsync() // Clear any existing breakpoints before proceeding await breakpointService.RemoveAllBreakpointsAsync().ConfigureAwait(false); }) - .OnInitialized(async (server, request, response, cancellationToken) => { + .OnInitialized((server, request, response, cancellationToken) => { response.SupportsConditionalBreakpoints = true; response.SupportsConfigurationDoneRequest = true; response.SupportsFunctionBreakpoints = true; response.SupportsHitConditionalBreakpoints = true; response.SupportsLogPoints = true; response.SupportsSetVariable = true; - }); - logger.LogInformation("Handlers added"); + return Task.CompletedTask; + }); }).ConfigureAwait(false); } diff --git a/src/PowerShellEditorServices/Services/DebugAdapter/DebugEventHandlerService.cs b/src/PowerShellEditorServices/Services/DebugAdapter/DebugEventHandlerService.cs index 9e48625b5..d91295d76 100644 --- a/src/PowerShellEditorServices/Services/DebugAdapter/DebugEventHandlerService.cs +++ b/src/PowerShellEditorServices/Services/DebugAdapter/DebugEventHandlerService.cs @@ -9,7 +9,7 @@ using Microsoft.PowerShell.EditorServices.Services.PowerShellContext; using Microsoft.PowerShell.EditorServices.Utility; using OmniSharp.Extensions.DebugAdapter.Protocol.Events; -using OmniSharp.Extensions.JsonRpc; +using OmniSharp.Extensions.DebugAdapter.Protocol.Server; namespace Microsoft.PowerShell.EditorServices.Services { @@ -19,20 +19,20 @@ internal class DebugEventHandlerService private readonly PowerShellContextService _powerShellContextService; private readonly DebugService _debugService; private readonly DebugStateService _debugStateService; - private readonly IJsonRpcServer _jsonRpcServer; + private readonly IDebugAdapterServerFacade _debugAdapterServer; public DebugEventHandlerService( ILoggerFactory factory, PowerShellContextService powerShellContextService, DebugService debugService, DebugStateService debugStateService, - IJsonRpcServer jsonRpcServer) + IDebugAdapterServerFacade debugAdapterServer) { _logger = factory.CreateLogger(); _powerShellContextService = powerShellContextService; _debugService = debugService; _debugStateService = debugStateService; - _jsonRpcServer = jsonRpcServer; + _debugAdapterServer = debugAdapterServer; } internal void RegisterEventHandlers() @@ -79,7 +79,7 @@ e.OriginalEvent.Breakpoints[0] is CommandBreakpoint : "breakpoint"; } - _jsonRpcServer.SendNotification(EventNames.Stopped, + _debugAdapterServer.SendNotification(EventNames.Stopped, new StoppedEvent { ThreadId = 1, @@ -93,10 +93,10 @@ private void PowerShellContext_RunspaceChanged(object sender, RunspaceChangedEve e.ChangeAction == RunspaceChangeAction.Enter && e.NewRunspace.Context == RunspaceContext.DebuggedRunspace) { - // Send the InitializedEvent so that the debugger will continue + // Sends the InitializedEvent so that the debugger will continue // sending configuration requests _debugStateService.WaitingForAttach = false; - _jsonRpcServer.SendNotification(EventNames.Initialized); + _debugStateService.ServerStarted.SetResult(true); } else if ( e.ChangeAction == RunspaceChangeAction.Exit && @@ -105,7 +105,7 @@ private void PowerShellContext_RunspaceChanged(object sender, RunspaceChangedEve // Exited the session while the debugger is stopped, // send a ContinuedEvent so that the client changes the // UI to appear to be running again - _jsonRpcServer.SendNotification(EventNames.Continued, + _debugAdapterServer.SendNotification(EventNames.Continued, new ContinuedEvent { ThreadId = 1, @@ -116,7 +116,7 @@ private void PowerShellContext_RunspaceChanged(object sender, RunspaceChangedEve private void PowerShellContext_DebuggerResumed(object sender, DebuggerResumeAction e) { - _jsonRpcServer.SendNotification(EventNames.Continued, + _debugAdapterServer.SendNotification(EventNames.Continued, new ContinuedEvent { AllThreadsContinued = true, @@ -164,7 +164,7 @@ private void DebugService_BreakpointUpdated(object sender, BreakpointUpdatedEven breakpoint.Verified = e.UpdateType != BreakpointUpdateType.Disabled; - _jsonRpcServer.SendNotification(EventNames.Breakpoint, + _debugAdapterServer.SendNotification(EventNames.Breakpoint, new BreakpointEvent { Reason = reason, diff --git a/src/PowerShellEditorServices/Services/DebugAdapter/DebugStateService.cs b/src/PowerShellEditorServices/Services/DebugAdapter/DebugStateService.cs index f60d945a6..071e978ff 100644 --- a/src/PowerShellEditorServices/Services/DebugAdapter/DebugStateService.cs +++ b/src/PowerShellEditorServices/Services/DebugAdapter/DebugStateService.cs @@ -38,6 +38,8 @@ internal class DebugStateService internal bool IsUsingTempIntegratedConsole { get; set; } + internal TaskCompletionSource ServerStarted { get; set; } + internal void ReleaseSetBreakpointHandle() { _setBreakpointInProgressHandle.Release(); diff --git a/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/ConfigurationDoneHandler.cs b/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/ConfigurationDoneHandler.cs index 12e77552e..f0c9674ae 100644 --- a/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/ConfigurationDoneHandler.cs +++ b/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/ConfigurationDoneHandler.cs @@ -14,14 +14,14 @@ using Microsoft.PowerShell.EditorServices.Services.TextDocument; using OmniSharp.Extensions.DebugAdapter.Protocol.Events; using OmniSharp.Extensions.DebugAdapter.Protocol.Requests; -using OmniSharp.Extensions.JsonRpc; +using OmniSharp.Extensions.DebugAdapter.Protocol.Server; namespace Microsoft.PowerShell.EditorServices.Handlers { internal class ConfigurationDoneHandler : IConfigurationDoneHandler { private readonly ILogger _logger; - private readonly IJsonRpcServer _jsonRpcServer; + private readonly IDebugAdapterServerFacade _debugAdapterServer; private readonly DebugService _debugService; private readonly DebugStateService _debugStateService; private readonly DebugEventHandlerService _debugEventHandlerService; @@ -30,7 +30,7 @@ internal class ConfigurationDoneHandler : IConfigurationDoneHandler public ConfigurationDoneHandler( ILoggerFactory loggerFactory, - IJsonRpcServer jsonRpcServer, + IDebugAdapterServerFacade debugAdapterServer, DebugService debugService, DebugStateService debugStateService, DebugEventHandlerService debugEventHandlerService, @@ -38,7 +38,7 @@ public ConfigurationDoneHandler( WorkspaceService workspaceService) { _logger = loggerFactory.CreateLogger(); - _jsonRpcServer = jsonRpcServer; + _debugAdapterServer = debugAdapterServer; _debugService = debugService; _debugStateService = debugStateService; _debugEventHandlerService = debugEventHandlerService; @@ -127,7 +127,7 @@ await _powerShellContextService .ExecuteScriptWithArgsAsync(scriptToLaunch, _debugStateService.Arguments, writeInputToHost: true).ConfigureAwait(false); } - _jsonRpcServer.SendNotification(EventNames.Terminated); + _debugAdapterServer.SendNotification(EventNames.Terminated); } } } diff --git a/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/LaunchAndAttachHandler.cs b/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/LaunchAndAttachHandler.cs index 02e62a5f9..8317b11b4 100644 --- a/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/LaunchAndAttachHandler.cs +++ b/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/LaunchAndAttachHandler.cs @@ -15,31 +15,20 @@ using Microsoft.PowerShell.EditorServices.Services; using Microsoft.PowerShell.EditorServices.Services.PowerShellContext; using OmniSharp.Extensions.DebugAdapter.Protocol.Events; -using MediatR; using OmniSharp.Extensions.JsonRpc; using Microsoft.PowerShell.EditorServices.Services.TextDocument; +using OmniSharp.Extensions.DebugAdapter.Protocol.Requests; +using OmniSharp.Extensions.DebugAdapter.Protocol.Server; namespace Microsoft.PowerShell.EditorServices.Handlers { - [Serial, Method("launch")] - internal interface IPsesLaunchHandler : IJsonRpcRequestHandler { } - - [Serial, Method("attach")] - internal interface IPsesAttachHandler : IJsonRpcRequestHandler { } - - internal class PsesLaunchRequestArguments : IRequest + internal class PsesLaunchRequestArguments : LaunchRequestArguments { /// /// Gets or sets the absolute path to the script to debug. /// public string Script { get; set; } - /// - /// Gets or sets a boolean value that indicates whether the script should be - /// run with (false) or without (true) debugging support. - /// - public bool NoDebug { get; set; } - /// /// Gets or sets a boolean value that determines whether to automatically stop /// target after launch. If not specified, target does not stop. @@ -53,7 +42,7 @@ internal class PsesLaunchRequestArguments : IRequest /// /// Gets or sets the working directory of the launched debuggee (specified as an absolute path). - /// If omitted the debuggee is lauched in its own directory. + /// If omitted the debuggee is launched in its own directory. /// public string Cwd { get; set; } @@ -81,7 +70,7 @@ internal class PsesLaunchRequestArguments : IRequest public Dictionary Env { get; set; } } - internal class PsesAttachRequestArguments : IRequest + internal class PsesAttachRequestArguments : AttachRequestArguments { public string ComputerName { get; set; } @@ -94,35 +83,40 @@ internal class PsesAttachRequestArguments : IRequest public string CustomPipeName { get; set; } } - internal class LaunchHandler : IPsesLaunchHandler + internal class LaunchAndAttachHandler : ILaunchHandler, IAttachHandler, IOnDebugAdapterServerStarted { - private readonly ILogger _logger; + private static readonly Version s_minVersionForCustomPipeName = new Version(6, 2); + private readonly ILogger _logger; + private readonly BreakpointService _breakpointService; private readonly DebugService _debugService; private readonly PowerShellContextService _powerShellContextService; private readonly DebugStateService _debugStateService; private readonly DebugEventHandlerService _debugEventHandlerService; - private readonly IJsonRpcServer _jsonRpcServer; + private readonly IDebugAdapterServerFacade _debugAdapterServer; private readonly RemoteFileManagerService _remoteFileManagerService; - public LaunchHandler( + public LaunchAndAttachHandler( ILoggerFactory factory, - IJsonRpcServer jsonRpcServer, + IDebugAdapterServerFacade debugAdapterServer, + BreakpointService breakpointService, + DebugEventHandlerService debugEventHandlerService, DebugService debugService, - PowerShellContextService powerShellContextService, DebugStateService debugStateService, - DebugEventHandlerService debugEventHandlerService, + PowerShellContextService powerShellContextService, RemoteFileManagerService remoteFileManagerService) { - _logger = factory.CreateLogger(); - _jsonRpcServer = jsonRpcServer; + _logger = factory.CreateLogger(); + _debugAdapterServer = debugAdapterServer; + _breakpointService = breakpointService; + _debugEventHandlerService = debugEventHandlerService; _debugService = debugService; - _powerShellContextService = powerShellContextService; _debugStateService = debugStateService; - _debugEventHandlerService = debugEventHandlerService; + _debugStateService.ServerStarted = new TaskCompletionSource(); + _powerShellContextService = powerShellContextService; _remoteFileManagerService = remoteFileManagerService; } - public async Task Handle(PsesLaunchRequestArguments request, CancellationToken cancellationToken) + public async Task Handle(PsesLaunchRequestArguments request, CancellationToken cancellationToken) { _debugEventHandlerService.RegisterEventHandlers(); @@ -168,12 +162,12 @@ public async Task Handle(PsesLaunchRequestArguments request, CancellationT await _powerShellContextService.SetWorkingDirectoryAsync(workingDir, isPathAlreadyEscaped: false).ConfigureAwait(false); } - _logger.LogTrace($"Working dir " + (string.IsNullOrEmpty(workingDir) ? "not set." : $"set to '{workingDir}'")); + _logger.LogTrace("Working dir " + (string.IsNullOrEmpty(workingDir) ? "not set." : $"set to '{workingDir}'")); } // Prepare arguments to the script - if specified string arguments = null; - if ((request.Args != null) && (request.Args.Length > 0)) + if (request.Args?.Length > 0) { arguments = string.Join(" ", request.Args); _logger.LogTrace("Script arguments are: " + arguments); @@ -207,45 +201,14 @@ public async Task Handle(PsesLaunchRequestArguments request, CancellationT // debugging session _debugStateService.IsInteractiveDebugSession = string.IsNullOrEmpty(_debugStateService.ScriptToLaunch); - // Send the InitializedEvent so that the debugger will continue + // Sends the InitializedEvent so that the debugger will continue // sending configuration requests - _jsonRpcServer.SendNotification(EventNames.Initialized); + _debugStateService.ServerStarted.SetResult(true); - return Unit.Value; + return new LaunchResponse(); } - } - internal class AttachHandler : IPsesAttachHandler - { - private static readonly Version s_minVersionForCustomPipeName = new Version(6, 2); - - private readonly ILogger _logger; - private readonly DebugService _debugService; - private readonly BreakpointService _breakpointService; - private readonly PowerShellContextService _powerShellContextService; - private readonly DebugStateService _debugStateService; - private readonly DebugEventHandlerService _debugEventHandlerService; - private readonly IJsonRpcServer _jsonRpcServer; - - public AttachHandler( - ILoggerFactory factory, - IJsonRpcServer jsonRpcServer, - DebugService debugService, - PowerShellContextService powerShellContextService, - DebugStateService debugStateService, - BreakpointService breakpointService, - DebugEventHandlerService debugEventHandlerService) - { - _logger = factory.CreateLogger(); - _jsonRpcServer = jsonRpcServer; - _debugService = debugService; - _breakpointService = breakpointService; - _powerShellContextService = powerShellContextService; - _debugStateService = debugStateService; - _debugEventHandlerService = debugEventHandlerService; - } - - public async Task Handle(PsesAttachRequestArguments request, CancellationToken cancellationToken) + public async Task Handle(PsesAttachRequestArguments request, CancellationToken cancellationToken) { _debugStateService.IsAttachSession = true; @@ -279,7 +242,7 @@ public async Task Handle(PsesAttachRequestArguments request, CancellationT } else if (_powerShellContextService.CurrentRunspace.Location == RunspaceLocation.Remote) { - throw new RpcErrorException(0, $"Cannot attach to a process in a remote session when already in a remote session."); + throw new RpcErrorException(0, "Cannot attach to a process in a remote session when already in a remote session."); } await _powerShellContextService.ExecuteScriptStringAsync( @@ -303,7 +266,7 @@ await _powerShellContextService.ExecuteScriptStringAsync( await _powerShellContextService.ExecuteScriptStringAsync( $"Enter-PSHostProcess -Id {processId}", - errorMessages).ConfigureAwait(false); + errorMessages); if (errorMessages.Length > 0) { @@ -319,7 +282,7 @@ await _powerShellContextService.ExecuteScriptStringAsync( await _powerShellContextService.ExecuteScriptStringAsync( $"Enter-PSHostProcess -CustomPipeName {request.CustomPipeName}", - errorMessages).ConfigureAwait(false); + errorMessages); if (errorMessages.Length > 0) { @@ -385,9 +348,16 @@ await _powerShellContextService.ExecuteScriptStringAsync( if (runspaceVersion.Version.Major >= 7) { - _jsonRpcServer.SendNotification(EventNames.Initialized); + _debugStateService.ServerStarted.SetResult(true); } - return Unit.Value; + return new AttachResponse(); + } + + public async Task OnStarted(IDebugAdapterServer server, CancellationToken cancellationToken) + { + // We wait for this task to be finished before triggering the initialized message to + // be sent to the client. + await _debugStateService.ServerStarted.Task.ConfigureAwait(false); } private async Task OnExecutionCompletedAsync(Task executeTask) @@ -431,7 +401,7 @@ private async Task OnExecutionCompletedAsync(Task executeTask) } _debugService.IsClientAttached = false; - _jsonRpcServer.SendNotification(EventNames.Terminated); + _debugAdapterServer.SendNotification(EventNames.Terminated); } } } From 16f0a477443d61b58c109ed389d186a9aa109f15 Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Tue, 27 Oct 2020 15:28:31 -0700 Subject: [PATCH 05/13] merge handler classes into classes that make sense --- .../Server/PsesDebugServer.cs | 10 +- .../Handlers/BreakpointHandlers.cs | 174 +++++++----------- .../Handlers/DebuggerActionHandlers.cs | 61 +----- 3 files changed, 74 insertions(+), 171 deletions(-) diff --git a/src/PowerShellEditorServices/Server/PsesDebugServer.cs b/src/PowerShellEditorServices/Server/PsesDebugServer.cs index ebac16897..d1d1df8a6 100644 --- a/src/PowerShellEditorServices/Server/PsesDebugServer.cs +++ b/src/PowerShellEditorServices/Server/PsesDebugServer.cs @@ -103,19 +103,13 @@ public async Task StartAsync() .AddPsesDebugServices(ServiceProvider, this, _useTempSession)) .WithHandler() .WithHandler() - .WithHandler() - .WithHandler() + .WithHandler() .WithHandler() .WithHandler() - .WithHandler() .WithHandler() .WithHandler() .WithHandler() - .WithHandler() - .WithHandler() - .WithHandler() - .WithHandler() - .WithHandler() + .WithHandler() .WithHandler() .WithHandler() .WithHandler() diff --git a/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/BreakpointHandlers.cs b/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/BreakpointHandlers.cs index a1c7a030a..b0bf314c0 100644 --- a/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/BreakpointHandlers.cs +++ b/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/BreakpointHandlers.cs @@ -19,121 +19,20 @@ namespace Microsoft.PowerShell.EditorServices.Handlers { - internal class SetFunctionBreakpointsHandler : ISetFunctionBreakpointsHandler - { - private readonly ILogger _logger; - private readonly DebugService _debugService; - private readonly DebugStateService _debugStateService; - - public SetFunctionBreakpointsHandler( - ILoggerFactory loggerFactory, - DebugService debugService, - DebugStateService debugStateService) - { - _logger = loggerFactory.CreateLogger(); - _debugService = debugService; - _debugStateService = debugStateService; - } - - public async Task Handle(SetFunctionBreakpointsArguments request, CancellationToken cancellationToken) - { - CommandBreakpointDetails[] breakpointDetails = request.Breakpoints - .Select((funcBreakpoint) => CommandBreakpointDetails.Create( - funcBreakpoint.Name, - funcBreakpoint.Condition, - funcBreakpoint.HitCondition)) - .ToArray(); - - // If this is a "run without debugging (Ctrl+F5)" session ignore requests to set breakpoints. - CommandBreakpointDetails[] updatedBreakpointDetails = breakpointDetails; - if (!_debugStateService.NoDebug) - { - await _debugStateService.WaitForSetBreakpointHandleAsync().ConfigureAwait(false); - - try - { - updatedBreakpointDetails = - await _debugService.SetCommandBreakpointsAsync( - breakpointDetails).ConfigureAwait(false); - } - catch (Exception e) - { - // Log whatever the error is - _logger.LogException($"Caught error while setting command breakpoints", e); - } - finally - { - _debugStateService.ReleaseSetBreakpointHandle(); - } - } - - return new SetFunctionBreakpointsResponse - { - Breakpoints = updatedBreakpointDetails - .Select(LspDebugUtils.CreateBreakpoint) - .ToArray() - }; - } - } - - internal class SetExceptionBreakpointsHandler : ISetExceptionBreakpointsHandler - { - private readonly ILogger _logger; - private readonly DebugService _debugService; - private readonly DebugStateService _debugStateService; - - public SetExceptionBreakpointsHandler( - ILoggerFactory loggerFactory, - DebugService debugService, - DebugStateService debugStateService) - { - _logger = loggerFactory.CreateLogger(); - _debugService = debugService; - _debugStateService = debugStateService; - } - - public Task Handle(SetExceptionBreakpointsArguments request, CancellationToken cancellationToken) - { - // TODO: When support for exception breakpoints (unhandled and/or first chance) - // are added to the PowerShell engine, wire up the VSCode exception - // breakpoints here using the pattern below to prevent bug regressions. - //if (!noDebug) - //{ - // setBreakpointInProgress = true; - - // try - // { - // // Set exception breakpoints in DebugService - // } - // catch (Exception e) - // { - // // Log whatever the error is - // Logger.WriteException($"Caught error while setting exception breakpoints", e); - // } - // finally - // { - // setBreakpointInProgress = false; - // } - //} - - return Task.FromResult(new SetExceptionBreakpointsResponse()); - } - } - - internal class SetBreakpointsHandler : ISetBreakpointsHandler + internal class BreakpointHandlers : ISetFunctionBreakpointsHandler, ISetBreakpointsHandler, ISetExceptionBreakpointsHandler { private readonly ILogger _logger; private readonly DebugService _debugService; private readonly DebugStateService _debugStateService; private readonly WorkspaceService _workspaceService; - public SetBreakpointsHandler( + public BreakpointHandlers( ILoggerFactory loggerFactory, DebugService debugService, DebugStateService debugStateService, WorkspaceService workspaceService) { - _logger = loggerFactory.CreateLogger(); + _logger = loggerFactory.CreateLogger(); _debugService = debugService; _debugStateService = debugStateService; _workspaceService = workspaceService; @@ -218,5 +117,72 @@ await _debugService.SetLineBreakpointsAsync( .Select(LspDebugUtils.CreateBreakpoint)) }; } + + public async Task Handle(SetFunctionBreakpointsArguments request, CancellationToken cancellationToken) + { + CommandBreakpointDetails[] breakpointDetails = request.Breakpoints + .Select((funcBreakpoint) => CommandBreakpointDetails.Create( + funcBreakpoint.Name, + funcBreakpoint.Condition, + funcBreakpoint.HitCondition)) + .ToArray(); + + // If this is a "run without debugging (Ctrl+F5)" session ignore requests to set breakpoints. + CommandBreakpointDetails[] updatedBreakpointDetails = breakpointDetails; + if (!_debugStateService.NoDebug) + { + await _debugStateService.WaitForSetBreakpointHandleAsync().ConfigureAwait(false); + + try + { + updatedBreakpointDetails = + await _debugService.SetCommandBreakpointsAsync( + breakpointDetails).ConfigureAwait(false); + } + catch (Exception e) + { + // Log whatever the error is + _logger.LogException($"Caught error while setting command breakpoints", e); + } + finally + { + _debugStateService.ReleaseSetBreakpointHandle(); + } + } + + return new SetFunctionBreakpointsResponse + { + Breakpoints = updatedBreakpointDetails + .Select(LspDebugUtils.CreateBreakpoint) + .ToArray() + }; + } + + public Task Handle(SetExceptionBreakpointsArguments request, CancellationToken cancellationToken) + { + // TODO: When support for exception breakpoints (unhandled and/or first chance) + // are added to the PowerShell engine, wire up the VSCode exception + // breakpoints here using the pattern below to prevent bug regressions. + //if (!noDebug) + //{ + // setBreakpointInProgress = true; + + // try + // { + // // Set exception breakpoints in DebugService + // } + // catch (Exception e) + // { + // // Log whatever the error is + // Logger.WriteException($"Caught error while setting exception breakpoints", e); + // } + // finally + // { + // setBreakpointInProgress = false; + // } + //} + + return Task.FromResult(new SetExceptionBreakpointsResponse()); + } } } diff --git a/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/DebuggerActionHandlers.cs b/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/DebuggerActionHandlers.cs index 21c520881..fd5a4f9ba 100644 --- a/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/DebuggerActionHandlers.cs +++ b/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/DebuggerActionHandlers.cs @@ -4,7 +4,6 @@ // using System; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; @@ -14,12 +13,12 @@ namespace Microsoft.PowerShell.EditorServices.Handlers { - internal class ContinueHandler : IContinueHandler + internal class DebuggerActionHandlers : IContinueHandler, INextHandler, IPauseHandler, IStepInHandler, IStepOutHandler { private readonly ILogger _logger; private readonly DebugService _debugService; - public ContinueHandler( + public DebuggerActionHandlers( ILoggerFactory loggerFactory, DebugService debugService) { @@ -32,40 +31,12 @@ public Task Handle(ContinueArguments request, CancellationToke _debugService.Continue(); return Task.FromResult(new ContinueResponse()); } - } - - internal class NextHandler : INextHandler - { - private readonly ILogger _logger; - private readonly DebugService _debugService; - - public NextHandler( - ILoggerFactory loggerFactory, - DebugService debugService) - { - _logger = loggerFactory.CreateLogger(); - _debugService = debugService; - } public Task Handle(NextArguments request, CancellationToken cancellationToken) { _debugService.StepOver(); return Task.FromResult(new NextResponse()); } - } - - internal class PauseHandler : IPauseHandler - { - private readonly ILogger _logger; - private readonly DebugService _debugService; - - public PauseHandler( - ILoggerFactory loggerFactory, - DebugService debugService) - { - _logger = loggerFactory.CreateLogger(); - _debugService = debugService; - } public Task Handle(PauseArguments request, CancellationToken cancellationToken) { @@ -79,40 +50,12 @@ public Task Handle(PauseArguments request, CancellationToken canc throw new RpcErrorException(0, e.Message); } } - } - - internal class StepInHandler : IStepInHandler - { - private readonly ILogger _logger; - private readonly DebugService _debugService; - - public StepInHandler( - ILoggerFactory loggerFactory, - DebugService debugService) - { - _logger = loggerFactory.CreateLogger(); - _debugService = debugService; - } public Task Handle(StepInArguments request, CancellationToken cancellationToken) { _debugService.StepIn(); return Task.FromResult(new StepInResponse()); } - } - - internal class StepOutHandler : IStepOutHandler - { - private readonly ILogger _logger; - private readonly DebugService _debugService; - - public StepOutHandler( - ILoggerFactory loggerFactory, - DebugService debugService) - { - _logger = loggerFactory.CreateLogger(); - _debugService = debugService; - } public Task Handle(StepOutArguments request, CancellationToken cancellationToken) { From 0c2107ada0291d2dbbe492aa1e841a8ace8d8687 Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Tue, 27 Oct 2020 15:38:14 -0700 Subject: [PATCH 06/13] debugging works the first time now! --- src/PowerShellEditorServices/Server/PsesDebugServer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/PowerShellEditorServices/Server/PsesDebugServer.cs b/src/PowerShellEditorServices/Server/PsesDebugServer.cs index d1d1df8a6..96b1893f9 100644 --- a/src/PowerShellEditorServices/Server/PsesDebugServer.cs +++ b/src/PowerShellEditorServices/Server/PsesDebugServer.cs @@ -75,6 +75,8 @@ public async Task StartAsync() { _debugAdapterServer = await DebugAdapterServer.From(options => { + options.WithSerializer(new DapProtocolSerializer()); + // 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. _powerShellContextService = ServiceProvider.GetService(); From 2e152ede16afc6052f4fdd295683a4acfbb94557 Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Wed, 28 Oct 2020 14:28:06 -0700 Subject: [PATCH 07/13] dispose of streams --- src/PowerShellEditorServices/Server/PsesDebugServer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/PowerShellEditorServices/Server/PsesDebugServer.cs b/src/PowerShellEditorServices/Server/PsesDebugServer.cs index 96b1893f9..374dff0f8 100644 --- a/src/PowerShellEditorServices/Server/PsesDebugServer.cs +++ b/src/PowerShellEditorServices/Server/PsesDebugServer.cs @@ -137,6 +137,8 @@ public void Dispose() { _powerShellContextService.IsDebugServerActive = false; _debugAdapterServer.Dispose(); + _inputStream.Dispose(); + _outputStream.Dispose(); _serverStopped.SetResult(true); } From 46b33292cede3862bdde1163011b733876cf4363 Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Wed, 28 Oct 2020 14:32:29 -0700 Subject: [PATCH 08/13] remove initializeHandler since it's covered by OnInitialize* --- .../Handlers/InitializeHandler.cs | 47 ------------------- 1 file changed, 47 deletions(-) delete mode 100644 src/PowerShellEditorServices/Services/DebugAdapter/Handlers/InitializeHandler.cs diff --git a/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/InitializeHandler.cs b/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/InitializeHandler.cs deleted file mode 100644 index 92d684ef1..000000000 --- a/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/InitializeHandler.cs +++ /dev/null @@ -1,47 +0,0 @@ -// -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. -// - -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Microsoft.PowerShell.EditorServices.Services; -using OmniSharp.Extensions.DebugAdapter.Protocol.Requests; - -namespace Microsoft.PowerShell.EditorServices.Handlers -{ - internal class InitializeHandler : IDebugAdapterInitializeHandler - { - private readonly ILogger _logger; - private readonly DebugService _debugService; - private readonly BreakpointService _breakpointService; - - public InitializeHandler( - ILoggerFactory factory, - DebugService debugService, - BreakpointService breakpointService) - { - _logger = factory.CreateLogger(); - _debugService = debugService; - _breakpointService = breakpointService; - } - - public async Task Handle(InitializeRequestArguments request, CancellationToken cancellationToken) - { - // Clear any existing breakpoints before proceeding - await _breakpointService.RemoveAllBreakpointsAsync().ConfigureAwait(false); - - // Now send the Initialize response to continue setup - return new InitializeResponse - { - SupportsConditionalBreakpoints = true, - SupportsConfigurationDoneRequest = true, - SupportsFunctionBreakpoints = true, - SupportsHitConditionalBreakpoints = true, - SupportsLogPoints = true, - SupportsSetVariable = true - }; - } - } -} From 984cf55bd9db3a41b92487bed4438d364ccbcbb5 Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Thu, 29 Oct 2020 11:38:45 -0700 Subject: [PATCH 09/13] add DAP E2E tests! --- .../DAPTestsFixures.cs | 69 ++++++++++++++++ .../DebugAdapterProtocolMessageTests.cs | 79 +++++++++++++++++++ .../LSPTestsFixures.cs | 26 +++--- .../PowerShellEditorServices.Test.E2E.csproj | 2 +- 4 files changed, 161 insertions(+), 15 deletions(-) create mode 100644 test/PowerShellEditorServices.Test.E2E/DAPTestsFixures.cs create mode 100644 test/PowerShellEditorServices.Test.E2E/DebugAdapterProtocolMessageTests.cs diff --git a/test/PowerShellEditorServices.Test.E2E/DAPTestsFixures.cs b/test/PowerShellEditorServices.Test.E2E/DAPTestsFixures.cs new file mode 100644 index 000000000..d3ea12a05 --- /dev/null +++ b/test/PowerShellEditorServices.Test.E2E/DAPTestsFixures.cs @@ -0,0 +1,69 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using OmniSharp.Extensions.DebugAdapter.Client; +using OmniSharp.Extensions.DebugAdapter.Protocol.Requests; +using OmniSharp.Extensions.DebugAdapter.Protocol.Serialization; + +namespace PowerShellEditorServices.Test.E2E +{ + public class DAPTestsFixture : TestsFixture + { + public override bool IsDebugAdapterTests => true; + + public DebugAdapterClient PsesDebugAdapterClient { get; private set; } + + public bool Started { get; private set; } + + public async override Task CustomInitializeAsync( + ILoggerFactory factory, + Stream inputStream, + Stream outputStream) + { + var started = new TaskCompletionSource(); + PsesDebugAdapterClient = DebugAdapterClient.Create(options => + { + options.WithSerializer(new DapProtocolSerializer()); + options + .WithInput(inputStream) + .WithOutput(outputStream) + .OnStarted((client, token) => { + Started = true; + return Task.CompletedTask; + }) + .OnInitialized((client, request, response, token) => { + started.SetResult(true); + return Task.CompletedTask; + }); + }); + + PsesDebugAdapterClient.Initialize(CancellationToken.None).ConfigureAwait(false); + await started.Task.ConfigureAwait(false); + } + + public override async Task DisposeAsync() + { + try + { + await PsesDebugAdapterClient.RequestDisconnect(new DisconnectArguments + { + Restart = false, + TerminateDebuggee = true + }).ConfigureAwait(false); + await _psesProcess.Stop().ConfigureAwait(false); + PsesDebugAdapterClient?.Dispose(); + } + catch (ObjectDisposedException) + { + // Language client has a disposal bug in it + } + } + } +} diff --git a/test/PowerShellEditorServices.Test.E2E/DebugAdapterProtocolMessageTests.cs b/test/PowerShellEditorServices.Test.E2E/DebugAdapterProtocolMessageTests.cs new file mode 100644 index 000000000..b01c46b8d --- /dev/null +++ b/test/PowerShellEditorServices.Test.E2E/DebugAdapterProtocolMessageTests.cs @@ -0,0 +1,79 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System; +using System.IO; +using System.Reflection; +using System.Threading.Tasks; +using Microsoft.PowerShell.EditorServices.Handlers; +using Xunit; +using OmniSharp.Extensions.DebugAdapter.Client; +using OmniSharp.Extensions.DebugAdapter.Protocol.Requests; + +namespace PowerShellEditorServices.Test.E2E +{ + public class DebugAdapterProtocolMessageTests : IClassFixture + { + private readonly static string s_binDir = + Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + + private readonly DebugAdapterClient PsesDebugAdapterClient; + private readonly DAPTestsFixture _dapTestsFixture; + + public DebugAdapterProtocolMessageTests(DAPTestsFixture data) + { + _dapTestsFixture = data; + PsesDebugAdapterClient = data.PsesDebugAdapterClient; + } + + private string NewTestFile(string script, bool isPester = false) + { + string fileExt = isPester ? ".Tests.ps1" : ".ps1"; + string filePath = Path.Combine(s_binDir, Path.GetRandomFileName() + fileExt); + File.WriteAllText(filePath, script); + + return filePath; + } + + [Fact] + public void CanInitializeWithCorrectServerSettings() + { + Assert.True(PsesDebugAdapterClient.ServerSettings.SupportsConditionalBreakpoints); + Assert.True(PsesDebugAdapterClient.ServerSettings.SupportsConfigurationDoneRequest); + Assert.True(PsesDebugAdapterClient.ServerSettings.SupportsFunctionBreakpoints); + Assert.True(PsesDebugAdapterClient.ServerSettings.SupportsHitConditionalBreakpoints); + Assert.True(PsesDebugAdapterClient.ServerSettings.SupportsLogPoints); + Assert.True(PsesDebugAdapterClient.ServerSettings.SupportsSetVariable); + } + + [Fact] + public async Task CanLaunchScriptWithNoBreakpointsAsync() + { + string filePath = NewTestFile("'works' > \"$PSScriptRoot/testFile.txt\""); + LaunchResponse launchResponse = await PsesDebugAdapterClient.RequestLaunch(new PsesLaunchRequestArguments + { + NoDebug = false, + Script = filePath, + Cwd = "", + CreateTemporaryIntegratedConsole = false, + }).ConfigureAwait(false); + + Assert.NotNull(launchResponse); + + // This will check to see if we received the Initialized event from the server. + Assert.True(_dapTestsFixture.Started); + + ConfigurationDoneResponse configDoneResponse = await PsesDebugAdapterClient.RequestConfigurationDone(new ConfigurationDoneArguments()).ConfigureAwait(false); + Assert.NotNull(configDoneResponse); + + // At this point the script should be running so lets give it time + await Task.Delay(2000).ConfigureAwait(false); + + string testFile = Path.Join(Path.GetDirectoryName(filePath), "testFile.txt"); + string contents = await File.ReadAllTextAsync(testFile).ConfigureAwait(false); + Assert.Equal($"works{Environment.NewLine}", contents); + } + } +} diff --git a/test/PowerShellEditorServices.Test.E2E/LSPTestsFixures.cs b/test/PowerShellEditorServices.Test.E2E/LSPTestsFixures.cs index 11ea4a49a..168c8d261 100644 --- a/test/PowerShellEditorServices.Test.E2E/LSPTestsFixures.cs +++ b/test/PowerShellEditorServices.Test.E2E/LSPTestsFixures.cs @@ -1,4 +1,9 @@ -using System; +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -42,27 +47,20 @@ public async override Task CustomInitializeAsync( .WithInput(inputStream) .WithOutput(outputStream) .WithRootUri(DocumentUri.FromFileSystemPath(testdir.FullName)) - .OnPublishDiagnostics(diagnosticParams => - { - Diagnostics.AddRange(diagnosticParams.Diagnostics.Where(d => d != null)); - }) - .OnLogMessage(logMessageParams => - { - Output?.WriteLine($"{logMessageParams.Type.ToString()}: {logMessageParams.Message}"); - }); + .OnPublishDiagnostics(diagnosticParams => Diagnostics.AddRange(diagnosticParams.Diagnostics.Where(d => d != null))) + .OnLogMessage(logMessageParams => Output?.WriteLine($"{logMessageParams.Type.ToString()}: {logMessageParams.Message}")); // Enable all capabilities this this is for testing. // This will be a built in feature of the Omnisharp client at some point. var capabilityTypes = typeof(ICapability).Assembly.GetExportedTypes() - .Where(z => typeof(ICapability).IsAssignableFrom(z)) - .Where(z => z.IsClass && !z.IsAbstract); + .Where(z => typeof(ICapability).IsAssignableFrom(z) && z.IsClass && !z.IsAbstract); foreach (Type capabilityType in capabilityTypes) { options.WithCapability(Activator.CreateInstance(capabilityType, Array.Empty()) as ICapability); } }); - await PsesLanguageClient.Initialize(CancellationToken.None); + await PsesLanguageClient.Initialize(CancellationToken.None).ConfigureAwait(false); // Make sure Script Analysis is enabled because we'll need it in the tests. PsesLanguageClient.Workspace.DidChangeConfiguration( @@ -84,8 +82,8 @@ public override async Task DisposeAsync() { try { - await PsesLanguageClient.Shutdown(); - await _psesProcess.Stop(); + await PsesLanguageClient.Shutdown().ConfigureAwait(false); + await _psesProcess.Stop().ConfigureAwait(false); PsesLanguageClient?.Dispose(); } catch (ObjectDisposedException) diff --git a/test/PowerShellEditorServices.Test.E2E/PowerShellEditorServices.Test.E2E.csproj b/test/PowerShellEditorServices.Test.E2E/PowerShellEditorServices.Test.E2E.csproj index e0631b3cf..1686da37b 100644 --- a/test/PowerShellEditorServices.Test.E2E/PowerShellEditorServices.Test.E2E.csproj +++ b/test/PowerShellEditorServices.Test.E2E/PowerShellEditorServices.Test.E2E.csproj @@ -11,6 +11,7 @@ + @@ -26,4 +27,3 @@ - From 7e04076dcb666470af59042d5ad453c10e8c7a6a Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Thu, 29 Oct 2020 14:41:14 -0700 Subject: [PATCH 10/13] use taskcompletionsource --- test/PowerShellEditorServices.Test.E2E/DAPTestsFixures.cs | 4 ++-- .../DebugAdapterProtocolMessageTests.cs | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/test/PowerShellEditorServices.Test.E2E/DAPTestsFixures.cs b/test/PowerShellEditorServices.Test.E2E/DAPTestsFixures.cs index d3ea12a05..c1d73d099 100644 --- a/test/PowerShellEditorServices.Test.E2E/DAPTestsFixures.cs +++ b/test/PowerShellEditorServices.Test.E2E/DAPTestsFixures.cs @@ -20,7 +20,7 @@ public class DAPTestsFixture : TestsFixture public DebugAdapterClient PsesDebugAdapterClient { get; private set; } - public bool Started { get; private set; } + public TaskCompletionSource Started { get; private set; } public async override Task CustomInitializeAsync( ILoggerFactory factory, @@ -35,7 +35,7 @@ public async override Task CustomInitializeAsync( .WithInput(inputStream) .WithOutput(outputStream) .OnStarted((client, token) => { - Started = true; + Started.SetResult(true); return Task.CompletedTask; }) .OnInitialized((client, request, response, token) => { diff --git a/test/PowerShellEditorServices.Test.E2E/DebugAdapterProtocolMessageTests.cs b/test/PowerShellEditorServices.Test.E2E/DebugAdapterProtocolMessageTests.cs index b01c46b8d..7bd21f6b4 100644 --- a/test/PowerShellEditorServices.Test.E2E/DebugAdapterProtocolMessageTests.cs +++ b/test/PowerShellEditorServices.Test.E2E/DebugAdapterProtocolMessageTests.cs @@ -11,6 +11,7 @@ using Xunit; using OmniSharp.Extensions.DebugAdapter.Client; using OmniSharp.Extensions.DebugAdapter.Protocol.Requests; +using System.Threading; namespace PowerShellEditorServices.Test.E2E { @@ -63,7 +64,9 @@ public async Task CanLaunchScriptWithNoBreakpointsAsync() Assert.NotNull(launchResponse); // This will check to see if we received the Initialized event from the server. - Assert.True(_dapTestsFixture.Started); + await Task.Run( + async () => await _dapTestsFixture.Started.Task.ConfigureAwait(false), + new CancellationTokenSource(2000).Token).ConfigureAwait(false); ConfigurationDoneResponse configDoneResponse = await PsesDebugAdapterClient.RequestConfigurationDone(new ConfigurationDoneArguments()).ConfigureAwait(false); Assert.NotNull(configDoneResponse); From 8a0c6fd6611fc15f9b9eb3d1baa420c90f85ded7 Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Thu, 29 Oct 2020 16:21:30 -0700 Subject: [PATCH 11/13] new up TCS --- test/PowerShellEditorServices.Test.E2E/DAPTestsFixures.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/PowerShellEditorServices.Test.E2E/DAPTestsFixures.cs b/test/PowerShellEditorServices.Test.E2E/DAPTestsFixures.cs index c1d73d099..3d449f8da 100644 --- a/test/PowerShellEditorServices.Test.E2E/DAPTestsFixures.cs +++ b/test/PowerShellEditorServices.Test.E2E/DAPTestsFixures.cs @@ -20,14 +20,14 @@ public class DAPTestsFixture : TestsFixture public DebugAdapterClient PsesDebugAdapterClient { get; private set; } - public TaskCompletionSource Started { get; private set; } + public TaskCompletionSource Started { get; } = new TaskCompletionSource(); public async override Task CustomInitializeAsync( ILoggerFactory factory, Stream inputStream, Stream outputStream) { - var started = new TaskCompletionSource(); + var initialized = new TaskCompletionSource(); PsesDebugAdapterClient = DebugAdapterClient.Create(options => { options.WithSerializer(new DapProtocolSerializer()); @@ -39,13 +39,13 @@ public async override Task CustomInitializeAsync( return Task.CompletedTask; }) .OnInitialized((client, request, response, token) => { - started.SetResult(true); + initialized.SetResult(true); return Task.CompletedTask; }); }); PsesDebugAdapterClient.Initialize(CancellationToken.None).ConfigureAwait(false); - await started.Task.ConfigureAwait(false); + await initialized.Task.ConfigureAwait(false); } public override async Task DisposeAsync() From e25a1123bdd55401992d030cb857667f24c19501 Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Mon, 2 Nov 2020 15:16:03 -0800 Subject: [PATCH 12/13] move to 0.18.2 --- src/PowerShellEditorServices/PowerShellEditorServices.csproj | 4 ++-- src/PowerShellEditorServices/Server/PsesDebugServer.cs | 2 -- test/PowerShellEditorServices.Test.E2E/DAPTestsFixures.cs | 1 - .../PowerShellEditorServices.Test.E2E.csproj | 4 ++-- .../PowerShellEditorServices.Test.csproj | 2 +- 5 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/PowerShellEditorServices/PowerShellEditorServices.csproj b/src/PowerShellEditorServices/PowerShellEditorServices.csproj index 8aff14b55..4657dfbcb 100644 --- a/src/PowerShellEditorServices/PowerShellEditorServices.csproj +++ b/src/PowerShellEditorServices/PowerShellEditorServices.csproj @@ -39,8 +39,8 @@ - - + + diff --git a/src/PowerShellEditorServices/Server/PsesDebugServer.cs b/src/PowerShellEditorServices/Server/PsesDebugServer.cs index 374dff0f8..062645e60 100644 --- a/src/PowerShellEditorServices/Server/PsesDebugServer.cs +++ b/src/PowerShellEditorServices/Server/PsesDebugServer.cs @@ -75,8 +75,6 @@ public async Task StartAsync() { _debugAdapterServer = await DebugAdapterServer.From(options => { - options.WithSerializer(new DapProtocolSerializer()); - // 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. _powerShellContextService = ServiceProvider.GetService(); diff --git a/test/PowerShellEditorServices.Test.E2E/DAPTestsFixures.cs b/test/PowerShellEditorServices.Test.E2E/DAPTestsFixures.cs index 3d449f8da..acc3214d5 100644 --- a/test/PowerShellEditorServices.Test.E2E/DAPTestsFixures.cs +++ b/test/PowerShellEditorServices.Test.E2E/DAPTestsFixures.cs @@ -30,7 +30,6 @@ public async override Task CustomInitializeAsync( var initialized = new TaskCompletionSource(); PsesDebugAdapterClient = DebugAdapterClient.Create(options => { - options.WithSerializer(new DapProtocolSerializer()); options .WithInput(inputStream) .WithOutput(outputStream) diff --git a/test/PowerShellEditorServices.Test.E2E/PowerShellEditorServices.Test.E2E.csproj b/test/PowerShellEditorServices.Test.E2E/PowerShellEditorServices.Test.E2E.csproj index 1686da37b..83c921171 100644 --- a/test/PowerShellEditorServices.Test.E2E/PowerShellEditorServices.Test.E2E.csproj +++ b/test/PowerShellEditorServices.Test.E2E/PowerShellEditorServices.Test.E2E.csproj @@ -10,8 +10,8 @@ - - + + diff --git a/test/PowerShellEditorServices.Test/PowerShellEditorServices.Test.csproj b/test/PowerShellEditorServices.Test/PowerShellEditorServices.Test.csproj index f808f7a70..4d80ae1d9 100644 --- a/test/PowerShellEditorServices.Test/PowerShellEditorServices.Test.csproj +++ b/test/PowerShellEditorServices.Test/PowerShellEditorServices.Test.csproj @@ -30,7 +30,7 @@ - + From 59b9eeada84fd02f630e0eaa0cf2345e7a91b6b4 Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Tue, 3 Nov 2020 14:19:23 -0800 Subject: [PATCH 13/13] misc comments and feedback --- .../Server/PsesDebugServer.cs | 4 ++++ .../DebugAdapter/DebugStateService.cs | 3 ++- .../Handlers/BreakpointHandlers.cs | 2 +- .../Handlers/LaunchAndAttachHandler.cs | 15 +++++++++++++- .../DAPTestsFixures.cs | 20 ++++++++++++++++++- 5 files changed, 40 insertions(+), 4 deletions(-) diff --git a/src/PowerShellEditorServices/Server/PsesDebugServer.cs b/src/PowerShellEditorServices/Server/PsesDebugServer.cs index 062645e60..042fccceb 100644 --- a/src/PowerShellEditorServices/Server/PsesDebugServer.cs +++ b/src/PowerShellEditorServices/Server/PsesDebugServer.cs @@ -113,11 +113,15 @@ public async Task StartAsync() .WithHandler() .WithHandler() .WithHandler() + // 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, request, cancellationToken) => { var breakpointService = server.GetService(); // Clear any existing breakpoints before proceeding await breakpointService.RemoveAllBreakpointsAsync().ConfigureAwait(false); }) + // The OnInitialized delegate gets run right before the server responds to the _Initialize_ request: + // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Initialize .OnInitialized((server, request, response, cancellationToken) => { response.SupportsConditionalBreakpoints = true; response.SupportsConfigurationDoneRequest = true; diff --git a/src/PowerShellEditorServices/Services/DebugAdapter/DebugStateService.cs b/src/PowerShellEditorServices/Services/DebugAdapter/DebugStateService.cs index 071e978ff..515fa9baa 100644 --- a/src/PowerShellEditorServices/Services/DebugAdapter/DebugStateService.cs +++ b/src/PowerShellEditorServices/Services/DebugAdapter/DebugStateService.cs @@ -38,7 +38,8 @@ internal class DebugStateService internal bool IsUsingTempIntegratedConsole { get; set; } - internal TaskCompletionSource ServerStarted { get; set; } + // This gets set at the end of the Launch/Attach handler which set debug state. + internal TaskCompletionSource ServerStarted { get; set; } internal void ReleaseSetBreakpointHandle() { diff --git a/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/BreakpointHandlers.cs b/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/BreakpointHandlers.cs index b0bf314c0..777d032e3 100644 --- a/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/BreakpointHandlers.cs +++ b/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/BreakpointHandlers.cs @@ -161,7 +161,7 @@ await _debugService.SetCommandBreakpointsAsync( public Task Handle(SetExceptionBreakpointsArguments request, CancellationToken cancellationToken) { // TODO: When support for exception breakpoints (unhandled and/or first chance) - // are added to the PowerShell engine, wire up the VSCode exception + // is added to the PowerShell engine, wire up the VSCode exception // breakpoints here using the pattern below to prevent bug regressions. //if (!noDebug) //{ diff --git a/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/LaunchAndAttachHandler.cs b/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/LaunchAndAttachHandler.cs index 8317b11b4..0f065baa8 100644 --- a/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/LaunchAndAttachHandler.cs +++ b/src/PowerShellEditorServices/Services/DebugAdapter/Handlers/LaunchAndAttachHandler.cs @@ -111,7 +111,7 @@ public LaunchAndAttachHandler( _debugEventHandlerService = debugEventHandlerService; _debugService = debugService; _debugStateService = debugStateService; - _debugStateService.ServerStarted = new TaskCompletionSource(); + _debugStateService.ServerStarted = new TaskCompletionSource(); _powerShellContextService = powerShellContextService; _remoteFileManagerService = remoteFileManagerService; } @@ -353,6 +353,19 @@ await _powerShellContextService.ExecuteScriptStringAsync( return new AttachResponse(); } + // PSES follows the following flow: + // Receive a Initialize request + // Run Initialize handler and send response back + // Receive a Launch/Attach request + // Run Launch/Attach handler and send response back + // PSES sends the initialized event at the end of the Launch/Attach handler + + // The way that the Omnisharp server works is that this OnStarted handler runs after OnInitialized + // (after the Initialize DAP response is sent to the client) but before the _Initalized_ DAP event + // gets sent to the client. Because of the way PSES handles breakpoints, + // we can't send the Initialized event until _after_ we finish the Launch/Attach handler. + // The flow above depicts this. To achieve this, we wait until _debugStateService.ServerStarted + // is set, which will be done by the Launch/Attach handlers. public async Task OnStarted(IDebugAdapterServer server, CancellationToken cancellationToken) { // We wait for this task to be finished before triggering the initialized message to diff --git a/test/PowerShellEditorServices.Test.E2E/DAPTestsFixures.cs b/test/PowerShellEditorServices.Test.E2E/DAPTestsFixures.cs index acc3214d5..a56cf6ff0 100644 --- a/test/PowerShellEditorServices.Test.E2E/DAPTestsFixures.cs +++ b/test/PowerShellEditorServices.Test.E2E/DAPTestsFixures.cs @@ -27,22 +27,40 @@ public async override Task CustomInitializeAsync( Stream inputStream, Stream outputStream) { - var initialized = new TaskCompletionSource(); + var initialized = new TaskCompletionSource(); PsesDebugAdapterClient = DebugAdapterClient.Create(options => { options .WithInput(inputStream) .WithOutput(outputStream) + // The OnStarted delegate gets run when we receive the _Initialized_ event from the server: + // https://microsoft.github.io/debug-adapter-protocol/specification#Events_Initialized .OnStarted((client, token) => { Started.SetResult(true); return Task.CompletedTask; }) + // The OnInitialized delegate gets run when we first receive the _Initialize_ response: + // https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Initialize .OnInitialized((client, request, response, token) => { initialized.SetResult(true); return Task.CompletedTask; }); }); + // PSES follows the following flow: + // Receive a Initialize request + // Run Initialize handler and send response back + // Receive a Launch/Attach request + // Run Launch/Attach handler and send response back + // PSES sends the initialized event at the end of the Launch/Attach handler + + // The way that the Omnisharp client works is that this Initialize method doesn't return until + // after OnStarted is run... which only happens when Initialized is received from the server. + // so if we would await this task, it would deadlock. + // To get around this, we run the Initialize() without await but use a `TaskCompletionSource` + // that gets completed when we receive the response to Initialize + // This tells us that we are ready to send messages to PSES... but are not stuck waiting for + // Initialized. PsesDebugAdapterClient.Initialize(CancellationToken.None).ConfigureAwait(false); await initialized.Task.ConfigureAwait(false); }