@@ -313,9 +313,9 @@ private bool CancelForegroundAndPrepend(ISynchronousTask task, bool isIdle = fal
313
313
314
314
_skipNextPrompt = true ;
315
315
316
- if ( task is SynchronousPowerShellTask < PSObject > psTask )
316
+ if ( task is ISynchronousPowerShellTask t )
317
317
{
318
- psTask . MaybeAddToHistory ( ) ;
318
+ t . MaybeAddToHistory ( ) ;
319
319
}
320
320
321
321
using ( _taskQueue . BlockConsumers ( ) )
@@ -334,6 +334,26 @@ private bool CancelForegroundAndPrepend(ISynchronousTask task, bool isIdle = fal
334
334
return true ;
335
335
}
336
336
337
+ // This handles executing the task while also notifying the client that the pipeline is
338
+ // currently busy with a PowerShell task. The extension indicates this with a spinner.
339
+ private void ExecuteTaskSynchronously ( ISynchronousTask task , CancellationToken cancellationToken )
340
+ {
341
+ if ( task is ISynchronousPowerShellTask t
342
+ && ( t . PowerShellExecutionOptions . AddToHistory
343
+ || t . PowerShellExecutionOptions . FromRepl ) )
344
+ {
345
+ _languageServer ? . SendNotification ( "powerShell/executionBusyStatus" , true ) ;
346
+ }
347
+ try
348
+ {
349
+ task . ExecuteSynchronously ( cancellationToken ) ;
350
+ }
351
+ finally
352
+ {
353
+ _languageServer ? . SendNotification ( "powerShell/executionBusyStatus" , false ) ;
354
+ }
355
+ }
356
+
337
357
public Task < T > InvokeTaskOnPipelineThreadAsync < T > ( SynchronousTask < T > task )
338
358
{
339
359
if ( CancelForegroundAndPrepend ( task ) )
@@ -769,8 +789,13 @@ private void RunExecutionLoop(bool isForDebug = false)
769
789
{
770
790
try
771
791
{
772
- task . ExecuteSynchronously ( cancellationScope . CancellationToken ) ;
792
+ ExecuteTaskSynchronously ( task , cancellationScope . CancellationToken ) ;
773
793
}
794
+ // Our flaky extension command test seems to be such because sometimes another
795
+ // task gets queued, and since it runs in the foreground it cancels that task.
796
+ // Interactively, this happens in the first loop (with DoOneRepl) which catches
797
+ // the cancellation exception, but when under test that is a no-op, so it
798
+ // happens in this second loop. Hence we need to catch it here too.
774
799
catch ( OperationCanceledException e )
775
800
{
776
801
_logger . LogDebug ( e , "Task {Task} was canceled!" , task ) ;
@@ -935,19 +960,27 @@ private string InvokeReadLine(CancellationToken cancellationToken)
935
960
}
936
961
}
937
962
963
+ // TODO: Should we actually be directly invoking input versus queueing it as a task like everything else?
938
964
private void InvokeInput ( string input , CancellationToken cancellationToken )
939
965
{
940
- PSCommand command = new PSCommand ( ) . AddScript ( input , useLocalScope : false ) ;
941
- InvokePSCommand (
942
- command ,
943
- new PowerShellExecutionOptions
944
- {
945
- AddToHistory = true ,
946
- ThrowOnError = false ,
947
- WriteOutputToHost = true ,
948
- FromRepl = true ,
949
- } ,
950
- cancellationToken ) ;
966
+ _languageServer ? . SendNotification ( "powerShell/executionBusyStatus" , true ) ;
967
+ try
968
+ {
969
+ InvokePSCommand (
970
+ new PSCommand ( ) . AddScript ( input , useLocalScope : false ) ,
971
+ new PowerShellExecutionOptions
972
+ {
973
+ AddToHistory = true ,
974
+ ThrowOnError = false ,
975
+ WriteOutputToHost = true ,
976
+ FromRepl = true ,
977
+ } ,
978
+ cancellationToken ) ;
979
+ }
980
+ finally
981
+ {
982
+ _languageServer ? . SendNotification ( "powerShell/executionBusyStatus" , false ) ;
983
+ }
951
984
}
952
985
953
986
private void AddRunspaceEventHandlers ( Runspace runspace )
@@ -1076,16 +1109,18 @@ private void OnPowerShellIdle(CancellationToken idleCancellationToken)
1076
1109
while ( ! cancellationScope . CancellationToken . IsCancellationRequested
1077
1110
&& _taskQueue . TryTake ( out ISynchronousTask task ) )
1078
1111
{
1112
+ // Tasks which require the foreground cannot run under this idle handler, so the
1113
+ // current foreground tasks gets canceled, the new task gets prepended, and this
1114
+ // handler returns.
1079
1115
if ( CancelForegroundAndPrepend ( task , isIdle : true ) )
1080
1116
{
1081
1117
return ;
1082
1118
}
1083
1119
1084
- // If we're executing a task, we don't need to run an extra pipeline later for events
1085
- // TODO: This may not be a PowerShell task, so ideally we can differentiate that here.
1086
- // For now it's mostly true and an easy assumption to make.
1087
- runPipelineForEventProcessing = false ;
1088
- task . ExecuteSynchronously ( cancellationScope . CancellationToken ) ;
1120
+ // If we're executing a PowerShell task, we don't need to run an extra pipeline
1121
+ // later for events.
1122
+ runPipelineForEventProcessing = task is not ISynchronousPowerShellTask ;
1123
+ ExecuteTaskSynchronously ( task , cancellationScope . CancellationToken ) ;
1089
1124
}
1090
1125
}
1091
1126
0 commit comments