@@ -23,6 +23,7 @@ namespace Microsoft.PowerShell.EditorServices.Protocol.Server
23
23
public class DebugAdapter : DebugAdapterBase
24
24
{
25
25
private EditorSession editorSession ;
26
+ private OutputDebouncer outputDebouncer ;
26
27
27
28
private bool noDebug ;
28
29
private bool isRemoteAttach ;
@@ -59,7 +60,12 @@ public DebugAdapter(
59
60
this . editorSession . StartDebugSession ( hostDetails , profilePaths , editorOperations ) ;
60
61
this . editorSession . PowerShellContext . RunspaceChanged += this . powerShellContext_RunspaceChanged ;
61
62
this . editorSession . DebugService . DebuggerStopped += this . DebugService_DebuggerStopped ;
62
- }
63
+
64
+ // The assumption in this overload is that the debugger
65
+ // is running in UI-hosted mode, no terminal interface
66
+ this . editorSession . ConsoleService . OutputWritten += this . powerShellContext_OutputWritten ;
67
+ this . outputDebouncer = new OutputDebouncer ( this ) ;
68
+ }
63
69
64
70
protected override void Initialize ( )
65
71
{
@@ -103,6 +109,12 @@ private async Task OnExecutionCompleted(Task executeTask)
103
109
104
110
this . executionCompleted = true ;
105
111
112
+ // Make sure remaining output is flushed before exiting
113
+ if ( this . outputDebouncer != null )
114
+ {
115
+ await this . outputDebouncer . Flush ( ) ;
116
+ }
117
+
106
118
if ( this . isAttachSession )
107
119
{
108
120
// Ensure the read loop is stopped
@@ -151,6 +163,12 @@ protected override void Shutdown()
151
163
{
152
164
Logger . Write ( LogLevel . Normal , "Debug adapter is shutting down..." ) ;
153
165
166
+ // Make sure remaining output is flushed before exiting
167
+ if ( this . outputDebouncer != null )
168
+ {
169
+ this . outputDebouncer . Flush ( ) . Wait ( ) ;
170
+ }
171
+
154
172
if ( this . editorSession != null )
155
173
{
156
174
this . editorSession . PowerShellContext . RunspaceChanged -= this . powerShellContext_RunspaceChanged ;
@@ -195,8 +213,8 @@ protected async Task HandleLaunchRequest(
195
213
LaunchRequestArguments launchParams ,
196
214
RequestContext < object > requestContext )
197
215
{
198
- // Set the working directory for the PowerShell runspace to the cwd passed in via launch.json.
199
- // In case that is null, use the the folder of the script to be executed. If the resulting
216
+ // Set the working directory for the PowerShell runspace to the cwd passed in via launch.json.
217
+ // In case that is null, use the the folder of the script to be executed. If the resulting
200
218
// working dir path is a file path then extract the directory and use that.
201
219
string workingDir =
202
220
launchParams . Cwd ??
@@ -289,7 +307,7 @@ protected async Task HandleAttachRequest(
289
307
290
308
// If there are no host processes to attach to or the user cancels selection, we get a null for the process id.
291
309
// This is not an error, just a request to stop the original "attach to" request.
292
- // Testing against "undefined" is a HACK because I don't know how to make "Cancel" on quick pick loading
310
+ // Testing against "undefined" is a HACK because I don't know how to make "Cancel" on quick pick loading
293
311
// to cancel on the VSCode side without sending an attachRequest with processId set to "undefined".
294
312
if ( string . IsNullOrEmpty ( attachParams . ProcessId ) || ( attachParams . ProcessId == "undefined" ) )
295
313
{
@@ -427,7 +445,7 @@ protected async Task HandleSetBreakpointsRequest(
427
445
catch ( Exception e ) when ( e is FileNotFoundException || e is DirectoryNotFoundException )
428
446
{
429
447
Logger . Write (
430
- LogLevel . Warning ,
448
+ LogLevel . Warning ,
431
449
$ "Attempted to set breakpoints on a non-existing file: { setBreakpointsParams . Source . Path } ") ;
432
450
433
451
string message = this . noDebug ? string . Empty : "Source does not exist, breakpoint not set." ;
@@ -450,9 +468,9 @@ await requestContext.SendResult(
450
468
{
451
469
SourceBreakpoint srcBreakpoint = setBreakpointsParams . Breakpoints [ i ] ;
452
470
breakpointDetails [ i ] = BreakpointDetails . Create (
453
- scriptFile . FilePath ,
454
- srcBreakpoint . Line ,
455
- srcBreakpoint . Column ,
471
+ scriptFile . FilePath ,
472
+ srcBreakpoint . Line ,
473
+ srcBreakpoint . Column ,
456
474
srcBreakpoint . Condition ,
457
475
srcBreakpoint . HitCondition ) ;
458
476
}
@@ -604,7 +622,7 @@ protected async Task HandleStackTraceRequest(
604
622
// be referenced back to the current list of stack frames
605
623
newStackFrames . Add (
606
624
StackFrame . Create (
607
- stackFrames [ i ] ,
625
+ stackFrames [ i ] ,
608
626
i ) ) ;
609
627
}
610
628
@@ -720,13 +738,34 @@ protected async Task HandleEvaluateRequest(
720
738
"repl" ,
721
739
StringComparison . CurrentCultureIgnoreCase ) ;
722
740
723
- if ( ! isFromRepl )
741
+ if ( isFromRepl )
742
+ {
743
+ // Check for special commands
744
+ if ( string . Equals ( "!ctrlc" , evaluateParams . Expression , StringComparison . CurrentCultureIgnoreCase ) )
745
+ {
746
+ editorSession . PowerShellContext . AbortExecution ( ) ;
747
+ }
748
+ else if ( string . Equals ( "!break" , evaluateParams . Expression , StringComparison . CurrentCultureIgnoreCase ) )
749
+ {
750
+ editorSession . DebugService . Break ( ) ;
751
+ }
752
+ else
753
+ {
754
+ // Send the input through the console service
755
+ var notAwaited =
756
+ this . editorSession
757
+ . PowerShellContext
758
+ . ExecuteScriptString ( evaluateParams . Expression , false , true )
759
+ . ConfigureAwait ( false ) ;
760
+ }
761
+ }
762
+ else
724
763
{
725
764
VariableDetails result =
726
- await editorSession . DebugService . EvaluateExpression (
727
- evaluateParams . Expression ,
728
- evaluateParams . FrameId ,
729
- isFromRepl ) ;
765
+ await editorSession . DebugService . EvaluateExpression (
766
+ evaluateParams . Expression ,
767
+ evaluateParams . FrameId ,
768
+ isFromRepl ) ;
730
769
731
770
if ( result != null )
732
771
{
@@ -736,12 +775,6 @@ await editorSession.DebugService.EvaluateExpression(
736
775
result . Id : 0 ;
737
776
}
738
777
}
739
- else
740
- {
741
- Logger . Write (
742
- LogLevel . Verbose ,
743
- $ "Debug adapter client attempted to evaluate command in REPL: { evaluateParams . Expression } ") ;
744
- }
745
778
746
779
await requestContext . SendResult (
747
780
new EvaluateResponseBody
@@ -755,13 +788,22 @@ await requestContext.SendResult(
755
788
756
789
#region Event Handlers
757
790
791
+ private async void powerShellContext_OutputWritten ( object sender , OutputWrittenEventArgs e )
792
+ {
793
+ if ( this . outputDebouncer != null )
794
+ {
795
+ // Queue the output for writing
796
+ await this . outputDebouncer . Invoke ( e ) ;
797
+ }
798
+ }
799
+
758
800
async void DebugService_DebuggerStopped ( object sender , DebuggerStoppedEventArgs e )
759
801
{
760
802
// Provide the reason for why the debugger has stopped script execution.
761
803
// See https://github.com/Microsoft/vscode/issues/3648
762
- // The reason is displayed in the breakpoints viewlet. Some recommended reasons are:
804
+ // The reason is displayed in the breakpoints viewlet. Some recommended reasons are:
763
805
// "step", "breakpoint", "function breakpoint", "exception" and "pause".
764
- // We don't support exception breakpoints and for "pause", we can't distinguish
806
+ // We don't support exception breakpoints and for "pause", we can't distinguish
765
807
// between stepping and the user pressing the pause/break button in the debug toolbar.
766
808
string debuggerStoppedReason = "step" ;
767
809
if ( e . OriginalEvent . Breakpoints . Count > 0 )
@@ -799,7 +841,7 @@ async void powerShellContext_RunspaceChanged(object sender, RunspaceChangedEvent
799
841
await this . SendEvent ( InitializedEvent . Type , null ) ;
800
842
}
801
843
else if (
802
- e . ChangeAction == RunspaceChangeAction . Exit &&
844
+ e . ChangeAction == RunspaceChangeAction . Exit &&
803
845
( this . editorSession == null ||
804
846
this . editorSession . PowerShellContext . IsDebuggerStopped ) )
805
847
{
0 commit comments