Skip to content

Separate parse from invocation configurations #2606

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Jul 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions System.CommandLine.v3.ncrunchsolution
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<SolutionConfiguration>
<Settings>
<AllowParallelTestExecution>True</AllowParallelTestExecution>
<CustomBuildProperties>
<Value>TargetFrameworks = net8.0</Value>
<Value>TargetFramework = net8.0</Value>
</CustomBuildProperties>
<EnableRDI>False</EnableRDI>
<RdiConfigured>True</RdiConfigured>
<SolutionConfigured>True</SolutionConfigured>
</Settings>
</SolutionConfiguration>
Original file line number Diff line number Diff line change
Expand Up @@ -49,31 +49,13 @@
public System.Void Add(Option option)
public System.Void Add(Command command)
public System.Collections.Generic.IEnumerable<System.CommandLine.Completions.CompletionItem> GetCompletions(System.CommandLine.Completions.CompletionContext context)
public ParseResult Parse(System.Collections.Generic.IReadOnlyList<System.String> args, CommandLineConfiguration configuration = null)
public ParseResult Parse(System.String commandLine, CommandLineConfiguration configuration = null)
public ParseResult Parse(System.Collections.Generic.IReadOnlyList<System.String> args, ParserConfiguration configuration = null)
public ParseResult Parse(System.String commandLine, ParserConfiguration configuration = null)
public System.Void SetAction(System.Action<ParseResult> action)
public System.Void SetAction(System.Func<ParseResult,System.Int32> action)
public System.Void SetAction(System.Func<ParseResult,System.Threading.CancellationToken,System.Threading.Tasks.Task> action)
public System.Void SetAction(System.Func<ParseResult,System.Threading.Tasks.Task> action)
public System.Void SetAction(System.Func<ParseResult,System.Threading.CancellationToken,System.Threading.Tasks.Task<System.Int32>> action)
public class CommandLineConfiguration
.ctor(Command rootCommand)
public System.Boolean EnableDefaultExceptionHandler { get; set; }
public System.Boolean EnablePosixBundling { get; set; }
public System.IO.TextWriter Error { get; set; }
public System.IO.TextWriter Output { get; set; }
public System.Nullable<System.TimeSpan> ProcessTerminationTimeout { get; set; }
public System.CommandLine.Parsing.TryReplaceToken ResponseFileTokenReplacer { get; set; }
public Command RootCommand { get; }
public System.Int32 Invoke(System.String commandLine)
public System.Int32 Invoke(System.String[] args)
public System.Threading.Tasks.Task<System.Int32> InvokeAsync(System.String commandLine, System.Threading.CancellationToken cancellationToken = null)
public System.Threading.Tasks.Task<System.Int32> InvokeAsync(System.String[] args, System.Threading.CancellationToken cancellationToken = null)
public ParseResult Parse(System.Collections.Generic.IReadOnlyList<System.String> args)
public ParseResult Parse(System.String commandLine)
public System.Void ThrowIfInvalid()
public class CommandLineConfigurationException : System.Exception, System.Runtime.Serialization.ISerializable
.ctor(System.String message)
public static class CompletionSourceExtensions
public static System.Void Add(this System.Collections.Generic.List<System.Func<System.CommandLine.Completions.CompletionContext,System.Collections.Generic.IEnumerable<System.CommandLine.Completions.CompletionItem>>> completionSources, System.Func<System.CommandLine.Completions.CompletionContext,System.Collections.Generic.IEnumerable<System.String>> completionsDelegate)
public static System.Void Add(this System.Collections.Generic.List<System.Func<System.CommandLine.Completions.CompletionContext,System.Collections.Generic.IEnumerable<System.CommandLine.Completions.CompletionItem>>> completionSources, System.String[] completions)
Expand All @@ -88,6 +70,12 @@
public class EnvironmentVariablesDirective : Directive
.ctor()
public System.CommandLine.Invocation.CommandLineAction Action { get; set; }
public class InvocationConfiguration
.ctor()
public System.Boolean EnableDefaultExceptionHandler { get; set; }
public System.IO.TextWriter Error { get; set; }
public System.IO.TextWriter Output { get; set; }
public System.Nullable<System.TimeSpan> ProcessTerminationTimeout { get; set; }
public abstract class Option : Symbol
public System.CommandLine.Invocation.CommandLineAction Action { get; set; }
public System.Collections.Generic.ICollection<System.String> Aliases { get; }
Expand Down Expand Up @@ -115,11 +103,16 @@
public static Option<System.IO.DirectoryInfo> AcceptExistingOnly(this Option<System.IO.DirectoryInfo> option)
public static Option<System.IO.FileSystemInfo> AcceptExistingOnly(this Option<System.IO.FileSystemInfo> option)
public static Option<T> AcceptExistingOnly<T>(this Option<T> option)
public class ParserConfiguration
.ctor()
public System.Boolean EnablePosixBundling { get; set; }
public System.CommandLine.Parsing.TryReplaceToken ResponseFileTokenReplacer { get; set; }
public class ParseResult
public System.CommandLine.Invocation.CommandLineAction Action { get; }
public System.CommandLine.Parsing.CommandResult CommandResult { get; }
public CommandLineConfiguration Configuration { get; }
public ParserConfiguration Configuration { get; }
public System.Collections.Generic.IReadOnlyList<System.CommandLine.Parsing.ParseError> Errors { get; }
public InvocationConfiguration InvocationConfiguration { get; }
public System.CommandLine.Parsing.CommandResult RootCommandResult { get; }
public System.Collections.Generic.IReadOnlyList<System.CommandLine.Parsing.Token> Tokens { get; }
public System.Collections.Generic.IReadOnlyList<System.String> UnmatchedTokens { get; }
Expand All @@ -137,8 +130,8 @@
public T GetValue<T>(Argument<T> argument)
public T GetValue<T>(Option<T> option)
public T GetValue<T>(System.String name)
public System.Int32 Invoke()
public System.Threading.Tasks.Task<System.Int32> InvokeAsync(System.Threading.CancellationToken cancellationToken = null)
public System.Int32 Invoke(InvocationConfiguration configuration = null)
public System.Threading.Tasks.Task<System.Int32> InvokeAsync(InvocationConfiguration configuration = null, System.Threading.CancellationToken cancellationToken = null)
public System.String ToString()
public class RootCommand : Command, System.Collections.IEnumerable
public static System.String ExecutableName { get; }
Expand Down Expand Up @@ -212,8 +205,8 @@ System.CommandLine.Parsing
public System.Void OnlyTake(System.Int32 numberOfTokens)
public System.String ToString()
public static class CommandLineParser
public static System.CommandLine.ParseResult Parse(System.CommandLine.Command command, System.Collections.Generic.IReadOnlyList<System.String> args, System.CommandLine.CommandLineConfiguration configuration = null)
public static System.CommandLine.ParseResult Parse(System.CommandLine.Command command, System.String commandLine, System.CommandLine.CommandLineConfiguration configuration = null)
public static System.CommandLine.ParseResult Parse(System.CommandLine.Command command, System.Collections.Generic.IReadOnlyList<System.String> args, System.CommandLine.ParserConfiguration configuration = null)
public static System.CommandLine.ParseResult Parse(System.CommandLine.Command command, System.String commandLine, System.CommandLine.ParserConfiguration configuration = null)
public static System.Collections.Generic.IEnumerable<System.String> SplitCommandLine(System.String commandLine)
public class CommandResult : SymbolResult
public System.Collections.Generic.IEnumerable<SymbolResult> Children { get; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,7 @@ static async Task Main(string[] args)
return Task.CompletedTask;
});

