diff --git a/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs b/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs index ac445ef83..cfc7b192a 100644 --- a/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs +++ b/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs @@ -20,6 +20,8 @@ public class DebugAdapter : DebugAdapterBase { private EditorSession editorSession; private OutputDebouncer outputDebouncer; + private bool isConfigurationDoneRequestComplete; + private bool isLaunchRequestComplete; private string scriptPathToLaunch; private string arguments; @@ -65,6 +67,23 @@ protected override void Initialize() this.SetRequestHandler(EvaluateRequest.Type, this.HandleEvaluateRequest); } + protected Task LaunchScript(RequestContext requestContext) + { + return editorSession.PowerShellContext + .ExecuteScriptAtPath(this.scriptPathToLaunch, this.arguments) + .ContinueWith( + async (t) => { + Logger.Write(LogLevel.Verbose, "Execution completed, terminating..."); + + await requestContext.SendEvent( + TerminatedEvent.Type, + null); + + // Stop the server + this.Stop(); + }); + } + protected override void Shutdown() { // Make sure remaining output is flushed before exiting @@ -81,6 +100,23 @@ protected override void Shutdown() #region Built-in Message Handlers + protected async Task HandleConfigurationDoneRequest( + object args, + RequestContext requestContext) + { + // The order of debug protocol messages apparently isn't as guaranteed as we might like. + // Need to be able to handle the case where we get the configurationDone request after the + // launch request. + if (this.isLaunchRequestComplete) + { + this.LaunchScript(requestContext); + } + + this.isConfigurationDoneRequestComplete = true; + + await requestContext.SendResult(null); + } + protected async Task HandleLaunchRequest( LaunchRequestArguments launchParams, RequestContext requestContext) @@ -114,14 +150,24 @@ protected async Task HandleLaunchRequest( Logger.Write(LogLevel.Verbose, "Script arguments are: " + arguments); } - // NOTE: We don't actually launch the script in response to this - // request. We wait until we receive the configurationDone request - // to actually launch the script under the debugger. This gives - // us and VSCode a chance to finish configuring all the types of - // breakpoints. + // We may not actually launch the script in response to this + // request unless it comes after the configurationDone request. + // If the launch request comes first, then stash the launch + // params so that the subsequent configurationDone request handler + // can launch the script. this.scriptPathToLaunch = launchParams.Program; this.arguments = arguments; + // The order of debug protocol messages apparently isn't as guaranteed as we might like. + // Need to be able to handle the case where we get the launch request after the + // configurationDone request. + if (this.isConfigurationDoneRequestComplete) + { + this.LaunchScript(requestContext); + } + + this.isLaunchRequestComplete = true; + await requestContext.SendResult(null); } @@ -133,31 +179,6 @@ protected Task HandleAttachRequest( throw new NotImplementedException(); } - protected async Task HandleConfigurationDoneRequest( - object args, - RequestContext requestContext) - { - // Execute the given PowerShell script and send the response. - // Note that we aren't waiting for execution to complete here - // because the debugger could stop while the script executes. - Task executeTask = - editorSession.PowerShellContext - .ExecuteScriptAtPath(this.scriptPathToLaunch, this.arguments) - .ContinueWith( - async (t) => { - Logger.Write(LogLevel.Verbose, "Execution completed, terminating..."); - - await requestContext.SendEvent( - TerminatedEvent.Type, - null); - - // Stop the server - this.Stop(); - }); - - await requestContext.SendResult(null); - } - protected Task HandleDisconnectRequest( object disconnectParams, RequestContext requestContext)