Skip to content

Commit a759b74

Browse files
committed
Avoid recording debugger commands in the history
This changes how we decide to run a given command through `ExecuteInDebugger` or `ExecuteNormally`. It now restricts the former such that it is only used for PowerShell's intrinsic debugger commands (like `s`, `c`, etc.) and the latter is used for any other user command or, more importantly, our own implementation's debugger commands (like when we call `Get-Variable`).
1 parent 6754359 commit a759b74

File tree

2 files changed

+31
-9
lines changed

2 files changed

+31
-9
lines changed

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

+29-6
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ public SynchronousPowerShellTask(
4545

4646
public override ExecutionOptions ExecutionOptions => PowerShellExecutionOptions;
4747

48+
// These are PowerShell's intrinsic debugger commands that must be run via
49+
// `ProcessDebugCommand`.
50+
private static readonly string[] DebuggerCommands = {"continue", "c", "k", "h", "?", "list", "l", "stepInto", "s", "stepOut", "o", "stepOver", "v", "quit", "q", "detach", "d"};
51+
4852
public override IReadOnlyList<TResult> Run(CancellationToken cancellationToken)
4953
{
5054
_pwsh = _psesHost.CurrentPowerShell;
@@ -55,6 +59,9 @@ public override IReadOnlyList<TResult> Run(CancellationToken cancellationToken)
5559
}
5660

5761
return _pwsh.Runspace.Debugger.InBreakpoint
62+
&& Array.Exists(
63+
DebuggerCommands,
64+
c => c.Equals(_psCommand.GetInvocationText(), StringComparison.CurrentCultureIgnoreCase))
5865
? ExecuteInDebugger(cancellationToken)
5966
: ExecuteNormally(cancellationToken);
6067
}
@@ -89,9 +96,15 @@ private IReadOnlyList<TResult> ExecuteNormally(CancellationToken cancellationTok
8996
result = _pwsh.InvokeCommand<TResult>(_psCommand, invocationSettings);
9097
cancellationToken.ThrowIfCancellationRequested();
9198
}
92-
// Test if we've been cancelled. If we're remoting, PSRemotingDataStructureException effectively means the pipeline was stopped.
99+
// Test if we've been cancelled. If we're remoting, PSRemotingDataStructureException
100+
// effectively means the pipeline was stopped.
93101
catch (Exception e) when (cancellationToken.IsCancellationRequested || e is PipelineStoppedException || e is PSRemotingDataStructureException)
94102
{
103+
// ExecuteNormally handles user commands in a debug session. Perhaps we should clean all this up somehow.
104+
if (_pwsh.Runspace.Debugger.InBreakpoint)
105+
{
106+
StopDebuggerIfRemoteDebugSessionFailed();
107+
}
95108
throw new OperationCanceledException();
96109
}
97110
// We only catch RuntimeExceptions here in case writing errors to output was requested
@@ -124,6 +137,8 @@ private IReadOnlyList<TResult> ExecuteNormally(CancellationToken cancellationTok
124137

125138
private IReadOnlyList<TResult> ExecuteInDebugger(CancellationToken cancellationToken)
126139
{
140+
// TODO: How much of this method can we remove now that it only processes PowerShell's
141+
// intrinsic debugger commands?
127142
cancellationToken.Register(CancelDebugExecution);
128143

129144
var outputCollection = new PSDataCollection<PSObject>();
@@ -148,14 +163,22 @@ private IReadOnlyList<TResult> ExecuteInDebugger(CancellationToken cancellationT
148163
DebuggerCommandResults debuggerResult = null;
149164
try
150165
{
151-
// In the PowerShell debugger, extra debugger commands are made available, like "l", "s", "c", etc.
152-
// Executing those commands produces a result that needs to be set on the debugger stop event args.
153-
// So we use the Debugger.ProcessCommand() API to properly execute commands in the debugger
154-
// and then call DebugContext.ProcessDebuggerResult() later to handle the command appropriately
166+
// In the PowerShell debugger, intrinsic debugger commands are made available, like
167+
// "l", "s", "c", etc. Executing those commands produces a result that needs to be
168+
// set on the debugger stop event args. So we use the Debugger.ProcessCommand() API
169+
// to properly execute commands in the debugger and then call
170+
// DebugContext.ProcessDebuggerResult() later to handle the command appropriately
171+
//
172+
// Unfortunately, this API does not allow us to pass in the InvocationSettings,
173+
// which means (for instance) that we cannot instruct it to avoid adding our
174+
// debugger implmentation's commands to the history. So instead we now only call
175+
// `ExecuteInDebugger` for PowerShell's own intrinsic debugger commands.
155176
debuggerResult = _pwsh.Runspace.Debugger.ProcessCommand(_psCommand, outputCollection);
156177
cancellationToken.ThrowIfCancellationRequested();
157178
}
158-
// Test if we've been cancelled. If we're remoting, PSRemotingDataStructureException effectively means the pipeline was stopped.
179+
180+
// Test if we've been cancelled. If we're remoting, PSRemotingDataStructureException
181+
// effectively means the pipeline was stopped.
159182
catch (Exception e) when (cancellationToken.IsCancellationRequested || e is PipelineStoppedException || e is PSRemotingDataStructureException)
160183
{
161184
StopDebuggerIfRemoteDebugSessionFailed();

test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs

+2-3
Original file line numberDiff line numberDiff line change
@@ -586,10 +586,9 @@ await debugService.SetLineBreakpointsAsync(
586586
VariableDetailsBase[] variables = GetVariables(VariableContainerDetails.LocalScopeName);
587587

588588
// Test set of a local string variable (not strongly typed)
589-
const string newStrValue = "Goodbye";
589+
const string newStrValue = "\"Goodbye\"";
590590
VariableScope localScope = Array.Find(scopes, s => s.Name == VariableContainerDetails.LocalScopeName);
591-
// TODO: Fix this so it has the second quotes again?
592-
string setStrValue = await debugService.SetVariableAsync(localScope.Id, "$strVar", '"' + newStrValue + '"').ConfigureAwait(true);
591+
string setStrValue = await debugService.SetVariableAsync(localScope.Id, "$strVar", newStrValue).ConfigureAwait(true);
593592
Assert.Equal(newStrValue, setStrValue);
594593

595594
// Test set of script scope int variable (not strongly typed)

0 commit comments

Comments
 (0)