CommandLineConfiguration commandLine = new (rootCommand);

await commandLine.InvokeAsync(args);
await rootCommand.Parse(args).InvokeAsync();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.IO;
using System.Threading;
using System.Threading.Tasks;
using FluentAssertions;
using Xunit;
Expand All @@ -10,32 +11,34 @@ namespace System.CommandLine.Suggest.Tests
{
public class SuggestionShellScriptHandlerTest
{
private readonly CommandLineConfiguration _configuration;
private readonly RootCommand _rootCommand;
private readonly InvocationConfiguration _configuration;

public SuggestionShellScriptHandlerTest()
{
_configuration = new SuggestionDispatcher(new TestSuggestionRegistration()).Configuration;
_rootCommand = new SuggestionDispatcher(new TestSuggestionRegistration()).RootCommand;
_configuration = new()
{
Output = new StringWriter(),
Error = new StringWriter()
};
}

[Fact]
public async Task When_shell_type_is_not_supported_it_throws()
{
_configuration.Error = new StringWriter();

await _configuration.InvokeAsync("script 123");
await _rootCommand.Parse("script 123").InvokeAsync(_configuration, CancellationToken.None);

_configuration.Error
.ToString()
.Should()
.Contain("Shell '123' is not supported.");
.ToString()
.Should()
.Contain("Shell '123' is not supported.");
}

[Fact]
public async Task It_should_print_bash_shell_script()
{
_configuration.Output = new StringWriter();

await _configuration.InvokeAsync("script bash");
await _rootCommand.Parse("script bash").InvokeAsync(_configuration, CancellationToken.None);

_configuration.Output.ToString().Should().Contain("_dotnet_bash_complete()");
_configuration.Output.ToString().Should().NotContain("\r\n");
Expand All @@ -44,9 +47,7 @@ public async Task It_should_print_bash_shell_script()
[Fact]
public async Task It_should_print_powershell_shell_script()
{
_configuration.Output = new StringWriter();

await _configuration.InvokeAsync("script powershell");
await _rootCommand.Parse("script powershell").InvokeAsync(_configuration, CancellationToken.None);

_configuration.Output.ToString().Should().Contain("Register-ArgumentCompleter");
_configuration.Output.ToString().Should().Contain("\r\n");
Expand All @@ -55,12 +56,10 @@ public async Task It_should_print_powershell_shell_script()
[Fact]
public async Task It_should_print_zsh_shell_script()
{
_configuration.Output = new StringWriter();
await _rootCommand.Parse("script zsh").InvokeAsync(_configuration, CancellationToken.None);

await _configuration.InvokeAsync("script zsh");

_configuration.Output.ToString().Should().Contain("_dotnet_zsh_complete()");
_configuration.Output.ToString().Should().NotContain("\r\n");
}
}
}
}
21 changes: 11 additions & 10 deletions src/System.CommandLine.Suggest/SuggestionDispatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.CommandLine.Completions;

namespace System.CommandLine.Suggest
{
Expand All @@ -29,7 +28,7 @@ public SuggestionDispatcher(ISuggestionRegistration suggestionRegistration, ISug
};
CompleteScriptCommand.SetAction(context =>
{
SuggestionShellScriptHandler.Handle(context.Configuration.Output, context.GetValue(shellTypeArgument));
SuggestionShellScriptHandler.Handle(context.InvocationConfiguration.Output, context.GetValue(shellTypeArgument));
});

ListCommand = new Command("list")
Expand All @@ -38,7 +37,7 @@ public SuggestionDispatcher(ISuggestionRegistration suggestionRegistration, ISug
};
ListCommand.SetAction((ctx, cancellationToken) =>
{
ctx.Configuration.Output.WriteLine(ShellPrefixesToMatch(_suggestionRegistration));
ctx.InvocationConfiguration.Output.WriteLine(ShellPrefixesToMatch(_suggestionRegistration));
return Task.CompletedTask;
});

Expand All @@ -59,25 +58,27 @@ public SuggestionDispatcher(ISuggestionRegistration suggestionRegistration, ISug

RegisterCommand.SetAction((context, cancellationToken) =>
{
Register(context.GetValue(commandPathOption), context.Configuration.Output);
Register(context.GetValue(commandPathOption), context.InvocationConfiguration.Output);
return Task.CompletedTask;
});

var root = new RootCommand
RootCommand = new RootCommand
{
ListCommand,
GetCommand,
RegisterCommand,
CompleteScriptCommand,
};
root.TreatUnmatchedTokensAsErrors = false;
Configuration = new CommandLineConfiguration(root);
RootCommand.TreatUnmatchedTokensAsErrors = false;
Configuration = new InvocationConfiguration();
}

private Command CompleteScriptCommand { get; }

private Command GetCommand { get; }

public RootCommand RootCommand { get; }

private Option<FileInfo> ExecutableOption { get; } = GetExecutableOption();

private static Option<FileInfo> GetExecutableOption()
Expand All @@ -98,11 +99,11 @@ private static Option<FileInfo> GetExecutableOption()

private Command RegisterCommand { get; }

public CommandLineConfiguration Configuration { get; }
public InvocationConfiguration Configuration { get; }

public TimeSpan Timeout { get; set; } = TimeSpan.FromMilliseconds(5000);

public Task<int> InvokeAsync(string[] args) => Configuration.InvokeAsync(args);
public Task<int> InvokeAsync(string[] args) => RootCommand.Parse(args).InvokeAsync(Configuration);

private void Register(
string commandPath,
Expand Down Expand Up @@ -168,7 +169,7 @@ private Task<int> Get(ParseResult parseResult, CancellationToken cancellationTok
Program.LogDebug($"dotnet-suggest returning: \"{completions.Replace("\r", "\\r").Replace("\n", "\\n")}\"");
#endif

parseResult.Configuration.Output.Write(completions);
parseResult.InvocationConfiguration.Output.Write(completions);

return Task.FromResult(0);
}
Expand Down
2 changes: 1 addition & 1 deletion src/System.CommandLine.Tests/Binding/TestModels.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public class ClassWithMethodHavingParameter<T>

public ClassWithMethodHavingParameter(ParseResult parseResult)
{
_output = parseResult.Configuration.Output;
_output = parseResult.InvocationConfiguration.Output;
}

public int Handle(T value)
Expand Down
Loading
Loading