@@ -162,8 +162,18 @@ public RunspaceDetails CurrentRunspace
162162 /// </summary>
163163 public string InitialWorkingDirectory { get ; private set ; }
164164
165+ /// <summary>
166+ /// Tracks the state of the LSP debug server (not the PowerShell debugger).
167+ /// </summary>
165168 internal bool IsDebugServerActive { get ; set ; }
166169
170+ /// <summary>
171+ /// Tracks if the PowerShell session started the debug server itself (true), or if it was
172+ /// started by an LSP notification (false). Essentially, this marks if we're responsible for
173+ /// stopping the debug server (and thus need to send a notification to do so).
174+ /// </summary>
175+ internal bool OwnsDebugServerState { get ; set ; }
176+
167177 internal DebuggerStopEventArgs CurrentDebuggerStopEventArgs { get ; private set ; }
168178
169179 #endregion
@@ -182,7 +192,6 @@ public PowerShellContextService(
182192 OmniSharp . Extensions . LanguageServer . Protocol . Server . ILanguageServerFacade languageServer ,
183193 bool isPSReadLineEnabled )
184194 {
185- logger . LogTrace ( "Instantiating PowerShellContextService and adding event handlers" ) ;
186195 _languageServer = languageServer ;
187196 this . logger = logger ;
188197 this . isPSReadLineEnabled = isPSReadLineEnabled ;
@@ -204,7 +213,7 @@ public static PowerShellContextService Create(
204213 // Respect a user provided bundled module path.
205214 if ( Directory . Exists ( hostStartupInfo . BundledModulePath ) )
206215 {
207- logger . LogTrace ( $ "Using new bundled module path: { hostStartupInfo . BundledModulePath } ") ;
216+ logger . LogDebug ( $ "Using new bundled module path: { hostStartupInfo . BundledModulePath } ") ;
208217 s_bundledModulePath = hostStartupInfo . BundledModulePath ;
209218 }
210219
@@ -228,7 +237,6 @@ public static PowerShellContextService Create(
228237 hostUserInterface ,
229238 logger ) ;
230239
231- logger . LogTrace ( "Creating initial PowerShell runspace" ) ;
232240 Runspace initialRunspace = PowerShellContextService . CreateRunspace ( psHost , hostStartupInfo . InitialSessionState ) ;
233241 powerShellContext . Initialize ( hostStartupInfo . ProfilePaths , initialRunspace , true , hostUserInterface ) ;
234242 powerShellContext . ImportCommandsModuleAsync ( ) ;
@@ -317,7 +325,6 @@ public void Initialize(
317325 IHostOutput consoleHost )
318326 {
319327 Validate . IsNotNull ( "initialRunspace" , initialRunspace ) ;
320- this . logger . LogTrace ( $ "Initializing PowerShell context with runspace { initialRunspace . Name } ") ;
321328
322329 this . ownsInitialRunspace = ownsInitialRunspace ;
323330 this . SessionState = PowerShellContextState . NotStarted ;
@@ -353,6 +360,7 @@ public void Initialize(
353360 }
354361 else
355362 {
363+ // TODO: Also throw for PowerShell 6
356364 throw new NotSupportedException (
357365 "This computer has an unsupported version of PowerShell installed: " +
358366 powerShellVersion . ToString ( ) ) ;
@@ -567,10 +575,9 @@ public Task<IEnumerable<TResult>> ExecuteCommandAsync<TResult>(
567575 cancellationToken ) ;
568576 }
569577
570-
571578 /// <summary>
572579 /// Executes a PSCommand against the session's runspace and returns
573- /// a collection of results of the expected type.
580+ /// a collection of results of the expected type. This function needs help.
574581 /// </summary>
575582 /// <typeparam name="TResult">The expected result type.</typeparam>
576583 /// <param name="psCommand">The PSCommand to be executed.</param>
@@ -591,8 +598,6 @@ public async Task<IEnumerable<TResult>> ExecuteCommandAsync<TResult>(
591598 Validate . IsNotNull ( nameof ( psCommand ) , psCommand ) ;
592599 Validate . IsNotNull ( nameof ( executionOptions ) , executionOptions ) ;
593600
594- this . logger . LogTrace ( $ "Attempting to execute command(s): { GetStringForPSCommand ( psCommand ) } ") ;
595-
596601 // Add history to PSReadLine before cancelling, otherwise it will be restored as the
597602 // cancelled prompt when it's called again.
598603 if ( executionOptions . AddToHistory )
@@ -626,8 +631,6 @@ public async Task<IEnumerable<TResult>> ExecuteCommandAsync<TResult>(
626631 this . ShouldExecuteWithEventing ( executionOptions ) ||
627632 ( PromptNest . IsRemote && executionOptions . IsReadLine ) ) )
628633 {
629- this . logger . LogTrace ( "Passing command execution to pipeline thread" ) ;
630-
631634 if ( shouldCancelReadLine && PromptNest . IsReadLineBusy ( ) )
632635 {
633636 // If a ReadLine pipeline is running in the debugger then we'll stop responding here
@@ -705,6 +708,7 @@ public async Task<IEnumerable<TResult>> ExecuteCommandAsync<TResult>(
705708 }
706709 try
707710 {
711+ this . logger . LogTrace ( $ "Executing in debugger: { GetStringForPSCommand ( psCommand ) } ") ;
708712 return this . ExecuteCommandInDebugger < TResult > (
709713 psCommand ,
710714 executionOptions . WriteOutputToHost ) ;
@@ -732,8 +736,6 @@ public async Task<IEnumerable<TResult>> ExecuteCommandAsync<TResult>(
732736 AddToHistory = executionOptions . AddToHistory
733737 } ;
734738
735- this . logger . LogTrace ( "Passing to PowerShell" ) ;
736-
737739 PowerShell shell = this . PromptNest . GetPowerShell ( executionOptions . IsReadLine ) ;
738740
739741 // Due to the following PowerShell bug, we can't just assign shell.Commands to psCommand
@@ -757,6 +759,8 @@ public async Task<IEnumerable<TResult>> ExecuteCommandAsync<TResult>(
757759 : this . CurrentRunspace . Runspace ;
758760 try
759761 {
762+ this . logger . LogDebug ( $ "Invoking: { GetStringForPSCommand ( psCommand ) } ") ;
763+
760764 // Nested PowerShell instances can't be invoked asynchronously. This occurs
761765 // in nested prompts and pipeline requests from eventing.
762766 if ( shell . IsNested )
@@ -777,6 +781,21 @@ public async Task<IEnumerable<TResult>> ExecuteCommandAsync<TResult>(
777781 await this . sessionStateLock . ReleaseForExecuteCommand ( ) . ConfigureAwait ( false ) ;
778782 }
779783
784+ // This is the edge case where the debug server is running because it was
785+ // started by PowerShell (and not by an LSP event), and we're no longer in the
786+ // debugger within PowerShell, so since we own the state we need to stop the
787+ // debug server too.
788+ //
789+ // Strangely one would think we could check `!PromptNest.IsInDebugger` but that
790+ // doesn't work, we have to check if the shell is nested instead. Therefore this
791+ // is a bit fragile, and I don't know how it'll work in a remoting scenario.
792+ if ( IsDebugServerActive && OwnsDebugServerState && ! shell . IsNested )
793+ {
794+ logger . LogDebug ( "Stopping LSP debugger because PowerShell debugger stopped running!" ) ;
795+ OwnsDebugServerState = false ;
796+ _languageServer ? . SendNotification ( "powerShell/stopDebugger" ) ;
797+ }
798+
780799 if ( shell . HadErrors )
781800 {
782801 var strBld = new StringBuilder ( 1024 ) ;
@@ -810,15 +829,11 @@ public async Task<IEnumerable<TResult>> ExecuteCommandAsync<TResult>(
810829
811830 hadErrors = true ;
812831 }
813- else
814- {
815- this . logger . LogTrace ( "Execution completed successfully" ) ;
816- }
817832 }
818833 }
819834 catch ( PSRemotingDataStructureException e )
820835 {
821- this . logger . LogHandledException ( "Pipeline stopped while executing command" , e ) ;
836+ this . logger . LogHandledException ( "PSRemotingDataStructure exception while executing command" , e ) ;
822837 errorMessages ? . Append ( e . Message ) ;
823838 }
824839 catch ( PipelineStoppedException e )
@@ -1063,7 +1078,7 @@ public async Task ExecuteScriptWithArgsAsync(string script, string arguments = n
10631078 . FirstOrDefault ( )
10641079 . ProviderPath ;
10651080
1066- this . logger . LogTrace ( $ "Prepending working directory { workingDir } to script path { script } ") ;
1081+ this . logger . LogDebug ( $ "Prepending working directory { workingDir } to script path { script } ") ;
10671082 script = Path . Combine ( workingDir , script ) ;
10681083 }
10691084 catch ( System . Management . Automation . DriveNotFoundException e )
@@ -1095,7 +1110,6 @@ public async Task ExecuteScriptWithArgsAsync(string script, string arguments = n
10951110 strBld . Append ( ' ' ) . Append ( arguments ) ;
10961111
10971112 var launchedScript = strBld . ToString ( ) ;
1098- this . logger . LogTrace ( $ "Launch script is: { launchedScript } ") ;
10991113
11001114 command . AddScript ( launchedScript , false ) ;
11011115 }
@@ -1237,15 +1251,15 @@ public void AbortExecution()
12371251 /// </param>
12381252 public void AbortExecution ( bool shouldAbortDebugSession )
12391253 {
1254+ this . logger . LogTrace ( "Execution abort requested..." ) ;
1255+
12401256 if ( this . SessionState == PowerShellContextState . Aborting
12411257 || this . SessionState == PowerShellContextState . Disposed )
12421258 {
12431259 this . logger . LogTrace ( $ "Execution abort requested when already aborted (SessionState = { this . SessionState } )") ;
12441260 return ;
12451261 }
12461262
1247- this . logger . LogTrace ( "Execution abort requested..." ) ;
1248-
12491263 if ( shouldAbortDebugSession )
12501264 {
12511265 this . ExitAllNestedPrompts ( ) ;
@@ -1391,7 +1405,7 @@ private void ResumeDebugger(DebuggerResumeAction resumeAction, bool shouldWaitFo
13911405 /// </summary>
13921406 public void Close ( )
13931407 {
1394- logger . LogDebug ( "Closing PowerShellContextService..." ) ;
1408+ logger . LogTrace ( "Closing PowerShellContextService..." ) ;
13951409 this . PromptNest . Dispose ( ) ;
13961410 this . SessionState = PowerShellContextState . Disposed ;
13971411
@@ -1829,13 +1843,7 @@ private void OnSessionStateChanged(object sender, SessionStateChangedEventArgs e
18291843 {
18301844 if ( this . SessionState != PowerShellContextState . Disposed )
18311845 {
1832- this . logger . LogTrace (
1833- string . Format (
1834- "Session state changed --\r \n \r \n Old state: {0}\r \n New state: {1}\r \n Result: {2}" ,
1835- this . SessionState . ToString ( ) ,
1836- e . NewSessionState . ToString ( ) ,
1837- e . ExecutionResult ) ) ;
1838-
1846+ this . logger . LogTrace ( $ "Session state was: { SessionState } , is now: { e . NewSessionState } , result: { e . ExecutionResult } ") ;
18391847 this . SessionState = e . NewSessionState ;
18401848 this . SessionStateChanged ? . Invoke ( sender , e ) ;
18411849 }
@@ -1881,8 +1889,6 @@ private void OnExecutionStatusChanged(
18811889 /// </remarks>
18821890 private void PowerShellContext_RunspaceChangedAsync ( object sender , RunspaceChangedEventArgs e )
18831891 {
1884- this . logger . LogTrace ( "Sending runspaceChanged notification" ) ;
1885-
18861892 _languageServer ? . SendNotification (
18871893 "powerShell/runspaceChanged" ,
18881894 new MinifiedRunspaceDetails ( e . NewRunspace ) ) ;
@@ -1927,8 +1933,6 @@ public MinifiedRunspaceDetails(RunspaceDetails eventArgs)
19271933 /// <param name="e">details of the execution status change</param>
19281934 private void PowerShellContext_ExecutionStatusChangedAsync ( object sender , ExecutionStatusChangedEventArgs e )
19291935 {
1930- this . logger . LogTrace ( "Sending executionStatusChanged notification" ) ;
1931-
19321936 // The cancelling of the prompt (PSReadLine) causes an ExecutionStatus.Aborted to be sent after every
19331937 // actual execution (ExecutionStatus.Running) on the pipeline. We ignore that event since it's counterintuitive to
19341938 // the goal of this method which is to send updates when the pipeline is actually running something.
@@ -1948,8 +1952,6 @@ private void PowerShellContext_ExecutionStatusChangedAsync(object sender, Execut
19481952
19491953 private IEnumerable < TResult > ExecuteCommandInDebugger < TResult > ( PSCommand psCommand , bool sendOutputToHost )
19501954 {
1951- this . logger . LogTrace ( $ "Attempting to execute command(s) in the debugger: { GetStringForPSCommand ( psCommand ) } ") ;
1952-
19531955 IEnumerable < TResult > output =
19541956 this . versionSpecificOperations . ExecuteCommandInDebugger < TResult > (
19551957 this ,
@@ -2422,8 +2424,14 @@ private void OnDebuggerStop(object sender, DebuggerStopEventArgs e)
24222424 // when the DebugServer is fully started.
24232425 CurrentDebuggerStopEventArgs = e ;
24242426
2427+ // If this event has fired but the LSP debug server is not active, it means that the
2428+ // PowerShell debugger has started some other way (most likely an existing PSBreakPoint
2429+ // was executed). So not only do we have to start the server, but later we will be
2430+ // responsible for stopping it too.
24252431 if ( ! IsDebugServerActive )
24262432 {
2433+ logger . LogDebug ( "Starting LSP debugger because PowerShell debugger is running!" ) ;
2434+ OwnsDebugServerState = true ;
24272435 _languageServer ? . SendNotification ( "powerShell/startDebugger" ) ;
24282436 }
24292437
0 commit comments