diff --git a/System.CommandLine.v3.ncrunchsolution b/System.CommandLine.v3.ncrunchsolution new file mode 100644 index 0000000000..ed49d122c2 --- /dev/null +++ b/System.CommandLine.v3.ncrunchsolution @@ -0,0 +1,12 @@ + + + True + + TargetFrameworks = net8.0 + TargetFramework = net8.0 + + False + True + True + + \ No newline at end of file diff --git a/src/System.CommandLine.ApiCompatibility.Tests/ApiCompatibilityApprovalTests.System_CommandLine_api_is_not_changed.approved.txt b/src/System.CommandLine.ApiCompatibility.Tests/ApiCompatibilityApprovalTests.System_CommandLine_api_is_not_changed.approved.txt index 45cc9f778a..1ef7bd503e 100644 --- a/src/System.CommandLine.ApiCompatibility.Tests/ApiCompatibilityApprovalTests.System_CommandLine_api_is_not_changed.approved.txt +++ b/src/System.CommandLine.ApiCompatibility.Tests/ApiCompatibilityApprovalTests.System_CommandLine_api_is_not_changed.approved.txt @@ -49,31 +49,13 @@ public System.Void Add(Option option) public System.Void Add(Command command) public System.Collections.Generic.IEnumerable GetCompletions(System.CommandLine.Completions.CompletionContext context) - public ParseResult Parse(System.Collections.Generic.IReadOnlyList args, CommandLineConfiguration configuration = null) - public ParseResult Parse(System.String commandLine, CommandLineConfiguration configuration = null) + public ParseResult Parse(System.Collections.Generic.IReadOnlyList args, ParserConfiguration configuration = null) + public ParseResult Parse(System.String commandLine, ParserConfiguration configuration = null) public System.Void SetAction(System.Action action) public System.Void SetAction(System.Func action) public System.Void SetAction(System.Func action) public System.Void SetAction(System.Func action) public System.Void SetAction(System.Func> 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 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 InvokeAsync(System.String commandLine, System.Threading.CancellationToken cancellationToken = null) - public System.Threading.Tasks.Task InvokeAsync(System.String[] args, System.Threading.CancellationToken cancellationToken = null) - public ParseResult Parse(System.Collections.Generic.IReadOnlyList 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>> completionSources, System.Func> completionsDelegate) public static System.Void Add(this System.Collections.Generic.List>> completionSources, System.String[] completions) @@ -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 ProcessTerminationTimeout { get; set; } public abstract class Option : Symbol public System.CommandLine.Invocation.CommandLineAction Action { get; set; } public System.Collections.Generic.ICollection Aliases { get; } @@ -115,11 +103,16 @@ public static Option AcceptExistingOnly(this Option option) public static Option AcceptExistingOnly(this Option option) public static Option AcceptExistingOnly(this Option 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 Errors { get; } + public InvocationConfiguration InvocationConfiguration { get; } public System.CommandLine.Parsing.CommandResult RootCommandResult { get; } public System.Collections.Generic.IReadOnlyList Tokens { get; } public System.Collections.Generic.IReadOnlyList UnmatchedTokens { get; } @@ -137,8 +130,8 @@ public T GetValue(Argument argument) public T GetValue(Option option) public T GetValue(System.String name) - public System.Int32 Invoke() - public System.Threading.Tasks.Task InvokeAsync(System.Threading.CancellationToken cancellationToken = null) + public System.Int32 Invoke(InvocationConfiguration configuration = null) + public System.Threading.Tasks.Task 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; } @@ -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 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 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 SplitCommandLine(System.String commandLine) public class CommandResult : SymbolResult public System.Collections.Generic.IEnumerable Children { get; } diff --git a/src/System.CommandLine.Suggest.Tests/EndToEndTestApp/Program.cs b/src/System.CommandLine.Suggest.Tests/EndToEndTestApp/Program.cs index d11e622ce2..eef852577c 100644 --- a/src/System.CommandLine.Suggest.Tests/EndToEndTestApp/Program.cs +++ b/src/System.CommandLine.Suggest.Tests/EndToEndTestApp/Program.cs @@ -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(); } } } diff --git a/src/System.CommandLine.Suggest.Tests/SuggestionShellScriptHandlerTest.cs b/src/System.CommandLine.Suggest.Tests/SuggestionShellScriptHandlerTest.cs index 0ca6f6805e..4cdf78fa9f 100644 --- a/src/System.CommandLine.Suggest.Tests/SuggestionShellScriptHandlerTest.cs +++ b/src/System.CommandLine.Suggest.Tests/SuggestionShellScriptHandlerTest.cs @@ -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; @@ -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"); @@ -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"); @@ -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"); } } -} +} \ No newline at end of file diff --git a/src/System.CommandLine.Suggest/SuggestionDispatcher.cs b/src/System.CommandLine.Suggest/SuggestionDispatcher.cs index 512712a41f..ec16bd3f16 100644 --- a/src/System.CommandLine.Suggest/SuggestionDispatcher.cs +++ b/src/System.CommandLine.Suggest/SuggestionDispatcher.cs @@ -6,7 +6,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using System.CommandLine.Completions; namespace System.CommandLine.Suggest { @@ -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") @@ -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; }); @@ -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 ExecutableOption { get; } = GetExecutableOption(); private static Option GetExecutableOption() @@ -98,11 +99,11 @@ private static Option GetExecutableOption() private Command RegisterCommand { get; } - public CommandLineConfiguration Configuration { get; } + public InvocationConfiguration Configuration { get; } public TimeSpan Timeout { get; set; } = TimeSpan.FromMilliseconds(5000); - public Task InvokeAsync(string[] args) => Configuration.InvokeAsync(args); + public Task InvokeAsync(string[] args) => RootCommand.Parse(args).InvokeAsync(Configuration); private void Register( string commandPath, @@ -168,7 +169,7 @@ private Task 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); } diff --git a/src/System.CommandLine.Tests/Binding/TestModels.cs b/src/System.CommandLine.Tests/Binding/TestModels.cs index 49cd584e22..972f5eb8a2 100644 --- a/src/System.CommandLine.Tests/Binding/TestModels.cs +++ b/src/System.CommandLine.Tests/Binding/TestModels.cs @@ -71,7 +71,7 @@ public class ClassWithMethodHavingParameter public ClassWithMethodHavingParameter(ParseResult parseResult) { - _output = parseResult.Configuration.Output; + _output = parseResult.InvocationConfiguration.Output; } public int Handle(T value) diff --git a/src/System.CommandLine.Tests/CommandLineConfigurationTests.cs b/src/System.CommandLine.Tests/CommandLineConfigurationTests.cs deleted file mode 100644 index c0587785ea..0000000000 --- a/src/System.CommandLine.Tests/CommandLineConfigurationTests.cs +++ /dev/null @@ -1,296 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using FluentAssertions; -using Xunit; - -namespace System.CommandLine.Tests; - -public class CommandLineConfigurationTests -{ - [Fact] - public void ThrowIfInvalid_throws_if_there_are_duplicate_sibling_option_aliases_on_the_root_command() - { - var option1 = new Option("--dupe"); - var option2 = new Option("-y"); - option2.Aliases.Add("--dupe"); - - var command = new RootCommand - { - option1, - option2 - }; - - var config = new CommandLineConfiguration(command); - - var validate = () => config.ThrowIfInvalid(); - - validate.Should() - .Throw() - .Which - .Message - .Should() - .Be($"Duplicate alias '--dupe' found on command '{command.Name}'."); - } - - [Fact] - public void ThrowIfInvalid_throws_if_there_are_duplicate_sibling_option_aliases_on_a_subcommand() - { - var option1 = new Option("--dupe"); - var option2 = new Option("--ok"); - option2.Aliases.Add("--dupe"); - - var command = new RootCommand - { - new Command("subcommand") - { - option1, - option2 - } - }; - - var config = new CommandLineConfiguration(command); - - var validate = () => config.ThrowIfInvalid(); - - validate.Should() - .Throw() - .Which - .Message - .Should() - .Be("Duplicate alias '--dupe' found on command 'subcommand'."); - } - - [Fact] - public void ThrowIfInvalid_throws_if_there_are_duplicate_sibling_subcommand_aliases_on_the_root_command() - { - var command1 = new Command("dupe"); - var command2 = new Command("not-a-dupe"); - command2.Aliases.Add("dupe"); - - var rootCommand = new RootCommand - { - command1, - command2 - }; - - var config = new CommandLineConfiguration(rootCommand); - - var validate = () => config.ThrowIfInvalid(); - - validate.Should() - .Throw() - .Which - .Message - .Should() - .Be($"Duplicate alias 'dupe' found on command '{rootCommand.Name}'."); - } - - [Fact] - public void ThrowIfInvalid_throws_if_there_are_duplicate_sibling_subcommand_aliases_on_a_subcommand() - { - var command = new RootCommand - { - new Command("subcommand") - { - new Command("dupe"), - new Command("not-a-dupe") { Aliases = { "dupe" } } - } - }; - - var config = new CommandLineConfiguration(command); - - var validate = () => config.ThrowIfInvalid(); - - validate.Should() - .Throw() - .Which - .Message - .Should() - .Be("Duplicate alias 'dupe' found on command 'subcommand'."); - } - - [Fact] - public void ThrowIfInvalid_throws_if_sibling_command_and_option_aliases_collide_on_the_root_command() - { - var option = new Option("dupe"); - var command = new Command("not-a-dupe"); - command.Aliases.Add("dupe"); - - var rootCommand = new RootCommand - { - option, - command - }; - - var config = new CommandLineConfiguration(rootCommand); - - var validate = () => config.ThrowIfInvalid(); - - validate.Should() - .Throw() - .Which - .Message - .Should() - .Be($"Duplicate alias 'dupe' found on command '{rootCommand.Name}'."); - } - - [Fact] - public void ThrowIfInvalid_throws_if_sibling_command_and_option_aliases_collide_on_a_subcommand() - { - var option = new Option("dupe"); - var command = new Command("not-a-dupe"); - command.Aliases.Add("dupe"); - - var rootCommand = new RootCommand - { - new Command("subcommand") - { - option, - command - } - }; - - var config = new CommandLineConfiguration(rootCommand); - - var validate = () => config.ThrowIfInvalid(); - - validate.Should() - .Throw() - .Which - .Message - .Should() - .Be("Duplicate alias 'dupe' found on command 'subcommand'."); - } - - [Fact] - public void ThrowIfInvalid_throws_if_there_are_duplicate_sibling_global_option_aliases_on_the_root_command() - { - var option1 = new Option("--dupe") { Recursive = true }; - var option2 = new Option("-y") { Recursive = true }; - option2.Aliases.Add("--dupe"); - - var command = new RootCommand(); - command.Options.Add(option1); - command.Options.Add(option2); - - var config = new CommandLineConfiguration(command); - - var validate = () => config.ThrowIfInvalid(); - - validate.Should() - .Throw() - .Which - .Message - .Should() - .Be($"Duplicate alias '--dupe' found on command '{command.Name}'."); - } - - [Fact] - public void ThrowIfInvalid_does_not_throw_if_global_option_alias_is_the_same_as_local_option_alias() - { - var rootCommand = new RootCommand - { - new Command("subcommand") - { - new Option("--dupe") - } - }; - rootCommand.Options.Add(new Option("--dupe") { Recursive = true }); - - var config = new CommandLineConfiguration(rootCommand); - - var validate = () => config.ThrowIfInvalid(); - - validate.Should().NotThrow(); - } - - [Fact] - public void ThrowIfInvalid_does_not_throw_if_global_option_alias_is_the_same_as_subcommand_alias() - { - var rootCommand = new RootCommand - { - new Command("subcommand") - { - new Command("--dupe") - } - }; - rootCommand.Options.Add(new Option("--dupe") { Recursive = true }); - - var config = new CommandLineConfiguration(rootCommand); - - var validate = () => config.ThrowIfInvalid(); - - validate.Should().NotThrow(); - } - - [Fact] - public void ThrowIfInvalid_throws_if_a_command_is_its_own_parent() - { - var command = new RootCommand(); - command.Add(command); - - var config = new CommandLineConfiguration(command); - - var validate = () => config.ThrowIfInvalid(); - - validate.Should() - .Throw() - .Which - .Message - .Should() - .Be($"Cycle detected in command tree. Command '{command.Name}' is its own ancestor."); - } - - [Fact] - public void ThrowIfInvalid_throws_if_a_parentage_cycle_is_detected() - { - var command = new Command("command"); - var rootCommand = new RootCommand { command }; - command.Add(rootCommand); - - var config = new CommandLineConfiguration(rootCommand); - - var validate = () => config.ThrowIfInvalid(); - - validate.Should() - .Throw() - .Which - .Message - .Should() - .Be($"Cycle detected in command tree. Command '{rootCommand.Name}' is its own ancestor."); - } - - [Fact] - public void It_can_be_subclassed_to_provide_additional_context() - { - var command = new RootCommand(); - var commandWasInvoked = false; - command.SetAction(parseResult => - { - var appConfig = (CustomAppConfiguration)parseResult.Configuration; - - // access custom config - - commandWasInvoked = true; - - return 0; - }); - - var config = new CustomAppConfiguration(command); - - config.Invoke(""); - - commandWasInvoked.Should().BeTrue(); - } -} - -public class CustomAppConfiguration : CommandLineConfiguration -{ - public CustomAppConfiguration(RootCommand command) : base(command) - { - EnableDefaultExceptionHandler = false; - } - - public IServiceProvider ServiceProvider { get; } -} \ No newline at end of file diff --git a/src/System.CommandLine.Tests/CompletionTests.cs b/src/System.CommandLine.Tests/CompletionTests.cs index 84d931f863..4d902ff07a 100644 --- a/src/System.CommandLine.Tests/CompletionTests.cs +++ b/src/System.CommandLine.Tests/CompletionTests.cs @@ -185,8 +185,8 @@ public void Command_GetCompletions_with_text_to_match_orders_by_match_position_t new Command("andmyothersubcommand"), }; - CommandLineConfiguration simpleConfig = new (command); - var completions = command.Parse("my", simpleConfig).GetCompletions(); + + var completions = command.Parse("my").GetCompletions(); completions .Select(item => item.Label) @@ -204,8 +204,8 @@ public void When_an_option_has_a_default_value_it_will_still_be_suggested() new Option("--cherry") }; - CommandLineConfiguration simpleConfig = new (command); - var result = command.Parse("", simpleConfig); + + var result = command.Parse(""); _output.WriteLine(result.ToString()); @@ -235,8 +235,7 @@ public void Command_GetCompletions_can_access_ParseResult() cloneOption }; - CommandLineConfiguration simpleConfig = new (rootCommand); - var result = rootCommand.Parse("--origin test --clone ", simpleConfig); + var result = rootCommand.Parse("--origin test --clone "); _output.WriteLine(result.ToString()); @@ -278,8 +277,8 @@ public void When_one_option_has_been_specified_then_it_and_its_siblings_will_sti }; var commandLine = "--apple grannysmith"; - CommandLineConfiguration simpleConfig = new (command); - var result = command.Parse(commandLine, simpleConfig); + + var result = command.Parse(commandLine); result.GetCompletions(commandLine.Length + 1) .Select(item => item.Label) @@ -306,9 +305,8 @@ public void When_a_subcommand_has_been_specified_then_its_sibling_commands_will_ new Option("--rainier") } }; - CommandLineConfiguration simpleConfig = new (rootCommand); - var result = rootCommand.Parse("cherry ", simpleConfig); + var result = rootCommand.Parse("cherry "); result.GetCompletions() .Select(item => item.Label) @@ -336,9 +334,8 @@ public void When_a_subcommand_has_been_specified_then_its_sibling_commands_alias apple, banana }; - CommandLineConfiguration simpleConfig = new (rootCommand); - var result = rootCommand.Parse("banana ", simpleConfig); + var result = rootCommand.Parse("banana "); result.GetCompletions() .Select(item => item.Label) @@ -356,8 +353,8 @@ public void When_a_subcommand_has_been_specified_then_its_sibling_options_will_n }; var commandLine = "child"; - CommandLineConfiguration simpleConfig = new (command); - var parseResult = command.Parse(commandLine, simpleConfig); + + var parseResult = command.Parse(commandLine); parseResult .GetCompletions(commandLine.Length + 1) @@ -377,8 +374,8 @@ public void When_a_subcommand_has_been_specified_then_its_sibling_options_with_a }; var commandLine = "--parent-option 123 child"; - CommandLineConfiguration simpleConfig = new (command); - var parseResult = command.Parse(commandLine, simpleConfig); + + var parseResult = command.Parse(commandLine); parseResult .GetCompletions(commandLine.Length + 1) @@ -400,8 +397,8 @@ public void When_a_subcommand_has_been_specified_then_its_child_options_will_be_ }; var commandLine = "child "; - CommandLineConfiguration simpleConfig = new (command); - var parseResult = command.Parse(commandLine, simpleConfig); + + var parseResult = command.Parse(commandLine); parseResult .GetCompletions(commandLine.Length + 1) @@ -430,8 +427,8 @@ public void When_a_subcommand_with_subcommands_has_been_specified_then_its_sibli }; var commandLine = "cherry"; - CommandLineConfiguration simpleConfig = new (rootCommand); - var result = rootCommand.Parse(commandLine, simpleConfig); + + var result = rootCommand.Parse(commandLine); result.GetCompletions(commandLine.Length + 1) .Select(item => item.Label) @@ -450,8 +447,8 @@ public void When_one_option_has_been_partially_specified_then_nonmatching_siblin }; var input = "a"; - CommandLineConfiguration simpleConfig = new (command); - var result = command.Parse(input, simpleConfig); + + var result = command.Parse(input); result.GetCompletions(input.Length) .Select(item => item.Label) @@ -472,8 +469,7 @@ public void An_option_can_be_hidden_from_completions_by_setting_IsHidden_to_true new Option("-n") { Description = "Not hidden" } }; - CommandLineConfiguration simpleConfig = new (command); - var completions = command.Parse("the-command ", simpleConfig).GetCompletions(); + var completions = command.Parse("the-command ").GetCompletions(); completions.Select(item => item.Label).Should().NotContain("--hide-me"); } @@ -488,8 +484,8 @@ public void Parser_options_can_supply_context_sensitive_matches() }; var commandLine = "--bread"; - CommandLineConfiguration simpleConfig = new (command); - var result = command.Parse(commandLine, simpleConfig); + + var result = command.Parse(commandLine); result.GetCompletions(commandLine.Length + 1) .Select(item => item.Label) @@ -497,7 +493,7 @@ public void Parser_options_can_supply_context_sensitive_matches() .BeEquivalentTo("rye", "sourdough", "wheat"); commandLine = "--bread wheat --cheese "; - result = command.Parse(commandLine, simpleConfig); + result = command.Parse(commandLine); result.GetCompletions(commandLine.Length + 1) .Select(item => item.Label) @@ -516,8 +512,8 @@ public void Subcommand_names_are_available_as_suggestions() }; var commandLine = "test"; - CommandLineConfiguration simpleConfig = new (command); - command.Parse(commandLine, simpleConfig) + + command.Parse(commandLine) .GetCompletions(commandLine.Length + 1) .Select(item => item.Label) .Should() @@ -535,8 +531,8 @@ public void Both_subcommands_and_options_are_available_as_suggestions() }; var commandLine = "test"; - CommandLineConfiguration simpleConfig = new (command); - command.Parse(commandLine, simpleConfig) + + command.Parse(commandLine) .GetCompletions(commandLine.Length + 1) .Select(item => item.Label) .Should() @@ -555,8 +551,8 @@ public void Option_GetCompletions_are_not_provided_without_matching_prefix(strin new Option("--three") }; - CommandLineConfiguration simpleConfig = new (command); - ParseResult result = command.Parse(input, simpleConfig); + + ParseResult result = command.Parse(input); result.GetCompletions() .Select(item => item.Label) .Should() @@ -574,8 +570,8 @@ public void Option_GetCompletions_can_be_based_on_the_proximate_option() }; var commandLine = "outer"; - CommandLineConfiguration simpleConfig = new (outer); - ParseResult result = outer.Parse(commandLine, simpleConfig); + + ParseResult result = outer.Parse(commandLine); result.GetCompletions(commandLine.Length + 1) .Select(item => item.Label) @@ -593,8 +589,8 @@ public void Argument_completions_can_be_based_on_the_proximate_option() }; var commandLine = "outer --two"; - CommandLineConfiguration simpleConfig = new (outer); - ParseResult result = outer.Parse(commandLine, simpleConfig); + + ParseResult result = outer.Parse(commandLine); result.GetCompletions(commandLine.Length + 1) .Select(item => item.Label) @@ -612,8 +608,7 @@ public void Option_GetCompletions_can_be_based_on_the_proximate_option_and_parti new Command("three", "Command three") }; - CommandLineConfiguration simpleConfig = new (outer); - ParseResult result = outer.Parse("outer o", simpleConfig); + ParseResult result = outer.Parse("outer o"); result.GetCompletions() .Select(item => item.Label) @@ -632,15 +627,15 @@ public void Completions_can_be_provided_in_the_absence_of_validation() option }; - CommandLineConfiguration simpleConfig = new (command); - command.Parse("the-command -t m", simpleConfig) + + command.Parse("the-command -t m") .GetCompletions() .Select(item => item.Label) .Should() .BeEquivalentTo("animal", "mineral"); - command.Parse("the-command -t something-else", simpleConfig) + command.Parse("the-command -t something-else") .Errors .Should() .BeEmpty(); @@ -697,9 +692,7 @@ public void When_caller_does_the_tokenizing_then_argument_completions_are_based_ CreateOptionWithAcceptOnlyFromAmong(name: "three", "three-a", "three-b", "three-c") }; - var configuration = new CommandLineConfiguration(command); - - var result = command.Parse("outer two b", configuration); + var result = command.Parse("outer two b"); result.GetCompletions() .Select(item => item.Label) @@ -788,8 +781,8 @@ public void When_parsing_from_text_if_the_proximate_option_is_completed_then_com CreateOptionWithAcceptOnlyFromAmong(name: "--language", "C#"), new Option("--langVersion") }; - var configuration = new CommandLineConfiguration(command); - var completions = command.Parse("--framework net8.0 --l", configuration).GetCompletions(); + + var completions = command.Parse("--framework net8.0 --l").GetCompletions(); completions.Select(item => item.Label) .Should() @@ -805,8 +798,8 @@ public void When_parsing_from_array_if_the_proximate_option_is_completed_then_co CreateOptionWithAcceptOnlyFromAmong(name: "--language", "C#"), new Option("--langVersion") }; - var configuration = new CommandLineConfiguration(command); - var completions = command.Parse(new[]{"--framework","net8.0","--l"}, configuration).GetCompletions(); + + var completions = command.Parse(new[]{"--framework","net8.0","--l"}).GetCompletions(); completions.Select(item => item.Label) .Should() @@ -840,8 +833,8 @@ public void Options_that_have_been_specified_to_their_maximum_arity_are_not_sugg }; var commandLine = "--allows-one x"; - CommandLineConfiguration simpleConfig = new (command); - var completions = command.Parse(commandLine, simpleConfig).GetCompletions(commandLine.Length + 1); + + var completions = command.Parse(commandLine).GetCompletions(commandLine.Length + 1); completions.Select(item => item.Label) .Should() @@ -851,13 +844,13 @@ public void Options_that_have_been_specified_to_their_maximum_arity_are_not_sugg [Fact] public void When_current_symbol_is_an_option_that_requires_arguments_then_parent_symbol_completions_are_omitted() { - var configuration = new CommandLineConfiguration(new RootCommand - { - new Option("--allows-one"), - new Option("--allows-many") - }); + var rootCommand = new RootCommand + { + new Option("--allows-one"), + new Option("--allows-many") + }; - var completions = configuration.Parse("--allows-one ").GetCompletions(); + var completions = rootCommand.Parse("--allows-one ").GetCompletions(); completions.Should().BeEmpty(); } @@ -918,8 +911,8 @@ public void Default_completions_can_be_cleared_and_replaced() { argument }; - CommandLineConfiguration simpleConfig = new (command); - var completions = command.Parse("the-command s", simpleConfig) + + var completions = command.Parse("the-command s") .GetCompletions(); completions.Select(item => item.Label) @@ -938,8 +931,8 @@ public void Default_completions_can_be_appended_to() } }; - CommandLineConfiguration simpleConfig = new (command); - var completions = command.Parse("the-command s", simpleConfig) + + var completions = command.Parse("the-command s") .GetCompletions(); completions @@ -991,9 +984,8 @@ public void When_option_completions_are_available_then_they_are_suggested_when_a { Option option = new ("--day"); RootCommand rootCommand = new () { option }; - CommandLineConfiguration simpleConfig = new (rootCommand); - var result = rootCommand.Parse("--day SleepyDay", simpleConfig); + var result = rootCommand.Parse("--day SleepyDay"); result.Errors .Should() diff --git a/src/System.CommandLine.Tests/CustomParsingTests.cs b/src/System.CommandLine.Tests/CustomParsingTests.cs index fe706a04cb..7a1d14936d 100644 --- a/src/System.CommandLine.Tests/CustomParsingTests.cs +++ b/src/System.CommandLine.Tests/CustomParsingTests.cs @@ -220,8 +220,7 @@ public void Option_ArgumentResult_Parent_is_set_correctly_when_token_is_implicit } }; - CommandLineConfiguration simpleConfig = new (command); - command.Parse("", simpleConfig); + command.Parse(""); argumentResult .Parent diff --git a/src/System.CommandLine.Tests/DirectiveTests.cs b/src/System.CommandLine.Tests/DirectiveTests.cs index 9d2851726d..d4a70847b4 100644 --- a/src/System.CommandLine.Tests/DirectiveTests.cs +++ b/src/System.CommandLine.Tests/DirectiveTests.cs @@ -49,11 +49,11 @@ public void Multiple_directives_are_allowed() RootCommand root = new() { new Option("-y") }; Directive parseDirective = new ("parse"); Directive suggestDirective = new ("suggest"); - CommandLineConfiguration config = new(root); + root.Add(parseDirective); root.Add(suggestDirective); - var result = root.Parse("[parse] [suggest] -y", config); + var result = root.Parse("[parse] [suggest] -y"); result.GetResult(parseDirective).Should().NotBeNull(); result.GetResult(suggestDirective).Should().NotBeNull(); @@ -77,21 +77,21 @@ public async Task Multiple_instances_of_the_same_directive_can_be_invoked(bool i : new SynchronousTestAction(incrementCallCount, terminating: false) }; - var config = new CommandLineConfiguration(new RootCommand + var rootCommand = new RootCommand { Action = invokeAsync ? new AsynchronousTestAction(verifyActionWasCalled, terminating: false) : new SynchronousTestAction(verifyActionWasCalled, terminating: false), Directives = { testDirective } - }); + }; if (invokeAsync) { - await config.InvokeAsync("[test:1] [test:2]"); + await rootCommand.Parse("[test:1] [test:2]").InvokeAsync(); } else { - config.Invoke("[test:1] [test:2]"); + rootCommand.Parse("[test:1] [test:2]").Invoke(); } using var _ = new AssertionScope(); @@ -117,18 +117,19 @@ public async Task Multiple_different_directives_can_be_invoked(bool invokeAsync) { Action = new SynchronousTestAction(_ => directiveTwoActionWasCalled = true, terminating: false) }; - var config = new CommandLineConfiguration(new RootCommand + + var rootCommand = new RootCommand { Action = new SynchronousTestAction(_ => commandActionWasCalled = true, terminating: false), Directives = { directiveOne, directiveTwo } - }); + }; if (invokeAsync) { - await config.InvokeAsync("[one] [two]"); + await rootCommand.Parse("[one] [two]").InvokeAsync(); } else { - config.Invoke("[one] [two]"); + rootCommand.Parse("[one] [two]").Invoke(); } using var _ = new AssertionScope(); @@ -213,10 +214,10 @@ public void When_a_directive_is_specified_more_than_once_then_its_values_are_agg private static ParseResult Parse(Option option, Directive directive, string commandLine) { RootCommand root = new() { option }; - CommandLineConfiguration config = new(root); + root.Directives.Add(directive); - return root.Parse(commandLine, config); + return root.Parse(commandLine); } } } \ No newline at end of file diff --git a/src/System.CommandLine.Tests/EnvironmentVariableDirectiveTests.cs b/src/System.CommandLine.Tests/EnvironmentVariableDirectiveTests.cs index 47ee79f8ce..218cd3a8e0 100644 --- a/src/System.CommandLine.Tests/EnvironmentVariableDirectiveTests.cs +++ b/src/System.CommandLine.Tests/EnvironmentVariableDirectiveTests.cs @@ -1,4 +1,5 @@ -using System.CommandLine.Help; +using System.Collections; +using System.CommandLine.Help; using System.CommandLine.Invocation; using FluentAssertions; using System.Linq; @@ -28,12 +29,12 @@ public async Task Sets_environment_variable_to_value() Environment.GetEnvironmentVariable(_testVariableName).Should().Be(value); }); - var config = new CommandLineConfiguration(rootCommand) + var config = new InvocationConfiguration { EnableDefaultExceptionHandler = false }; - await config.InvokeAsync($"[env:{_testVariableName}={value}]"); + await rootCommand.Parse($"[env:{_testVariableName}={value}]").InvokeAsync(config); asserted.Should().BeTrue(); } @@ -53,12 +54,12 @@ public async Task Sets_environment_variable_value_containing_equals_sign() Environment.GetEnvironmentVariable(_testVariableName).Should().Be(value); }); - var config = new CommandLineConfiguration(rootCommand) + var config = new InvocationConfiguration { EnableDefaultExceptionHandler = false }; - await config.InvokeAsync($"[env:{_testVariableName}={value}]" ); + await rootCommand.Parse($"[env:{_testVariableName}={value}]" ).InvokeAsync(config); asserted.Should().BeTrue(); } @@ -78,12 +79,12 @@ public async Task Ignores_environment_directive_without_equals_sign() Environment.GetEnvironmentVariable(variable).Should().BeNull(); }); - var config = new CommandLineConfiguration(rootCommand) + var config = new InvocationConfiguration { EnableDefaultExceptionHandler = false }; - await config.InvokeAsync( $"[env:{variable}]" ); + await rootCommand.Parse( $"[env:{variable}]" ).InvokeAsync(config); asserted.Should().BeTrue(); } @@ -97,23 +98,20 @@ public static async Task Ignores_environment_directive_with_empty_variable_name( { new EnvironmentVariablesDirective() }; + + IDictionary env = null; rootCommand.SetAction(_ => { asserted = true; - var env = Environment.GetEnvironmentVariables(); - env.Values.Cast().Should().NotContain(value); + env = Environment.GetEnvironmentVariables(); }); - var config = new CommandLineConfiguration(rootCommand) - { - EnableDefaultExceptionHandler = false - }; - - var result = config.Parse($"[env:={value}]"); + var result = rootCommand.Parse($"[env:={value}]"); await result.InvokeAsync(); asserted.Should().BeTrue(); + env.Values.Cast().Should().NotContain(value); } [Fact] @@ -125,10 +123,9 @@ public void It_does_not_prevent_help_from_being_invoked() var customHelpAction = new CustomHelpAction(); root.Options.OfType().Single().Action = customHelpAction; - var config = new CommandLineConfiguration(root); root.Directives.Add(new EnvironmentVariablesDirective()); - root.Parse($"[env:{_testVariableName}=1] -h", config).Invoke(); + root.Parse($"[env:{_testVariableName}=1] -h").Invoke(); customHelpAction.WasCalled.Should().BeTrue(); Environment.GetEnvironmentVariable(_testVariableName).Should().Be("1"); diff --git a/src/System.CommandLine.Tests/Help/CustomHelpAction.cs b/src/System.CommandLine.Tests/Help/CustomHelpAction.cs index cac66dc599..e24e22c729 100644 --- a/src/System.CommandLine.Tests/Help/CustomHelpAction.cs +++ b/src/System.CommandLine.Tests/Help/CustomHelpAction.cs @@ -1,4 +1,7 @@ -using System.CommandLine.Invocation; +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.CommandLine.Invocation; namespace System.CommandLine.Help { @@ -21,7 +24,7 @@ internal HelpBuilder Builder /// public override int Invoke(ParseResult parseResult) { - var output = parseResult.Configuration.Output; + var output = parseResult.InvocationConfiguration.Output; var helpContext = new HelpContext(Builder, parseResult.CommandResult.Command, diff --git a/src/System.CommandLine.Tests/Help/HelpBuilderTests.Customization.cs b/src/System.CommandLine.Tests/Help/HelpBuilderTests.Customization.cs index 22cced02a0..703f33d221 100644 --- a/src/System.CommandLine.Tests/Help/HelpBuilderTests.Customization.cs +++ b/src/System.CommandLine.Tests/Help/HelpBuilderTests.Customization.cs @@ -1,11 +1,12 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using FluentAssertions; +using Microsoft.VisualStudio.TestPlatform.Utilities; using System.Collections.Generic; using System.CommandLine.Help; using System.IO; using System.Linq; -using FluentAssertions; using Xunit; using static System.Environment; @@ -92,26 +93,22 @@ public void Option_can_customize_first_column_text_based_on_parse_result() ctx.Command.Equals(commandA) ? optionAFirstColumnText : optionBFirstColumnText); - command.Options.Add(new HelpOption() + command.Options.Add(new HelpOption { - Action = new CustomHelpAction() + Action = new CustomHelpAction { Builder = helpBuilder } }); - var console = new StringWriter(); - var config = new CommandLineConfiguration(command) - { - Output = console - }; - command.Parse("root a -h", config).Invoke(); - console.ToString().Should().Contain(optionAFirstColumnText); + var output = new StringWriter(); + + command.Parse("root a -h").Invoke(new() { Output = output }); + output.ToString().Should().Contain(optionAFirstColumnText); - console = new StringWriter(); - config.Output = console; - command.Parse("root b -h", config).Invoke(); - console.ToString().Should().Contain(optionBFirstColumnText); + output = new StringWriter(); + command.Parse("root b -h").Invoke(new() { Output = output }); + output.ToString().Should().Contain(optionBFirstColumnText); } [Fact] @@ -146,17 +143,14 @@ public void Option_can_customize_second_column_text_based_on_parse_result() } }); - var config = new CommandLineConfiguration(command) - { - Output = new StringWriter() - }; + var output = new StringWriter(); - config.Invoke("root a -h"); - config.Output.ToString().Should().Contain($"option {optionADescription}"); + command.Parse("root a -h").Invoke(new(){Output=output}); + output.ToString().Should().Contain($"option {optionADescription}"); - config.Output = new StringWriter(); - config.Invoke("root b -h"); - config.Output.ToString().Should().Contain($"option {optionBDescription}"); + output = new StringWriter(); + command.Parse("root b -h").Invoke(new(){Output=output}); + output.ToString().Should().Contain($"option {optionBDescription}"); } [Fact] @@ -275,11 +269,9 @@ public void Option_can_fallback_to_default_when_customizing(bool conditionA, boo } }); - CommandLineConfiguration config = new (command); - var console = new StringWriter(); - config.Output = console; - command.Parse("test -h", config).Invoke(); - console.ToString().Should().MatchRegex(expected); + var output = new StringWriter(); + command.Parse("test -h").Invoke(new() { Output = output }); + output.ToString().Should().MatchRegex(expected); } [Theory] @@ -312,9 +304,6 @@ public void Argument_can_fallback_to_default_when_customizing( secondColumnText: ctx => conditionB ? "custom 2nd" : HelpBuilder.Default.GetArgumentDescription(argument), defaultValue: ctx => conditionC ? "custom def" : HelpBuilder.Default.GetArgumentDefaultValue(argument)); - - CommandLineConfiguration config = new (command); - command.Options.Add(new HelpOption { Action = new CustomHelpAction @@ -323,9 +312,9 @@ public void Argument_can_fallback_to_default_when_customizing( } }); - config.Output = new StringWriter(); - command.Parse("test -h", config).Invoke(); - config.Output.ToString().Should().MatchRegex(expected); + var output = new StringWriter(); + command.Parse("test -h").Invoke(new() { Output = output }); + output.ToString().Should().MatchRegex(expected); } [Fact] @@ -351,17 +340,13 @@ public void Individual_symbols_can_be_customized() } }; - CommandLineConfiguration config = new(rootCommand) - { - Output = new StringWriter() - }; + var output = new StringWriter(); - ParseResult parseResult = rootCommand.Parse("-h", config); + ParseResult parseResult = rootCommand.Parse("-h"); - parseResult.Invoke(); + parseResult.Invoke(new() { Output = output }); - config.Output - .ToString() + output.ToString() .Should() .ContainAll("The custom command description", "The custom option description", @@ -374,16 +359,16 @@ public void Help_sections_can_be_replaced() CustomHelpAction helpAction = new(); helpAction.Builder.CustomizeLayout(CustomLayout); - CommandLineConfiguration config = new(new Command("name") { new HelpOption() { Action = helpAction} }) + var command = new Command("name") { - Output = new StringWriter() + new HelpOption { Action = helpAction } }; - ParseResult parseResult = config.Parse("-h"); + var output = new StringWriter(); - parseResult.Invoke(); + command.Parse("-h").Invoke(new() { Output = output }); - config.Output.ToString().Should().Be($"one{NewLine}{NewLine}two{NewLine}{NewLine}three{NewLine}{NewLine}"); + output.ToString().Should().Be($"one{NewLine}{NewLine}two{NewLine}{NewLine}three{NewLine}{NewLine}"); IEnumerable> CustomLayout(HelpContext _) { @@ -399,22 +384,18 @@ public void Help_sections_can_be_supplemented() CustomHelpAction helpAction = new(); helpAction.Builder.CustomizeLayout(CustomLayout); - CommandLineConfiguration config = new(new Command("hello") { new HelpOption() { Action = helpAction } }) + var command = new Command("hello") { - Output = new StringWriter(), + new HelpOption { Action = helpAction } }; var defaultHelp = GetDefaultHelp(new Command("hello")); - ParseResult parseResult = config.Parse("-h"); - - parseResult.Invoke(); - - var output = config.Output.ToString(); + var output = new StringWriter(); - var expected = $"first{NewLine}{NewLine}{defaultHelp}{NewLine}last{NewLine}{NewLine}"; + command.Parse("-h").Invoke(new() { Output = output }); - output.Should().Be(expected); + output.ToString().Should().Be($"first{NewLine}{NewLine}{defaultHelp}{NewLine}last{NewLine}{NewLine}"); IEnumerable> CustomLayout(HelpContext _) { @@ -446,7 +427,6 @@ public void Layout_can_be_composed_dynamically_based_on_context() Builder = helpBuilder }; - var config = new CommandLineConfiguration(command); helpBuilder.CustomizeLayout(c => c.Command == commandWithTypicalHelp ? HelpBuilder.Default.GetLayout() @@ -454,12 +434,10 @@ public void Layout_can_be_composed_dynamically_based_on_context() .Concat(HelpBuilder.Default.GetLayout())); var typicalOutput = new StringWriter(); - config.Output = typicalOutput; - command.Parse("typical -h", config).Invoke(); + command.Parse("typical -h").Invoke(new() { Output = typicalOutput }); var customOutput = new StringWriter(); - config.Output = customOutput; - command.Parse("custom -h", config).Invoke(); + command.Parse("custom -h").Invoke(new() { Output = customOutput }); typicalOutput.ToString().Should().Be(GetDefaultHelp(commandWithTypicalHelp, false)); customOutput.ToString().Should().Be($"Custom layout!{NewLine}{NewLine}{GetDefaultHelp(commandWithCustomHelp, false)}"); @@ -484,15 +462,10 @@ public void Help_default_sections_can_be_wrapped() } }; - CommandLineConfiguration config = new(command) - { - Output = new StringWriter() - }; + var output = new StringWriter(); + command.Parse("test -h").Invoke(new() { Output = output }); - config.Invoke("test -h"); - - string result = config.Output.ToString(); - result.Should().Be( + output.ToString().Should().Be( $"Description:{NewLine}{NewLine}" + $"Usage:{NewLine} test [options]{NewLine}{NewLine}" + $"Options:{NewLine}" + @@ -510,16 +483,16 @@ public void Help_customized_sections_can_be_wrapped() helpAction.Builder = new HelpBuilder(10); helpAction.Builder.CustomizeLayout(CustomLayout); - CommandLineConfiguration config = new(new Command("name") { new HelpOption() { Action = helpAction } }) + var command = new Command("name") { - Output = new StringWriter() + new HelpOption { Action = helpAction } }; - ParseResult parseResult = config.Parse("-h"); + var output = new StringWriter(); - parseResult.Invoke(); + command.Parse("-h").Invoke(new() { Output = output }); - string result = config.Output.ToString(); + string result = output.ToString(); result.Should().Be($" 123 123{NewLine} 456 456{NewLine} 78 789{NewLine} 0{NewLine}{NewLine}"); IEnumerable> CustomLayout(HelpContext _) @@ -544,12 +517,12 @@ private string GetDefaultHelp(Command command, bool trimOneNewline = true) command.Options.Add(defaultHelp); } - CommandLineConfiguration config = new(command) + InvocationConfiguration config = new() { Output = new StringWriter() }; - config.Invoke("-h"); + command.Parse("-h").Invoke(config); var output = config.Output.ToString(); diff --git a/src/System.CommandLine.Tests/Help/HelpBuilderTests.cs b/src/System.CommandLine.Tests/Help/HelpBuilderTests.cs index 4a735a4aad..1b41d2b9cb 100644 --- a/src/System.CommandLine.Tests/Help/HelpBuilderTests.cs +++ b/src/System.CommandLine.Tests/Help/HelpBuilderTests.cs @@ -1235,9 +1235,9 @@ public void Required_options_are_indicated_when_argument_is_named() [Fact] public void Help_option_is_shown_in_help() { - var configuration = new CommandLineConfiguration(new RootCommand()); + var rootCommand = new RootCommand(); - _helpBuilder.Write(configuration.RootCommand, _console); + _helpBuilder.Write(rootCommand, _console); var help = _console.ToString(); diff --git a/src/System.CommandLine.Tests/HelpOptionTests.cs b/src/System.CommandLine.Tests/HelpOptionTests.cs index 401656ce0c..5a6261a48d 100644 --- a/src/System.CommandLine.Tests/HelpOptionTests.cs +++ b/src/System.CommandLine.Tests/HelpOptionTests.cs @@ -2,10 +2,12 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using FluentAssertions; +using Microsoft.VisualStudio.TestPlatform.Utilities; using System.CommandLine.Help; using System.CommandLine.Invocation; using System.CommandLine.Tests.Utility; using System.IO; +using System.Threading; using System.Threading.Tasks; using Xunit; @@ -24,18 +26,14 @@ public async Task Help_option_writes_help_for_the_specified_command() } }; - CommandLineConfiguration config = new(command) - { - Output = new StringWriter() - }; - - var result = command.Parse("command subcommand --help", config); + var result = command.Parse("command subcommand --help"); - await result.InvokeAsync(); + var output = new StringWriter(); + await result.InvokeAsync(new() { Output = output }, CancellationToken.None); - config.Output.ToString().Should().Contain($"{RootCommand.ExecutableName} command subcommand [options]"); + output.ToString().Should().Contain($"{RootCommand.ExecutableName} command subcommand [options]"); } - + [Fact] public async Task Help_option_interrupts_execution_of_the_specified_command() { @@ -45,12 +43,9 @@ public async Task Help_option_interrupts_execution_of_the_specified_command() subcommand.SetAction(_ => wasCalled = true); command.Subcommands.Add(subcommand); - CommandLineConfiguration config = new(command) - { - Output = new StringWriter() - }; + var output = new StringWriter(); - await command.Parse("command subcommand --help", config).InvokeAsync(); + await command.Parse("command subcommand --help").InvokeAsync(new() { Output = output }, CancellationToken.None); wasCalled.Should().BeFalse(); } @@ -62,17 +57,16 @@ public async Task Help_option_interrupts_execution_of_the_specified_command() [InlineData("/?")] public async Task Help_option_accepts_default_values(string value) { - CommandLineConfiguration config = new(new Command("command") { new HelpOption() }) + var command = new Command("command") { - Output = new StringWriter() + new HelpOption() }; - StringWriter console = new(); - config.Output = console; + StringWriter output = new(); - await config.InvokeAsync($"command {value}"); + await command.Parse($"command {value}").InvokeAsync(new() { Output = output }, CancellationToken.None); - console.ToString().Should().ShowHelp(); + output.ToString().Should().ShowHelp(); } [Fact] @@ -80,15 +74,12 @@ public async Task Help_option_does_not_display_when_option_defined_with_same_ali { var command = new Command("command"); command.Options.Add(new Option("-h")); + + var output = new StringWriter(); - CommandLineConfiguration config = new(command) - { - Output = new StringWriter() - }; - - await command.Parse("command -h", config).InvokeAsync(); + await command.Parse("command -h").InvokeAsync(new() { Output = output }, CancellationToken.None); - config.Output.ToString().Should().NotShowHelp(); + output.ToString().Should().NotShowHelp(); } [Fact] @@ -156,14 +147,11 @@ public async Task HelpOption_with_custom_aliases_uses_aliases(string helpAlias) { new HelpOption("/lost", "--confused") }; - CommandLineConfiguration config = new(command) - { - Output = new StringWriter() - }; + var output = new StringWriter(); - await config.InvokeAsync(helpAlias); + await command.Parse(helpAlias).InvokeAsync(new() { Output = output }, CancellationToken.None); - config.Output.ToString().Should().ShowHelp(); + output.ToString().Should().ShowHelp(); } [Theory] @@ -178,14 +166,11 @@ public async Task Help_option_with_custom_aliases_does_not_recognize_default_ali command.Options.Clear(); command.Options.Add(new HelpOption("--confused")); - CommandLineConfiguration config = new(command) - { - Output = new StringWriter(), - }; + var output = new StringWriter(); - await config.InvokeAsync(helpAlias); + await command.Parse(helpAlias).InvokeAsync(new() { Output = output }, CancellationToken.None); - config.Output.ToString().Should().NotContain(helpAlias); + output.ToString().Should().NotContain(helpAlias); } [Theory] @@ -208,13 +193,9 @@ public void The_users_can_provide_usage_examples(bool subcommand) }); TextWriter output = new StringWriter(); - CommandLineConfiguration config = new(rootCommand) - { - Output = output - }; - - var result = subcommand ? config.Parse("subcommand -h") : config.Parse("-h"); - + + var result = subcommand ? rootCommand.Parse("subcommand -h") : rootCommand.Parse("-h"); + result.InvocationConfiguration.Output = output; result.Invoke(); if (subcommand) @@ -242,13 +223,9 @@ public void The_users_can_print_help_output_of_a_subcommand() }; rootCommand.Subcommands.Add(subcommand); - TextWriter output = new StringWriter(); - CommandLineConfiguration config = new(subcommand) - { - Output = output - }; + var output = new StringWriter(); - subcommand.Parse("--help", config).Invoke(); + subcommand.Parse("--help").Invoke(new() { Output = output }); output.ToString().Should().Contain(SubcommandDescription); output.ToString().Should().NotContain(RootDescription); @@ -271,7 +248,7 @@ public override int Invoke(ParseResult parseResult) if (parseResult.CommandResult.Command.Name == "subcommand") { - var output = parseResult.Configuration.Output; + var output = parseResult.InvocationConfiguration.Output; output.WriteLine(CustomUsageText); } diff --git a/src/System.CommandLine.Tests/Invocation/CancelOnProcessTerminationTests.cs b/src/System.CommandLine.Tests/Invocation/CancelOnProcessTerminationTests.cs index 656bba1424..3197ad3198 100644 --- a/src/System.CommandLine.Tests/Invocation/CancelOnProcessTerminationTests.cs +++ b/src/System.CommandLine.Tests/Invocation/CancelOnProcessTerminationTests.cs @@ -56,10 +56,12 @@ private static Task Program(string[] args) }; command.Action = new CustomCliAction(); - return new CommandLineConfiguration(command) + var config = new InvocationConfiguration { ProcessTerminationTimeout = TimeSpan.FromSeconds(2) - }.InvokeAsync(args); + }; + + return command.Parse(args).InvokeAsync(config); } private sealed class CustomCliAction : AsynchronousCommandLineAction diff --git a/src/System.CommandLine.Tests/Invocation/InvocationTests.cs b/src/System.CommandLine.Tests/Invocation/InvocationTests.cs index b9cc930bef..0de312a178 100644 --- a/src/System.CommandLine.Tests/Invocation/InvocationTests.cs +++ b/src/System.CommandLine.Tests/Invocation/InvocationTests.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.CommandLine.Help; -using System.CommandLine.Invocation; using System.CommandLine.Parsing; using System.IO; using System.Threading; @@ -16,7 +15,7 @@ namespace System.CommandLine.Tests.Invocation public class InvocationTests { [Fact] - public async Task Command_InvokeAsync_enables_help_by_default() + public async Task Command_InvokeAsync_displays_help_when_HelpOption_is_present() { var command = new Command("the-command") { @@ -26,12 +25,8 @@ public async Task Command_InvokeAsync_enables_help_by_default() command.Description = theHelpText; StringWriter output = new(); - CommandLineConfiguration config = new(command) - { - Output = output - }; - - await command.Parse("-h", config).InvokeAsync(); + + await command.Parse("-h").InvokeAsync(new() { Output = output }, CancellationToken.None); output.ToString() .Should() @@ -39,7 +34,7 @@ public async Task Command_InvokeAsync_enables_help_by_default() } [Fact] - public void Command_Invoke_enables_help_by_default() + public void Command_Invoke_displays_help_when_HelpOption_is_present() { var command = new Command("the-command") { @@ -49,12 +44,8 @@ public void Command_Invoke_enables_help_by_default() command.Description = theHelpText; StringWriter output = new (); - CommandLineConfiguration config = new(command) - { - Output = output - }; - - command.Parse("-h", config).Invoke(); + + command.Parse("-h").Invoke(new() { Output = output }); output.ToString() .Should() @@ -313,7 +304,7 @@ public void Directive_action_takes_precedence_over_option_action() directive }; - ParseResult parseResult = command.Parse("[directive] cmd -x", new CommandLineConfiguration(command)); + ParseResult parseResult = command.Parse("[directive] cmd -x"); using var _ = new AssertionScope(); diff --git a/src/System.CommandLine.Tests/Invocation/TypoCorrectionTests.cs b/src/System.CommandLine.Tests/Invocation/TypoCorrectionTests.cs index 9a9322dc35..1bac77d500 100644 --- a/src/System.CommandLine.Tests/Invocation/TypoCorrectionTests.cs +++ b/src/System.CommandLine.Tests/Invocation/TypoCorrectionTests.cs @@ -1,9 +1,12 @@ -using System.CommandLine.Help; +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using FluentAssertions; +using Microsoft.VisualStudio.TestPlatform.Utilities; using System.CommandLine.Invocation; using System.IO; -using System.Linq; +using System.Threading; using System.Threading.Tasks; -using FluentAssertions; using Xunit; using static System.Environment; @@ -14,21 +17,18 @@ public class TypoCorrectionTests [Fact] public async Task When_option_is_mistyped_it_is_suggested() { - RootCommand rootCommand = new () + RootCommand rootCommand = new() { new Option("info") }; - CommandLineConfiguration config = new(rootCommand) - { - Output = new StringWriter() - }; + var output = new StringWriter(); - var result = rootCommand.Parse("niof", config); + var result = rootCommand.Parse("niof"); - await result.InvokeAsync(); + await result.InvokeAsync(new() { Output = output }, CancellationToken.None); - config.Output.ToString().Should().Contain($"'niof' was not matched. Did you mean one of the following?{NewLine}info"); + output.ToString().Should().Contain($"'niof' was not matched. Did you mean one of the following?{NewLine}info"); } [Fact] @@ -39,39 +39,18 @@ public async Task Typo_corrections_can_be_disabled() new Option("info") }; - CommandLineConfiguration config = new(rootCommand) - { - Output = new StringWriter() - }; + var output = new StringWriter(); - var result = rootCommand.Parse("niof", config); + var result = rootCommand.Parse("niof"); if (result.Action is ParseErrorAction parseError) { parseError.ShowTypoCorrections = false; } - await result.InvokeAsync(); + await result.InvokeAsync(new() { Output = output }, CancellationToken.None); - config.Output.ToString().Should().NotContain("Did you mean"); - } - - [Fact] - public async Task When_there_are_no_matches_then_nothing_is_suggested() - { - var option = new Option("info"); - RootCommand rootCommand = new() { option }; - - CommandLineConfiguration configuration = new(rootCommand) - { - Output = new StringWriter() - }; - - var result = rootCommand.Parse("zzzzzzz", configuration); - - await result.InvokeAsync(); - - configuration.Output.ToString().Should().NotContain("was not matched"); + output.ToString().Should().NotContain("Did you mean"); } [Fact] @@ -80,16 +59,13 @@ public async Task When_command_is_mistyped_it_is_suggested() var command = new Command("restore"); RootCommand rootCommand = new() { command }; - CommandLineConfiguration configuration = new(rootCommand) - { - Output = new StringWriter() - }; + var output = new StringWriter(); - var result = rootCommand.Parse("sertor", configuration); + var result = rootCommand.Parse("sertor"); - await result.InvokeAsync(); + await result.InvokeAsync(new() { Output = output }, CancellationToken.None); - configuration.Output.ToString().Should().Contain($"'sertor' was not matched. Did you mean one of the following?{NewLine}restore"); + output.ToString().Should().Contain($"'sertor' was not matched. Did you mean one of the following?{NewLine}restore"); } [Fact] @@ -99,23 +75,35 @@ public async Task When_there_are_multiple_matches_it_picks_the_best_matches() var seenCommand = new Command("seen"); var aOption = new Option("a"); var beenOption = new Option("been"); - RootCommand rootCommand = new () + RootCommand rootCommand = new() { fromCommand, seenCommand, aOption, beenOption }; - CommandLineConfiguration configuration = new(rootCommand) - { - Output = new StringWriter() - }; - var result = rootCommand.Parse("een", configuration); + var output = new StringWriter(); - await result.InvokeAsync(); + var result = rootCommand.Parse("een"); - configuration.Output.ToString().Should().Contain($"'een' was not matched. Did you mean one of the following?{NewLine}seen{NewLine}been"); + await result.InvokeAsync(new() { Output = output }, CancellationToken.None); + + output.ToString().Should().Contain($"'een' was not matched. Did you mean one of the following?{NewLine}seen{NewLine}been"); + } + + [Fact] + public async Task When_there_are_no_matches_then_nothing_is_suggested() + { + var option = new Option("info"); + RootCommand rootCommand = new() { option }; + + var output = new StringWriter(); + var result = rootCommand.Parse("zzzzzzz"); + + await result.InvokeAsync(new() { Output = output }, CancellationToken.None); + + output.ToString().Should().NotContain("was not matched"); } [Fact] @@ -131,16 +119,13 @@ public async Task Hidden_commands_are_not_suggested() beenCommand }; - CommandLineConfiguration configuration = new(rootCommand) - { - Output = new StringWriter() - }; + var output = new StringWriter(); - var result = rootCommand.Parse("een", configuration); + var result = rootCommand.Parse("een"); - await result.InvokeAsync(); + await result.InvokeAsync(new() { Output = output }, CancellationToken.None); - configuration.Output.ToString().Should().Contain($"'een' was not matched. Did you mean one of the following?{NewLine}been"); + output.ToString().Should().Contain($"'een' was not matched. Did you mean one of the following?{NewLine}been"); } [Fact] @@ -154,20 +139,17 @@ public async Task Arguments_are_not_suggested() command }; - CommandLineConfiguration configuration = new(rootCommand) - { - Output = new StringWriter() - }; + var output = new StringWriter(); - var result = rootCommand.Parse("een", configuration); + var result = rootCommand.Parse("een"); var parseErrorAction = (ParseErrorAction)result.Action; parseErrorAction.ShowHelp = false; parseErrorAction.ShowTypoCorrections = true; - - await result.InvokeAsync(); - configuration.Output.ToString().Should().NotContain("the-argument"); + await result.InvokeAsync(new() { Output = output }, CancellationToken.None); + + output.ToString().Should().NotContain("the-argument"); } [Fact] @@ -182,16 +164,13 @@ public async Task Hidden_options_are_not_suggested() seenOption, beenOption }; - CommandLineConfiguration config = new(rootCommand) - { - Output = new StringWriter() - }; + var output = new StringWriter(); - var result = rootCommand.Parse("een", config); + var result = rootCommand.Parse("een"); - await result.InvokeAsync(); + await result.InvokeAsync(new() { Output = output }, CancellationToken.None); - config.Output.ToString().Should().Contain($"'een' was not matched. Did you mean one of the following?{NewLine}been"); + output.ToString().Should().Contain($"'een' was not matched. Did you mean one of the following?{NewLine}been"); } [Fact] @@ -202,15 +181,12 @@ public async Task Suggestions_favor_matches_with_prefix() new Option("/call", "-call", "--call"), new Option("/email", "-email", "--email") }; - CommandLineConfiguration config = new(rootCommand) - { - Output = new StringWriter() - }; - var result = rootCommand.Parse("-all", config); + var output = new StringWriter(); + var result = rootCommand.Parse("-all"); - await result.InvokeAsync(); + await result.InvokeAsync(new() { Output = output }, CancellationToken.None); - config.Output.ToString().Should().Contain($"'-all' was not matched. Did you mean one of the following?{NewLine}-call"); + output.ToString().Should().Contain($"'-all' was not matched. Did you mean one of the following?{NewLine}-call"); } } } \ No newline at end of file diff --git a/src/System.CommandLine.Tests/InvocationConfigurationTests.cs b/src/System.CommandLine.Tests/InvocationConfigurationTests.cs new file mode 100644 index 0000000000..a92626c709 --- /dev/null +++ b/src/System.CommandLine.Tests/InvocationConfigurationTests.cs @@ -0,0 +1,40 @@ +using FluentAssertions; +using Xunit; + +namespace System.CommandLine.Tests; + +public class InvocationConfigurationTests +{ + [Fact] + public void It_can_be_subclassed_to_provide_additional_context() + { + var command = new RootCommand(); + var commandWasInvoked = false; + command.SetAction(parseResult => + { + var appConfig = (CustomAppConfiguration)parseResult.InvocationConfiguration; + + // access custom config + + commandWasInvoked = true; + + return 0; + }); + + var config = new CustomAppConfiguration(); + + command.Parse("").Invoke(config); + + commandWasInvoked.Should().BeTrue(); + } + + public class CustomAppConfiguration : InvocationConfiguration + { + public CustomAppConfiguration() + { + EnableDefaultExceptionHandler = false; + } + + public IServiceProvider ServiceProvider { get; } + } +} \ No newline at end of file diff --git a/src/System.CommandLine.Tests/ParseDirectiveTests.cs b/src/System.CommandLine.Tests/ParseDirectiveTests.cs index 8ed409e34d..8490e8d579 100644 --- a/src/System.CommandLine.Tests/ParseDirectiveTests.cs +++ b/src/System.CommandLine.Tests/ParseDirectiveTests.cs @@ -3,6 +3,7 @@ using FluentAssertions; using System.IO; +using System.Threading; using System.Threading.Tasks; using Xunit; using Xunit.Abstractions; @@ -30,73 +31,55 @@ public async Task Diagram_directive_writes_parse_diagram(bool treatUnmatchedToke subcommand.Options.Add(option); subcommand.TreatUnmatchedTokensAsErrors = treatUnmatchedTokensAsErrors; - CommandLineConfiguration config = new(rootCommand) - { - Output = new StringWriter() - }; + var output = new StringWriter(); - var result = rootCommand.Parse("[diagram] subcommand -c 34 --nonexistent wat", config); + var result = rootCommand.Parse("[diagram] subcommand -c 34 --nonexistent wat"); - output.WriteLine(result.Diagram()); - - await result.InvokeAsync(); + await result.InvokeAsync(new() { Output = output }, CancellationToken.None); string expected = treatUnmatchedTokensAsErrors ? $"[ {RootCommand.ExecutableName} ![ subcommand [ -c <34> ] ] ] ???--> --nonexistent wat" + Environment.NewLine : $"[ {RootCommand.ExecutableName} [ subcommand [ -c <34> ] ] ] ???--> --nonexistent wat" + Environment.NewLine; - config.Output - .ToString() + output.ToString() .Should() .Be(expected); } [Fact] - public async Task When_diagram_directive_is_used_the_help_is_not_displayed() + public async Task When_diagram_directive_is_used_then_help_is_not_displayed() { RootCommand rootCommand = new() { new DiagramDirective() }; - CommandLineConfiguration config = new(rootCommand) - { - Output = new StringWriter(), - }; - - var result = rootCommand.Parse("[diagram] --help", config); + var output = new StringWriter(); - output.WriteLine(result.Diagram()); + var result = rootCommand.Parse("[diagram] --help"); - await result.InvokeAsync(); + await result.InvokeAsync(new() { Output = output }, CancellationToken.None); - config.Output - .ToString() - .Should() - .Be($"[ {RootCommand.ExecutableName} [ --help ] ]" + Environment.NewLine); + output.ToString() + .Should() + .Be($"[ {RootCommand.ExecutableName} [ --help ] ]" + Environment.NewLine); } [Fact] - public async Task When_diagram_directive_is_used_the_version_is_not_displayed() + public async Task When_diagram_directive_is_used_then_version_is_not_displayed() { RootCommand rootCommand = new() { new DiagramDirective() }; - CommandLineConfiguration config = new(rootCommand) - { - Output = new StringWriter() - }; - - var result = rootCommand.Parse("[diagram] --version", config); + var output = new StringWriter(); - output.WriteLine(result.Diagram()); + var result = rootCommand.Parse("[diagram] --version"); - await result.InvokeAsync(); + await result.InvokeAsync(new() { Output = output }, CancellationToken.None); - config.Output - .ToString() + output.ToString() .Should() .Be($"[ {RootCommand.ExecutableName} [ --version ] ]" + Environment.NewLine); } @@ -110,12 +93,9 @@ public async Task When_there_are_no_errors_then_diagram_directive_sets_exit_code new DiagramDirective() }; - CommandLineConfiguration config = new(command) - { - Output = new StringWriter(), - }; + var output = new StringWriter(); - var exitCode = await command.Parse("[diagram] -x 123", config).InvokeAsync(); + var exitCode = await command.Parse("[diagram] -x 123").InvokeAsync(new() { Output = output }, CancellationToken.None); exitCode.Should().Be(0); } @@ -129,12 +109,9 @@ public async Task When_there_are_errors_then_diagram_directive_sets_exit_code_1( new DiagramDirective() }; - CommandLineConfiguration config = new(command) - { - Output = new StringWriter(), - }; + var output = new StringWriter(); - var exitCode = await command.Parse("[diagram] -x not-an-int", config).InvokeAsync(); + var exitCode = await command.Parse("[diagram] -x not-an-int").InvokeAsync(new() { Output = output }, CancellationToken.None); exitCode.Should().Be(1); } @@ -151,12 +128,10 @@ public async Task When_there_are_errors_then_diagram_directive_sets_exit_code_to } }; - CommandLineConfiguration config = new(command) - { - Output = new StringWriter() - }; + var output = new StringWriter(); - int exitCode = await config.InvokeAsync("[diagram] -x not-an-int"); + int exitCode = await command.Parse("[diagram] -x not-an-int") + .InvokeAsync(new() { Output = output }, CancellationToken.None); exitCode.Should().Be(42); } diff --git a/src/System.CommandLine.Tests/ParseErrorReportingTests.cs b/src/System.CommandLine.Tests/ParseErrorReportingTests.cs index 48d3d0e2d4..d66f37d816 100644 --- a/src/System.CommandLine.Tests/ParseErrorReportingTests.cs +++ b/src/System.CommandLine.Tests/ParseErrorReportingTests.cs @@ -1,14 +1,15 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using FluentAssertions; using System.CommandLine.Help; using System.CommandLine.Invocation; -using System.IO; -using FluentAssertions; -using Xunit; using System.CommandLine.Tests.Utility; +using System.IO; using System.Linq; +using System.Threading; using System.Threading.Tasks; +using Xunit; namespace System.CommandLine.Tests; @@ -24,14 +25,11 @@ public void Parse_error_reporting_reports_error_when_help_is_used_and_required_s }; var output = new StringWriter(); - var parseResult = root.Parse("", new CommandLineConfiguration(root) - { - Output = output, - }); + var parseResult = root.Parse(""); parseResult.Errors.Should().NotBeEmpty(); - var result = parseResult.Invoke(); + var result = parseResult.Invoke(new() { Output = output }); result.Should().Be(1); output.ToString().Should().ShowHelp(); @@ -45,23 +43,18 @@ public void Help_display_can_be_disabled() new Option("--verbose") }; - CommandLineConfiguration config = new(rootCommand) - { - Output = new StringWriter() - }; + var output = new StringWriter(); - var result = rootCommand.Parse("oops", config); + var result = rootCommand.Parse("oops"); if (result.Action is ParseErrorAction parseError) { parseError.ShowHelp = false; } - result.Invoke(); + result.Invoke(new() { Output = output }); - var output = config.Output.ToString(); - - output.Should().NotShowHelp(); + output.ToString().Should().NotShowHelp(); } [Theory] // https://github.com/dotnet/command-line-api/issues/2226 @@ -104,9 +97,7 @@ public async Task When_there_are_parse_errors_then_customized_help_action_on_anc rootHelpWasCalled = true; }); - var config = new CommandLineConfiguration(rootCommand); - - await config.Parse("child grandchild oops").InvokeAsync(); + await rootCommand.Parse("child grandchild oops").InvokeAsync(); rootHelpWasCalled.Should().BeTrue(); } @@ -117,12 +108,8 @@ public void When_no_help_option_is_present_then_help_is_not_shown_for_parse_erro RootCommand rootCommand = new(); rootCommand.Options.Clear(); var output = new StringWriter(); - CommandLineConfiguration config = new(rootCommand) - { - Output = output - }; - - config.Parse("oops").Invoke(); + + rootCommand.Parse("oops").Invoke(new() { Output = output } ); output.ToString().Should().NotShowHelp(); } diff --git a/src/System.CommandLine.Tests/ParserTests.SetupErrors.cs b/src/System.CommandLine.Tests/ParserTests.SetupErrors.cs index d5e2e52202..915a1d4290 100644 --- a/src/System.CommandLine.Tests/ParserTests.SetupErrors.cs +++ b/src/System.CommandLine.Tests/ParserTests.SetupErrors.cs @@ -60,9 +60,8 @@ public void When_command_names_collide_with_directive_names_it_does_not_throw_on var rootCommand = new RootCommand(); rootCommand.Add(new Command("one")); rootCommand.Add(new Directive("one")); - var cliConfiguration = new CommandLineConfiguration(rootCommand); - rootCommand.Invoking(c => c.Parse("", cliConfiguration)).Should().NotThrow(); + rootCommand.Invoking(c => c.Parse("")).Should().NotThrow(); } } } \ No newline at end of file diff --git a/src/System.CommandLine.Tests/ParserTests.cs b/src/System.CommandLine.Tests/ParserTests.cs index 410c67bc82..09a3d8a397 100644 --- a/src/System.CommandLine.Tests/ParserTests.cs +++ b/src/System.CommandLine.Tests/ParserTests.cs @@ -137,7 +137,7 @@ public void Options_short_forms_do_not_get_unbundled_if_unbundling_is_turned_off } }; - CommandLineConfiguration configuration = new (rootCommand) + ParserConfiguration configuration = new() { EnablePosixBundling = false }; diff --git a/src/System.CommandLine.Tests/ResponseFileTests.cs b/src/System.CommandLine.Tests/ResponseFileTests.cs index 5404fdee22..1528009e3b 100644 --- a/src/System.CommandLine.Tests/ResponseFileTests.cs +++ b/src/System.CommandLine.Tests/ResponseFileTests.cs @@ -287,9 +287,8 @@ public void When_response_file_parse_as_space_separated_returns_expected_values( optionOne, optionTwo }; - CommandLineConfiguration config = new (rootCommand); - var result = rootCommand.Parse($"@{responseFile}", config); + var result = rootCommand.Parse($"@{responseFile}"); result.GetValue(optionOne).Should().Be("first value"); result.GetValue(optionTwo).Should().Be(123); @@ -302,7 +301,7 @@ public void When_response_file_processing_is_disabled_then_it_returns_response_f { new Argument>("arg") }; - CommandLineConfiguration configuration = new(command) + ParserConfiguration configuration = new() { ResponseFileTokenReplacer = null }; diff --git a/src/System.CommandLine.Tests/SuggestDirectiveTests.cs b/src/System.CommandLine.Tests/SuggestDirectiveTests.cs index ec1bb7209e..ccb482315f 100644 --- a/src/System.CommandLine.Tests/SuggestDirectiveTests.cs +++ b/src/System.CommandLine.Tests/SuggestDirectiveTests.cs @@ -1,11 +1,13 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using FluentAssertions; +using Microsoft.VisualStudio.TestPlatform.Utilities; using System.CommandLine.Completions; using System.CommandLine.Help; using System.IO; +using System.Threading; using System.Threading.Tasks; -using FluentAssertions; using Xunit; using static System.Environment; @@ -42,16 +44,13 @@ public async Task It_writes_suggestions_for_option_arguments_when_under_subcomma _eatCommand, new SuggestDirective() }; - CommandLineConfiguration config = new(rootCommand) - { - Output = new StringWriter() - }; + var output= new StringWriter(); - var result = rootCommand.Parse("[suggest:13] \"eat --fruit\"", config); + var result = rootCommand.Parse("[suggest:13] \"eat --fruit\""); - await result.InvokeAsync(); + await result.InvokeAsync(new() { Output = output }, CancellationToken.None); - config.Output + output .ToString() .Should() .Be($"apple{NewLine}banana{NewLine}cherry{NewLine}"); @@ -60,24 +59,21 @@ public async Task It_writes_suggestions_for_option_arguments_when_under_subcomma [Fact] public async Task It_writes_suggestions_for_option_arguments_when_under_root_command() { - RootCommand rootCommand = new () + RootCommand rootCommand = new() { _fruitOption, _vegetableOption }; - CommandLineConfiguration config = new (rootCommand) - { - Output = new StringWriter() - }; + var output = new StringWriter(); - var result = rootCommand.Parse($"[suggest:8] \"--fruit\"", config); + var result = rootCommand.Parse($"[suggest:8] \"--fruit\""); - await result.InvokeAsync(); + await result.InvokeAsync(new() { Output = output }, CancellationToken.None); - config.Output - .ToString() - .Should() - .Be($"apple{NewLine}banana{NewLine}cherry{NewLine}"); + output + .ToString() + .Should() + .Be($"apple{NewLine}banana{NewLine}cherry{NewLine}"); } [Theory] @@ -86,21 +82,18 @@ public async Task It_writes_suggestions_for_option_arguments_when_under_root_com public async Task It_writes_suggestions_for_option_aliases_under_subcommand(string commandLine, string[] expectedCompletions) { RootCommand rootCommand = new() { _eatCommand }; - CommandLineConfiguration config = new(rootCommand) - { - Output = new StringWriter() - }; + + var output = new StringWriter(); - var result = rootCommand.Parse(commandLine, config); + var result = rootCommand.Parse(commandLine); - await result.InvokeAsync(); + await result.InvokeAsync(new() { Output = output }, CancellationToken.None); string expected = string.Join(NewLine, expectedCompletions) + NewLine; - config.Output - .ToString() - .Should() - .Be(expected); + output.ToString() + .Should() + .Be(expected); } [Theory] @@ -115,18 +108,14 @@ public async Task It_writes_suggestions_for_option_aliases_under_root_command(st _vegetableOption, _fruitOption }; - CommandLineConfiguration config = new(rootCommand) - { - Output = new StringWriter() - }; + var output = new StringWriter(); - var result = rootCommand.Parse(input, config); - await result.InvokeAsync(); + await rootCommand.Parse(input).InvokeAsync(new() { Output = output }, CancellationToken.None); - config.Output - .ToString() - .Should() - .Be($"--fruit{NewLine}--help{NewLine}--vegetable{NewLine}--version{NewLine}-?{NewLine}-h{NewLine}/?{NewLine}/h{NewLine}"); + output + .ToString() + .Should() + .Be($"--fruit{NewLine}--help{NewLine}--vegetable{NewLine}--version{NewLine}-?{NewLine}-h{NewLine}/?{NewLine}/h{NewLine}"); } [Fact] @@ -136,18 +125,15 @@ public async Task It_writes_suggestions_for_subcommand_aliases_under_root_comman { _eatCommand }; - CommandLineConfiguration config = new(rootCommand) - { - Output = new StringWriter() - }; + var output = new StringWriter(); - var result = rootCommand.Parse("[suggest]", config); - await result.InvokeAsync(); + var result = rootCommand.Parse("[suggest]"); + await result.InvokeAsync(new() { Output = output }, CancellationToken.None); - config.Output - .ToString() - .Should() - .Be($"--help{NewLine}--version{NewLine}-?{NewLine}-h{NewLine}/?{NewLine}/h{NewLine}eat{NewLine}"); + output + .ToString() + .Should() + .Be($"--help{NewLine}--version{NewLine}-?{NewLine}-h{NewLine}/?{NewLine}/h{NewLine}eat{NewLine}"); } [Fact] @@ -158,19 +144,17 @@ public async Task It_writes_suggestions_for_partial_option_aliases_under_root_co _fruitOption, _vegetableOption }; - CommandLineConfiguration config = new (rootCommand) - { - Output = new StringWriter(), - }; + + var output = new StringWriter(); - var result = rootCommand.Parse("[suggest:1] \"f\"", config); + var result = rootCommand.Parse("[suggest:1] \"f\""); - await result.InvokeAsync(); + await result.InvokeAsync(new() { Output = output }, CancellationToken.None); - config.Output - .ToString() - .Should() - .Be($"--fruit{NewLine}"); + output + .ToString() + .Should() + .Be($"--fruit{NewLine}"); } [Fact] @@ -181,19 +165,16 @@ public async Task It_writes_suggestions_for_partial_subcommand_aliases_under_roo _eatCommand, new Command("wash-dishes") }; - CommandLineConfiguration config = new (rootCommand) - { - Output = new StringWriter() - }; + var output = new StringWriter(); - var result = rootCommand.Parse("[suggest:1] \"d\"", config); + var result = rootCommand.Parse("[suggest:1] \"d\""); - await result.InvokeAsync(); + await result.InvokeAsync(new() { Output = output }, CancellationToken.None); - config.Output - .ToString() - .Should() - .Be($"wash-dishes{NewLine}"); + output + .ToString() + .Should() + .Be($"wash-dishes{NewLine}"); } [Fact] @@ -204,19 +185,16 @@ public async Task It_writes_suggestions_for_partial_option_and_subcommand_aliase _eatCommand, new Command("wash-dishes"), }; - CommandLineConfiguration config = new (rootCommand) - { - Output = new StringWriter() - }; + var output = new StringWriter(); - var result = rootCommand.Parse("[suggest:5] \"--ver\"", config); + var result = rootCommand.Parse("[suggest:5] \"--ver\""); - await result.InvokeAsync(); + await result.InvokeAsync(new() { Output = output }, CancellationToken.None); - config.Output - .ToString() - .Should() - .Be($"--version{NewLine}"); + output + .ToString() + .Should() + .Be($"--version{NewLine}"); } [Fact] @@ -229,17 +207,14 @@ public async Task It_writes_suggestions_for_partial_option_and_subcommand_aliase new Option("--option2"), new Argument("arg") }; - CommandLineConfiguration config = new (command) - { - Output = new StringWriter() - }; - await config.InvokeAsync("[suggest:3] \"opt\""); + var output = new StringWriter(); - config.Output - .ToString() - .Should() - .Be($"--option1{NewLine}--option2{NewLine}"); + await command.Parse("[suggest:3] \"opt\"").InvokeAsync(new() { Output = output }, CancellationToken.None); + + output.ToString() + .Should() + .Be($"--option1{NewLine}--option2{NewLine}"); } [Fact] @@ -249,19 +224,17 @@ public async Task It_does_not_repeat_suggestion_for_already_specified_bool_optio { new Option("--bool-option") }; - CommandLineConfiguration config = new (command) - { - Output = new StringWriter() - }; + + var output = new StringWriter(); var commandLine = "--bool-option false"; - await command.Parse($"[suggest:{commandLine.Length + 1}] \"{commandLine}\"", config).InvokeAsync(); + await command.Parse($"[suggest:{commandLine.Length + 1}] \"{commandLine}\"").InvokeAsync(new() { Output = output }, CancellationToken.None); - config.Output - .ToString() - .Should() - .NotContain("--bool-option"); + output + .ToString() + .Should() + .NotContain("--bool-option"); } } } diff --git a/src/System.CommandLine.Tests/TestApps/NativeAOT/Program.cs b/src/System.CommandLine.Tests/TestApps/NativeAOT/Program.cs index 37c80614cd..c390af65a6 100644 --- a/src/System.CommandLine.Tests/TestApps/NativeAOT/Program.cs +++ b/src/System.CommandLine.Tests/TestApps/NativeAOT/Program.cs @@ -17,7 +17,7 @@ private static int Main(string[] args) command.SetAction(Run); - return new CommandLineConfiguration(command).Invoke(args); + return command.Parse(args).Invoke(); void Run(ParseResult parseResult) { diff --git a/src/System.CommandLine.Tests/TokenReplacementTests.cs b/src/System.CommandLine.Tests/TokenReplacementTests.cs index c96af9b0c2..908ba5cc23 100644 --- a/src/System.CommandLine.Tests/TokenReplacementTests.cs +++ b/src/System.CommandLine.Tests/TokenReplacementTests.cs @@ -18,7 +18,7 @@ public void Token_replacer_receives_the_token_from_the_command_line_with_the_lea string receivedToken = null; - CommandLineConfiguration config = new (command) + ParserConfiguration config = new () { ResponseFileTokenReplacer = (string tokenToReplace, out IReadOnlyList tokens, out string message) => { @@ -41,7 +41,7 @@ public void Token_replacer_can_expand_argument_values() var command = new RootCommand { argument }; - CommandLineConfiguration config = new(command) + ParserConfiguration config = new() { ResponseFileTokenReplacer = (string tokenToReplace, out IReadOnlyList tokens, out string message) => { @@ -65,7 +65,7 @@ public void Custom_token_replacer_can_expand_option_argument_values() var command = new RootCommand { option }; - CommandLineConfiguration config = new(command) + ParserConfiguration config = new() { ResponseFileTokenReplacer = (string tokenToReplace, out IReadOnlyList tokens, out string message) => { @@ -89,7 +89,7 @@ public void Custom_token_replacer_can_expand_subcommands_and_options_and_argumen var command = new RootCommand { new Command("subcommand") { option } }; - CommandLineConfiguration config = new(command) + ParserConfiguration config = new() { ResponseFileTokenReplacer = (string tokenToReplace, out IReadOnlyList tokens, out string message) => { @@ -113,7 +113,7 @@ public void Expanded_tokens_containing_whitespace_are_parsed_as_single_tokens() var command = new RootCommand { argument }; - CommandLineConfiguration config = new(command) + ParserConfiguration config = new() { ResponseFileTokenReplacer = (string tokenToReplace, out IReadOnlyList tokens, out string message) => { @@ -135,7 +135,7 @@ public void Token_replacer_can_set_a_custom_error_message() var command = new RootCommand { argument }; - CommandLineConfiguration config = new(command) + ParserConfiguration config = new() { ResponseFileTokenReplacer = (string tokenToReplace, out IReadOnlyList tokens, out string message) => { @@ -159,7 +159,7 @@ public void When_token_replacer_returns_false_without_setting_an_error_message_t var command = new RootCommand { argument }; - CommandLineConfiguration config = new(command) + ParserConfiguration config = new() { ResponseFileTokenReplacer = (string tokenToReplace, out IReadOnlyList tokens, out string message) => { @@ -183,7 +183,7 @@ public void Token_replacer_will_delete_token_when_delegate_returns_true_and_sets var command = new RootCommand { argument }; - CommandLineConfiguration config = new(command) + ParserConfiguration config = new() { ResponseFileTokenReplacer = (string tokenToReplace, out IReadOnlyList tokens, out string message) => { @@ -207,7 +207,7 @@ public void Token_replacer_will_delete_token_when_delegate_returns_true_and_sets var command = new RootCommand { argument }; - CommandLineConfiguration config = new(command) + ParserConfiguration config = new() { ResponseFileTokenReplacer = (string tokenToReplace, out IReadOnlyList tokens, out string message) => { diff --git a/src/System.CommandLine.Tests/UseExceptionHandlerTests.cs b/src/System.CommandLine.Tests/UseExceptionHandlerTests.cs index 5acf4b59b5..201f0ee869 100644 --- a/src/System.CommandLine.Tests/UseExceptionHandlerTests.cs +++ b/src/System.CommandLine.Tests/UseExceptionHandlerTests.cs @@ -1,9 +1,10 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using FluentAssertions; using System.IO; +using System.Threading; using System.Threading.Tasks; -using FluentAssertions; using Xunit; namespace System.CommandLine.Tests @@ -16,12 +17,7 @@ public async Task UseExceptionHandler_catches_command_handler_exceptions_and_set var command = new Command("the-command"); command.SetAction((_, __) => Task.FromException(new Exception("oops!"))); - CommandLineConfiguration config = new(command) - { - Error = new StringWriter(), - }; - - var resultCode = await config.InvokeAsync("the-command"); + var resultCode = await command.Parse("the-command").InvokeAsync(new() { Error = new StringWriter() },CancellationToken.None); resultCode.Should().Be(1); } @@ -32,14 +28,11 @@ public async Task UseExceptionHandler_catches_command_handler_exceptions_and_wri var command = new Command("the-command"); command.SetAction((_, __) => Task.FromException(new Exception("oops!"))); - CommandLineConfiguration config = new(command) - { - Error = new StringWriter(), - }; + var error = new StringWriter(); - await config.InvokeAsync("the-command"); + await command.Parse("the-command").InvokeAsync(new() { Error = error }, CancellationToken.None); - config.Error.ToString().Should().Contain("System.Exception: oops!"); + error.ToString().Should().Contain("System.Exception: oops!"); } [Fact] @@ -48,16 +41,12 @@ public async Task When_thrown_exception_is_from_cancelation_no_output_is_generat Command command = new("the-command"); command.SetAction((_, __) => throw new OperationCanceledException()); - CommandLineConfiguration config = new(command) - { - Output = new StringWriter(), - Error = new StringWriter() - }; + var output = new StringWriter(); + var error = new StringWriter(); - int resultCode = await config - .InvokeAsync("the-command"); + int resultCode = await command.Parse("the-command").InvokeAsync(new() { Output = output, Error = error }, CancellationToken.None); - config.Output.ToString().Should().BeEmpty(); + output.ToString().Should().BeEmpty(); resultCode.Should().NotBe(0); } @@ -70,24 +59,26 @@ public async Task Exception_output_can_be_customized(bool async) Command command = new("the-command"); command.SetAction((_, __) => throw expectedException); - CommandLineConfiguration config = new(command) + InvocationConfiguration config = new() { Error = new StringWriter(), EnableDefaultExceptionHandler = false }; - ParseResult parseResult = command.Parse("the-command", config); + ParseResult parseResult = command.Parse("the-command"); int resultCode = 0; try { - resultCode = async ? await parseResult.InvokeAsync() : parseResult.Invoke(); + resultCode = async + ? await parseResult.InvokeAsync(config) + : parseResult.Invoke(config); } catch (Exception ex) { ex.Should().Be(expectedException); - parseResult.Configuration.Error.Write("Well that's awkward."); + parseResult.InvocationConfiguration.Error.Write("Well that's awkward."); resultCode = 22; } diff --git a/src/System.CommandLine.Tests/Utility/ParseResultExtensions.cs b/src/System.CommandLine.Tests/Utility/ParseResultExtensions.cs index 60e46a2599..9cfadd03b2 100644 --- a/src/System.CommandLine.Tests/Utility/ParseResultExtensions.cs +++ b/src/System.CommandLine.Tests/Utility/ParseResultExtensions.cs @@ -7,19 +7,19 @@ internal static class ParseResultExtensions { internal static string Diagram(this ParseResult parseResult) { - TextWriter outputBefore = parseResult.Configuration.Output; + TextWriter outputBefore = parseResult.InvocationConfiguration.Output; try { - parseResult.Configuration.Output = new StringWriter(); + parseResult.InvocationConfiguration.Output = new StringWriter(); ((SynchronousCommandLineAction)new DiagramDirective().Action!).Invoke(parseResult); - return parseResult.Configuration.Output.ToString() + return parseResult.InvocationConfiguration.Output.ToString() .TrimEnd(); // the directive adds a new line, tests that used to rely on Diagram extension method don't expect it } finally { // some of the tests check the Output after getting the Diagram - parseResult.Configuration.Output = outputBefore; + parseResult.InvocationConfiguration.Output = outputBefore; } } } diff --git a/src/System.CommandLine.Tests/VersionOptionTests.cs b/src/System.CommandLine.Tests/VersionOptionTests.cs index b796349136..5e7f528ab2 100644 --- a/src/System.CommandLine.Tests/VersionOptionTests.cs +++ b/src/System.CommandLine.Tests/VersionOptionTests.cs @@ -1,12 +1,13 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using FluentAssertions; -using FluentAssertions.Execution; using System.IO; using System.Linq; using System.Reflection; +using System.Threading; using System.Threading.Tasks; +using FluentAssertions; +using FluentAssertions.Execution; using Xunit; using static System.Environment; @@ -14,21 +15,21 @@ namespace System.CommandLine.Tests { public class VersionOptionTests { - private static readonly string version = (Assembly.GetEntryAssembly() ?? Assembly.GetExecutingAssembly()) - .GetCustomAttribute() - .InformationalVersion; + private static readonly string version = (Assembly.GetEntryAssembly() ?? + Assembly.GetExecutingAssembly()) + .GetCustomAttribute() + .InformationalVersion; [Fact] public async Task When_the_version_option_is_specified_then_the_version_is_written_to_standard_out() { - CommandLineConfiguration configuration = new(new RootCommand()) - { - Output = new StringWriter() - }; + var rootCommand = new RootCommand(); - await configuration.InvokeAsync("--version"); + var output = new StringWriter(); - configuration.Output.ToString().Should().Be($"{version}{NewLine}"); + await rootCommand.Parse("--version").InvokeAsync(new() { Output = output }, CancellationToken.None); + + output.ToString().Should().Be($"{version}{NewLine}"); } [Fact] @@ -38,12 +39,9 @@ public async Task When_the_version_option_is_specified_then_invocation_is_short_ var rootCommand = new RootCommand(); rootCommand.SetAction((_) => wasCalled = true); - CommandLineConfiguration configuration = new(rootCommand) - { - Output = new StringWriter() - }; + var output = new StringWriter(); - await configuration.InvokeAsync("--version"); + await rootCommand.Parse("--version").InvokeAsync(new() { Output = output }, CancellationToken.None); wasCalled.Should().BeFalse(); } @@ -51,17 +49,13 @@ public async Task When_the_version_option_is_specified_then_invocation_is_short_ [Fact] public async Task Version_option_appears_in_help() { - CommandLineConfiguration configuration = new(new RootCommand()) - { - Output = new StringWriter() - }; - - await configuration.InvokeAsync("--help"); + var output = new StringWriter(); + await new RootCommand().Parse("--help").InvokeAsync(new() { Output = output }, CancellationToken.None); - configuration.Output - .ToString() - .Should() - .Match("*Options:*--version*Show version information*"); + output + .ToString() + .Should() + .Match("*Options:*--version*Show version information*"); } [Fact] @@ -76,33 +70,27 @@ public async Task When_the_version_option_is_specified_and_there_are_default_opt }; rootCommand.SetAction((_) => { }); - CommandLineConfiguration configuration = new(rootCommand) - { - Output = new StringWriter() - }; + var output = new StringWriter(); - await configuration.InvokeAsync("--version"); + await rootCommand.Parse("--version").InvokeAsync(new() { Output = output }, CancellationToken.None); - configuration.Output.ToString().Should().Be($"{version}{NewLine}"); + output.ToString().Should().Be($"{version}{NewLine}"); } [Fact] public async Task When_the_version_option_is_specified_and_there_are_default_arguments_then_the_version_is_written_to_standard_out() { - RootCommand rootCommand = new () + RootCommand rootCommand = new() { - new Argument("x") { DefaultValueFactory =(_) => true }, + new Argument("x") { DefaultValueFactory = (_) => true }, }; rootCommand.SetAction((_) => { }); - CommandLineConfiguration configuration = new(rootCommand) - { - Output = new StringWriter() - }; + var output = new StringWriter(); - await configuration.InvokeAsync("--version"); + await rootCommand.Parse("--version").InvokeAsync(new() { Output = output }, CancellationToken.None); - configuration.Output.ToString().Should().Be($"{version}{NewLine}"); + output.ToString().Should().Be($"{version}{NewLine}"); } [Theory] @@ -119,40 +107,27 @@ public void Version_is_not_valid_with_other_tokens(string commandLine) }; rootCommand.SetAction(_ => { }); - CommandLineConfiguration configuration = new(rootCommand) - { - Output = new StringWriter() - }; + var output = new StringWriter(); - var result = rootCommand.Parse(commandLine, configuration); + var result = rootCommand.Parse(commandLine); result.Errors.Should().Contain(e => e.Message == "--version option cannot be combined with other arguments."); } - + [Fact] public void Version_option_is_not_added_to_subcommands() { - var childCommand = new Command("subcommand"); - childCommand.SetAction(_ => { }); - var rootCommand = new RootCommand { - childCommand - }; - rootCommand.SetAction(_ => { }); - - CommandLineConfiguration configuration = new(rootCommand) - { - Output = new StringWriter() + new Command("subcommand") }; - configuration - .RootCommand - .Subcommands - .Single(c => c.Name == "subcommand") - .Options - .Should() - .BeEmpty(); + rootCommand + .Subcommands + .Single(c => c.Name == "subcommand") + .Options + .Should() + .BeEmpty(); } [Fact] @@ -163,19 +138,16 @@ public async Task Version_can_specify_additional_alias() rootCommand.Options.Clear(); rootCommand.Add(new VersionOption("-v", "-version")); - CommandLineConfiguration configuration = new(rootCommand) - { - Output = new StringWriter() - }; + var output = new StringWriter(); using var _ = new AssertionScope(); - await configuration.InvokeAsync("-v"); - configuration.Output.ToString().Should().Be($"{version}{NewLine}"); + await rootCommand.Parse("-v").InvokeAsync(new() { Output = output }, CancellationToken.None); + output.ToString().Should().Be($"{version}{NewLine}"); - configuration.Output = new StringWriter(); - await configuration.InvokeAsync("-version"); - configuration.Output.ToString().Should().Be($"{version}{NewLine}"); + output = new StringWriter(); + await rootCommand.Parse("-version").InvokeAsync(new() { Output = output }, CancellationToken.None); + output.ToString().Should().Be($"{version}{NewLine}"); } [Fact] @@ -193,14 +165,9 @@ public void Version_is_not_valid_with_other_tokens_when_it_uses_custom_alias() rootCommand.SetAction(_ => { }); - CommandLineConfiguration configuration = new(rootCommand) - { - Output = new StringWriter() - }; - - var result = rootCommand.Parse("-v subcommand", configuration); + var result = rootCommand.Parse("-v subcommand"); result.Errors.Should().ContainSingle(e => e.Message == "-v option cannot be combined with other arguments."); } } -} +} \ No newline at end of file diff --git a/src/System.CommandLine/Command.cs b/src/System.CommandLine/Command.cs index 7a1bc15941..44e60aa1e8 100644 --- a/src/System.CommandLine/Command.cs +++ b/src/System.CommandLine/Command.cs @@ -222,7 +222,7 @@ public void SetAction(Func> action) /// The string arguments to parse. /// The configuration on which the parser's grammar and behaviors are based. /// A parse result describing the outcome of the parse operation. - public ParseResult Parse(IReadOnlyList args, CommandLineConfiguration? configuration = null) + public ParseResult Parse(IReadOnlyList args, ParserConfiguration? configuration = null) => CommandLineParser.Parse(this, args, configuration); /// @@ -232,7 +232,7 @@ public ParseResult Parse(IReadOnlyList args, CommandLineConfiguration? c /// A command line string to parse, which can include spaces and quotes equivalent to what can be entered into a terminal. /// The configuration on which the parser's grammar and behaviors are based. /// A parse result describing the outcome of the parse operation. - public ParseResult Parse(string commandLine, CommandLineConfiguration? configuration = null) + public ParseResult Parse(string commandLine, ParserConfiguration? configuration = null) => CommandLineParser.Parse(this, commandLine, configuration); /// diff --git a/src/System.CommandLine/CommandLineConfiguration.cs b/src/System.CommandLine/CommandLineConfiguration.cs deleted file mode 100644 index 80800653e1..0000000000 --- a/src/System.CommandLine/CommandLineConfiguration.cs +++ /dev/null @@ -1,225 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System.Collections.Generic; -using System.CommandLine.Parsing; -using System.Linq; -using System.Threading.Tasks; -using System.Threading; -using System.IO; -using System.CommandLine.Invocation; - -namespace System.CommandLine -{ - /// - /// Represents the configuration used by the . - /// - public class CommandLineConfiguration - { - private TextWriter? _output, _error; - - /// - /// Initializes a new instance of the class. - /// - /// The root command for the parser. - public CommandLineConfiguration(Command rootCommand) - { - RootCommand = rootCommand ?? throw new ArgumentNullException(nameof(rootCommand)); - } - - internal bool HasDirectives => - RootCommand switch - { - RootCommand root => root.Directives.Count > 0, - _ => false - }; - - /// - /// Enables the parser to recognize and expand POSIX-style bundled options. - /// - /// to parse POSIX bundles; otherwise, . - /// - /// POSIX conventions recommend that single-character options be allowed to be specified together after a single - prefix. When is set to , the following command lines are equivalent: - /// - /// - /// > myapp -a -b -c - /// > myapp -abc - /// - /// - /// If an argument is provided after an option bundle, it applies to the last option in the bundle. When is set to , all of the following command lines are equivalent: - /// - /// > myapp -a -b -c arg - /// > myapp -abc arg - /// > myapp -abcarg - /// - /// - /// - public bool EnablePosixBundling { get; set; } = true; - - /// - /// Enables a default exception handler to catch any unhandled exceptions thrown during invocation. Enabled by default. - /// - public bool EnableDefaultExceptionHandler { get; set; } = true; - - /// - /// Enables signaling and handling of process termination (Ctrl+C, SIGINT, SIGTERM) via a - /// that can be passed to a during invocation. - /// If not provided, a default timeout of 2 seconds is enforced. - /// - public TimeSpan? ProcessTerminationTimeout { get; set; } = TimeSpan.FromSeconds(2); - - /// - /// Response file token replacer, enabled by default. - /// To disable response files support, this property needs to be set to null. - /// - /// - /// When enabled, any token prefixed with @ can be replaced with zero or more other tokens. This is mostly commonly used to expand tokens from response files and interpolate them into a command line prior to parsing. - /// - public TryReplaceToken? ResponseFileTokenReplacer { get; set; } = StringExtensions.TryReadResponseFile; - - /// - /// Gets the root command. - /// - public Command RootCommand { get; } - - /// - /// The standard output. Used by Help and other facilities that write non-error information. - /// By default it's set to . - /// For testing purposes, it can be set to a new instance of . - /// If you want to disable the output, please set it to . - /// - public TextWriter Output - { - get => _output ??= Console.Out; - set => _output = value ?? throw new ArgumentNullException(nameof(value), "Use TextWriter.Null to disable the output"); - } - - /// - /// The standard error. Used for printing error information like parse errors. - /// By default it's set to . - /// For testing purposes, it can be set to a new instance of . - /// - public TextWriter Error - { - get => _error ??= Console.Error; - set => _error = value ?? throw new ArgumentNullException(nameof(value), "Use TextWriter.Null to disable the output"); - } - - /// - /// Parses an array strings using the configured . - /// - /// The string arguments to parse. - /// A parse result describing the outcome of the parse operation. - public ParseResult Parse(IReadOnlyList args) - => CommandLineParser.Parse(RootCommand, args, this); - - /// - /// Parses a command line string value using the configured . - /// - /// The command line string input will be split into tokens as if it had been passed on the command line. - /// A command line string to parse, which can include spaces and quotes equivalent to what can be entered into a terminal. - /// A parse result describing the outcome of the parse operation. - public ParseResult Parse(string commandLine) - => CommandLineParser.Parse(RootCommand, commandLine, this); - - /// - /// Parses a command line string value and invokes the handler for the indicated command. - /// - /// The exit code for the invocation. - /// The command line string input will be split into tokens as if it had been passed on the command line. - public int Invoke(string commandLine) - => RootCommand.Parse(commandLine, this).Invoke(); - - /// - /// Parses a command line string array and invokes the handler for the indicated command. - /// - /// The exit code for the invocation. - public int Invoke(string[] args) - => RootCommand.Parse(args, this).Invoke(); - - /// - /// Parses a command line string value and invokes the handler for the indicated command. - /// - /// The exit code for the invocation. - /// The command line string input will be split into tokens as if it had been passed on the command line. - public Task InvokeAsync(string commandLine, CancellationToken cancellationToken = default) - => RootCommand.Parse(commandLine, this).InvokeAsync(cancellationToken); - - /// - /// Parses a command line string array and invokes the handler for the indicated command. - /// - /// The exit code for the invocation. - public Task InvokeAsync(string[] args, CancellationToken cancellationToken = default) - => RootCommand.Parse(args, this).InvokeAsync(cancellationToken); - - /// - /// Throws an exception if the parser configuration is ambiguous or otherwise not valid. - /// - /// Due to the performance cost of this method, it is recommended to be used in unit testing or in scenarios where the parser is configured dynamically at runtime. - /// Thrown if the configuration is found to be invalid. - public void ThrowIfInvalid() - { - ThrowIfInvalid(RootCommand); - - static void ThrowIfInvalid(Command command) - { - if (command.Parents.FlattenBreadthFirst(c => c.Parents).Any(ancestor => ancestor == command)) - { - throw new CommandLineConfigurationException($"Cycle detected in command tree. Command '{command.Name}' is its own ancestor."); - } - - int count = command.Subcommands.Count + command.Options.Count; - for (var i = 0; i < count; i++) - { - Symbol symbol1 = GetChild(i, command, out AliasSet? aliases1); - for (var j = i + 1; j < count; j++) - { - Symbol symbol2 = GetChild(j, command, out AliasSet? aliases2); - - if (symbol1.Name.Equals(symbol2.Name, StringComparison.Ordinal) - || (aliases1 is not null && aliases1.Contains(symbol2.Name))) - { - throw new CommandLineConfigurationException($"Duplicate alias '{symbol2.Name}' found on command '{command.Name}'."); - } - else if (aliases2 is not null && aliases2.Contains(symbol1.Name)) - { - throw new CommandLineConfigurationException($"Duplicate alias '{symbol1.Name}' found on command '{command.Name}'."); - } - - if (aliases1 is not null && aliases2 is not null) - { - // take advantage of the fact that we are dealing with two hash sets - if (aliases1.Overlaps(aliases2)) - { - foreach (string symbol2Alias in aliases2) - { - if (aliases1.Contains(symbol2Alias)) - { - throw new CommandLineConfigurationException($"Duplicate alias '{symbol2Alias}' found on command '{command.Name}'."); - } - } - } - } - } - - if (symbol1 is Command childCommand) - { - ThrowIfInvalid(childCommand); - } - } - } - - static Symbol GetChild(int index, Command command, out AliasSet? aliases) - { - if (index < command.Subcommands.Count) - { - aliases = command.Subcommands[index]._aliases; - return command.Subcommands[index]; - } - - aliases = command.Options[index - command.Subcommands.Count]._aliases; - return command.Options[index - command.Subcommands.Count]; - } - } - } -} \ No newline at end of file diff --git a/src/System.CommandLine/CommandLineConfigurationException.cs b/src/System.CommandLine/CommandLineConfigurationException.cs deleted file mode 100644 index 35a49e88a6..0000000000 --- a/src/System.CommandLine/CommandLineConfigurationException.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -namespace System.CommandLine; - -/// -/// Indicates that a command line configuration is invalid. -/// -public class CommandLineConfigurationException : Exception -{ - /// - public CommandLineConfigurationException(string message) : base(message) - { - } -} \ No newline at end of file diff --git a/src/System.CommandLine/Completions/CompletionAction.cs b/src/System.CommandLine/Completions/CompletionAction.cs index 7757568630..e6e8f36518 100644 --- a/src/System.CommandLine/Completions/CompletionAction.cs +++ b/src/System.CommandLine/Completions/CompletionAction.cs @@ -30,7 +30,9 @@ public override int Invoke(ParseResult parseResult) var completions = completionParseResult.GetCompletions(position); - parseResult.Configuration.Output.WriteLine( + var output = parseResult.InvocationConfiguration.Output; + + output.WriteLine( string.Join( Environment.NewLine, completions)); diff --git a/src/System.CommandLine/Help/HelpAction.cs b/src/System.CommandLine/Help/HelpAction.cs index 7658ddeaad..c04a8db53a 100644 --- a/src/System.CommandLine/Help/HelpAction.cs +++ b/src/System.CommandLine/Help/HelpAction.cs @@ -21,7 +21,7 @@ internal HelpBuilder Builder /// public override int Invoke(ParseResult parseResult) { - var output = parseResult.Configuration.Output; + var output = parseResult.InvocationConfiguration.Output; var helpContext = new HelpContext(Builder, parseResult.CommandResult.Command, @@ -32,4 +32,4 @@ public override int Invoke(ParseResult parseResult) return 0; } } -} \ No newline at end of file +} \ No newline at end of file diff --git a/src/System.CommandLine/Invocation/InvocationPipeline.cs b/src/System.CommandLine/Invocation/InvocationPipeline.cs index 3cbca453ea..c41d2686d6 100644 --- a/src/System.CommandLine/Invocation/InvocationPipeline.cs +++ b/src/System.CommandLine/Invocation/InvocationPipeline.cs @@ -45,9 +45,12 @@ internal static async Task InvokeAsync(ParseResult parseResult, Cancellatio case AsynchronousCommandLineAction asyncAction: var startedInvocation = asyncAction.InvokeAsync(parseResult, cts.Token); - if (parseResult.Configuration.ProcessTerminationTimeout.HasValue) + + var timeout = parseResult.InvocationConfiguration.ProcessTerminationTimeout; + + if (timeout.HasValue) { - terminationHandler = new(cts, startedInvocation, parseResult.Configuration.ProcessTerminationTimeout.Value); + terminationHandler = new(cts, startedInvocation, timeout.Value); } if (terminationHandler is null) @@ -67,9 +70,9 @@ internal static async Task InvokeAsync(ParseResult parseResult, Cancellatio throw new ArgumentOutOfRangeException(nameof(parseResult.Action)); } } - catch (Exception ex) when (parseResult.Configuration.EnableDefaultExceptionHandler) + catch (Exception ex) when (parseResult.InvocationConfiguration.EnableDefaultExceptionHandler) { - return DefaultExceptionHandler(ex, parseResult.Configuration); + return DefaultExceptionHandler(ex, parseResult); } finally { @@ -96,7 +99,7 @@ internal static int Invoke(ParseResult parseResult) if (action is not SynchronousCommandLineAction) { - parseResult.Configuration.EnableDefaultExceptionHandler = false; + parseResult.InvocationConfiguration.EnableDefaultExceptionHandler = false; throw new Exception( $"This should not happen. An instance of {nameof(AsynchronousCommandLineAction)} ({action}) was called within {nameof(InvocationPipeline)}.{nameof(Invoke)}. This is supposed to be detected earlier resulting in a call to {nameof(InvocationPipeline)}{nameof(InvokeAsync)}"); } @@ -114,9 +117,9 @@ internal static int Invoke(ParseResult parseResult) return syncAction.Invoke(parseResult); } - catch (Exception ex) when (parseResult.Configuration.EnableDefaultExceptionHandler) + catch (Exception ex) when (parseResult.InvocationConfiguration.EnableDefaultExceptionHandler) { - return DefaultExceptionHandler(ex, parseResult.Configuration); + return DefaultExceptionHandler(ex, parseResult); } default: @@ -124,15 +127,17 @@ internal static int Invoke(ParseResult parseResult) } } - private static int DefaultExceptionHandler(Exception exception, CommandLineConfiguration config) + private static int DefaultExceptionHandler(Exception exception, ParseResult parseResult) { if (exception is not OperationCanceledException) { ConsoleHelpers.ResetTerminalForegroundColor(); ConsoleHelpers.SetTerminalForegroundRed(); - config.Error.Write(LocalizationResources.ExceptionHandlerHeader()); - config.Error.WriteLine(exception.ToString()); + var error = parseResult.InvocationConfiguration.Error; + + error.Write(LocalizationResources.ExceptionHandlerHeader()); + error.WriteLine(exception.ToString()); ConsoleHelpers.ResetTerminalForegroundColor(); } diff --git a/src/System.CommandLine/Invocation/ParseErrorAction.cs b/src/System.CommandLine/Invocation/ParseErrorAction.cs index fdb4e01b45..3f35c2cc1e 100644 --- a/src/System.CommandLine/Invocation/ParseErrorAction.cs +++ b/src/System.CommandLine/Invocation/ParseErrorAction.cs @@ -49,12 +49,14 @@ private static void WriteErrorDetails(ParseResult parseResult) ConsoleHelpers.ResetTerminalForegroundColor(); ConsoleHelpers.SetTerminalForegroundRed(); + var stdErr = parseResult.InvocationConfiguration.Error; + foreach (var error in parseResult.Errors) { - parseResult.Configuration.Error.WriteLine(error.Message); + stdErr.WriteLine(error.Message); } - parseResult.Configuration.Error.WriteLine(); + stdErr.WriteLine(); ConsoleHelpers.ResetTerminalForegroundColor(); } @@ -96,17 +98,17 @@ private static void WriteTypoCorrectionSuggestions(ParseResult parseResult) { if (first) { - parseResult.Configuration.Output.WriteLine(LocalizationResources.SuggestionsTokenNotMatched(token)); + parseResult.InvocationConfiguration.Output.WriteLine(LocalizationResources.SuggestionsTokenNotMatched(token)); first = false; } - parseResult.Configuration.Output.WriteLine(suggestion); + parseResult.InvocationConfiguration.Output.WriteLine(suggestion); } } if (unmatchedTokens.Count != 0) { - parseResult.Configuration.Output.WriteLine(); + parseResult.InvocationConfiguration.Output.WriteLine(); } static IEnumerable GetPossibleTokens(Command targetSymbol, string token) diff --git a/src/System.CommandLine/InvocationConfiguration.cs b/src/System.CommandLine/InvocationConfiguration.cs new file mode 100644 index 0000000000..a7eaeedd85 --- /dev/null +++ b/src/System.CommandLine/InvocationConfiguration.cs @@ -0,0 +1,45 @@ +using System.CommandLine.Invocation; +using System.IO; +using System.Threading; + +namespace System.CommandLine; + +public class InvocationConfiguration +{ + private TextWriter? _output, _error; + + /// + /// Enables a default exception handler to catch any unhandled exceptions thrown during invocation. Enabled by default. + /// + public bool EnableDefaultExceptionHandler { get; set; } = true; + + /// + /// Enables signaling and handling of process termination (Ctrl+C, SIGINT, SIGTERM) via a + /// that can be passed to a during invocation. + /// If not provided, a default timeout of 2 seconds is enforced. + /// + public TimeSpan? ProcessTerminationTimeout { get; set; } + + /// + /// The standard output. Used by Help and other facilities that write non-error information. + /// By default it's set to . + /// For testing purposes, it can be set to a new instance of . + /// If you want to disable the output, please set it to . + /// + public TextWriter Output + { + get => _output ??= Console.Out; + set => _output = value ?? throw new ArgumentNullException(nameof(value), "Use TextWriter.Null to disable the output"); + } + + /// + /// The standard error. Used for printing error information like parse errors. + /// By default it's set to . + /// For testing purposes, it can be set to a new instance of . + /// + public TextWriter Error + { + get => _error ??= Console.Error; + set => _error = value ?? throw new ArgumentNullException(nameof(value), "Use TextWriter.Null to disable the output"); + } +} \ No newline at end of file diff --git a/src/System.CommandLine/ParseResult.cs b/src/System.CommandLine/ParseResult.cs index 0bbd1ad093..3153acaacf 100644 --- a/src/System.CommandLine/ParseResult.cs +++ b/src/System.CommandLine/ParseResult.cs @@ -21,9 +21,10 @@ public sealed class ParseResult private CompletionContext? _completionContext; private readonly CommandLineAction? _action; private readonly List? _preActions; + private InvocationConfiguration? _invocationConfiguration; internal ParseResult( - CommandLineConfiguration configuration, + ParserConfiguration configuration, CommandResult rootCommandResult, CommandResult commandResult, List tokens, @@ -68,7 +69,16 @@ internal ParseResult( /// /// The configuration used to produce the parse result. /// - public CommandLineConfiguration Configuration { get; } + public ParserConfiguration Configuration { get; } + + /// + /// The configuration used to specify command line runtime behavior. + /// + public InvocationConfiguration InvocationConfiguration + { + get => _invocationConfiguration ??= new(); + private set => _invocationConfiguration = value; + } /// /// Gets the root command result. @@ -262,19 +272,35 @@ static string[] OptionsWithArgumentLimitReached(CommandResult commandResult) => /// /// Invokes the appropriate command handler for a parsed command line input. /// + /// The configuration used to define invocation behaviors. /// A token that can be used to cancel an invocation. /// A task whose result can be used as a process exit code. - public Task InvokeAsync(CancellationToken cancellationToken = default) - => InvocationPipeline.InvokeAsync(this, cancellationToken); + public Task InvokeAsync( + InvocationConfiguration? configuration = null, + CancellationToken cancellationToken = default) + { + if (configuration is not null) + { + InvocationConfiguration = configuration; + } + + return InvocationPipeline.InvokeAsync(this, cancellationToken); + } /// /// Invokes the appropriate command handler for a parsed command line input. /// + /// The configuration used to define invocation behaviors. /// A value that can be used as a process exit code. - public int Invoke() + public int Invoke(InvocationConfiguration? configuration = null) { var useAsync = false; + if (configuration is not null) + { + InvocationConfiguration = configuration; + } + if (Action is AsynchronousCommandLineAction) { useAsync = true; diff --git a/src/System.CommandLine/ParserConfiguration.cs b/src/System.CommandLine/ParserConfiguration.cs new file mode 100644 index 0000000000..f239c3c4b4 --- /dev/null +++ b/src/System.CommandLine/ParserConfiguration.cs @@ -0,0 +1,45 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.CommandLine.Parsing; +using System.Linq; + +namespace System.CommandLine +{ + /// + /// Represents the configuration used by the . + /// + public class ParserConfiguration + { + /// + /// Enables the parser to recognize and expand POSIX-style bundled options. + /// + /// to parse POSIX bundles; otherwise, . + /// + /// POSIX conventions recommend that single-character options be allowed to be specified together after a single - prefix. When is set to , the following command lines are equivalent: + /// + /// + /// > myapp -a -b -c + /// > myapp -abc + /// + /// + /// If an argument is provided after an option bundle, it applies to the last option in the bundle. When is set to , all of the following command lines are equivalent: + /// + /// > myapp -a -b -c arg + /// > myapp -abc arg + /// > myapp -abcarg + /// + /// + /// + public bool EnablePosixBundling { get; set; } = true; + + /// + /// Response file token replacer, enabled by default. + /// To disable response files support, this property needs to be set to null. + /// + /// + /// When enabled, any token prefixed with @ can be replaced with zero or more other tokens. This is mostly commonly used to expand tokens from response files and interpolate them into a command line prior to parsing. + /// + public TryReplaceToken? ResponseFileTokenReplacer { get; set; } = StringExtensions.TryReadResponseFile; + } +} \ No newline at end of file diff --git a/src/System.CommandLine/Parsing/CommandLineParser.cs b/src/System.CommandLine/Parsing/CommandLineParser.cs index 69d629b31d..ee60feddda 100644 --- a/src/System.CommandLine/Parsing/CommandLineParser.cs +++ b/src/System.CommandLine/Parsing/CommandLineParser.cs @@ -18,7 +18,7 @@ public static class CommandLineParser /// The string array typically passed to a program's Main method. /// The configuration on which the parser's grammar and behaviors are based. /// A providing details about the parse operation. - public static ParseResult Parse(Command command, IReadOnlyList args, CommandLineConfiguration? configuration = null) + public static ParseResult Parse(Command command, IReadOnlyList args, ParserConfiguration? configuration = null) => Parse(command, args, null, configuration); /// @@ -29,7 +29,7 @@ public static ParseResult Parse(Command command, IReadOnlyList args, Com /// The configuration on which the parser's grammar and behaviors are based. /// The command line string input will be split into tokens as if it had been passed on the command line. /// A providing details about the parse operation. - public static ParseResult Parse(Command command, string commandLine, CommandLineConfiguration? configuration = null) + public static ParseResult Parse(Command command, string commandLine, ParserConfiguration? configuration = null) => Parse(command, SplitCommandLine(commandLine).ToArray(), commandLine, configuration); /// @@ -139,16 +139,17 @@ private static ParseResult Parse( Command command, IReadOnlyList arguments, string? rawInput, - CommandLineConfiguration? configuration) + ParserConfiguration? configuration) { if (arguments is null) { throw new ArgumentNullException(nameof(arguments)); } - configuration ??= new CommandLineConfiguration(command); + configuration ??= new ParserConfiguration(); arguments.Tokenize( + command, configuration, inferRootCommand: rawInput is not null, out List tokens, @@ -156,6 +157,7 @@ private static ParseResult Parse( var operation = new ParseOperation( tokens, + command, configuration, tokenizationErrors, rawInput); diff --git a/src/System.CommandLine/Parsing/ParseDiagramAction.cs b/src/System.CommandLine/Parsing/ParseDiagramAction.cs index bbae57609f..f3abe87b27 100644 --- a/src/System.CommandLine/Parsing/ParseDiagramAction.cs +++ b/src/System.CommandLine/Parsing/ParseDiagramAction.cs @@ -20,7 +20,7 @@ internal sealed class ParseDiagramAction : SynchronousCommandLineAction public override int Invoke(ParseResult parseResult) { - parseResult.Configuration.Output.WriteLine(Diagram(parseResult)); + parseResult.InvocationConfiguration.Output.WriteLine(Diagram(parseResult)); return parseResult.Errors.Count == 0 ? 0 : _parseErrorReturnValue; } diff --git a/src/System.CommandLine/Parsing/ParseOperation.cs b/src/System.CommandLine/Parsing/ParseOperation.cs index de7dcf2d9c..8ac410566c 100644 --- a/src/System.CommandLine/Parsing/ParseOperation.cs +++ b/src/System.CommandLine/Parsing/ParseOperation.cs @@ -10,7 +10,7 @@ namespace System.CommandLine.Parsing internal sealed class ParseOperation { private readonly List _tokens; - private readonly CommandLineConfiguration _configuration; + private readonly ParserConfiguration _configuration; private readonly string? _rawInput; private readonly SymbolResultTree _symbolResultTree; private readonly CommandResult _rootCommandResult; @@ -21,22 +21,25 @@ internal sealed class ParseOperation private bool _isTerminatingDirectiveSpecified; private CommandLineAction? _primaryAction; private List? _preActions; + private readonly Command _rootCommand; public ParseOperation( List tokens, - CommandLineConfiguration configuration, + Command rootCommand, + ParserConfiguration configuration, List? tokenizeErrors, string? rawInput) { _tokens = tokens; _configuration = configuration; + _rootCommand = rootCommand; _rawInput = rawInput; - _symbolResultTree = new(_configuration.RootCommand, tokenizeErrors); + _symbolResultTree = new(_rootCommand, tokenizeErrors); _innermostCommandResult = _rootCommandResult = new CommandResult( - _configuration.RootCommand, + _rootCommand, CurrentToken, _symbolResultTree); - _symbolResultTree.Add(_configuration.RootCommand, _rootCommandResult); + _symbolResultTree.Add(_rootCommand, _rootCommandResult); Advance(); } @@ -296,7 +299,7 @@ private void ParseDirectives() { while (More(out TokenType currentTokenType) && currentTokenType == TokenType.Directive) { - if (_configuration.HasDirectives) + if (_rootCommand is RootCommand { Directives.Count: > 0 }) { ParseDirective(); // kept in separate method to avoid JIT } diff --git a/src/System.CommandLine/Parsing/StringExtensions.cs b/src/System.CommandLine/Parsing/StringExtensions.cs index b1f7b63ad2..a018f04fcd 100644 --- a/src/System.CommandLine/Parsing/StringExtensions.cs +++ b/src/System.CommandLine/Parsing/StringExtensions.cs @@ -27,7 +27,8 @@ internal static int IndexOfCaseInsensitive( // this method is not returning a Value Tuple or a dedicated type to avoid JITting internal static void Tokenize( this IReadOnlyList args, - CommandLineConfiguration configuration, + Command rootCommand, + ParserConfiguration configuration, bool inferRootCommand, out List tokens, out List? errors) @@ -36,22 +37,22 @@ internal static void Tokenize( List? errorList = null; - var currentCommand = configuration.RootCommand; + var currentCommand = rootCommand; var foundDoubleDash = false; var foundEndOfDirectives = false; var tokenList = new List(args.Count); - var knownTokens = configuration.RootCommand.ValidTokens(); + var knownTokens = rootCommand.ValidTokens(); - int i = FirstArgumentIsRootCommand(args, configuration.RootCommand, inferRootCommand) + int i = FirstArgumentIsRootCommand(args, rootCommand, inferRootCommand) ? 0 : FirstArgIsNotRootCommand; for (; i < args.Count; i++) { var arg = i == FirstArgIsNotRootCommand - ? configuration.RootCommand.Name + ? rootCommand.Name : args[i]; if (foundDoubleDash) @@ -96,7 +97,7 @@ internal static void Tokenize( continue; } - if (!configuration.RootCommand.EqualsNameOrAlias(arg)) + if (!rootCommand.EqualsNameOrAlias(arg)) { foundEndOfDirectives = true; } @@ -143,7 +144,7 @@ internal static void Tokenize( Command cmd = (Command)token.Symbol!; if (cmd != currentCommand) { - if (cmd != configuration.RootCommand) + if (cmd != rootCommand) { knownTokens = cmd.ValidTokens(); // config contains Directives, they are allowed only for RootCommand } diff --git a/src/System.CommandLine/VersionOption.cs b/src/System.CommandLine/VersionOption.cs index 6c31ab1485..0fde75a764 100644 --- a/src/System.CommandLine/VersionOption.cs +++ b/src/System.CommandLine/VersionOption.cs @@ -65,7 +65,7 @@ private sealed class VersionOptionAction : SynchronousCommandLineAction { public override int Invoke(ParseResult parseResult) { - parseResult.Configuration.Output.WriteLine(RootCommand.ExecutableVersion); + parseResult.InvocationConfiguration.Output.WriteLine(RootCommand.ExecutableVersion); return 0; } }