@@ -162,8 +162,18 @@ public RunspaceDetails CurrentRunspace
162
162
/// </summary>
163
163
public string InitialWorkingDirectory { get ; private set ; }
164
164
165
+ /// <summary>
166
+ /// Tracks the state of the LSP debug server (not the PowerShell debugger).
167
+ /// </summary>
165
168
internal bool IsDebugServerActive { get ; set ; }
166
169
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
+
167
177
internal DebuggerStopEventArgs CurrentDebuggerStopEventArgs { get ; private set ; }
168
178
169
179
#endregion
@@ -182,7 +192,6 @@ public PowerShellContextService(
182
192
OmniSharp . Extensions . LanguageServer . Protocol . Server . ILanguageServerFacade languageServer ,
183
193
bool isPSReadLineEnabled )
184
194
{
185
- logger . LogTrace ( "Instantiating PowerShellContextService and adding event handlers" ) ;
186
195
_languageServer = languageServer ;
187
196
this . logger = logger ;
188
197
this . isPSReadLineEnabled = isPSReadLineEnabled ;
@@ -204,7 +213,7 @@ public static PowerShellContextService Create(
204
213
// Respect a user provided bundled module path.
205
214
if ( Directory . Exists ( hostStartupInfo . BundledModulePath ) )
206
215
{
207
- logger . LogTrace ( $ "Using new bundled module path: { hostStartupInfo . BundledModulePath } ") ;
216
+ logger . LogDebug ( $ "Using new bundled module path: { hostStartupInfo . BundledModulePath } ") ;
208
217
s_bundledModulePath = hostStartupInfo . BundledModulePath ;
209
218
}
210
219
@@ -228,7 +237,6 @@ public static PowerShellContextService Create(
228
237
hostUserInterface ,
229
238
logger ) ;
230
239
231
- logger . LogTrace ( "Creating initial PowerShell runspace" ) ;
232
240
Runspace initialRunspace = PowerShellContextService . CreateRunspace ( psHost , hostStartupInfo . InitialSessionState ) ;
233
241
powerShellContext . Initialize ( hostStartupInfo . ProfilePaths , initialRunspace , true , hostUserInterface ) ;
234
242
powerShellContext . ImportCommandsModuleAsync ( ) ;
@@ -317,7 +325,6 @@ public void Initialize(
317
325
IHostOutput consoleHost )
318
326
{
319
327
Validate . IsNotNull ( "initialRunspace" , initialRunspace ) ;
320
- this . logger . LogTrace ( $ "Initializing PowerShell context with runspace { initialRunspace . Name } ") ;
321
328
322
329
this . ownsInitialRunspace = ownsInitialRunspace ;
323
330
this . SessionState = PowerShellContextState . NotStarted ;
@@ -353,6 +360,7 @@ public void Initialize(
353
360
}
354
361
else
355
362
{
363
+ // TODO: Also throw for PowerShell 6
356
364
throw new NotSupportedException (
357
365
"This computer has an unsupported version of PowerShell installed: " +
358
366
powerShellVersion . ToString ( ) ) ;
@@ -567,10 +575,9 @@ public Task<IEnumerable<TResult>> ExecuteCommandAsync<TResult>(
567
575
cancellationToken ) ;
568
576
}
569
577
570
-
571
578
/// <summary>
572
579
/// 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.
574
581
/// </summary>
575
582
/// <typeparam name="TResult">The expected result type.</typeparam>
576
583
/// <param name="psCommand">The PSCommand to be executed.</param>
@@ -591,8 +598,6 @@ public async Task<IEnumerable<TResult>> ExecuteCommandAsync<TResult>(
591
598
Validate . IsNotNull ( nameof ( psCommand ) , psCommand ) ;
592
599
Validate . IsNotNull ( nameof ( executionOptions ) , executionOptions ) ;
593
600
594
- this . logger . LogTrace ( $ "Attempting to execute command(s): { GetStringForPSCommand ( psCommand ) } ") ;
595
-
596
601
// Add history to PSReadLine before cancelling, otherwise it will be restored as the
597
602
// cancelled prompt when it's called again.
598
603
if ( executionOptions . AddToHistory )
@@ -626,8 +631,6 @@ public async Task<IEnumerable<TResult>> ExecuteCommandAsync<TResult>(
626
631
this . ShouldExecuteWithEventing ( executionOptions ) ||
627
632
( PromptNest . IsRemote && executionOptions . IsReadLine ) ) )
628
633
{
629
- this . logger . LogTrace ( "Passing command execution to pipeline thread" ) ;
630
-
631
634
if ( shouldCancelReadLine && PromptNest . IsReadLineBusy ( ) )
632
635
{
633
636
// 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>(
705
708
}
706
709
try
707
710
{
711
+ this . logger . LogTrace ( $ "Executing in debugger: { GetStringForPSCommand ( psCommand ) } ") ;
708
712
return this . ExecuteCommandInDebugger < TResult > (
709
713
psCommand ,
710
714
executionOptions . WriteOutputToHost ) ;
@@ -732,8 +736,6 @@ public async Task<IEnumerable<TResult>> ExecuteCommandAsync<TResult>(
732
736
AddToHistory = executionOptions . AddToHistory
733
737
} ;
734
738
735
- this . logger . LogTrace ( "Passing to PowerShell" ) ;
736
-
737
739
PowerShell shell = this . PromptNest . GetPowerShell ( executionOptions . IsReadLine ) ;
738
740
739
741
// 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>(
757
759
: this . CurrentRunspace . Runspace ;
758
760
try
759
761
{
762
+ this . logger . LogDebug ( $ "Invoking: { GetStringForPSCommand ( psCommand ) } ") ;
763
+
760
764
// Nested PowerShell instances can't be invoked asynchronously. This occurs
761
765
// in nested prompts and pipeline requests from eventing.
762
766
if ( shell . IsNested )
@@ -777,6 +781,21 @@ public async Task<IEnumerable<TResult>> ExecuteCommandAsync<TResult>(
777
781
await this . sessionStateLock . ReleaseForExecuteCommand ( ) . ConfigureAwait ( false ) ;
778
782
}
779
783
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
+
780
799
if ( shell . HadErrors )
781
800
{
782
801
var strBld = new StringBuilder ( 1024 ) ;
@@ -810,15 +829,11 @@ public async Task<IEnumerable<TResult>> ExecuteCommandAsync<TResult>(
810
829
811
830
hadErrors = true ;
812
831
}
813
- else
814
- {
815
- this . logger . LogTrace ( "Execution completed successfully" ) ;
816
- }
817
832
}
818
833
}
819
834
catch ( PSRemotingDataStructureException e )
820
835
{
821
- this . logger . LogHandledException ( "Pipeline stopped while executing command" , e ) ;
836
+ this . logger . LogHandledException ( "PSRemotingDataStructure exception while executing command" , e ) ;
822
837
errorMessages ? . Append ( e . Message ) ;
823
838
}
824
839
catch ( PipelineStoppedException e )
@@ -1063,7 +1078,7 @@ public async Task ExecuteScriptWithArgsAsync(string script, string arguments = n
1063
1078
. FirstOrDefault ( )
1064
1079
. ProviderPath ;
1065
1080
1066
- this . logger . LogTrace ( $ "Prepending working directory { workingDir } to script path { script } ") ;
1081
+ this . logger . LogDebug ( $ "Prepending working directory { workingDir } to script path { script } ") ;
1067
1082
script = Path . Combine ( workingDir , script ) ;
1068
1083
}
1069
1084
catch ( System . Management . Automation . DriveNotFoundException e )
@@ -1095,7 +1110,6 @@ public async Task ExecuteScriptWithArgsAsync(string script, string arguments = n
1095
1110
strBld . Append ( ' ' ) . Append ( arguments ) ;
1096
1111
1097
1112
var launchedScript = strBld . ToString ( ) ;
1098
- this . logger . LogTrace ( $ "Launch script is: { launchedScript } ") ;
1099
1113
1100
1114
command . AddScript ( launchedScript , false ) ;
1101
1115
}
@@ -1237,15 +1251,15 @@ public void AbortExecution()
1237
1251
/// </param>
1238
1252
public void AbortExecution ( bool shouldAbortDebugSession )
1239
1253
{
1254
+ this . logger . LogTrace ( "Execution abort requested..." ) ;
1255
+
1240
1256
if ( this . SessionState == PowerShellContextState . Aborting
1241
1257
|| this . SessionState == PowerShellContextState . Disposed )
1242
1258
{
1243
1259
this . logger . LogTrace ( $ "Execution abort requested when already aborted (SessionState = { this . SessionState } )") ;
1244
1260
return ;
1245
1261
}
1246
1262
1247
- this . logger . LogTrace ( "Execution abort requested..." ) ;
1248
-
1249
1263
if ( shouldAbortDebugSession )
1250
1264
{
1251
1265
this . ExitAllNestedPrompts ( ) ;
@@ -1391,7 +1405,7 @@ private void ResumeDebugger(DebuggerResumeAction resumeAction, bool shouldWaitFo
1391
1405
/// </summary>
1392
1406
public void Close ( )
1393
1407
{
1394
- logger . LogDebug ( "Closing PowerShellContextService..." ) ;
1408
+ logger . LogTrace ( "Closing PowerShellContextService..." ) ;
1395
1409
this . PromptNest . Dispose ( ) ;
1396
1410
this . SessionState = PowerShellContextState . Disposed ;
1397
1411
@@ -1829,13 +1843,7 @@ private void OnSessionStateChanged(object sender, SessionStateChangedEventArgs e
1829
1843
{
1830
1844
if ( this . SessionState != PowerShellContextState . Disposed )
1831
1845
{
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 } ") ;
1839
1847
this . SessionState = e . NewSessionState ;
1840
1848
this . SessionStateChanged ? . Invoke ( sender , e ) ;
1841
1849
}
@@ -1881,8 +1889,6 @@ private void OnExecutionStatusChanged(
1881
1889
/// </remarks>
1882
1890
private void PowerShellContext_RunspaceChangedAsync ( object sender , RunspaceChangedEventArgs e )
1883
1891
{
1884
- this . logger . LogTrace ( "Sending runspaceChanged notification" ) ;
1885
-
1886
1892
_languageServer ? . SendNotification (
1887
1893
"powerShell/runspaceChanged" ,
1888
1894
new MinifiedRunspaceDetails ( e . NewRunspace ) ) ;
@@ -1927,8 +1933,6 @@ public MinifiedRunspaceDetails(RunspaceDetails eventArgs)
1927
1933
/// <param name="e">details of the execution status change</param>
1928
1934
private void PowerShellContext_ExecutionStatusChangedAsync ( object sender , ExecutionStatusChangedEventArgs e )
1929
1935
{
1930
- this . logger . LogTrace ( "Sending executionStatusChanged notification" ) ;
1931
-
1932
1936
// The cancelling of the prompt (PSReadLine) causes an ExecutionStatus.Aborted to be sent after every
1933
1937
// actual execution (ExecutionStatus.Running) on the pipeline. We ignore that event since it's counterintuitive to
1934
1938
// 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
1948
1952
1949
1953
private IEnumerable < TResult > ExecuteCommandInDebugger < TResult > ( PSCommand psCommand , bool sendOutputToHost )
1950
1954
{
1951
- this . logger . LogTrace ( $ "Attempting to execute command(s) in the debugger: { GetStringForPSCommand ( psCommand ) } ") ;
1952
-
1953
1955
IEnumerable < TResult > output =
1954
1956
this . versionSpecificOperations . ExecuteCommandInDebugger < TResult > (
1955
1957
this ,
@@ -2422,8 +2424,14 @@ private void OnDebuggerStop(object sender, DebuggerStopEventArgs e)
2422
2424
// when the DebugServer is fully started.
2423
2425
CurrentDebuggerStopEventArgs = e ;
2424
2426
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.
2425
2431
if ( ! IsDebugServerActive )
2426
2432
{
2433
+ logger . LogDebug ( "Starting LSP debugger because PowerShell debugger is running!" ) ;
2434
+ OwnsDebugServerState = true ;
2427
2435
_languageServer ? . SendNotification ( "powerShell/startDebugger" ) ;
2428
2436
}
2429
2437
0 commit comments