Skip to content

Commit

Permalink
Add the ability to use the Call operator instead of the DotSource ope…
Browse files Browse the repository at this point in the history
…rator

With a unit test!
  • Loading branch information
LucasArona authored and andyleejordan committed Oct 10, 2024
1 parent 9cda0eb commit c52e26d
Show file tree
Hide file tree
Showing 6 changed files with 32 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ internal class DebugStateService

internal bool IsUsingTempIntegratedConsole { get; set; }

internal string ExecuteMode { get; set; }

// This gets set at the end of the Launch/Attach handler which set debug state.
internal TaskCompletionSource<bool> ServerStarted { get; set; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,11 @@ internal async Task LaunchScriptAsync(string scriptToLaunch)
PSCommand command;
if (System.IO.File.Exists(scriptToLaunch))
{
// For a saved file we just execute its path (after escaping it).
// For a saved file we just execute its path (after escaping it), with the configured operator
// (which can't be called that because it's a reserved keyword in C#).
string executeMode = _debugStateService?.ExecuteMode == "Call" ? "&" : ".";
command = PSCommandHelpers.BuildDotSourceCommandWithArguments(
PSCommandHelpers.EscapeScriptFilePath(scriptToLaunch), _debugStateService?.Arguments);
PSCommandHelpers.EscapeScriptFilePath(scriptToLaunch), _debugStateService?.Arguments, executeMode);
}
else // It's a URI to an untitled script, or a raw script.
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ internal record PsesLaunchRequestArguments : LaunchRequestArguments
/// </summary>
public string[] RuntimeArgs { get; set; }

/// <summary>
/// Gets or sets the script execution mode, either "DotSource" or "Call".
/// </summary>
public string ExecuteMode { get; set; }

/// <summary>
/// Gets or sets optional environment variables to pass to the debuggee. The string valued
/// properties of the 'environmentVariables' are used as key/value pairs.
Expand Down Expand Up @@ -186,6 +191,7 @@ public async Task<LaunchResponse> Handle(PsesLaunchRequestArguments request, Can
_debugStateService.ScriptToLaunch = request.Script;
_debugStateService.Arguments = request.Args;
_debugStateService.IsUsingTempIntegratedConsole = request.CreateTemporaryIntegratedConsole;
_debugStateService.ExecuteMode = request.ExecuteMode;

if (request.CreateTemporaryIntegratedConsole
&& !string.IsNullOrEmpty(request.Script)
Expand Down
6 changes: 4 additions & 2 deletions src/PowerShellEditorServices/Utility/PSCommandExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,12 @@ private static StringBuilder AddCommandText(this StringBuilder sb, Command comma

public static string EscapeScriptFilePath(string f) => string.Concat("'", f.Replace("'", "''"), "'");

public static PSCommand BuildDotSourceCommandWithArguments(string command, IEnumerable<string> arguments)
// Operator defaults to dot-source but could also be call (ampersand).
// It can't be called that because it's a reserved keyword in C#.
public static PSCommand BuildDotSourceCommandWithArguments(string command, IEnumerable<string> arguments, string executeMode = ".")
{
string args = string.Join(" ", arguments ?? Array.Empty<string>());
string script = string.Concat(". ", command, string.IsNullOrEmpty(args) ? "" : " ", args);
string script = string.Concat(executeMode, " ", command, string.IsNullOrEmpty(args) ? "" : " ", args);
// HACK: We use AddScript instead of AddArgument/AddParameter to reuse Powershell parameter binding logic.
return new PSCommand().AddScript(script);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,17 @@ namespace PowerShellEditorServices.Test.E2E
{
public static class DebugAdapterClientExtensions
{
public static async Task LaunchScript(this DebugAdapterClient debugAdapterClient, string script, TaskCompletionSource<object> started)
public static async Task LaunchScript(this DebugAdapterClient debugAdapterClient, string script, TaskCompletionSource<object> started, string executeMode = "DotSource")
{
LaunchResponse launchResponse = await debugAdapterClient.Launch(
_ = await debugAdapterClient.Launch(
new PsesLaunchRequestArguments
{
NoDebug = false,
Script = script,
Cwd = "",
CreateTemporaryIntegratedConsole = false
});

if (launchResponse is null)
{
throw new Exception("Launch response was null.");
}
CreateTemporaryIntegratedConsole = false,
ExecuteMode = executeMode,
}) ?? throw new Exception("Launch response was null.");

// This will check to see if we received the Initialized event from the server.
await started.Task;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,17 @@ public async Task UsesDotSourceOperatorAndQuotesAsync()
(i) => Assert.StartsWith(". '", i));
}

[Fact]
public async Task UsesCallOperatorWithSettingAsync()
{
string filePath = NewTestFile(GenerateScriptFromLoggingStatements("$($MyInvocation.Line)"));
await PsesDebugAdapterClient.LaunchScript(filePath, Started, executeMode: "Call");
ConfigurationDoneResponse configDoneResponse = await PsesDebugAdapterClient.RequestConfigurationDone(new ConfigurationDoneArguments());
Assert.NotNull(configDoneResponse);
Assert.Collection(await GetLog(),
(i) => Assert.StartsWith("& '", i));
}

[Fact]
public async Task CanLaunchScriptWithNoBreakpointsAsync()
{
Expand Down

0 comments on commit c52e26d

Please sign in to comment.