Skip to content

Commit 9036dc8

Browse files
committed
Improve debug handling, add remoting support, improve idle processing
1 parent 2e91bd9 commit 9036dc8

File tree

7 files changed

+159
-69
lines changed

7 files changed

+159
-69
lines changed

src/PowerShellEditorServices/Services/PowerShell/Console/ConsoleReadLine.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,8 @@ private Task<string> ReadLineAsync(bool isCommandLine, CancellationToken cancell
145145

146146
private string InvokePSReadLine(CancellationToken cancellationToken)
147147
{
148-
return _psrlProxy.ReadLine(_executionService.EditorServicesHost.Runspace, _executionService.EngineIntrinsics, cancellationToken);
148+
EngineIntrinsics engineIntrinsics = _executionService.EditorServicesHost.IsRunspacePushed ? null : _executionService.EngineIntrinsics;
149+
return _psrlProxy.ReadLine(_executionService.EditorServicesHost.Runspace, engineIntrinsics, cancellationToken);
149150
}
150151

151152
/// <summary>

src/PowerShellEditorServices/Services/PowerShell/Execution/PowerShellContext.cs

Lines changed: 61 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,32 +12,33 @@
1212
namespace Microsoft.PowerShell.EditorServices.Services.PowerShell.Execution
1313
{
1414
[Flags]
15-
internal enum PromptFrameType
15+
internal enum PowerShellFrameType
1616
{
1717
Normal = 0,
1818
NestedPrompt = 1,
1919
Debug = 2,
2020
Remote = 4,
21+
NonInteractive = 8,
2122
}
2223

2324
internal class PowerShellPushedArgs
2425
{
25-
public PowerShellPushedArgs(PromptFrameType frameType)
26+
public PowerShellPushedArgs(PowerShellFrameType frameType)
2627
{
2728
FrameType = frameType;
2829
}
2930

30-
public PromptFrameType FrameType { get; }
31+
public PowerShellFrameType FrameType { get; }
3132
}
3233

3334
internal class PowerShellPoppedArgs
3435
{
35-
public PowerShellPoppedArgs(PromptFrameType frameType)
36+
public PowerShellPoppedArgs(PowerShellFrameType frameType)
3637
{
3738
FrameType = frameType;
3839
}
3940

40-
public PromptFrameType FrameType { get; }
41+
public PowerShellFrameType FrameType { get; }
4142
}
4243

4344
internal class DebuggerResumingArgs
@@ -54,7 +55,7 @@ internal class PromptCancellationRequestedArgs
5455
{
5556
}
5657

57-
internal class NestedPromptExitingArgs
58+
internal class FrameExitingArgs
5859
{
5960
}
6061

@@ -108,48 +109,59 @@ public CancellationTokenSource CurrentCancellationSource
108109

109110
public event Action<object, PowerShellPoppedArgs> PowerShellPopped;
110111

111-
public event Action<object, NestedPromptExitingArgs> NestedPromptExiting;
112+
public event Action<object, FrameExitingArgs> FrameExiting;
112113

113114
public event Action<object, DebuggerStopEventArgs> DebuggerStopped;
114115

115116
public event Action<object, DebuggerResumingArgs> DebuggerResumed;
116117

117118
public event Action<object, BreakpointUpdatedEventArgs> BreakpointUpdated;
118119

119-
public void BeginExiting()
120+
public void SetShouldExit()
120121
{
122+
if (_frameStack.Count <= 1)
123+
{
124+
return;
125+
}
126+
121127
IsExiting = true;
122-
NestedPromptExiting?.Invoke(this, new NestedPromptExitingArgs());
128+
FrameExiting?.Invoke(this, new FrameExitingArgs());
123129
}
124130

125131
public void ProcessDebuggerResult(DebuggerCommandResults result)
126132
{
127133
if (result.ResumeAction != null)
128134
{
129135
DebuggerResumed?.Invoke(this, new DebuggerResumingArgs(result.ResumeAction));
136+
FrameExiting?.Invoke(this, new FrameExitingArgs());
130137
}
131138
}
132139

140+
public void PushNonInteractivePowerShell()
141+
{
142+
PushNestedPowerShell(PowerShellFrameType.NonInteractive);
143+
}
144+
133145
public void PushNestedPowerShell()
134146
{
135-
PushNestedPowerShell(PromptFrameType.Normal);
147+
PushNestedPowerShell(PowerShellFrameType.Normal);
136148
}
137149

138150
public void PushDebugPowerShell()
139151
{
140-
PushNestedPowerShell(PromptFrameType.Debug);
152+
PushNestedPowerShell(PowerShellFrameType.Debug);
141153
}
142154

143155
public void PushPowerShell(Runspace runspace)
144156
{
145157
var pwsh = SMA.PowerShell.Create();
146158
pwsh.Runspace = runspace;
147159

148-
PromptFrameType frameType = PromptFrameType.Normal;
160+
PowerShellFrameType frameType = PowerShellFrameType.Normal;
149161

150162
if (runspace.RunspaceIsRemote)
151163
{
152-
frameType |= PromptFrameType.Remote;
164+
frameType |= PowerShellFrameType.Remote;
153165
}
154166

155167
PushFrame(new ContextFrame(pwsh, frameType, new CancellationTokenSource()));
@@ -184,19 +196,28 @@ private void PushInitialPowerShell(Runspace runspace)
184196
var pwsh = SMA.PowerShell.Create();
185197
pwsh.Runspace = runspace;
186198

187-
PushFrame(new ContextFrame(pwsh, PromptFrameType.Normal, new CancellationTokenSource()));
199+
PushFrame(new ContextFrame(pwsh, PowerShellFrameType.Normal, new CancellationTokenSource()));
188200
}
189201

190-
private void PushNestedPowerShell(PromptFrameType frameType)
202+
private void PushNestedPowerShell(PowerShellFrameType frameType)
191203
{
192204
SMA.PowerShell pwsh = CreateNestedPowerShell();
193-
PromptFrameType newFrameType = _frameStack.Peek().FrameType | PromptFrameType.NestedPrompt | frameType;
205+
PowerShellFrameType newFrameType = _frameStack.Peek().FrameType | PowerShellFrameType.NestedPrompt | frameType;
194206
PushFrame(new ContextFrame(pwsh, newFrameType, new CancellationTokenSource()));
195207
}
196208

197209
private SMA.PowerShell CreateNestedPowerShell()
198210
{
211+
ContextFrame currentFrame = _frameStack.Peek();
212+
if ((currentFrame.FrameType & PowerShellFrameType.Remote) != 0)
213+
{
214+
var remotePwsh = SMA.PowerShell.Create();
215+
remotePwsh.Runspace = currentFrame.PowerShell.Runspace;
216+
return remotePwsh;
217+
}
218+
199219
// PowerShell.CreateNestedPowerShell() sets IsNested but not IsChild
220+
// This means it throws due to the parent pipeline not running...
200221
// So we must use the RunspaceMode.CurrentRunspace option on PowerShell.Create() instead
201222
var pwsh = SMA.PowerShell.Create(RunspaceMode.CurrentRunspace);
202223
pwsh.Runspace.ThreadOptions = PSThreadOptions.UseCurrentThread;
@@ -205,9 +226,12 @@ private SMA.PowerShell CreateNestedPowerShell()
205226

206227
private void PushFrame(ContextFrame frame)
207228
{
229+
if (_frameStack.Count > 0)
230+
{
231+
RemoveRunspaceEventHandlers(CurrentPowerShell.Runspace);
232+
}
233+
AddRunspaceEventHandlers(frame.PowerShell.Runspace);
208234
_frameStack.Push(frame);
209-
frame.PowerShell.Runspace.Debugger.DebuggerStop += OnDebuggerStopped;
210-
frame.PowerShell.Runspace.Debugger.BreakpointUpdated += OnBreakpointUpdated;
211235
PowerShellPushed?.Invoke(this, new PowerShellPushedArgs(frame.FrameType));
212236
}
213237

@@ -217,8 +241,11 @@ private void PopFrame()
217241
ContextFrame frame = _frameStack.Pop();
218242
try
219243
{
220-
frame.PowerShell.Runspace.Debugger.DebuggerStop -= OnDebuggerStopped;
221-
frame.PowerShell.Runspace.Debugger.BreakpointUpdated -= OnBreakpointUpdated;
244+
RemoveRunspaceEventHandlers(frame.PowerShell.Runspace);
245+
if (_frameStack.Count > 0)
246+
{
247+
AddRunspaceEventHandlers(CurrentPowerShell.Runspace);
248+
}
222249
PowerShellPopped?.Invoke(this, new PowerShellPoppedArgs(frame.FrameType));
223250
}
224251
finally
@@ -227,6 +254,18 @@ private void PopFrame()
227254
}
228255
}
229256

257+
private void AddRunspaceEventHandlers(Runspace runspace)
258+
{
259+
runspace.Debugger.DebuggerStop += OnDebuggerStopped;
260+
runspace.Debugger.BreakpointUpdated += OnBreakpointUpdated;
261+
}
262+
263+
private void RemoveRunspaceEventHandlers(Runspace runspace)
264+
{
265+
runspace.Debugger.DebuggerStop -= OnDebuggerStopped;
266+
runspace.Debugger.BreakpointUpdated -= OnBreakpointUpdated;
267+
}
268+
230269
private void OnDebuggerStopped(object sender, DebuggerStopEventArgs args)
231270
{
232271
DebuggerStopped?.Invoke(this, args);
@@ -241,7 +280,7 @@ private class ContextFrame : IDisposable
241280
{
242281
private bool disposedValue;
243282

244-
public ContextFrame(SMA.PowerShell powerShell, PromptFrameType frameType, CancellationTokenSource cancellationTokenSource)
283+
public ContextFrame(SMA.PowerShell powerShell, PowerShellFrameType frameType, CancellationTokenSource cancellationTokenSource)
245284
{
246285
PowerShell = powerShell;
247286
FrameType = frameType;
@@ -250,7 +289,7 @@ public ContextFrame(SMA.PowerShell powerShell, PromptFrameType frameType, Cancel
250289

251290
public SMA.PowerShell PowerShell { get; }
252291

253-
public PromptFrameType FrameType { get; }
292+
public PowerShellFrameType FrameType { get; }
254293

255294
public CancellationTokenSource CancellationTokenSource { get; }
256295

src/PowerShellEditorServices/Services/PowerShell/Host/EditorServicesConsolePSHost.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public override void EnterNestedPrompt()
5454

5555
public override void ExitNestedPrompt()
5656
{
57-
_pwshContext.BeginExiting();
57+
_pwshContext.SetShouldExit();
5858
}
5959

6060
public override void NotifyBeginApplication()
@@ -72,13 +72,12 @@ public void PushRunspace(Runspace runspace)
7272

7373
public void PopRunspace()
7474
{
75-
// TODO: What if we're in a nested prompt in a remote/debug session?
76-
_pwshContext.PopPowerShell();
75+
_pwshContext.SetShouldExit();
7776
}
7877

7978
public override void SetShouldExit(int exitCode)
8079
{
81-
_pwshContext.TryPopPowerShell();
80+
_pwshContext.SetShouldExit();
8281
}
8382

8483
internal void RegisterPowerShellContext(PowerShellContext pwshContext)

src/PowerShellEditorServices/Services/PowerShell/PowerShellConsoleService.cs

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@
22
using Microsoft.PowerShell.EditorServices.Services.PowerShell.Console;
33
using Microsoft.PowerShell.EditorServices.Services.PowerShell.Execution;
44
using Microsoft.PowerShell.EditorServices.Services.PowerShell.Host;
5+
using Microsoft.PowerShell.EditorServices.Services.PowerShell.Utility;
56
using Microsoft.PowerShell.EditorServices.Utility;
67
using System;
78
using System.Collections.Concurrent;
89
using System.Collections.Generic;
910
using System.Linq;
1011
using System.Management.Automation;
12+
using System.Management.Automation.Runspaces;
13+
using System.Reflection;
1114
using System.Text;
1215
using System.Threading;
1316
using System.Threading.Tasks;
@@ -75,10 +78,9 @@ public void StartRepl()
7578
System.Console.InputEncoding = Encoding.UTF8;
7679
System.Console.OutputEncoding = Encoding.UTF8;
7780
_psrlProxy.OverrideReadKey(ReadKey);
78-
_executionService.PowerShellPushed += OnPromptFramePushed;
81+
_executionService.PowerShellPushed += OnPowerShellPushed;
7982
_executionService.PromptCancellationRequested += OnPromptCancellationRequested;
80-
_executionService.NestedPromptExiting += OnNestedPromptExiting;
81-
_executionService.DebuggerResuming += OnDebuggerResuming;
83+
_executionService.FrameExiting += OnFrameExiting;
8284
PushNewReplTask();
8385
}
8486

@@ -90,10 +92,9 @@ public void Dispose()
9092
}
9193

9294
System.Console.CancelKeyPress -= OnCancelKeyPress;
93-
_executionService.PowerShellPushed -= OnPromptFramePushed;
95+
_executionService.PowerShellPushed -= OnPowerShellPushed;
9496
_executionService.PromptCancellationRequested -= OnPromptCancellationRequested;
95-
_executionService.NestedPromptExiting -= OnNestedPromptExiting;
96-
_executionService.DebuggerResuming -= OnDebuggerResuming;
97+
_executionService.FrameExiting -= OnFrameExiting;
9798
}
9899

99100
public void CancelCurrentPrompt()
@@ -125,8 +126,7 @@ private async Task RunReplLoopAsync()
125126

126127
try
127128
{
128-
129-
string promptString = (await GetPromptOutputAsync(currentCommandCancellation.CancellationSource.Token).ConfigureAwait(false)).FirstOrDefault() ?? "PS> ";
129+
string promptString = await GetPromptStringAsync(currentCommandCancellation.CancellationSource.Token).ConfigureAwait(false);
130130

131131
if (currentCommandCancellation.CancellationSource.IsCancellationRequested)
132132
{
@@ -186,6 +186,18 @@ private async Task RunReplLoopAsync()
186186

187187
}
188188

189+
private async Task<string> GetPromptStringAsync(CancellationToken cancellationToken)
190+
{
191+
string prompt = (await GetPromptOutputAsync(cancellationToken).ConfigureAwait(false)).FirstOrDefault() ?? "PS> ";
192+
193+
if (_editorServicesHost.Runspace.RunspaceIsRemote)
194+
{
195+
prompt = _editorServicesHost.Runspace.GetRemotePrompt(prompt);
196+
}
197+
198+
return prompt;
199+
}
200+
189201
private Task<IReadOnlyList<string>> GetPromptOutputAsync(CancellationToken cancellationToken)
190202
{
191203
var promptCommand = new PSCommand().AddCommand("prompt");
@@ -223,23 +235,20 @@ private void OnCancelKeyPress(object sender, ConsoleCancelEventArgs args)
223235
CancelCurrentPrompt();
224236
}
225237

226-
private void OnPromptFramePushed(object sender, PowerShellPushedArgs args)
238+
private void OnPowerShellPushed(object sender, PowerShellPushedArgs args)
227239
{
228-
PushNewReplTask();
240+
if ((args.FrameType & PowerShellFrameType.NonInteractive) == 0)
241+
{
242+
PushNewReplTask();
243+
}
229244
}
230245

231246
private void OnPromptCancellationRequested(object sender, PromptCancellationRequestedArgs args)
232247
{
233248
CancelCurrentPrompt();
234249
}
235250

236-
private void OnNestedPromptExiting(object sender, NestedPromptExitingArgs args)
237-
{
238-
_exiting = true;
239-
StopCurrentRepl();
240-
}
241-
242-
private void OnDebuggerResuming(object sender, DebuggerResumingArgs args)
251+
private void OnFrameExiting(object sender, FrameExitingArgs args)
243252
{
244253
_exiting = true;
245254
StopCurrentRepl();

0 commit comments

Comments
 (0)