Skip to content

Commit 7e22c88

Browse files
committed
Fix running untitled scripts with arguments (but break line breakpoints)
The extant hack that enabled line breakpoints in untitled scripts is untenable. It shifted the user's args by one since it ran the untitled script as the first arg to an inline script `. $args[0]` with `$args[0]` being the script contents, instead of the expected `. {<script>}`. This hack that conflicted with the correct passing of arguments to the debugged script. We are prioritizing the latter over the former, as command breakpoints and `Wait-Debugger` work as expected, and the extant behavior was rather unique (and unexpected) in the world of VS Code extensions, to the point that the extension can now automatically prompt to save untitled files.
1 parent 506c6db commit 7e22c88

File tree

2 files changed

+17
-44
lines changed

2 files changed

+17
-44
lines changed

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

+16-43
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) Microsoft Corporation.
1+
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

44
using Microsoft.Extensions.Logging;
@@ -77,7 +77,10 @@ public Task<ConfigurationDoneResponse> Handle(ConfigurationDoneArguments request
7777

7878
if (!string.IsNullOrEmpty(_debugStateService.ScriptToLaunch))
7979
{
80-
LaunchScriptAsync(_debugStateService.ScriptToLaunch).HandleErrorsAsync(_logger);
80+
// NOTE: This is an unawaited task because responding to "configuration done" means
81+
// setting up the debugger, and in our case that means starting the script but not
82+
// waiting for it to finish.
83+
Task _ = LaunchScriptAsync(_debugStateService.ScriptToLaunch).HandleErrorsAsync(_logger);
8184
}
8285

8386
if (_debugStateService.IsInteractiveDebugSession && _debugService.IsDebuggerStopped)
@@ -102,48 +105,18 @@ public Task<ConfigurationDoneResponse> Handle(ConfigurationDoneArguments request
102105

103106
private async Task LaunchScriptAsync(string scriptToLaunch)
104107
{
105-
// Is this an untitled script?
106-
if (ScriptFile.IsUntitledPath(scriptToLaunch))
107-
{
108-
ScriptFile untitledScript = _workspaceService.GetFile(scriptToLaunch);
109-
110-
if (BreakpointApiUtils.SupportsBreakpointApis(_runspaceContext.CurrentRunspace))
111-
{
112-
// Parse untitled files with their `Untitled:` URI as the file name which will cache the URI & contents within the PowerShell parser.
113-
// By doing this, we light up the ability to debug Untitled files with breakpoints.
114-
// This is only possible via the direct usage of the breakpoint APIs in PowerShell because
115-
// Set-PSBreakpoint validates that paths are actually on the filesystem.
116-
ScriptBlockAst ast = Parser.ParseInput(untitledScript.Contents, untitledScript.DocumentUri.ToString(), out Token[] tokens, out ParseError[] errors);
117-
118-
// This seems to be the simplest way to invoke a script block (which contains breakpoint information) via the PowerShell API.
119-
//
120-
// TODO: Fix this so the added script doesn't show up.
121-
var cmd = new PSCommand().AddScript(". $args[0]").AddArgument(ast.GetScriptBlock());
122-
await _executionService
123-
.ExecutePSCommandAsync<object>(cmd, CancellationToken.None, s_debuggerExecutionOptions)
124-
.ConfigureAwait(false);
125-
}
126-
else
127-
{
128-
await _executionService
129-
.ExecutePSCommandAsync(
130-
new PSCommand().AddScript(untitledScript.Contents),
131-
CancellationToken.None,
132-
s_debuggerExecutionOptions)
133-
.ConfigureAwait(false);
134-
}
135-
}
136-
else
137-
{
138-
// TODO: Fix this so the added script doesn't show up.
139-
await _executionService
140-
.ExecutePSCommandAsync(
141-
PSCommandHelpers.BuildCommandFromArguments(scriptToLaunch, _debugStateService.Arguments),
142-
CancellationToken.None,
143-
s_debuggerExecutionOptions)
144-
.ConfigureAwait(false);
145-
}
108+
// TODO: Theoretically we can make PowerShell respect line breakpoints in untitled
109+
// files, but the previous method was a hack that conflicted with correct passing of
110+
// arguments to the debugged script. We are prioritizing the latter over the former, as
111+
// command breakpoints and `Wait-Debugger` work fine.
112+
string command = ScriptFile.IsUntitledPath(scriptToLaunch)
113+
? string.Concat("{ ", _workspaceService.GetFile(scriptToLaunch).Contents, " }")
114+
: string.Concat('"', scriptToLaunch, '"');
146115

116+
await _executionService.ExecutePSCommandAsync(
117+
PSCommandHelpers.BuildCommandFromArguments(command, _debugStateService.Arguments),
118+
CancellationToken.None,
119+
s_debuggerExecutionOptions).ConfigureAwait(false);
147120
_debugAdapterServer.SendNotification(EventNames.Terminated);
148121
}
149122
}

test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ private VariableDetailsBase[] GetVariables(string scopeName)
9696
private Task ExecutePowerShellCommand(string command, params string[] args)
9797
{
9898
return psesHost.ExecutePSCommandAsync(
99-
PSCommandHelpers.BuildCommandFromArguments(command, args),
99+
PSCommandHelpers.BuildCommandFromArguments(string.Concat('"', command, '"'), args),
100100
CancellationToken.None);
101101
}
102102

0 commit comments

Comments
 (0)