diff --git a/src/System.CommandLine.Benchmarks/CommandLine/Perf_Parser_Directives_Suggest.cs b/src/System.CommandLine.Benchmarks/CommandLine/Perf_Parser_Directives_Suggest.cs index bf1339f669..257e4aeb08 100644 --- a/src/System.CommandLine.Benchmarks/CommandLine/Perf_Parser_Directives_Suggest.cs +++ b/src/System.CommandLine.Benchmarks/CommandLine/Perf_Parser_Directives_Suggest.cs @@ -29,12 +29,16 @@ public void Setup() new Option("--fruit") { Argument = new Argument() - .WithSuggestions("apple", "banana", "cherry") + { + Suggestions = {"apple", "banana", "cherry" } + } }, new Option("--vegetable") { Argument = new Argument() - .WithSuggestions("asparagus", "broccoli", "carrot") + { + Suggestions = {"asparagus", "broccoli", "carrot" } + } } }; diff --git a/src/System.CommandLine.Benchmarks/CommandLine/Perf_Suggestions.cs b/src/System.CommandLine.Benchmarks/CommandLine/Perf_Suggestions.cs index 9db69e7a9c..0b08672091 100644 --- a/src/System.CommandLine.Benchmarks/CommandLine/Perf_Suggestions.cs +++ b/src/System.CommandLine.Benchmarks/CommandLine/Perf_Suggestions.cs @@ -41,9 +41,9 @@ public void Setup_FromSymbol() { Argument = new Argument { - Arity = ArgumentArity.ExactlyOne - } - .WithSuggestions(GenerateSuggestionsArray(TestSuggestionsCount)) + Arity = ArgumentArity.ExactlyOne, + Suggestions = { GenerateSuggestionsArray(TestSuggestionsCount) } + } }; } diff --git a/src/System.CommandLine.Tests/ApprovalTests/Help/HelpBuilderTests.Approval.cs b/src/System.CommandLine.Tests/ApprovalTests/Help/HelpBuilderTests.Approval.cs index ead3f3b2fb..f2d7a8a6dc 100644 --- a/src/System.CommandLine.Tests/ApprovalTests/Help/HelpBuilderTests.Approval.cs +++ b/src/System.CommandLine.Tests/ApprovalTests/Help/HelpBuilderTests.Approval.cs @@ -61,7 +61,7 @@ public void Help_describes_default_values_for_complex_root_command_scenario() Argument = new Argument("the-root-option-arg", () => FileAccess.Read) { Description = "the-root-option-arg-description", - }, + } }, new Option(aliases: new string[] {"--the-root-option-required-enum-arg", "-trorea"}) { Description = "the-root-option-description", diff --git a/src/System.CommandLine.Tests/SuggestDirectiveTests.cs b/src/System.CommandLine.Tests/SuggestDirectiveTests.cs index 4172ede2c6..a184697a88 100644 --- a/src/System.CommandLine.Tests/SuggestDirectiveTests.cs +++ b/src/System.CommandLine.Tests/SuggestDirectiveTests.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.CommandLine.Builder; -using System.CommandLine.Invocation; using System.CommandLine.IO; using System.CommandLine.Parsing; using System.Threading.Tasks; @@ -23,10 +22,10 @@ public class SuggestDirectiveTests public SuggestDirectiveTests() { _fruitOption = new Option("--fruit") - .WithSuggestions("apple", "banana", "cherry"); + .AddSuggestions("apple", "banana", "cherry"); _vegetableOption = new Option("--vegetable") - .WithSuggestions("asparagus", "broccoli", "carrot"); + .AddSuggestions("asparagus", "broccoli", "carrot"); _eatCommand = new Command("eat") { diff --git a/src/System.CommandLine.Tests/SuggestionTests.cs b/src/System.CommandLine.Tests/SuggestionTests.cs index 7851a251da..ef063bc5ad 100644 --- a/src/System.CommandLine.Tests/SuggestionTests.cs +++ b/src/System.CommandLine.Tests/SuggestionTests.cs @@ -29,9 +29,9 @@ public void Option_Suggest_returns_argument_suggestions_if_configured() { Argument = new Argument { - Arity = ArgumentArity.ExactlyOne + Arity = ArgumentArity.ExactlyOne, + Suggestions = { "one", "two", "three" } } - .WithSuggestions("one", "two", "three") }; var suggestions = option.GetSuggestions(); @@ -102,9 +102,9 @@ public void Command_Suggest_returns_available_subcommands_and_option_aliases_and new Option("--option", "option"), new Argument { - Arity = ArgumentArity.OneOrMore + Arity = ArgumentArity.OneOrMore, + Suggestions = { "command-argument" } } - .WithSuggestions("command-argument") }; var suggestions = command.GetSuggestions(); @@ -505,9 +505,9 @@ public void Suggestions_can_be_provided_in_the_absence_of_validation() { Argument = new Argument { - Arity = ArgumentArity.ExactlyOne + Arity = ArgumentArity.ExactlyOne, + Suggestions = { "vegetable", "mineral", "animal" } } - .WithSuggestions("vegetable", "mineral", "animal") } }; @@ -529,14 +529,9 @@ public void Command_argument_suggestions_can_be_provided_using_a_delegate() { new Argument { - Arity = ArgumentArity.ExactlyOne + Arity = ArgumentArity.ExactlyOne, + Suggestions = { _ => new[] { "vegetable", "mineral", "animal" } } } - .WithSuggestionSource(_ => new[] - { - "vegetable", - "mineral", - "animal" - }) } }; @@ -551,13 +546,13 @@ public void Option_argument_suggestions_can_be_provided_using_a_delegate() { var command = new Command("the-command") { - new Option("-x") - .WithSuggestionSource(_ => new[] + new Option("-x") + { + Argument = new Argument() { - "vegetable", - "mineral", - "animal" - }) + Suggestions = { _ => new[] { "vegetable", "mineral", "animal" } } + } + } }; var parseResult = command.Parse("the-command -x m"); @@ -997,6 +992,49 @@ public void When_there_are_multiple_arguments_then_suggestions_are_only_offered_ { Assert.True(false, "Test testname is not written yet."); } + + [Fact] + public void Enum_suggestions_can_be_configured_with_list_clear() + { + var argument = new Argument(); + argument.Suggestions.Clear(); + argument.Suggestions.Add(new[] { "mon", "tues", "wed", "thur", "fri", "sat", "sun" }); + var command = new Command("the-command") + { + argument + }; + + var suggestions = command.Parse("the-command s") + .GetSuggestions(); + + suggestions.Should().BeEquivalentTo("sat", "sun","tues"); + } + + [Fact] + public void Enum_suggestions_can_be_configured_without_list_clear() + { + var command = new Command("the-command") + { + new Argument() + { + Suggestions = { "mon", "tues", "wed", "thur", "fri", "sat", "sun" } + } + }; + + var suggestions = command.Parse("the-command s") + .GetSuggestions(); + + suggestions + .Should() + .BeEquivalentTo( + "sat", + nameof(DayOfWeek.Saturday), + "sun", nameof(DayOfWeek.Sunday), + "tues", + nameof(DayOfWeek.Tuesday), + nameof(DayOfWeek.Thursday), + nameof(DayOfWeek.Wednesday)); + } } } } diff --git a/src/System.CommandLine/Argument.cs b/src/System.CommandLine/Argument.cs index 151258ea7d..65d2042a3e 100644 --- a/src/System.CommandLine/Argument.cs +++ b/src/System.CommandLine/Argument.cs @@ -12,8 +12,6 @@ namespace System.CommandLine public class Argument : Symbol, IArgument { private Func? _defaultValueFactory; - private readonly List _suggestions = new List(); - private readonly List _suggestionSources = new List(); private IArgumentArity? _arity; private TryConvertArgument? _convertArguments; private Type _argumentType = typeof(void); @@ -109,6 +107,24 @@ bool DefaultConvert(SymbolResult symbol, out object value) set => _convertArguments = value; } + + private List? _suggestions = null; + public List Suggestions + { + get + { + if (_suggestions is null) + { + _suggestions = new List() + { + SuggestionSource.ForType(ArgumentType) + }; + } + + return _suggestions; + } + } + public Type ArgumentType { get => _argumentType; @@ -158,36 +174,6 @@ public void SetDefaultValueFactory(Func getDefaultValue internal static Argument None => new Argument { Arity = ArgumentArity.Zero }; - public void AddSuggestions(IReadOnlyCollection suggestions) - { - if (suggestions is null) - { - throw new ArgumentNullException(nameof(suggestions)); - } - - _suggestions.AddRange(suggestions); - } - - public void AddSuggestionSource(ISuggestionSource suggest) - { - if (suggest is null) - { - throw new ArgumentNullException(nameof(suggest)); - } - - _suggestionSources.Add(suggest); - } - - public void AddSuggestionSource(Suggest suggest) - { - if (suggest is null) - { - throw new ArgumentNullException(nameof(suggest)); - } - - AddSuggestionSource(new AnonymousSuggestionSource(suggest)); - } - internal void AddAllowedValues(IEnumerable values) { if (AllowedValues is null) @@ -200,17 +186,10 @@ internal void AddAllowedValues(IEnumerable values) public override IEnumerable GetSuggestions(string? textToMatch = null) { - var fixedSuggestions = _suggestions; - - var dynamicSuggestions = _suggestionSources + var dynamicSuggestions = Suggestions .SelectMany(source => source.GetSuggestions(textToMatch)); - var typeSuggestions = SuggestionSource.ForType(ArgumentType) - .GetSuggestions(textToMatch); - - return fixedSuggestions - .Concat(dynamicSuggestions) - .Concat(typeSuggestions) + return dynamicSuggestions .Distinct() .OrderBy(c => c, StringComparer.OrdinalIgnoreCase) .Containing(textToMatch); diff --git a/src/System.CommandLine/ArgumentExtensions.cs b/src/System.CommandLine/ArgumentExtensions.cs index d25f1f7d9e..3fbd4650e2 100644 --- a/src/System.CommandLine/ArgumentExtensions.cs +++ b/src/System.CommandLine/ArgumentExtensions.cs @@ -3,9 +3,9 @@ using System.Collections.Generic; using System.CommandLine.Parsing; -using System.CommandLine.Suggestions; using System.Linq; using System.IO; +using System.CommandLine.Suggestions; namespace System.CommandLine { @@ -17,30 +17,11 @@ public static TArgument FromAmong( where TArgument : Argument { argument.AddAllowedValues(values); - argument.AddSuggestions(values); + argument.Suggestions.Add(values); return argument; } - public static TArgument WithSuggestions( - this TArgument argument, - params string[] suggestions) - where TArgument : Argument - { - argument.AddSuggestions(suggestions); - - return argument; - } - - public static TArgument WithSuggestionSource( - this TArgument argument, - Suggest suggest) - where TArgument : Argument - { - argument.AddSuggestionSource(suggest); - - return argument; - } public static Argument ExistingOnly(this Argument argument) { argument.AddValidator(symbol => diff --git a/src/System.CommandLine/OptionExtensions.cs b/src/System.CommandLine/OptionExtensions.cs index 4fbfeb3944..8493c3a4d5 100644 --- a/src/System.CommandLine/OptionExtensions.cs +++ b/src/System.CommandLine/OptionExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. +// 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; @@ -17,27 +17,27 @@ public static TOption FromAmong( where TOption : Option { option.Argument.AddAllowedValues(values); - option.Argument.AddSuggestions(values); + option.Argument.Suggestions.Add(values); return option; } - public static TOption WithSuggestions( + public static TOption AddSuggestions( this TOption option, - params string[] suggestions) + params string[] values) where TOption : Option { - option.Argument.AddSuggestions(suggestions); + option.Argument.Suggestions.Add(values); return option; } - public static TOption WithSuggestionSource( + public static TOption AddSuggestion( this TOption option, Suggest suggest) - where TOption : Option + where TOption : Option { - option.Argument.AddSuggestionSource(suggest); + option.Argument.Suggestions.Add(suggest); return option; } diff --git a/src/System.CommandLine/SuggestionSourceExtensions.cs b/src/System.CommandLine/SuggestionSourceExtensions.cs new file mode 100644 index 0000000000..f2d334dc9b --- /dev/null +++ b/src/System.CommandLine/SuggestionSourceExtensions.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.Collections.Generic; +using System.CommandLine.Suggestions; + +namespace System.CommandLine +{ + public static class SuggestionSourceExtensions + { + public static void Add( + this List suggestionSources, + Suggest suggest) + { + if (suggestionSources is null) + { + throw new ArgumentNullException(nameof(suggestionSources)); + } + + if (suggest is null) + { + throw new ArgumentNullException(nameof(suggest)); + } + + suggestionSources.Add(new AnonymousSuggestionSource(suggest)); + } + + public static void Add( + this List suggestionSources, + params string[] suggestions) + { + if (suggestionSources is null) + { + throw new ArgumentNullException(nameof(suggestionSources)); + } + + if (suggestions is null) + { + throw new ArgumentNullException(nameof(suggestions)); + } + + suggestionSources.Add(new AnonymousSuggestionSource(_ => suggestions)); + } + } +}