Skip to content

Commit 6a19daf

Browse files
committed
Make ExecuteCommandAsync cancellable
This lets us cancel the debugger more reliably.
1 parent 04e942e commit 6a19daf

10 files changed

+45
-30
lines changed

src/PowerShellEditorServices/Services/DebugAdapter/DebugEventHandlerService.cs

+6-6
Original file line numberDiff line numberDiff line change
@@ -139,16 +139,16 @@ private void DebugService_BreakpointUpdated(object sender, BreakpointUpdatedEven
139139
);
140140

141141
string reason = (e.UpdateType) switch {
142-
BreakpointUpdateType.Set => "new",
143-
BreakpointUpdateType.Removed => "removed",
144-
BreakpointUpdateType.Enabled => "changed",
145-
BreakpointUpdateType.Disabled => "changed",
146-
_ => "unknown"
142+
BreakpointUpdateType.Set => BreakpointEventReason.New,
143+
BreakpointUpdateType.Removed => BreakpointEventReason.Removed,
144+
BreakpointUpdateType.Enabled => BreakpointEventReason.Changed,
145+
BreakpointUpdateType.Disabled => BreakpointEventReason.Changed,
146+
_ => "InvalidBreakpointUpdateTypeEnum"
147147
};
148148

149149
_debugAdapterServer.SendNotification(
150150
EventNames.Breakpoint,
151-
new BreakpointEvent { Reason = reason, Breakpoint = breakpoint }
151+
new BreakpointEvent { Breakpoint = breakpoint, Reason = reason }
152152
);
153153
}
154154
else if (e.Breakpoint is CommandBreakpoint)

src/PowerShellEditorServices/Services/DebugAdapter/Handlers/LaunchAndAttachHandler.cs

+7-5
Original file line numberDiff line numberDiff line change
@@ -303,11 +303,13 @@ await _powerShellContextService.ExecuteScriptStringAsync(
303303
string debugRunspaceCmd;
304304
if (request.RunspaceName != null)
305305
{
306-
IEnumerable<int?> ids = await _powerShellContextService.ExecuteCommandAsync<int?>(new PSCommand()
307-
.AddCommand("Microsoft.PowerShell.Utility\\Get-Runspace")
308-
.AddParameter("Name", request.RunspaceName)
309-
.AddCommand("Microsoft.PowerShell.Utility\\Select-Object")
310-
.AddParameter("ExpandProperty", "Id")).ConfigureAwait(false);
306+
IEnumerable<int?> ids = await _powerShellContextService.ExecuteCommandAsync<int?>(
307+
new PSCommand()
308+
.AddCommand("Microsoft.PowerShell.Utility\\Get-Runspace")
309+
.AddParameter("Name", request.RunspaceName)
310+
.AddCommand("Microsoft.PowerShell.Utility\\Select-Object")
311+
.AddParameter("ExpandProperty", "Id"), cancellationToken: cancellationToken).ConfigureAwait(false);
312+
311313
foreach (var id in ids)
312314
{
313315
_debugStateService.RunspaceId = id;

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

+4-5
Original file line numberDiff line numberDiff line change
@@ -208,9 +208,8 @@ internal async Task<string> InvokeLegacyReadLineAsync(bool isCommandLine, Cancel
208208
command.AddParameter("CursorColumn", currentCursorIndex);
209209
command.AddParameter("Options", null);
210210

211-
var results = await this.powerShellContext
212-
.ExecuteCommandAsync<CommandCompletion>(command, sendOutputToHost: false, sendErrorToHost: false)
213-
.ConfigureAwait(false);
211+
var results = await this.powerShellContext.ExecuteCommandAsync<CommandCompletion>(
212+
command, sendOutputToHost: false, sendErrorToHost: false, cancellationToken).ConfigureAwait(false);
214213

215214
currentCompletion = results.FirstOrDefault();
216215
}
@@ -327,8 +326,8 @@ internal async Task<string> InvokeLegacyReadLineAsync(bool isCommandLine, Cancel
327326
PSCommand command = new PSCommand();
328327
command.AddCommand("Get-History");
329328

330-
currentHistory = await this.powerShellContext.ExecuteCommandAsync<PSObject>(command, sendOutputToHost: false, sendErrorToHost: false)
331-
.ConfigureAwait(false)
329+
currentHistory = await this.powerShellContext.ExecuteCommandAsync<PSObject>(
330+
command, sendOutputToHost: false, sendErrorToHost: false, cancellationToken).ConfigureAwait(false)
332331
as Collection<PSObject>;
333332

334333
if (currentHistory != null)

src/PowerShellEditorServices/Services/PowerShellContext/Handlers/ExpandAliasHandler.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ function __Expand-Alias {
6969
.AddStatement()
7070
.AddCommand("__Expand-Alias")
7171
.AddArgument(request.Text);
72-
var result = await _powerShellContextService.ExecuteCommandAsync<string>(psCommand).ConfigureAwait(false);
72+
var result = await _powerShellContextService.ExecuteCommandAsync<string>(
73+
psCommand, cancellationToken: cancellationToken).ConfigureAwait(false);
7374

7475
return new ExpandAliasResult
7576
{

src/PowerShellEditorServices/Services/PowerShellContext/Handlers/GetCommandHandler.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ public async Task<List<PSCommandMessage>> Handle(GetCommandParams request, Cance
5353
.AddCommand("Microsoft.PowerShell.Utility\\Sort-Object")
5454
.AddParameter("Property", "Name");
5555

56-
IEnumerable<CommandInfo> result = await _powerShellContextService.ExecuteCommandAsync<CommandInfo>(psCommand).ConfigureAwait(false);
56+
IEnumerable<CommandInfo> result = await _powerShellContextService.ExecuteCommandAsync<CommandInfo>(
57+
psCommand, cancellationToken: cancellationToken).ConfigureAwait(false);
5758

5859
var commandList = new List<PSCommandMessage>();
5960
if (result != null)

src/PowerShellEditorServices/Services/PowerShellContext/Handlers/PSHostProcessAndRunspaceHandlers.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ public async Task<RunspaceResponse[]> Handle(GetRunspaceParams request, Cancella
8787
var psCommand = new PSCommand().AddCommand("Microsoft.PowerShell.Utility\\Get-Runspace");
8888
var sb = new StringBuilder();
8989
// returns (not deserialized) Runspaces. For simpler code, we use PSObject and rely on dynamic later.
90-
runspaces = await _powerShellContextService.ExecuteCommandAsync<PSObject>(psCommand, sb).ConfigureAwait(false);
90+
runspaces = await _powerShellContextService.ExecuteCommandAsync<PSObject>(
91+
psCommand, sb, cancellationToken: cancellationToken).ConfigureAwait(false);
9192
}
9293

9394
var runspaceResponses = new List<RunspaceResponse>();

src/PowerShellEditorServices/Services/PowerShellContext/Handlers/ShowHelpHandler.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ public async Task<Unit> Handle(ShowHelpParams request, CancellationToken cancell
7272

7373
// TODO: Rather than print the help in the console, we should send the string back
7474
// to VSCode to display in a help pop-up (or similar)
75-
await _powerShellContextService.ExecuteCommandAsync<PSObject>(checkHelpPSCommand, sendOutputToHost: true).ConfigureAwait(false);
75+
await _powerShellContextService.ExecuteCommandAsync<PSObject>(
76+
checkHelpPSCommand, sendOutputToHost: true, cancellationToken: cancellationToken).ConfigureAwait(false);
7677
return Unit.Value;
7778
}
7879
}

src/PowerShellEditorServices/Services/PowerShellContext/PowerShellContextService.cs

+16-8
Original file line numberDiff line numberDiff line change
@@ -519,9 +519,11 @@ public Task<RunspaceHandle> GetRunspaceHandleAsync(CancellationToken cancellatio
519519
public Task<IEnumerable<TResult>> ExecuteCommandAsync<TResult>(
520520
PSCommand psCommand,
521521
bool sendOutputToHost = false,
522-
bool sendErrorToHost = true)
522+
bool sendErrorToHost = true,
523+
CancellationToken cancellationToken = default)
523524
{
524-
return ExecuteCommandAsync<TResult>(psCommand, errorMessages: null, sendOutputToHost, sendErrorToHost);
525+
return this.ExecuteCommandAsync<TResult>(
526+
psCommand, errorMessages: null, sendOutputToHost, sendErrorToHost, cancellationToken: cancellationToken);
525527
}
526528

527529
/// <summary>
@@ -549,7 +551,8 @@ public Task<IEnumerable<TResult>> ExecuteCommandAsync<TResult>(
549551
StringBuilder errorMessages,
550552
bool sendOutputToHost = false,
551553
bool sendErrorToHost = true,
552-
bool addToHistory = false)
554+
bool addToHistory = false,
555+
CancellationToken cancellationToken = default)
553556
{
554557
return
555558
this.ExecuteCommandAsync<TResult>(
@@ -560,7 +563,8 @@ public Task<IEnumerable<TResult>> ExecuteCommandAsync<TResult>(
560563
WriteOutputToHost = sendOutputToHost,
561564
WriteErrorsToHost = sendErrorToHost,
562565
AddToHistory = addToHistory
563-
});
566+
},
567+
cancellationToken);
564568
}
565569

566570

@@ -581,7 +585,8 @@ public Task<IEnumerable<TResult>> ExecuteCommandAsync<TResult>(
581585
public async Task<IEnumerable<TResult>> ExecuteCommandAsync<TResult>(
582586
PSCommand psCommand,
583587
StringBuilder errorMessages,
584-
ExecutionOptions executionOptions)
588+
ExecutionOptions executionOptions,
589+
CancellationToken cancellationToken = default)
585590
{
586591
Validate.IsNotNull(nameof(psCommand), psCommand);
587592
Validate.IsNotNull(nameof(executionOptions), executionOptions);
@@ -759,8 +764,11 @@ public async Task<IEnumerable<TResult>> ExecuteCommandAsync<TResult>(
759764
return shell.Invoke<TResult>(null, invocationSettings);
760765
}
761766

762-
// May need a cancellation token here
763-
return await Task.Run<IEnumerable<TResult>>(() => shell.Invoke<TResult>(input: null, invocationSettings), CancellationToken.None).ConfigureAwait(false);
767+
// This is the primary reason that ExecuteCommandAsync takes a CancellationToken
768+
cancellationToken.Register(() => shell.Stop());
769+
return await Task.Run<IEnumerable<TResult>>(
770+
() => shell.Invoke<TResult>(input: null, invocationSettings), cancellationToken)
771+
.ConfigureAwait(false);
764772
}
765773
finally
766774
{
@@ -872,7 +880,7 @@ public async Task<IEnumerable<TResult>> ExecuteCommandAsync<TResult>(
872880
// will exist already so we need to create one and then use it
873881
if (runspaceHandle == null)
874882
{
875-
runspaceHandle = await this.GetRunspaceHandleAsync().ConfigureAwait(false);
883+
runspaceHandle = await this.GetRunspaceHandleAsync(cancellationToken).ConfigureAwait(false);
876884
}
877885

878886
sessionDetails = this.GetSessionDetailsInRunspace(runspaceHandle.Runspace);

src/PowerShellEditorServices/Services/PowerShellContext/Session/Host/EditorServicesPSHostUserInterface.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -731,7 +731,8 @@ private async Task WritePromptStringToHostAsync(CancellationToken cancellationTo
731731

732732
cancellationToken.ThrowIfCancellationRequested();
733733
string promptString =
734-
(await this.powerShellContext.ExecuteCommandAsync<PSObject>(promptCommand, false, false).ConfigureAwait(false))
734+
(await this.powerShellContext.ExecuteCommandAsync<PSObject>(
735+
promptCommand, false, false, cancellationToken).ConfigureAwait(false))
735736
.Select(pso => pso.BaseObject)
736737
.OfType<string>()
737738
.FirstOrDefault() ?? "PS> ";

src/PowerShellEditorServices/Services/PowerShellContext/Session/PSReadLinePromptContext.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,8 @@ public async Task<string> InvokeReadLineAsync(bool isCommandLine, CancellationTo
143143
IEnumerable<string> readLineResults = await _powerShellContext.ExecuteCommandAsync<string>(
144144
readLineCommand,
145145
errorMessages: null,
146-
s_psrlExecutionOptions).ConfigureAwait(false);
146+
s_psrlExecutionOptions,
147+
cancellationToken).ConfigureAwait(false);
147148

148149
string line = readLineResults.FirstOrDefault();
149150

0 commit comments

Comments
 (0)