From 22f4e8bde5d43783909d89df73ec8308ebde72b4 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Thu, 2 Mar 2023 18:04:29 +0100 Subject: [PATCH 1/7] apply the changes to symbol types --- src/Common/OptionBuilder.cs | 7 +- ...ommandLine_api_is_not_changed.approved.txt | 36 ++-- .../CommandLine.cs | 5 + .../CommandResultExtensions.cs | 2 +- src/System.CommandLine/AliasSet.cs | 45 +++++ src/System.CommandLine/Argument.cs | 37 +--- src/System.CommandLine/Argument{T}.cs | 163 ++++-------------- .../Binding/ArgumentConversionResult.cs | 4 +- .../Builder/CommandLineBuilderExtensions.cs | 10 +- src/System.CommandLine/Command.cs | 30 ++-- .../CommandLineConfiguration.cs | 42 +++-- .../Help/HelpBuilder.Default.cs | 2 +- src/System.CommandLine/Help/HelpOption.cs | 7 +- src/System.CommandLine/Help/VersionOption.cs | 8 +- src/System.CommandLine/IdentifierSymbol.cs | 101 +---------- .../Invocation/TypoCorrection.cs | 5 +- src/System.CommandLine/Option.cs | 32 +--- src/System.CommandLine/Option{T}.cs | 89 ++-------- src/System.CommandLine/ParseResult.cs | 2 +- .../Parsing/CommandResult.cs | 2 +- .../Parsing/StringExtensions.cs | 90 +++++----- .../Parsing/SymbolResultExtensions.cs | 2 +- src/System.CommandLine/RootCommand.cs | 30 ++-- src/System.CommandLine/Symbol.cs | 34 ++-- 24 files changed, 281 insertions(+), 504 deletions(-) create mode 100644 src/System.CommandLine/AliasSet.cs diff --git a/src/Common/OptionBuilder.cs b/src/Common/OptionBuilder.cs index 25d33bb959..cd78d6b9bc 100644 --- a/src/Common/OptionBuilder.cs +++ b/src/Common/OptionBuilder.cs @@ -48,10 +48,11 @@ public static Option CreateOption(string name, Type valueType, string descriptio private class Bridge : Option { public Bridge(string name, Func defaultValueFactory, string description) - : base(name, - () => (T)defaultValueFactory(), // this type exists only for an easy Func => Func transformation - description) + : base(name) { + // this type exists only for an easy Func => Func transformation + DefaultValueFactory = (_) => (T)defaultValueFactory(); + Description = description; } } } \ 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 81680080eb..c00cfe4180 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 @@ -10,21 +10,14 @@ System.CommandLine public System.Object GetDefaultValue() public System.String ToString() public class Argument : Argument, IValueDescriptor, System.CommandLine.Binding.IValueDescriptor - .ctor() - .ctor(System.String name, System.String description = null) - .ctor(System.String name, Func defaultValueFactory, System.String description = null) - .ctor(System.String name, T defaultValue, System.String description = null) - .ctor(Func defaultValueFactory) - .ctor(System.String name, Func parse, System.Boolean isDefault = False, System.String description = null) - .ctor(Func parse, System.Boolean isDefault = False) + .ctor(System.String name) + public Func CustomParser { get; set; } + public Func DefaultValueFactory { get; set; } public System.Boolean HasDefaultValue { get; } public System.Type ValueType { get; } public System.Void AcceptLegalFileNamesOnly() public System.Void AcceptLegalFilePathsOnly() public System.Void AcceptOnlyFromAmong(System.String[] values) - public System.Void SetDefaultValue(T value) - public System.Void SetDefaultValueFactory(Func defaultValueFactory) - public System.Void SetDefaultValueFactory(Func defaultValueFactory) public struct ArgumentArity : System.ValueType, System.IEquatable public static ArgumentArity ExactlyOne { get; } public static ArgumentArity OneOrMore { get; } @@ -75,7 +68,7 @@ System.CommandLine public CommandLineBuilder UseEnvironmentVariableDirective() public CommandLineBuilder UseExceptionHandler(System.Action onException = null, System.Nullable errorExitCode = null) public CommandLineBuilder UseHelp(System.Nullable maxWidth = null) - public CommandLineBuilder UseHelp(System.String[] helpAliases) + public CommandLineBuilder UseHelp(System.String name, System.String[] helpAliases) public CommandLineBuilder UseHelp(System.Action customize, System.Nullable maxWidth = null) public CommandLineBuilder UseHelpBuilder(System.Func getHelpBuilder) public CommandLineBuilder UseParseDirective(System.Int32 errorExitCode = 1) @@ -84,7 +77,7 @@ System.CommandLine public CommandLineBuilder UseTokenReplacer(System.CommandLine.Parsing.TryReplaceToken replaceToken) public CommandLineBuilder UseTypoCorrections(System.Int32 maxLevenshteinDistance = 3) public CommandLineBuilder UseVersionOption() - public CommandLineBuilder UseVersionOption(System.String[] aliases) + public CommandLineBuilder UseVersionOption(System.String name, System.String[] aliases) public class CommandLineConfiguration public static CommandLineBuilder CreateBuilder(Command rootCommand) .ctor(Command command, System.Boolean enablePosixBundling = True, System.Boolean enableDirectives = True, System.Boolean enableTokenReplacement = True, System.Collections.Generic.IReadOnlyList middlewarePipeline = null, System.Func helpBuilderFactory = null, System.CommandLine.Parsing.TryReplaceToken tokenReplacer = null) @@ -138,31 +131,24 @@ System.CommandLine public System.Threading.Tasks.Task InvokeAsync(System.CommandLine.Invocation.InvocationContext context, System.Threading.CancellationToken cancellationToken = null) public interface IConsole : System.CommandLine.IO.IStandardError, System.CommandLine.IO.IStandardIn, System.CommandLine.IO.IStandardOut public abstract class IdentifierSymbol : Symbol - public System.Collections.Generic.IReadOnlyCollection Aliases { get; } - public System.Void AddAlias(System.String alias) - public System.Boolean HasAlias(System.String alias) + public System.Collections.Generic.ICollection Aliases { get; } public abstract class Option : IdentifierSymbol, System.CommandLine.Binding.IValueDescriptor public System.Boolean AllowMultipleArgumentsPerToken { get; set; } public System.Boolean AppliesToSelfAndChildren { get; set; } - public System.String ArgumentHelpName { get; set; } public ArgumentArity Arity { get; set; } public System.Collections.Generic.List>> CompletionSources { get; } + public System.String HelpName { get; set; } public System.Boolean IsRequired { get; set; } public System.Collections.Generic.List> Validators { get; } public System.Type ValueType { get; } public System.Collections.Generic.IEnumerable GetCompletions(System.CommandLine.Completions.CompletionContext context) public class Option : Option, IValueDescriptor, System.CommandLine.Binding.IValueDescriptor - .ctor(System.String name, System.String description = null) - .ctor(System.String[] aliases, System.String description = null) - .ctor(System.String name, Func parseArgument, System.Boolean isDefault = False, System.String description = null) - .ctor(System.String[] aliases, Func parseArgument, System.Boolean isDefault = False, System.String description = null) - .ctor(System.String name, Func defaultValueFactory, System.String description = null) - .ctor(System.String[] aliases, Func defaultValueFactory, System.String description = null) + .ctor(System.String name, System.String[] aliases) + public Func CustomParser { get; set; } + public Func DefaultValueFactory { get; set; } public System.Void AcceptLegalFileNamesOnly() public System.Void AcceptLegalFilePathsOnly() public System.Void AcceptOnlyFromAmong(System.String[] values) - public System.Void SetDefaultValue(T value) - public System.Void SetDefaultValueFactory(Func defaultValueFactory) public static class OptionValidation public static Option AcceptExistingOnly(this Option option) public static Option AcceptExistingOnly(this Option option) @@ -194,7 +180,7 @@ System.CommandLine public abstract class Symbol public System.String Description { get; set; } public System.Boolean IsHidden { get; set; } - public System.String Name { get; set; } + public System.String Name { get; } public System.Collections.Generic.IEnumerable Parents { get; } public System.Collections.Generic.IEnumerable GetCompletions(System.CommandLine.Completions.CompletionContext context) public System.String ToString() diff --git a/src/System.CommandLine.DragonFruit/CommandLine.cs b/src/System.CommandLine.DragonFruit/CommandLine.cs index 113dba6990..41bf355f91 100644 --- a/src/System.CommandLine.DragonFruit/CommandLine.cs +++ b/src/System.CommandLine.DragonFruit/CommandLine.cs @@ -310,6 +310,11 @@ private static bool HasAliasIgnoringPrefix(Option option, string alias) { ReadOnlySpan rawAlias = alias.AsSpan(GetPrefixLength(alias)); + if (MemoryExtensions.Equals(option.Name.AsSpan(GetPrefixLength(option.Name)), rawAlias, StringComparison.CurrentCulture)) + { + return true; + } + foreach (string existingAlias in option.Aliases) { if (MemoryExtensions.Equals(existingAlias.AsSpan(GetPrefixLength(existingAlias)), rawAlias, StringComparison.CurrentCulture)) diff --git a/src/System.CommandLine.NamingConventionBinder/CommandResultExtensions.cs b/src/System.CommandLine.NamingConventionBinder/CommandResultExtensions.cs index 3e7c95fa0b..8b96279a9d 100644 --- a/src/System.CommandLine.NamingConventionBinder/CommandResultExtensions.cs +++ b/src/System.CommandLine.NamingConventionBinder/CommandResultExtensions.cs @@ -72,7 +72,7 @@ static bool HasMatchingAlias( IValueDescriptor valueDescriptor, Option option) { - if (option.HasAlias(valueDescriptor.ValueName)) + if (option.Name == valueDescriptor.ValueName || option.Aliases.Contains(valueDescriptor.ValueName)) { return true; } diff --git a/src/System.CommandLine/AliasSet.cs b/src/System.CommandLine/AliasSet.cs new file mode 100644 index 0000000000..dd8424420c --- /dev/null +++ b/src/System.CommandLine/AliasSet.cs @@ -0,0 +1,45 @@ +using System.Collections; +using System.Collections.Generic; + +namespace System.CommandLine +{ + // this types exists only because we need to validate the added aliases ;) + // TODO: add struct enumerator + internal sealed class AliasSet : ICollection + { + private readonly HashSet _aliases; + + internal AliasSet() => _aliases = new(StringComparer.Ordinal); + + internal AliasSet(string[] aliases) + { + foreach (string alias in aliases) + { + Symbol.ThrowIfEmptyOrWithWhitespaces(alias, nameof(alias)); + } + + _aliases = new(aliases, StringComparer.Ordinal); + } + + public int Count => _aliases.Count; + + public bool IsReadOnly => throw new NotImplementedException(); + + public void Add(string item) + => _aliases.Add(Symbol.ThrowIfEmptyOrWithWhitespaces(item, nameof(item))); + + internal bool Overlaps(AliasSet other) => _aliases.Overlaps(other._aliases); + + public void Clear() => _aliases.Clear(); + + public bool Contains(string item) => _aliases.Contains(item); + + public void CopyTo(string[] array, int arrayIndex) => _aliases.CopyTo(array, arrayIndex); + + public IEnumerator GetEnumerator() => _aliases.GetEnumerator(); + + public bool Remove(string item) => _aliases.Remove(item); + + IEnumerator IEnumerable.GetEnumerator() => _aliases.GetEnumerator(); + } +} diff --git a/src/System.CommandLine/Argument.cs b/src/System.CommandLine/Argument.cs index ae5276b1b3..a70ac28abc 100644 --- a/src/System.CommandLine/Argument.cs +++ b/src/System.CommandLine/Argument.cs @@ -19,22 +19,8 @@ public abstract class Argument : Symbol, IValueDescriptor private List>>? _completionSources = null; private List>? _validators = null; - /// - /// Initializes a new instance of the Argument class. - /// - protected Argument() - { - } - - /// - /// Initializes a new instance of the Argument class. - /// - /// The name of the argument. - /// The description of the argument, shown in help. - protected Argument(string? name = null, string? description = null) + private protected Argument(string name) : base(name) { - Name = name!; - Description = description; } /// @@ -65,7 +51,7 @@ public ArgumentArity Arity internal TryConvertArgument? ConvertArguments { get => _convertArguments ??= ArgumentConverter.GetConverter(this); - init => _convertArguments = value; + set => _convertArguments = value; } /// @@ -82,25 +68,6 @@ internal TryConvertArgument? ConvertArguments /// public abstract Type ValueType { get; } - private protected override string DefaultName - { - get - { - if (FirstParent is not null && FirstParent.Next is null) - { - switch (FirstParent.Symbol) - { - case Option option: - return option.Name; - case Command _: - return ValueType.Name.ToLowerInvariant(); - } - } - - return ""; - } - } - /// /// Provides a list of argument validators. Validators can be used /// to provide custom errors based on user input. diff --git a/src/System.CommandLine/Argument{T}.cs b/src/System.CommandLine/Argument{T}.cs index 12f14ff65c..0d74ea0224 100644 --- a/src/System.CommandLine/Argument{T}.cs +++ b/src/System.CommandLine/Argument{T}.cs @@ -10,165 +10,72 @@ namespace System.CommandLine /// public class Argument : Argument, IValueDescriptor { - private Func? _defaultValueFactory; - private readonly bool _hasCustomParser; + private Func? _customParser; /// /// Initializes a new instance of the Argument class. /// - public Argument() + /// The name of the argument.> + public Argument(string name) : base(name) { } - /// - public Argument( - string? name, - string? description = null) : base(name, description) - { - } - - /// - /// Initializes a new instance of the Argument class. - /// - /// The name of the argument. - /// The delegate to invoke to return the default value. - /// The description of the argument, shown in help. - /// Thrown when is null. - public Argument( - string name, - Func defaultValueFactory, - string? description = null) : this(name, description) - { - SetDefaultValueFactory(defaultValueFactory); - } - - /// - /// Initializes a new instance of the Argument class. - /// - /// The name of the argument. - /// The default value. - /// The description of the argument, shown in help. - public Argument( - string name, - T defaultValue, - string? description = null) : this(name, description) - { - SetDefaultValue(defaultValue); - } - /// - /// Initializes a new instance of the Argument class. + /// The delegate to invoke to return the default value. /// - /// The delegate to invoke to return the default value. - /// Thrown when is null. - public Argument(Func defaultValueFactory) : this() - { - SetDefaultValueFactory(defaultValueFactory); - } + public Func? DefaultValueFactory { get; set; } /// - /// Initializes a new instance of the Argument class. + /// A custom argument parser. /// - /// The name of the argument. - /// A custom argument parser. - /// to use the result as default value. - /// The description of the argument, shown in help. - /// Thrown when is null. - public Argument( - string? name, - Func parse, - bool isDefault = false, - string? description = null) : this(name, description) + public Func? CustomParser { - if (parse is null) + get => _customParser; + set { - throw new ArgumentNullException(nameof(parse)); - } + _customParser = value; - if (isDefault) - { - SetDefaultValueFactory(parse); - } - - ConvertArguments = (ArgumentResult argumentResult, out object? value) => - { - int errorsBefore = argumentResult.SymbolResultTree.ErrorCount; - var result = parse(argumentResult); - - if (errorsBefore == argumentResult.SymbolResultTree.ErrorCount) + if (value is not null) { - value = result; - return true; - } - else - { - value = default(T)!; - return false; - } - }; - - _hasCustomParser = true; - } + // TODO: remove the following code or move it to the parsing logic + ConvertArguments = (ArgumentResult argumentResult, out object? parsedValue) => + { + int errorsBefore = argumentResult.SymbolResultTree.ErrorCount; + var result = value(argumentResult); - /// - /// Initializes a new instance of the Argument class. - /// - /// A custom argument parser. - /// to use the result as default value. - public Argument(Func parse, bool isDefault = false) : this(null!, parse, isDefault) - { + if (errorsBefore == argumentResult.SymbolResultTree.ErrorCount) + { + parsedValue = result; + return true; + } + else + { + parsedValue = default(T)!; + return false; + } + }; + } + } } - internal override bool HasCustomParser => _hasCustomParser; + // TODO: try removing it + internal override bool HasCustomParser => _customParser is not null; /// public override Type ValueType => typeof(T); + // TODO: try removing it, or at least make it internal /// - public override bool HasDefaultValue => _defaultValueFactory is not null; - - /// - /// Sets the default value for the argument. - /// - /// The default value for the argument. - public void SetDefaultValue(T value) - { - SetDefaultValueFactory(_ => value); - } - - /// - /// Sets a delegate to invoke when the default value for the argument is required. - /// - /// The delegate to invoke to return the default value. - /// Thrown when is null. - public void SetDefaultValueFactory(Func defaultValueFactory) - { - if (defaultValueFactory is null) - { - throw new ArgumentNullException(nameof(defaultValueFactory)); - } - - SetDefaultValueFactory(_ => defaultValueFactory()); - } - - /// - /// Sets a delegate to invoke when the default value for the argument is required. - /// - /// The delegate to invoke to return the default value. - /// In this overload, the is provided to the delegate. - public void SetDefaultValueFactory(Func defaultValueFactory) - { - _defaultValueFactory = defaultValueFactory ?? throw new ArgumentNullException(nameof(defaultValueFactory)); - } + public override bool HasDefaultValue => DefaultValueFactory is not null; internal override object? GetDefaultValue(ArgumentResult argumentResult) { - if (_defaultValueFactory is null) + if (DefaultValueFactory is null) { throw new InvalidOperationException($"Argument \"{Name}\" does not have a default value"); } - return _defaultValueFactory.Invoke(argumentResult); + return DefaultValueFactory.Invoke(argumentResult); } /// diff --git a/src/System.CommandLine/Binding/ArgumentConversionResult.cs b/src/System.CommandLine/Binding/ArgumentConversionResult.cs index 555125cf99..03bd2f85f0 100644 --- a/src/System.CommandLine/Binding/ArgumentConversionResult.cs +++ b/src/System.CommandLine/Binding/ArgumentConversionResult.cs @@ -47,7 +47,7 @@ private static string FormatErrorMessage( { if (argumentResult.Parent is CommandResult commandResult) { - string alias = commandResult.Command.GetLongestAlias(removePrefix: false); + string alias = commandResult.Command.Name; CompletionItem[] completionItems = argumentResult.Argument.GetCompletions(CompletionContext.Empty).ToArray(); if (completionItems.Length > 0) @@ -62,7 +62,7 @@ private static string FormatErrorMessage( } else if (argumentResult.Parent is OptionResult optionResult) { - string alias = optionResult.Option.GetLongestAlias(removePrefix: false); + string alias = optionResult.Option.Name; CompletionItem[] completionItems = optionResult.Option.GetCompletions(CompletionContext.Empty).ToArray(); if (completionItems.Length > 0) diff --git a/src/System.CommandLine/Builder/CommandLineBuilderExtensions.cs b/src/System.CommandLine/Builder/CommandLineBuilderExtensions.cs index 7318462005..807e9f5cc2 100644 --- a/src/System.CommandLine/Builder/CommandLineBuilderExtensions.cs +++ b/src/System.CommandLine/Builder/CommandLineBuilderExtensions.cs @@ -222,11 +222,12 @@ public CommandLineBuilder UseHelp(int? maxWidth = null) /// Configures the application to show help when one of the specified option aliases are used on the command line. /// /// The specified aliases will override the default values. + /// The name of the help option. /// The set of aliases that can be specified on the command line to request help. /// The reference to this instance. - public CommandLineBuilder UseHelp(params string[] helpAliases) + public CommandLineBuilder UseHelp(string name, params string[] helpAliases) { - return UseHelp(new HelpOption(helpAliases)); + return UseHelp(new HelpOption(name, helpAliases)); } /// @@ -390,13 +391,14 @@ public CommandLineBuilder UseVersionOption() } /// + /// The name of the version option. /// One or more aliases to use instead of the default to signal that version information should be displayed. /// The reference to this instance. - public CommandLineBuilder UseVersionOption(params string[] aliases) + public CommandLineBuilder UseVersionOption(string name, params string[] aliases) { if (VersionOption is null) { - OverwriteOrAdd(Command, VersionOption = new(aliases)); + OverwriteOrAdd(Command, VersionOption = new(name, aliases)); } return this; diff --git a/src/System.CommandLine/Command.cs b/src/System.CommandLine/Command.cs index 170bef6a70..62f6e1b74e 100644 --- a/src/System.CommandLine/Command.cs +++ b/src/System.CommandLine/Command.cs @@ -30,9 +30,8 @@ public class Command : IdentifierSymbol, IEnumerable /// /// The name of the command. /// The description of the command, shown in help. - public Command(string name, string? description = null) : base(name, description) - { - } + public Command(string name, string? description = null) : base(name) + => Description = description; /// /// Gets the child symbols. @@ -106,8 +105,6 @@ public void Add(Symbol symbol) } } - private protected override string DefaultName => throw new NotImplementedException(); - /// /// Gets or sets a value that indicates whether unmatched tokens should be treated as errors. For example, /// if set to and an extra command or argument is provided, validation will fail. @@ -163,7 +160,7 @@ public override IEnumerable GetCompletions(CompletionContext con var commands = Subcommands; for (int i = 0; i < commands.Count; i++) { - AddCompletionsFor(commands[i]); + AddCompletionsFor(commands[i], commands[i]._aliases); } } @@ -172,7 +169,7 @@ public override IEnumerable GetCompletions(CompletionContext con var options = Options; for (int i = 0; i < options.Count; i++) { - AddCompletionsFor(options[i]); + AddCompletionsFor(options[i], options[i]._aliases); } } @@ -207,7 +204,7 @@ public override IEnumerable GetCompletions(CompletionContext con if (option.AppliesToSelfAndChildren) { - AddCompletionsFor(option); + AddCompletionsFor(option, option._aliases); } } } @@ -224,16 +221,23 @@ public override IEnumerable GetCompletions(CompletionContext con .OrderBy(item => item.SortText.IndexOfCaseInsensitive(context.WordToComplete)) .ThenBy(symbol => symbol.Label, StringComparer.OrdinalIgnoreCase); - void AddCompletionsFor(IdentifierSymbol identifier) + void AddCompletionsFor(Symbol identifier, AliasSet? aliases) { if (!identifier.IsHidden) { - foreach (var alias in identifier.Aliases) + if (identifier.Name.ContainsCaseInsensitive(textToMatch)) + { + completions.Add(new CompletionItem(identifier.Name, CompletionItemKind.Keyword, detail: identifier.Description)); + } + + if (aliases is not null) { - if (alias is { } && - alias.ContainsCaseInsensitive(textToMatch)) + foreach (string alias in aliases) { - completions.Add(new CompletionItem(alias, CompletionItemKind.Keyword, detail: identifier.Description)); + if (alias.ContainsCaseInsensitive(textToMatch)) + { + completions.Add(new CompletionItem(alias, CompletionItemKind.Keyword, detail: identifier.Description)); + } } } } diff --git a/src/System.CommandLine/CommandLineConfiguration.cs b/src/System.CommandLine/CommandLineConfiguration.cs index 2d34c741c1..f6c8cfcd84 100644 --- a/src/System.CommandLine/CommandLineConfiguration.cs +++ b/src/System.CommandLine/CommandLineConfiguration.cs @@ -216,32 +216,52 @@ static void ThrowIfInvalid(Command command) int count = command.Subcommands.Count + command.Options.Count; for (var i = 0; i < count; i++) { - IdentifierSymbol symbol1AsIdentifier = GetChild(i, command); + Symbol symbol1 = GetChild(i, command, out AliasSet? aliases1); for (var j = i + 1; j < count; j++) { - IdentifierSymbol symbol2AsIdentifier = GetChild(j, command); + Symbol symbol2 = GetChild(j, command, out AliasSet? aliases2); - foreach (var symbol2Alias in symbol2AsIdentifier.Aliases) + if (symbol1.Name.Equals(symbol2.Name, StringComparison.Ordinal) + || (aliases1 is not null && aliases1.Contains(symbol2.Name)) + || (aliases2 is not null && aliases2.Contains(symbol1.Name))) { - if (symbol1AsIdentifier.Name.Equals(symbol2Alias, StringComparison.Ordinal) || - symbol1AsIdentifier.Aliases.Contains(symbol2Alias)) + throw new CommandLineConfigurationException($"Duplicate alias '{symbol2.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)) { - throw new CommandLineConfigurationException($"Duplicate alias '{symbol2Alias}' found on command '{command.Name}'."); + foreach (string symbol2Alias in aliases2) + { + if (aliases1.Contains(symbol2Alias)) + { + throw new CommandLineConfigurationException($"Duplicate alias '{symbol2Alias}' found on command '{command.Name}'."); + } + } } } } - if (symbol1AsIdentifier is Command childCommand) + if (symbol1 is Command childCommand) { ThrowIfInvalid(childCommand); } } } - static IdentifierSymbol GetChild(int index, Command command) - => index < command.Subcommands.Count - ? command.Subcommands[index] - : command.Options[index - command.Subcommands.Count]; + 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/Help/HelpBuilder.Default.cs b/src/System.CommandLine/Help/HelpBuilder.Default.cs index 04e1c24122..9641ea855e 100644 --- a/src/System.CommandLine/Help/HelpBuilder.Default.cs +++ b/src/System.CommandLine/Help/HelpBuilder.Default.cs @@ -106,7 +106,7 @@ public static string GetArgumentUsageLabel(Argument argument) /// Text to display. public static string GetIdentifierSymbolUsageLabel(IdentifierSymbol symbol, HelpContext context) { - var aliases = symbol.Aliases + var aliases = new [] {symbol.Name}.Concat(symbol.Aliases) .Select(r => r.SplitPrefix()) .OrderBy(r => r.Prefix, StringComparer.OrdinalIgnoreCase) .ThenBy(r => r.Alias, StringComparer.OrdinalIgnoreCase) diff --git a/src/System.CommandLine/Help/HelpOption.cs b/src/System.CommandLine/Help/HelpOption.cs index c5dca1a6c6..d9939495bb 100644 --- a/src/System.CommandLine/Help/HelpOption.cs +++ b/src/System.CommandLine/Help/HelpOption.cs @@ -8,13 +8,14 @@ namespace System.CommandLine.Help { internal class HelpOption : Option { - internal HelpOption(string[] aliases) - : base(aliases, LocalizationResources.HelpOptionDescription(), new Argument { Arity = ArgumentArity.Zero }) + internal HelpOption(string name, string[] aliases) + : base(name, aliases, new Argument(name) { Arity = ArgumentArity.Zero }) { AppliesToSelfAndChildren = true; + Description = LocalizationResources.HelpOptionDescription(); } - internal HelpOption() : this(new[] + internal HelpOption() : this("--help", new[] { "-h", "/h", diff --git a/src/System.CommandLine/Help/VersionOption.cs b/src/System.CommandLine/Help/VersionOption.cs index 3621a1ae04..25d1d2fc3e 100644 --- a/src/System.CommandLine/Help/VersionOption.cs +++ b/src/System.CommandLine/Help/VersionOption.cs @@ -11,14 +11,16 @@ namespace System.CommandLine.Help internal class VersionOption : Option { internal VersionOption() - : base("--version", LocalizationResources.VersionOptionDescription(), new Argument { Arity = ArgumentArity.Zero }) + : base("--version", new Argument("--version") { Arity = ArgumentArity.Zero }) { + Description = LocalizationResources.VersionOptionDescription(); AddValidators(); } - internal VersionOption(string[] aliases) - : base(aliases, LocalizationResources.VersionOptionDescription()) + internal VersionOption(string name, string[] aliases) + : base(name, aliases) { + Description = LocalizationResources.VersionOptionDescription(); AddValidators(); } diff --git a/src/System.CommandLine/IdentifierSymbol.cs b/src/System.CommandLine/IdentifierSymbol.cs index d52b09d2d9..b77a9ba366 100644 --- a/src/System.CommandLine/IdentifierSymbol.cs +++ b/src/System.CommandLine/IdentifierSymbol.cs @@ -2,8 +2,6 @@ // 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.Diagnostics; namespace System.CommandLine { @@ -12,103 +10,22 @@ namespace System.CommandLine /// public abstract class IdentifierSymbol : Symbol { - private readonly HashSet _aliases = new(StringComparer.Ordinal); + internal AliasSet? _aliases; - /// - /// Initializes a new instance of the class. - /// - /// The description of the symbol, which is displayed in command line help. - protected IdentifierSymbol(string? description = null) + private protected IdentifierSymbol(string name) : base(name) { - Description = description; } - /// - /// Initializes a new instance of the class. - /// - /// The name of the symbol. - /// The description of the symbol, which is displayed in command line help. - protected IdentifierSymbol(string name, string? description = null) - { - Name = name ?? throw new ArgumentNullException(nameof(name)); - Description = description; - } + private protected IdentifierSymbol(string name, string[] aliases) : base(name) + => _aliases = new(aliases ?? throw new ArgumentNullException(nameof(aliases))); /// - /// Gets the set of strings that can be used on the command line to specify the symbol. + /// Gets the unique set of strings that can be used on the command line to specify the symbol. /// - public IReadOnlyCollection Aliases => _aliases; - - /// - public override string Name - { - set - { - if (_name is null || !string.Equals(_name, value, StringComparison.Ordinal)) - { - AddAlias(value); - - if (_name != null) - { - RemoveAlias(_name); - } - - _name = value; - } - } - } + /// The collection does not contain the Name of the Symbol. + public ICollection Aliases => _aliases ??= new(); - /// - /// Adds an alias. - /// - /// The alias to add. - /// - /// You can add multiple aliases for a symbol. - /// - public void AddAlias(string alias) - { - ThrowIfAliasIsInvalid(alias); - - _aliases.Add(alias); - } - - private protected virtual void RemoveAlias(string alias) => _aliases.Remove(alias); - - /// - /// Determines whether the specified alias has already been defined. - /// - /// The alias to search for. - /// if the alias has already been defined; otherwise . - public bool HasAlias(string alias) => _aliases.Contains(alias); - - internal string GetLongestAlias(bool removePrefix) - { - string max = ""; - foreach (string alias in _aliases) - { - if (alias.Length > max.Length) - { - max = alias; - } - } - return removePrefix ? max.RemovePrefix() : max; - } - - [DebuggerStepThrough] - private void ThrowIfAliasIsInvalid(string alias) - { - if (string.IsNullOrWhiteSpace(alias)) - { - throw new ArgumentException("An alias cannot be null, empty, or consist entirely of whitespace."); - } - - for (var i = 0; i < alias.Length; i++) - { - if (char.IsWhiteSpace(alias[i])) - { - throw new ArgumentException($"Alias cannot contain whitespace: \"{alias}\"", nameof(alias)); - } - } - } + internal bool EqualsNameOrAlias(string name) + => Name.Equals(name, StringComparison.Ordinal) || (_aliases is not null && _aliases.Contains(name)); } } \ No newline at end of file diff --git a/src/System.CommandLine/Invocation/TypoCorrection.cs b/src/System.CommandLine/Invocation/TypoCorrection.cs index fcfc99c3bf..514962b169 100644 --- a/src/System.CommandLine/Invocation/TypoCorrection.cs +++ b/src/System.CommandLine/Invocation/TypoCorrection.cs @@ -45,9 +45,8 @@ private static IEnumerable GetPossibleTokens(Command targetSymbol, strin .Children .OfType() .Where(x => !x.IsHidden) - .Where(x => x.Aliases.Count > 0) - .Select(symbol => - symbol.Aliases + .Select(symbol => + new[] { symbol.Name }.Concat(symbol.Aliases) .OrderBy(x => GetDistance(token, x)) .ThenByDescending(x => GetStartsWithDistance(token, x)) .First() diff --git a/src/System.CommandLine/Option.cs b/src/System.CommandLine/Option.cs index 87292dee05..bd1b1f62ac 100644 --- a/src/System.CommandLine/Option.cs +++ b/src/System.CommandLine/Option.cs @@ -17,32 +17,12 @@ public abstract class Option : IdentifierSymbol, IValueDescriptor { private List>? _validators; - private protected Option(string name, string? description) : base(description) + private protected Option(string name) : base(name) { - if (name is null) - { - throw new ArgumentNullException(nameof(name)); - } - - AddAlias(name); } - private protected Option(string[] aliases, string? description) : base(description) + private protected Option(string name, string[] aliases) : base(name, aliases) { - if (aliases is null) - { - throw new ArgumentNullException(nameof(aliases)); - } - - if (aliases.Length == 0) - { - throw new ArgumentException("An option must have at least one alias.", nameof(aliases)); - } - - for (var i = 0; i < aliases.Length; i++) - { - AddAlias(aliases[i]); - } } /// @@ -51,12 +31,12 @@ private protected Option(string[] aliases, string? description) : base(descripti internal abstract Argument Argument { get; } /// - /// Gets or sets the name of the argument when displayed in help. + /// Gets or sets the name of the option when displayed in help. /// /// - /// The name of the argument when displayed in help. + /// The name of the option when displayed in help. /// - public string? ArgumentHelpName + public string? HelpName { get => Argument.HelpName; set => Argument.HelpName = value; @@ -124,8 +104,6 @@ internal virtual bool IsGreedy object? IValueDescriptor.GetDefaultValue() => Argument.GetDefaultValue(); - private protected override string DefaultName => GetLongestAlias(true); - /// public override IEnumerable GetCompletions(CompletionContext context) { diff --git a/src/System.CommandLine/Option{T}.cs b/src/System.CommandLine/Option{T}.cs index fcd623484c..0d3b9ebaf4 100644 --- a/src/System.CommandLine/Option{T}.cs +++ b/src/System.CommandLine/Option{T}.cs @@ -12,92 +12,39 @@ public class Option : Option, IValueDescriptor { private readonly Argument _argument; - /// - public Option( - string name, - string? description = null) - : this(name, description, new Argument()) - { } - - /// - public Option( - string[] aliases, - string? description = null) - : this(aliases, description, new Argument()) - { } - - /// - public Option( - string name, - Func parseArgument, - bool isDefault = false, - string? description = null) - : this(name, description, - new Argument(parseArgument ?? throw new ArgumentNullException(nameof(parseArgument)), isDefault)) - { } - - /// - public Option( - string[] aliases, - Func parseArgument, - bool isDefault = false, - string? description = null) - : this(aliases, description, new Argument(parseArgument ?? throw new ArgumentNullException(nameof(parseArgument)), isDefault)) - { } - - /// - public Option( - string name, - Func defaultValueFactory, - string? description = null) - : this(name, description, - new Argument(defaultValueFactory)) - { } - - /// - public Option( - string[] aliases, - Func defaultValueFactory, - string? description = null) - : this(aliases, description, new Argument(defaultValueFactory)) + public Option(string name, params string[] aliases) + : this(name, aliases, new Argument(name)) { } - private protected Option( - string name, - string? description, - Argument argument) - : base(name, description) + private protected Option(string name, Argument argument) : base(name) { argument.AddParent(this); _argument = argument; } - private protected Option( - string[] aliases, - string? description, - Argument argument) - : base(aliases, description) + private protected Option(string name, string[] aliases, Argument argument) + : base(name, aliases) { argument.AddParent(this); _argument = argument; } - internal sealed override Argument Argument => _argument; + /// + public Func? DefaultValueFactory + { + get => _argument.DefaultValueFactory; + set => _argument.DefaultValueFactory = value; + } - /// - /// Sets the default value for the option. - /// - /// The default value for the option. - public void SetDefaultValue(T value) => _argument.SetDefaultValue(value); + /// + public Func? CustomParser + { + get => _argument.CustomParser; + set => _argument.CustomParser = value; + } - /// - /// Sets a delegate to invoke when the default value for the option is required. - /// - /// The delegate to invoke to return the default value. - /// Thrown when is null. - public void SetDefaultValueFactory(Func defaultValueFactory) => - _argument.SetDefaultValueFactory(defaultValueFactory); + internal sealed override Argument Argument => _argument; /// /// Configures the option to accept only the specified values, and to suggest them as command line completions. diff --git a/src/System.CommandLine/ParseResult.cs b/src/System.CommandLine/ParseResult.cs index 699f51dc77..c8144c127b 100644 --- a/src/System.CommandLine/ParseResult.cs +++ b/src/System.CommandLine/ParseResult.cs @@ -224,7 +224,7 @@ static string[] OptionsWithArgumentLimitReached(CommandResult commandResult) => .OfType() .Where(c => c.IsArgumentLimitReached) .Select(o => o.Option) - .SelectMany(c => c.Aliases) + .SelectMany(c => new[] { c.Name }.Concat(c.Aliases)) .ToArray(); } diff --git a/src/System.CommandLine/Parsing/CommandResult.cs b/src/System.CommandLine/Parsing/CommandResult.cs index be6895f9b2..b727a63194 100644 --- a/src/System.CommandLine/Parsing/CommandResult.cs +++ b/src/System.CommandLine/Parsing/CommandResult.cs @@ -96,7 +96,7 @@ private void ValidateOptions(bool completeValidation) { if (option.IsRequired) { - AddError(LocalizationResources.RequiredOptionWasNotProvided(option.GetLongestAlias(removePrefix: false))); + AddError(LocalizationResources.RequiredOptionWasNotProvided(option.Name)); continue; } else if (option.Argument.HasDefaultValue) diff --git a/src/System.CommandLine/Parsing/StringExtensions.cs b/src/System.CommandLine/Parsing/StringExtensions.cs index 877674bfff..7b8ea16393 100644 --- a/src/System.CommandLine/Parsing/StringExtensions.cs +++ b/src/System.CommandLine/Parsing/StringExtensions.cs @@ -24,31 +24,6 @@ internal static int IndexOfCaseInsensitive( value, CompareOptions.OrdinalIgnoreCase); - internal static string RemovePrefix(this string alias) - { - int prefixLength = GetPrefixLength(alias); - return prefixLength > 0 - ? alias.Substring(prefixLength) - : alias; - } - - private static int GetPrefixLength(this string alias) - { - if (alias[0] == '-') - { - return alias.Length > 1 && alias[1] == '-' - ? 2 - : 1; - } - - if (alias[0] == '/') - { - return 1; - } - - return 0; - } - internal static (string? Prefix, string Alias) SplitPrefix(this string rawAlias) { if (rawAlias[0] == '/') @@ -125,7 +100,7 @@ internal static void Tokenize( continue; } - if (!configuration.RootCommand.HasAlias(arg)) + if (!configuration.RootCommand.EqualsNameOrAlias(arg)) { foundEndOfDirectives = true; } @@ -314,7 +289,7 @@ private static bool FirstArgumentIsRootCommand(IReadOnlyList args, Comma { var potentialRootCommand = Path.GetFileName(args[0]); - if (rootCommand.HasAlias(potentialRootCommand)) + if (rootCommand.EqualsNameOrAlias(potentialRootCommand)) { return true; } @@ -425,23 +400,14 @@ private static Dictionary ValidTokens(this Command command) { Dictionary tokens = new(StringComparer.Ordinal); - foreach (string commandAlias in command.Aliases) - { - tokens.Add( - commandAlias, - new Token(commandAlias, TokenType.Command, command, Token.ImplicitPosition)); - } + AddCommandTokens(tokens, command); if (command.HasSubcommands) { var subCommands = command.Subcommands; for (int childIndex = 0; childIndex < subCommands.Count; childIndex++) { - Command cmd = subCommands[childIndex]; - foreach (string childAlias in cmd.Aliases) - { - tokens.Add(childAlias, new Token(childAlias, TokenType.Command, cmd, Token.ImplicitPosition)); - } + AddCommandTokens(tokens, subCommands[childIndex]); } } @@ -451,12 +417,10 @@ private static Dictionary ValidTokens(this Command command) for (int childIndex = 0; childIndex < options.Count; childIndex++) { Option option = options[childIndex]; - foreach (string childAlias in option.Aliases) + + if (!option.AppliesToSelfAndChildren) { - if (!option.AppliesToSelfAndChildren || !tokens.ContainsKey(childAlias)) - { - tokens.Add(childAlias, new Token(childAlias, TokenType.Option, option, Token.ImplicitPosition)); - } + AddOptionTokens(tokens, option); } } } @@ -477,13 +441,7 @@ private static Dictionary ValidTokens(this Command command) Option option = parentCommand.Options[i]; if (option.AppliesToSelfAndChildren) { - foreach (var childAlias in option.Aliases) - { - if (!tokens.ContainsKey(childAlias)) - { - tokens.Add(childAlias, new Token(childAlias, TokenType.Option, option, Token.ImplicitPosition)); - } - } + AddOptionTokens(tokens, option); } } } @@ -496,6 +454,38 @@ private static Dictionary ValidTokens(this Command command) } return tokens; + + static void AddCommandTokens(Dictionary tokens, Command cmd) + { + tokens.Add(cmd.Name, new Token(cmd.Name, TokenType.Command, cmd, Token.ImplicitPosition)); + + if (cmd._aliases is not null) + { + foreach (string childAlias in cmd._aliases) + { + tokens.Add(childAlias, new Token(childAlias, TokenType.Command, cmd, Token.ImplicitPosition)); + } + } + } + + static void AddOptionTokens(Dictionary tokens, Option option) + { + if (!tokens.ContainsKey(option.Name)) + { + tokens.Add(option.Name, new Token(option.Name, TokenType.Option, option, Token.ImplicitPosition)); + } + + if (option._aliases is not null) + { + foreach (string childAlias in option._aliases) + { + if (!tokens.ContainsKey(childAlias)) + { + tokens.Add(childAlias, new Token(childAlias, TokenType.Option, option, Token.ImplicitPosition)); + } + } + } + } } } } \ No newline at end of file diff --git a/src/System.CommandLine/Parsing/SymbolResultExtensions.cs b/src/System.CommandLine/Parsing/SymbolResultExtensions.cs index 86a573f269..dc5ccea62d 100644 --- a/src/System.CommandLine/Parsing/SymbolResultExtensions.cs +++ b/src/System.CommandLine/Parsing/SymbolResultExtensions.cs @@ -30,7 +30,7 @@ internal static Token Token(this SymbolResult symbolResult) static Token CreateImplicitToken(Option option) { - return new Token(option.GetLongestAlias(removePrefix: false), TokenType.Option, option, Parsing.Token.ImplicitPosition); + return new Token(option.Name, TokenType.Option, option, Parsing.Token.ImplicitPosition); } } } diff --git a/src/System.CommandLine/RootCommand.cs b/src/System.CommandLine/RootCommand.cs index 59443d33b5..25746bcde9 100644 --- a/src/System.CommandLine/RootCommand.cs +++ b/src/System.CommandLine/RootCommand.cs @@ -21,22 +21,6 @@ public class RootCommand : Command private static string? _executableName; private static string? _executableVersion; - private static string GetExecutableVersion() - { - var assembly = GetAssembly(); - - var assemblyVersionAttribute = assembly.GetCustomAttribute(); - - if (assemblyVersionAttribute is null) - { - return assembly.GetName().Version?.ToString() ?? ""; - } - else - { - return assemblyVersionAttribute.InformationalVersion; - } - } - /// The description of the command, shown in help. public RootCommand(string description = "") : base(ExecutableName, description) { @@ -58,11 +42,19 @@ public static string ExecutableName internal static string ExecutableVersion => _executableVersion ??= GetExecutableVersion(); - private protected override void RemoveAlias(string alias) + private static string GetExecutableVersion() { - if (!string.Equals(alias, ExecutableName, StringComparison.Ordinal)) + var assembly = GetAssembly(); + + var assemblyVersionAttribute = assembly.GetCustomAttribute(); + + if (assemblyVersionAttribute is null) + { + return assembly.GetName().Version?.ToString() ?? ""; + } + else { - base.RemoveAlias(alias); + return assemblyVersionAttribute.InformationalVersion; } } } diff --git a/src/System.CommandLine/Symbol.cs b/src/System.CommandLine/Symbol.cs index 6a93eb4527..811371f4b0 100644 --- a/src/System.CommandLine/Symbol.cs +++ b/src/System.CommandLine/Symbol.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.CommandLine.Completions; +using System.Diagnostics; namespace System.CommandLine { @@ -11,11 +12,11 @@ namespace System.CommandLine /// public abstract class Symbol { - private protected string? _name; private ParentNode? _firstParent; - private protected Symbol() + private protected Symbol(string name) { + Name = ThrowIfEmptyOrWithWhitespaces(name, nameof(name)); } /// @@ -24,15 +25,9 @@ private protected Symbol() public string? Description { get; set; } /// - /// Gets or sets the name of the symbol. + /// Gets the name of the symbol. /// - public virtual string Name - { - get => _name ??= DefaultName; - set => _name = value; - } - - private protected abstract string DefaultName { get; } + public string Name { get; } /// /// Represents the first parent node. @@ -84,5 +79,24 @@ public IEnumerable Parents /// public override string ToString() => $"{GetType().Name}: {Name}"; + + [DebuggerStepThrough] + internal static string ThrowIfEmptyOrWithWhitespaces(string value, string paramName) + { + if (string.IsNullOrWhiteSpace(value)) + { + throw new ArgumentException("Names and aliases cannot be null, empty, or consist entirely of whitespace."); + } + + for (var i = 0; i < value.Length; i++) + { + if (char.IsWhiteSpace(value[i])) + { + throw new ArgumentException($"Names and aliases cannot contain whitespace: \"{value}\"", paramName); + } + } + + return value; + } } } \ No newline at end of file From 46b3509065f98c1eb834c9550a1df1b27034f683 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Thu, 2 Mar 2023 22:33:03 +0100 Subject: [PATCH 2/7] update tests --- .../LocalizationTests.cs | 2 +- .../Perf_Parser_CustomScenarios.cs | 2 +- .../CommandLine/Perf_Parser_Simple.cs | 4 +- .../GeneratedCommandHandlerTests.cs | 12 +- .../ModelBinderTests.cs | 53 +-- .../ModelBindingCommandHandlerTests.cs | 4 +- .../ParameterBindingTests.cs | 4 +- .../TableRenderingTests.cs | 20 +- .../SuggestionDispatcher.cs | 19 +- src/System.CommandLine.Tests/ArgumentTests.cs | 360 +++++++++-------- .../Binding/TypeConversionTests.cs | 24 +- .../CommandLineConfigurationTests.cs | 14 +- src/System.CommandLine.Tests/CommandTests.cs | 36 +- .../CompletionContextTests.cs | 6 +- .../CompletionTests.cs | 54 +-- .../DirectiveTests.cs | 2 +- .../GlobalOptionTests.cs | 2 +- .../Help/HelpBuilderTests.Approval.cs | 49 ++- .../Help/HelpBuilderTests.Customization.cs | 26 +- .../Help/HelpBuilderTests.cs | 376 ++++++++---------- .../IdentifierSymbolTests.cs | 24 -- .../Invocation/TypoCorrectionTests.cs | 4 +- .../OptionTests.MultipleArgumentsPerToken.cs | 12 +- src/System.CommandLine.Tests/OptionTests.cs | 134 +++---- .../ParseDiagramTests.cs | 20 +- .../ParseDirectiveTests.cs | 2 +- .../ParseResultTests.cs | 14 +- .../ParserTests.DoubleDash.cs | 14 +- .../ParserTests.MultipleArguments.cs | 57 ++- .../ParserTests.MultiplePositions.cs | 10 +- .../ParserTests.RootCommandAndArg0.cs | 3 +- src/System.CommandLine.Tests/ParserTests.cs | 124 +++--- .../ParsingValidationTests.cs | 39 +- .../ResponseFileTests.cs | 16 +- .../RootCommandTests.cs | 10 - .../SuggestDirectiveTests.cs | 2 +- src/System.CommandLine.Tests/SymbolTests.cs | 23 -- .../TokenReplacementTests.cs | 14 +- src/System.CommandLine.Tests/UseHelpTests.cs | 6 +- .../VersionOptionTests.cs | 7 +- 40 files changed, 751 insertions(+), 853 deletions(-) delete mode 100644 src/System.CommandLine.Tests/IdentifierSymbolTests.cs delete mode 100644 src/System.CommandLine.Tests/SymbolTests.cs diff --git a/src/System.CommandLine.ApiCompatibility.Tests/LocalizationTests.cs b/src/System.CommandLine.ApiCompatibility.Tests/LocalizationTests.cs index 97abc2e674..3d730759b1 100644 --- a/src/System.CommandLine.ApiCompatibility.Tests/LocalizationTests.cs +++ b/src/System.CommandLine.ApiCompatibility.Tests/LocalizationTests.cs @@ -21,7 +21,7 @@ public void ErrorMessages_AreLocalized(string cultureName, string expectedMessag Command command = new(CommandName) { - new Argument() + new Argument("arg") }; ParseResult parseResult = command.Parse(CommandName); diff --git a/src/System.CommandLine.Benchmarks/CommandLine/Perf_Parser_CustomScenarios.cs b/src/System.CommandLine.Benchmarks/CommandLine/Perf_Parser_CustomScenarios.cs index dce90160e9..c0af04fb20 100644 --- a/src/System.CommandLine.Benchmarks/CommandLine/Perf_Parser_CustomScenarios.cs +++ b/src/System.CommandLine.Benchmarks/CommandLine/Perf_Parser_CustomScenarios.cs @@ -21,7 +21,7 @@ public void SetupOneOptWithNestedCommand() { _rootCommand = new Command("root_command"); var nestedCommand = new Command("nested_command"); - var option = new Option("-opt1", () => 123); + var option = new Option("-opt1") { DefaultValueFactory = (_) => 123 }; nestedCommand.Options.Add(option); _rootCommand.Subcommands.Add(nestedCommand); diff --git a/src/System.CommandLine.Benchmarks/CommandLine/Perf_Parser_Simple.cs b/src/System.CommandLine.Benchmarks/CommandLine/Perf_Parser_Simple.cs index 38a51da411..ffe283f11b 100644 --- a/src/System.CommandLine.Benchmarks/CommandLine/Perf_Parser_Simple.cs +++ b/src/System.CommandLine.Benchmarks/CommandLine/Perf_Parser_Simple.cs @@ -24,8 +24,8 @@ public class Perf_Parser_Simple private static RootCommand BuildCommand() { - Option boolOption = new(new[] { "--bool", "-b" }, "Bool option"); - Option stringOption = new(new[] { "--string", "-s" }, "String option"); + Option boolOption = new("--bool", "-b") { Description = "Bool option" }; + Option stringOption = new("--string", "-s") { Description = "String option" }; RootCommand command = new() { diff --git a/src/System.CommandLine.Generator.Tests/GeneratedCommandHandlerTests.cs b/src/System.CommandLine.Generator.Tests/GeneratedCommandHandlerTests.cs index 1ba6b23c9e..c9a6e50fce 100644 --- a/src/System.CommandLine.Generator.Tests/GeneratedCommandHandlerTests.cs +++ b/src/System.CommandLine.Generator.Tests/GeneratedCommandHandlerTests.cs @@ -29,7 +29,7 @@ void Execute(string fullnameOrNickname, IConsole console, int age) boundAge = age; } - var nameArgument = new Argument(); + var nameArgument = new Argument("arg"); var ageOption = new Option("--age"); var command = new Command("command") @@ -55,7 +55,7 @@ public async Task Can_generate_handler_for_void_returning_delegate() int boundAge = default; IConsole? boundConsole = null; - var nameArgument = new Argument(); + var nameArgument = new Argument("arg"); var ageOption = new Option("--age"); var command = new Command("command") @@ -180,7 +180,7 @@ async Task ExecuteAsync(string fullnameOrNickname, IConsole console, int age) boundAge = age; } - var nameArgument = new Argument(); + var nameArgument = new Argument("arg"); var ageOption = new Option("--age"); var command = new Command("command") @@ -272,7 +272,7 @@ void Execute(string fullnameOrNickname, IConsole console, int age) boundAge = age; } - var nameArgument = new Argument(); + var nameArgument = new Argument("arg"); var ageOption = new Option("--age"); var command = new Command("command") @@ -297,7 +297,7 @@ public async Task Can_generate_handler_for_lambda() int boundAge = default; IConsole? boundConsole = null; - var nameArgument = new Argument(); + var nameArgument = new Argument("arg"); var ageOption = new Option("--age"); var command = new Command("command") @@ -327,7 +327,7 @@ public async Task Can_generate_handler_for_lambda_wth_return_type_specified() int boundAge = default; IConsole? boundConsole = null; - var nameArgument = new Argument(); + var nameArgument = new Argument("arg"); var ageOption = new Option("--age"); var command = new Command("command") diff --git a/src/System.CommandLine.NamingConventionBinder.Tests/ModelBinderTests.cs b/src/System.CommandLine.NamingConventionBinder.Tests/ModelBinderTests.cs index 02014170d4..ab3be94522 100644 --- a/src/System.CommandLine.NamingConventionBinder.Tests/ModelBinderTests.cs +++ b/src/System.CommandLine.NamingConventionBinder.Tests/ModelBinderTests.cs @@ -98,7 +98,10 @@ public void Command_arguments_are_bound_by_name_to_complex_constructor_parameter [Fact] public void Explicitly_configured_default_values_can_be_bound_by_name_to_constructor_parameters() { - var option = new Option("--string-option", () => "the default"); + var option = new Option("--string-option") + { + DefaultValueFactory = (_) => "the default", + }; var command = new Command("the-command"); command.Options.Add(option); @@ -185,7 +188,7 @@ public void Types_having_constructors_accepting_a_single_string_are_bound_using_ [Fact] public void Explicitly_configured_default_values_can_be_bound_by_name_to_property_setters() { - var option = new Option("--value", () => "the default"); + var option = new Option("--value") { DefaultValueFactory = (_) => "the default" }; var command = new Command("the-command"); command.Options.Add(option); @@ -286,7 +289,10 @@ public void Default_values_from_options_on_parent_commands_are_bound_by_name_by_ { var parentCommand = new Command("parent-command") { - new Option("--int-option", () => 123), + new Option("--int-option") + { + DefaultValueFactory = (_) => 123, + }, new Command("child-command") }; @@ -306,10 +312,7 @@ public void Values_from_parent_command_arguments_are_bound_by_name_by_default() { var parentCommand = new Command("parent-command") { - new Argument - { - Name = nameof(ClassWithMultiLetterSetters.IntOption) - }, + new Argument(nameof(ClassWithMultiLetterSetters.IntOption)), new Command("child-command") }; @@ -329,9 +332,9 @@ public void Default_values_from_parent_command_arguments_are_bound_by_name_by_de { var parentCommand = new Command("parent-command") { - new Argument(() => 123) + new Argument(nameof(ClassWithMultiLetterSetters.IntOption)) { - Name = nameof(ClassWithMultiLetterSetters.IntOption) + DefaultValueFactory = (_) => 123 }, new Command("child-command") }; @@ -415,7 +418,7 @@ public void PropertyInfo_can_be_bound_to_option() public void PropertyInfo_can_be_bound_to_argument() { var command = new Command("the-command"); - var argument = new Argument { Arity = ArgumentArity.ExactlyOne }; + var argument = new Argument("arg") { Arity = ArgumentArity.ExactlyOne }; command.Arguments.Add(argument); var type = typeof(ClassWithMultiLetterSetters); @@ -455,7 +458,7 @@ public void PropertyExpression_can_be_bound_to_option() public void PropertyExpression_can_be_bound_to_argument() { var command = new Command("the-command"); - var argument = new Argument { Arity = ArgumentArity.ExactlyOne }; + var argument = new Argument("arg") { Arity = ArgumentArity.ExactlyOne }; command.Arguments.Add(argument); var binder = new ModelBinder(); @@ -488,7 +491,7 @@ public void Option_argument_is_bound_to_longest_constructor() public void Command_argument_is_bound_to_longest_constructor() { var rootCommand = new RootCommand(); - rootCommand.Arguments.Add(new Argument { Name = nameof(ClassWithMultipleCtor.IntProperty) }); + rootCommand.Arguments.Add(new Argument(nameof(ClassWithMultipleCtor.IntProperty))); var bindingContext = new InvocationContext(rootCommand.Parse("42")).BindingContext; var binder = new ModelBinder(); @@ -633,7 +636,7 @@ public void Custom_ModelBinders_specified_via_BindingContext_can_be_used_for_com var rootCommand = new RootCommand { - new Argument() + new Argument("arg") }; rootCommand.Handler = CommandHandler.Create>(x => boundInstance = x); @@ -661,8 +664,8 @@ public void Default_values_from_options_with_the_same_type_are_bound_and_use_the var rootCommand = new RootCommand { - new Option("one", () => 1), - new Option("two", () => 2) + new Option("one") { DefaultValueFactory = (_) => 1 }, + new Option("two") { DefaultValueFactory = (_) => 2 } }; rootCommand.Handler = CommandHandler.Create((one, two) => { @@ -746,12 +749,14 @@ public void Binder_does_not_match_by_substring() { var rootCommand = new RootCommand { - new Option( - new[] { "-b", "--bundle" }, - "the path to the app bundle to be installed"), - new Option( - new[] { "-1", "--bundle_id", "--bundle-id" }, - "specify bundle id for list and upload") + new Option("--bundle", "-b") + { + Description = "the path to the app bundle to be installed" + }, + new Option("--bundle-id", "--bundle_id", "-1") + { + Description = "specify bundle id for list and upload" + } }; DeployOptions boundOptions = null; @@ -787,15 +792,15 @@ public void InvocationContext_GetValue_with_generic_option_returns_value() [Fact] public void InvocationContext_GetValue_with_generic_argument_returns_value() { - Argument option = new(); + Argument argument = new("arg"); Command command = new("the-command") { - option + argument }; InvocationContext invocationContext = new(command.Parse("the-command 42")); - invocationContext.GetValue(option) + invocationContext.GetValue(argument) .Should() .Be(42); } diff --git a/src/System.CommandLine.NamingConventionBinder.Tests/ModelBindingCommandHandlerTests.cs b/src/System.CommandLine.NamingConventionBinder.Tests/ModelBindingCommandHandlerTests.cs index 14d8748525..016aa1c0ad 100644 --- a/src/System.CommandLine.NamingConventionBinder.Tests/ModelBindingCommandHandlerTests.cs +++ b/src/System.CommandLine.NamingConventionBinder.Tests/ModelBindingCommandHandlerTests.cs @@ -136,9 +136,7 @@ public async Task When_binding_fails_due_to_parameter_naming_mismatch_then_handl { string[] received = { "this should get overwritten" }; - var o = new Option( - new[] { "-i" }, - "Path to an image or directory of supported images"); + var o = new Option("-i") { Description = "Path to an image or directory of supported images" }; var command = new Command("command") { o }; command.Handler = CommandHandler.Create((nameDoesNotMatch, c) => received = nameDoesNotMatch); diff --git a/src/System.CommandLine.NamingConventionBinder.Tests/ParameterBindingTests.cs b/src/System.CommandLine.NamingConventionBinder.Tests/ParameterBindingTests.cs index 614e0eebe8..0bdb91b6d0 100644 --- a/src/System.CommandLine.NamingConventionBinder.Tests/ParameterBindingTests.cs +++ b/src/System.CommandLine.NamingConventionBinder.Tests/ParameterBindingTests.cs @@ -124,8 +124,8 @@ void Execute(string name, int age) var command = new Command("command") { - new Option(new[] { "-n", "--NAME" }), - new Option(new[] { "-a", "--age" }) + new Option("--NAME", "-n"), + new Option("--age", "-a") }; command.Handler = CommandHandler.Create(Execute); diff --git a/src/System.CommandLine.Rendering.Tests/TableRenderingTests.cs b/src/System.CommandLine.Rendering.Tests/TableRenderingTests.cs index 69dd68eb03..905897b1c8 100644 --- a/src/System.CommandLine.Rendering.Tests/TableRenderingTests.cs +++ b/src/System.CommandLine.Rendering.Tests/TableRenderingTests.cs @@ -34,8 +34,8 @@ public TableRenderingTests(ITestOutputHelper output) public void A_row_is_written_for_each_item_and_a_header_for_each_column(OutputMode outputMode) { var options = new[] { - new Option("-s", "a short option"), - new Option("--very-long", "a long option") + new Option("-s") { Description = "a short option" }, + new Option("--very-long") { Description = "a long option" } }; var view = new OptionsHelpView(options); @@ -56,10 +56,10 @@ public void A_row_is_written_for_each_item_and_a_header_for_each_column(OutputMo public void A_row_is_written_for_each_item_and_a_header_for_each_column_in_file_mode() { var options = new[] - { - new Option("-s", "a short option"), - new Option("--very-long", "a long option") - }; + { + new Option("-s") { Description = "a short option" }, + new Option("--very-long") { Description = "a long option" } + }; var view = new OptionsHelpView(options); @@ -80,8 +80,8 @@ public void A_row_is_written_for_each_item_and_a_header_for_each_column_in_file_ public void Column_widths_are_aligned_to_the_longest_cell(OutputMode outputMode) { var options = new[] { - new Option("-s", "an option"), - new Option("--very-long", "an option") + new Option("-s") { Description = "an option" }, + new Option("--very-long") { Description = "an option" }, }; var view = new OptionsHelpView(options); @@ -101,8 +101,8 @@ public void Column_widths_are_aligned_to_the_longest_cell(OutputMode outputMode) public void Column_widths_are_aligned_to_the_longest_cell_in_file_mode() { var options = new[] { - new Option("-s", "an option"), - new Option("--very-long", "an option") + new Option("-s") { Description = "an option" }, + new Option("--very-long") { Description = "an option" }, }; var view = new OptionsHelpView(options); diff --git a/src/System.CommandLine.Suggest/SuggestionDispatcher.cs b/src/System.CommandLine.Suggest/SuggestionDispatcher.cs index e8fd907f5c..228eb06f7e 100644 --- a/src/System.CommandLine.Suggest/SuggestionDispatcher.cs +++ b/src/System.CommandLine.Suggest/SuggestionDispatcher.cs @@ -22,10 +22,7 @@ public SuggestionDispatcher(ISuggestionRegistration suggestionRegistration, ISug _suggestionStore = suggestionStore ?? new SuggestionStore(); - var shellTypeArgument = new Argument - { - Name = nameof(ShellType) - }; + var shellTypeArgument = new Argument(nameof(ShellType)); CompleteScriptCommand = new Command("script", "Print complete script for specific shell") { @@ -53,12 +50,12 @@ public SuggestionDispatcher(ISuggestionRegistration suggestionRegistration, ISug }; GetCommand.SetHandler(context => Get(context)); - var commandPathOption = new Option("--command-path", "The path to the command for which to register suggestions"); + var commandPathOption = new Option("--command-path") { Description = "The path to the command for which to register suggestions" }; RegisterCommand = new Command("register", "Registers an app for suggestions") { commandPathOption, - new Option("--suggestion-command", "The command to invoke to retrieve suggestions") + new Option("--suggestion-command") { Description = "The command to invoke to retrieve suggestions" } }; RegisterCommand.SetHandler((context, cancellationToken) => @@ -93,7 +90,7 @@ public SuggestionDispatcher(ISuggestionRegistration suggestionRegistration, ISug private static Option GetExecutableOption() { - var option = new Option(new[] { "-e", "--executable" }, "The executable to call for suggestions"); + var option = new Option("--executable", "-e") { Description = "The executable to call for suggestions" }; option.AcceptLegalFilePathsOnly(); return option; @@ -101,9 +98,11 @@ private static Option GetExecutableOption() private Command ListCommand { get; } - private Option PositionOption { get; } = new(new[] { "-p", "--position" }, - description: "The current character position on the command line", - defaultValueFactory: () => short.MaxValue); + private Option PositionOption { get; } = new("--position", "-p") + { + Description = "The current character position on the command line", + DefaultValueFactory = (_) => short.MaxValue + }; private Command RegisterCommand { get; } diff --git a/src/System.CommandLine.Tests/ArgumentTests.cs b/src/System.CommandLine.Tests/ArgumentTests.cs index 391d9adfbf..241a3e10d9 100644 --- a/src/System.CommandLine.Tests/ArgumentTests.cs +++ b/src/System.CommandLine.Tests/ArgumentTests.cs @@ -9,36 +9,25 @@ using System.Linq; using System.Threading.Tasks; using Xunit; -using System.CommandLine.Completions; namespace System.CommandLine.Tests { - public class ArgumentTests : SymbolTests + public class ArgumentTests { [Fact] public void By_default_there_is_no_default_value() { - var argument = new Argument(); + var argument = new Argument("arg"); argument.HasDefaultValue.Should().BeFalse(); } - [Fact] - public void When_default_value_is_set_to_null_then_HasDefaultValue_is_true() - { - var argument = new Argument(); - - argument.SetDefaultValue(null); - - argument.HasDefaultValue.Should().BeTrue(); - } - [Fact] public void When_default_value_factory_is_set_then_HasDefaultValue_is_true() { - var argument = new Argument(); + var argument = new Argument("arg"); - argument.SetDefaultValueFactory(() => null); + argument.DefaultValueFactory = (_) => null; argument.HasDefaultValue.Should().BeTrue(); } @@ -62,7 +51,10 @@ public class CustomParsing [Fact] public void HasDefaultValue_can_be_set_to_true() { - var argument = new Argument(result => null, true); + var argument = new Argument("arg") + { + DefaultValueFactory = result => null + }; argument.HasDefaultValue .Should() @@ -72,7 +64,10 @@ public void HasDefaultValue_can_be_set_to_true() [Fact] public void HasDefaultValue_can_be_set_to_false() { - var argument = new Argument(result => null, false); + var argument = new Argument("arg") + { + DefaultValueFactory = null + }; argument.HasDefaultValue .Should() @@ -82,7 +77,10 @@ public void HasDefaultValue_can_be_set_to_false() [Fact] public void GetDefaultValue_returns_specified_value() { - var argument = new Argument(result => "the-default", isDefault: true); + var argument = new Argument("arg") + { + DefaultValueFactory = result => "the-default" + }; argument.GetDefaultValue() .Should() @@ -92,17 +90,10 @@ public void GetDefaultValue_returns_specified_value() [Fact] public void GetDefaultValue_returns_null_when_parse_delegate_returns_true_without_setting_a_value() { - var argument = new Argument(result => null, isDefault: true); - - argument.GetDefaultValue() - .Should() - .BeNull(); - } - - [Fact] - public void GetDefaultValue_returns_null_when_parse_delegate_returns_true_and_sets_value_to_null() - { - var argument = new Argument(result => null, isDefault: true); + var argument = new Argument("arg") + { + DefaultValueFactory = result => null + }; argument.GetDefaultValue() .Should() @@ -112,7 +103,10 @@ public void GetDefaultValue_returns_null_when_parse_delegate_returns_true_and_se [Fact] public void GetDefaultValue_can_return_null() { - var argument = new Argument(result => null, isDefault: true); + var argument = new Argument("arg") + { + DefaultValueFactory = result => null + }; argument.GetDefaultValue() .Should() @@ -122,11 +116,14 @@ public void GetDefaultValue_can_return_null() [Fact] public void Validation_failure_message_can_be_specified_when_parsing_tokens() { - var argument = new Argument(result => + var argument = new Argument("arg") { - result.AddError("oops!"); - return null; - }); + CustomParser = result => + { + result.AddError("oops!"); + return null; + } + }; new RootCommand { argument }.Parse("x") .Errors @@ -141,11 +138,14 @@ public void Validation_failure_message_can_be_specified_when_parsing_tokens() [Fact] public void Validation_failure_message_can_be_specified_when_evaluating_default_argument_value() { - var argument = new Argument(result => + var argument = new Argument("arg") { - result.AddError("oops!"); - return null; - }, true); + DefaultValueFactory = result => + { + result.AddError("oops!"); + return null; + } + }; new RootCommand { argument }.Parse("") .Errors @@ -160,13 +160,14 @@ public void Validation_failure_message_can_be_specified_when_evaluating_default_ [Fact] public void Validation_failure_message_can_be_specified_when_evaluating_default_option_value() { - var option = new Option( - "-x", - result => + var option = new Option("-x") + { + DefaultValueFactory = result => { result.AddError("oops!"); return null; - }, true); + } + }; new RootCommand { option }.Parse("") .Errors @@ -181,7 +182,10 @@ public void Validation_failure_message_can_be_specified_when_evaluating_default_ [Fact] public void custom_parsing_of_scalar_value_from_an_argument_with_one_token() { - var argument = new Argument(result => int.Parse(result.Tokens.Single().Value)); + var argument = new Argument("arg") + { + CustomParser = result => int.Parse(result.Tokens.Single().Value) + }; new RootCommand { argument }.Parse("123") .GetValue(argument) @@ -192,7 +196,10 @@ public void custom_parsing_of_scalar_value_from_an_argument_with_one_token() [Fact] public void custom_parsing_of_sequence_value_from_an_argument_with_one_token() { - var argument = new Argument>(result => result.Tokens.Single().Value.Split(',').Select(int.Parse)); + var argument = new Argument>("arg") + { + CustomParser = result => result.Tokens.Single().Value.Split(',').Select(int.Parse) + }; new RootCommand { argument }.Parse("1,2,3") .GetValue(argument) @@ -203,10 +210,10 @@ public void custom_parsing_of_sequence_value_from_an_argument_with_one_token() [Fact] public void custom_parsing_of_sequence_value_from_an_argument_with_multiple_tokens() { - var argument = new Argument>(result => + var argument = new Argument>("arg") { - return result.Tokens.Select(t => int.Parse(t.Value)).ToArray(); - }); + CustomParser = result => result.Tokens.Select(t => int.Parse(t.Value)).ToArray() + }; new RootCommand { argument }.Parse("1 2 3") .GetValue(argument) @@ -217,8 +224,9 @@ public void custom_parsing_of_sequence_value_from_an_argument_with_multiple_toke [Fact] public void custom_parsing_of_scalar_value_from_an_argument_with_multiple_tokens() { - var argument = new Argument(result => result.Tokens.Select(t => int.Parse(t.Value)).Sum()) + var argument = new Argument("arg") { + CustomParser = result => result.Tokens.Select(t => int.Parse(t.Value)).Sum(), Arity = ArgumentArity.ZeroOrMore }; @@ -235,13 +243,14 @@ public void Option_ArgumentResult_Parent_is_set_correctly_when_token_is_implicit var command = new Command("the-command") { - new Option( - "-x", - parseArgument: argResult => + new Option("-x") + { + DefaultValueFactory = argResult => { argumentResult = argResult; return null; - }, isDefault: true) + } + } }; CommandLineConfiguration simpleConfig = new (command); @@ -264,13 +273,14 @@ public void Option_ArgumentResult_parentage_to_root_symbol_is_set_correctly_when var command = new Command("the-command") { - new Option( - "-x", - parseArgument: argResult => + new Option("-x") + { + CustomParser = argResult => { argumentResult = argResult; return null; - }, isDefault: true) + } + } }; command.Parse(""); @@ -292,17 +302,19 @@ public void Option_ArgumentResult_parentage_to_root_symbol_is_set_correctly_when public void Symbol_can_be_found_without_explicitly_traversing_result_tree(string commandLine) { SymbolResult resultForOptionX = null; - var optionX = new Option( - "-x", - parseArgument: _ => string.Empty); + var optionX = new Option("-x") + { + CustomParser = _ => string.Empty + }; - var optionY = new Option( - "-y", - parseArgument: argResult => + var optionY = new Option("-y") + { + CustomParser = argResult => { resultForOptionX = argResult.FindResultFor(optionX); return string.Empty; - }); + } + }; var command = new Command("the-command") { @@ -328,12 +340,14 @@ public void Command_ArgumentResult_Parent_is_set_correctly_when_token_is_implici var command = new Command("the-command") { - new Argument( - parse: argResult => + new Argument("arg") + { + CustomParser = argResult => { argumentResult = argResult; return null; - }, isDefault: true) + } + } }; command.Parse(""); @@ -354,11 +368,14 @@ public async Task Custom_argument_parser_is_only_called_once() var callCount = 0; var handlerWasCalled = false; - var option = new Option("--value", result => + var option = new Option("--value") { - callCount++; - return int.Parse(result.Tokens.Single().Value); - }); + CustomParser = result => + { + callCount++; + return int.Parse(result.Tokens.Single().Value); + } + }; var command = new RootCommand(); command.SetHandler((int value) => handlerWasCalled = true, option); @@ -373,8 +390,11 @@ public async Task Custom_argument_parser_is_only_called_once() [Fact] public void Default_value_and_custom_argument_parser_can_be_used_together() { - var argument = new Argument(_ => 789, true); - argument.SetDefaultValue(123); + var argument = new Argument("arg") + { + CustomParser = _ => 789, + DefaultValueFactory = _ => 123 + }; var result = new RootCommand { argument }.Parse(""); @@ -388,20 +408,22 @@ public void Multiple_command_arguments_can_have_custom_parse_delegates() { var root = new RootCommand { - new Argument("from", argumentResult => - { - argumentResult.AddError("nope"); - return null; - }, true) + new Argument("from") { + CustomParser = argumentResult => + { + argumentResult.AddError("nope"); + return null; + }, Arity = new ArgumentArity(0, 2) }, - new Argument("to", argumentResult => - { - argumentResult.AddError("UH UH"); - return null; - }, true) + new Argument("to") { + CustomParser = argumentResult => + { + argumentResult.AddError("UH UH"); + return null; + }, Arity = ArgumentArity.ExactlyOne } }; @@ -424,12 +446,15 @@ public void When_custom_conversion_fails_then_an_option_does_not_accept_further_ { var command = new Command("the-command") { - new Argument(), - new Option("-x", argResult => + new Argument("arg"), + new Option("-x") + { + CustomParser = argResult => { argResult.AddError("nope"); return default; - }) + } + } }; var result = command.Parse("the-command -x nope yep"); @@ -440,17 +465,20 @@ public void When_custom_conversion_fails_then_an_option_does_not_accept_further_ [Fact] public void When_argument_cannot_be_parsed_as_the_specified_type_then_getting_value_throws() { - var option = new Option(new[] { "-o", "--one" }, argumentResult => + var option = new Option("--one", "-o") { - if (int.TryParse(argumentResult.Tokens.Select(t => t.Value).Single(), out var value)) + CustomParser = argumentResult => { - return value; - } + if (int.TryParse(argumentResult.Tokens.Select(t => t.Value).Single(), out var value)) + { + return value; + } - argumentResult.AddError($"'{argumentResult.Tokens.Single().Value}' is not an integer"); + argumentResult.AddError($"'{argumentResult.Tokens.Single().Value}' is not an integer"); - return default; - }); + return default; + } + }; var command = new Command("the-command") { @@ -477,10 +505,10 @@ public void Parse_delegate_is_called_once_per_parse_operation() var command = new RootCommand { - new Option( - "-x", - result => ++i, - isDefault: true) + new Option("-x") + { + CustomParser = result => ++i, + } }; command.Parse(""); @@ -495,9 +523,9 @@ public void Parse_delegate_is_called_once_per_parse_operation() [InlineData("--bananas argument-is-specified", "argument-is-specified")] public void Parse_delegate_is_called_when_Option_Arity_allows_zero_tokens(string commandLine, string expectedValue) { - var opt = new Option( - "--bananas", - parseArgument: result => + var opt = new Option("--bananas") + { + CustomParser = result => { if (result.Tokens.Count == 0) { @@ -512,8 +540,7 @@ public void Parse_delegate_is_called_when_Option_Arity_allows_zero_tokens(string { return result.Tokens[0].Value; } - }, isDefault: true) - { + }, Arity = ArgumentArity.ZeroOrOne }; @@ -531,9 +558,9 @@ public void Parse_delegate_is_called_when_Option_Arity_allows_zero_tokens(string [InlineData("1 2 3 -o 999 4 5 6 7 8")] public void Custom_parser_can_pass_on_remaining_tokens(string commandLine) { - var argument1 = new Argument( - "one", - result => + var argument1 = new Argument("one") + { + CustomParser = result => { result.OnlyTake(3); @@ -543,10 +570,12 @@ public void Custom_parser_can_pass_on_remaining_tokens(string commandLine) int.Parse(result.Tokens[1].Value), int.Parse(result.Tokens[2].Value) }; - }); - var argument2 = new Argument( - "two", - result => result.Tokens.Select(t => t.Value).Select(int.Parse).ToArray()); + } + }; + var argument2 = new Argument("two") + { + CustomParser = result => result.Tokens.Select(t => t.Value).Select(int.Parse).ToArray() + }; var command = new RootCommand { argument1, @@ -573,9 +602,9 @@ public void Custom_parser_can_pass_on_remaining_tokens(string commandLine) public void When_tokens_are_passed_on_by_custom_parser_on_last_argument_then_they_become_unmatched_tokens() { - var argument1 = new Argument( - "one", - result => + var argument1 = new Argument("one") + { + CustomParser = result => { result.OnlyTake(3); @@ -585,7 +614,8 @@ public void When_tokens_are_passed_on_by_custom_parser_on_last_argument_then_the int.Parse(result.Tokens[1].Value), int.Parse(result.Tokens[2].Value) }; - }); + } + }; var command = new RootCommand { @@ -603,9 +633,9 @@ public void When_tokens_are_passed_on_by_custom_parser_on_last_argument_then_the [Fact] public void When_custom_parser_passes_on_tokens_the_argument_result_tokens_reflect_the_change() { - var argument1 = new Argument( - "one", - result => + var argument1 = new Argument("one") + { + CustomParser = result => { result.OnlyTake(3); @@ -615,10 +645,12 @@ public void When_custom_parser_passes_on_tokens_the_argument_result_tokens_refle int.Parse(result.Tokens[1].Value), int.Parse(result.Tokens[2].Value) }; - }); - var argument2 = new Argument( - "two", - result => result.Tokens.Select(t => t.Value).Select(int.Parse).ToArray()); + } + }; + var argument2 = new Argument("two") + { + CustomParser = result => result.Tokens.Select(t => t.Value).Select(int.Parse).ToArray() + }; var command = new RootCommand { argument1, @@ -645,14 +677,15 @@ public void When_custom_parser_passes_on_tokens_the_argument_result_tokens_refle [Fact] public void OnlyTake_throws_when_called_with_a_negative_value() { - var argument = new Argument( - "one", - result => - { - result.OnlyTake(-1); + var argument = new Argument("one") + { + CustomParser = result => + { + result.OnlyTake(-1); - return null; - }); + return null; + } + }; argument.Invoking(a => new RootCommand { a }.Parse("1 2 3")) .Should() @@ -666,15 +699,16 @@ public void OnlyTake_throws_when_called_with_a_negative_value() [Fact] public void OnlyTake_throws_when_called_twice() { - var argument = new Argument( - "one", - result => + var argument = new Argument("one") + { + CustomParser = result => { result.OnlyTake(1); result.OnlyTake(1); return null; - }); + } + }; argument.Invoking(a => new RootCommand { a }.Parse("1 2 3")) .Should() @@ -688,12 +722,15 @@ public void OnlyTake_throws_when_called_twice() [Fact] public void OnlyTake_can_pass_on_all_tokens_from_one_multiple_arity_argument_to_another() { - var argument1 = new Argument(result => + var argument1 = new Argument("arg1") { - result.OnlyTake(0); - return null; - }); - var argument2 = new Argument(); + CustomParser = result => + { + result.OnlyTake(0); + return null; + } + }; + var argument2 = new Argument("arg2"); var command = new RootCommand { argument1, @@ -710,12 +747,15 @@ public void OnlyTake_can_pass_on_all_tokens_from_one_multiple_arity_argument_to_ [Fact] // https://github.com/dotnet/command-line-api/issues/1759 public void OnlyTake_can_pass_on_all_tokens_from_a_single_arity_argument_to_another() { - var scalar = new Argument(parse: ctx => + var scalar = new Argument("arg") { - ctx.OnlyTake(0); - return null; - }); - Argument multiple = new(); + CustomParser = ctx => + { + ctx.OnlyTake(0); + return null; + } + }; + Argument multiple = new("args"); var command = new RootCommand { @@ -734,30 +774,33 @@ public void OnlyTake_can_pass_on_all_tokens_from_a_single_arity_argument_to_anot [Fact] //https://github.com/dotnet/command-line-api/issues/1779 public void OnlyTake_can_pass_on_all_tokens_from_a_single_arity_argument_to_another_that_also_passes_them_all_on() { - var first = new Argument(name: "first", parse: ctx => - { - ctx.OnlyTake(0); - return null; - }) + var first = new Argument("first") { + CustomParser = ctx => + { + ctx.OnlyTake(0); + return null; + }, Arity = ArgumentArity.ZeroOrOne }; - var second = new Argument(name: "second", parse: ctx => - { - ctx.OnlyTake(0); - return null; - }) + var second = new Argument(name: "second") { + CustomParser = ctx => + { + ctx.OnlyTake(0); + return null; + }, Arity = ArgumentArity.ZeroOrMore }; - var third = new Argument(name: "third", parse: ctx => - { - ctx.OnlyTake(3); - return new[] { "1", "2", "3" }; - }) + var third = new Argument(name: "third") { + CustomParser = ctx => + { + ctx.OnlyTake(3); + return new[] { "1", "2", "3" }; + }, Arity = ArgumentArity.ZeroOrMore }; @@ -779,7 +822,7 @@ public void OnlyTake_can_pass_on_all_tokens_from_a_single_arity_argument_to_anot [Fact] public void Argument_of_enum_can_limit_enum_members_as_valid_values() { - var argument = new Argument(); + var argument = new Argument("color"); argument.AcceptOnlyFromAmong(ConsoleColor.Red.ToString(), ConsoleColor.Green.ToString()); Command command = new("set-color") @@ -794,10 +837,5 @@ public void Argument_of_enum_can_limit_enum_members_as_valid_values() .Should() .BeEquivalentTo(new[] { $"Argument 'Fuschia' not recognized. Must be one of:\n\t'Red'\n\t'Green'" }); } - - protected override Symbol CreateSymbol(string name) - { - return new Argument(name); - } } } \ No newline at end of file diff --git a/src/System.CommandLine.Tests/Binding/TypeConversionTests.cs b/src/System.CommandLine.Tests/Binding/TypeConversionTests.cs index c3ce14de47..ad604d2779 100644 --- a/src/System.CommandLine.Tests/Binding/TypeConversionTests.cs +++ b/src/System.CommandLine.Tests/Binding/TypeConversionTests.cs @@ -99,7 +99,7 @@ public void Argument_of_array_of_FileInfo_can_be_called_without_custom_conversio [Fact] public void Argument_defaults_arity_to_One_for_non_IEnumerable_types() { - var argument = new Argument(); + var argument = new Argument("arg"); argument.Arity.Should().BeEquivalentTo(ArgumentArity.ExactlyOne); } @@ -107,7 +107,7 @@ public void Argument_defaults_arity_to_One_for_non_IEnumerable_types() [Fact] public void Argument_defaults_arity_to_ExactlyOne_for_string() { - var argument = new Argument(); + var argument = new Argument("arg"); argument.Arity.Should().BeEquivalentTo(ArgumentArity.ExactlyOne); } @@ -117,7 +117,7 @@ public void Command_Argument_defaults_arity_to_ZeroOrOne_for_nullable_types() { var command = new Command("the-command") { - new Argument() + new Argument("arg") }; command.Arguments.Single().Arity.Should().BeEquivalentTo(ArgumentArity.ZeroOrOne); @@ -137,7 +137,7 @@ public void Argument_infers_arity_of_IEnumerable_types_as_OneOrMore(Type type) [Fact] public void Argument_parses_as_the_default_value_when_the_option_has_not_been_applied() { - var option = new Option("-x", () => 123); + var option = new Option("-x") { DefaultValueFactory = (_) => 123 }; var command = new Command("something") { @@ -152,7 +152,7 @@ public void Argument_parses_as_the_default_value_when_the_option_has_not_been_ap [Fact] public void Option_does_not_parse_as_the_default_value_when_the_option_has_been_applied() { - var option = new Option("-x", () => 123); + var option = new Option("-x") { DefaultValueFactory = (_) => 123 }; var command = new Command("something") { @@ -322,7 +322,7 @@ public void By_default_a_bool_option_without_arguments_parses_as_false_when_it_i [Fact] public void An_option_with_a_default_value_parses_as_the_default_value_when_the_option_has_not_been_applied() { - var option = new Option("-x", () => "123"); + var option = new Option("-x") { DefaultValueFactory = (_) => "123" }; var command = new Command("something") { @@ -339,7 +339,7 @@ public void An_option_with_a_default_value_parses_as_the_default_value_when_the_ [Fact] public void An_option_with_a_default_value_of_null_parses_as_null_when_the_option_has_not_been_applied() { - var option = new Option("-x", () => null); + var option = new Option("-x") { DefaultValueFactory = (_) => null }; var command = new Command("something") { @@ -356,7 +356,7 @@ public void An_option_with_a_default_value_of_null_parses_as_null_when_the_optio [Fact] public void A_default_value_of_a_non_string_type_can_be_specified() { - var option = new Option("-x", () => 123); + var option = new Option("-x") { DefaultValueFactory = (_) => 123 }; var command = new Command("something") { @@ -374,7 +374,7 @@ public void A_default_value_with_a_custom_constructor_can_be_specified_for_an_op { var directoryInfo = new DirectoryInfo(Directory.GetCurrentDirectory()); - var option = new Option("-x", () => directoryInfo); + var option = new Option("-x") { DefaultValueFactory = (_) => directoryInfo }; var command = new Command("something") { @@ -391,7 +391,7 @@ public void A_default_value_with_a_custom_constructor_can_be_specified_for_a_com { var directoryInfo = new DirectoryInfo(Directory.GetCurrentDirectory()); - var argument = new Argument("the-arg", () => directoryInfo); + var argument = new Argument("the-arg") { DefaultValueFactory = (_) => directoryInfo }; var command = new Command("something") { @@ -410,7 +410,7 @@ public void A_default_value_with_a_custom_constructor_can_be_specified_for_a_com [Fact] public void Specifying_an_option_argument_overrides_the_default_value() { - var option = new Option("-x", () => 123); + var option = new Option("-x") { DefaultValueFactory = (_) => 123 }; var command = new Command("something") { @@ -947,7 +947,7 @@ public void When_getting_an_array_of_values_and_specifying_a_conversion_type_tha [Fact] public void String_defaults_to_null_when_not_specified() { - var argument = new Argument(); + var argument = new Argument("arg"); var command = new Command("mycommand") { argument diff --git a/src/System.CommandLine.Tests/CommandLineConfigurationTests.cs b/src/System.CommandLine.Tests/CommandLineConfigurationTests.cs index 1f992805f3..13cc0d5453 100644 --- a/src/System.CommandLine.Tests/CommandLineConfigurationTests.cs +++ b/src/System.CommandLine.Tests/CommandLineConfigurationTests.cs @@ -13,7 +13,7 @@ public void ThrowIfInvalid_throws_if_there_are_duplicate_sibling_option_aliases_ { var option1 = new Option("--dupe"); var option2 = new Option("-y"); - option2.AddAlias("--dupe"); + option2.Aliases.Add("--dupe"); var command = new RootCommand { @@ -38,7 +38,7 @@ public void ThrowIfInvalid_throws_if_there_are_duplicate_sibling_option_aliases_ { var option1 = new Option("--dupe"); var option2 = new Option("--ok"); - option2.AddAlias("--dupe"); + option2.Aliases.Add("--dupe"); var command = new RootCommand { @@ -66,7 +66,7 @@ public void ThrowIfInvalid_throws_if_there_are_duplicate_sibling_subcommand_alia { var command1 = new Command("dupe"); var command2 = new Command("not-a-dupe"); - command2.AddAlias("dupe"); + command2.Aliases.Add("dupe"); var rootCommand = new RootCommand { @@ -91,7 +91,7 @@ public void ThrowIfInvalid_throws_if_there_are_duplicate_sibling_subcommand_alia { var command1 = new Command("dupe"); var command2 = new Command("not-a-dupe"); - command2.AddAlias("dupe"); + command2.Aliases.Add("dupe"); var command = new RootCommand { @@ -119,7 +119,7 @@ public void ThrowIfInvalid_throws_if_sibling_command_and_option_aliases_collide_ { var option = new Option("dupe"); var command = new Command("not-a-dupe"); - command.AddAlias("dupe"); + command.Aliases.Add("dupe"); var rootCommand = new RootCommand { @@ -144,7 +144,7 @@ public void ThrowIfInvalid_throws_if_sibling_command_and_option_aliases_collide_ { var option = new Option("dupe"); var command = new Command("not-a-dupe"); - command.AddAlias("dupe"); + command.Aliases.Add("dupe"); var rootCommand = new RootCommand { @@ -172,7 +172,7 @@ public void ThrowIfInvalid_throws_if_there_are_duplicate_sibling_global_option_a { var option1 = new Option("--dupe") { AppliesToSelfAndChildren = true }; var option2 = new Option("-y") { AppliesToSelfAndChildren = true }; - option2.AddAlias("--dupe"); + option2.Aliases.Add("--dupe"); var command = new RootCommand(); command.Options.Add(option1); diff --git a/src/System.CommandLine.Tests/CommandTests.cs b/src/System.CommandLine.Tests/CommandTests.cs index 0766e5d5a6..45a63ae174 100644 --- a/src/System.CommandLine.Tests/CommandTests.cs +++ b/src/System.CommandLine.Tests/CommandTests.cs @@ -8,7 +8,7 @@ namespace System.CommandLine.Tests { - public class CommandTests : SymbolTests + public class CommandTests { private readonly Command _outerCommand; @@ -104,12 +104,12 @@ public void Commands_at_multiple_levels_can_have_their_own_arguments() { var outer = new Command("outer") { - new Argument() + new Argument("outer_arg") }; outer.Subcommands.Add( new Command("inner") { - new Argument() + new Argument("inner_arg") }); var result = outer.Parse("outer arg1 inner arg2 arg3"); @@ -133,10 +133,9 @@ public void Aliases_is_aware_of_added_alias() { var command = new Command("original"); - command.AddAlias("added"); + command.Aliases.Add("added"); command.Aliases.Should().Contain("added"); - command.HasAlias("added").Should().BeTrue(); } @@ -166,7 +165,7 @@ public void When_a_command_alias_is_added_and_contains_whitespace_then_an_inform { var command = new Command("-x"); - Action addAlias = () => command.AddAlias(alias); + Action addAlias = () => command.Aliases.Add(alias); addAlias .Should() @@ -208,7 +207,7 @@ public void ParseResult_Command_identifies_innermost_command(string input, strin public void Commands_can_have_aliases() { var command = new Command("this"); - command.AddAlias("that"); + command.Aliases.Add("that"); command.Aliases.Should().BeEquivalentTo("this", "that"); command.Aliases.Should().BeEquivalentTo("this", "that"); @@ -222,7 +221,7 @@ public void Commands_can_have_aliases() public void RootCommand_can_have_aliases() { var command = new RootCommand(); - command.AddAlias("that"); + command.Aliases.Add("that"); command.Aliases.Should().BeEquivalentTo(RootCommand.ExecutableName, "that"); command.Aliases.Should().BeEquivalentTo(RootCommand.ExecutableName, "that"); @@ -236,7 +235,7 @@ public void RootCommand_can_have_aliases() public void Subcommands_can_have_aliases() { var subcommand = new Command("this"); - subcommand.AddAlias("that"); + subcommand.Aliases.Add("that"); var rootCommand = new RootCommand { @@ -254,27 +253,12 @@ public void It_retains_argument_name_when_it_is_provided() { var command = new Command("-alias") { - new Argument - { - Name = "arg" - } + new Argument("arg") }; command.Arguments.Single().Name.Should().Be("arg"); } - [Fact] - public void When_Name_is_set_to_its_current_value_then_it_is_not_removed_from_aliases() - { - var command = new Command("name"); - - command.Name = "name"; - - command.HasAlias("name").Should().BeTrue(); - command.Aliases.Should().Contain("name"); - command.Aliases.Should().Contain("name"); - } - [Fact] public void AddGlobalOption_updates_Options_property() { @@ -307,7 +291,5 @@ public void When_Options_is_referenced_before_a_global_option_is_added_then_addi .Should() .Contain(option); } - - protected override Symbol CreateSymbol(string name) => new Command(name); } } diff --git a/src/System.CommandLine.Tests/CompletionContextTests.cs b/src/System.CommandLine.Tests/CompletionContextTests.cs index f2c36fb931..e843bcd677 100644 --- a/src/System.CommandLine.Tests/CompletionContextTests.cs +++ b/src/System.CommandLine.Tests/CompletionContextTests.cs @@ -114,7 +114,7 @@ public void When_position_is_greater_than_input_length_in_a_string_command_line_ var command = new Command("the-command") { - new Argument(), + new Argument("arg"), option1, new Option("--option2") }; @@ -186,7 +186,7 @@ public void When_position_is_unspecified_in_array_command_line_and_final_token_m { option1, new Option("--option2"), - new Argument() + new Argument("arg") }; string textToMatch = command.Parse(new[] { "the-command", "--option1", "a" }) @@ -210,7 +210,7 @@ public void When_position_is_specified_in_string_command_line_then_it_returns_ar var command = new Command("the-command") { - new Argument() + new Argument("arg") }; var position = commandLine.IndexOf("$", StringComparison.Ordinal); diff --git a/src/System.CommandLine.Tests/CompletionTests.cs b/src/System.CommandLine.Tests/CompletionTests.cs index dd32982ab1..90e9854c76 100644 --- a/src/System.CommandLine.Tests/CompletionTests.cs +++ b/src/System.CommandLine.Tests/CompletionTests.cs @@ -41,9 +41,9 @@ public void Command_GetCompletions_returns_available_option_aliases() { var command = new Command("command") { - new Option("--one", "option one"), - new Option("--two", "option two"), - new Option("--three", "option three") + new Option("--one") { Description = "option one" }, + new Option("--two") { Description = "option two" }, + new Option("--three") { Description = "option three" }, }; var completions = command.GetCompletions(CompletionContext.Empty); @@ -59,8 +59,8 @@ public void Command_GetCompletions_returns_available_option_aliases_for_global_o { var subcommand2 = new Command("command2") { - new Option("--one", "option one"), - new Option("--two", "option two") + new Option("--one") { Description = "option one" }, + new Option("--two") { Description = "option two" } }; var subcommand1 = new Command("command1") @@ -73,7 +73,11 @@ public void Command_GetCompletions_returns_available_option_aliases_for_global_o subcommand1 }; - rootCommand.Options.Add(new Option("--three", "option three") { AppliesToSelfAndChildren = true }); + rootCommand.Options.Add(new Option("--three") + { + Description = "option three", + AppliesToSelfAndChildren = true + }); var completions = subcommand2.GetCompletions(CompletionContext.Empty); @@ -123,8 +127,8 @@ public void Command_GetCompletions_returns_available_subcommands_and_option_alia var command = new Command("command") { new Command("subcommand", "subcommand"), - new Option("--option", "option"), - new Argument + new Option("--option") { Description = "option" }, + new Argument("args") { Arity = ArgumentArity.OneOrMore, CompletionSources = { "command-argument" } @@ -196,7 +200,7 @@ public void When_an_option_has_a_default_value_it_will_still_be_suggested() { var command = new RootCommand { - new Option("--apple", defaultValueFactory: () => "cortland"), + new Option("--apple") { DefaultValueFactory = (_) => "cortland" }, new Option("--banana"), new Option("--cherry") }; @@ -298,13 +302,13 @@ public void When_a_subcommand_has_been_specified_then_its_sibling_commands_alias { new Option("--cortland") }; - apple.AddAlias("apl"); + apple.Aliases.Add("apl"); var banana = new Command("banana") { new Option("--cavendish") }; - banana.AddAlias("bnn"); + banana.Aliases.Add("bnn"); var rootCommand = new RootCommand { @@ -348,7 +352,7 @@ public void When_a_subcommand_has_been_specified_then_its_sibling_options_with_a { new Command("child"), new Option("--parent-option"), - new Argument() + new Argument("arg") }; var commandLine = "--parent-option 123 child"; @@ -366,7 +370,7 @@ public void When_a_subcommand_has_been_specified_then_its_child_options_will_be_ { var command = new RootCommand("parent") { - new Argument(), + new Argument("arg"), new Command("child") { new Option("--child-option") @@ -443,7 +447,7 @@ public void An_option_can_be_hidden_from_completions_by_setting_IsHidden_to_true { IsHidden = true }, - new Option("-n", "Not hidden") + new Option("-n") { Description = "Not hidden" } }; CommandLineConfiguration simpleConfig = new (command); @@ -486,7 +490,7 @@ public void Subcommand_names_are_available_as_suggestions() { new Command("one", "Command one"), new Command("two", "Command two"), - new Argument() + new Argument("arg") }; var commandLine = "test"; @@ -505,7 +509,7 @@ public void Both_subcommands_and_options_are_available_as_suggestions() { new Command("one"), new Option("--one"), - new Argument() + new Argument("arg") }; var commandLine = "test"; @@ -627,7 +631,7 @@ public void Command_argument_completions_can_be_provided_using_a_delegate() { new Command("one") { - new Argument + new Argument("arg") { CompletionSources = { _ => new[] { "vegetable", "mineral", "animal" } } } @@ -796,7 +800,7 @@ public void Arguments_of_type_enum_provide_enum_values_as_suggestions() { var command = new Command("the-command") { - new Argument() + new Argument("arg") }; var completions = command.Parse("the-command create") @@ -847,8 +851,8 @@ public void Option_substring_matching_when_arguments_have_default_values() { var command = new Command("the-command") { - new Option("--implicit", () => "the-default"), - new Option("--not", () => "the-default") + new Option("--implicit") { DefaultValueFactory = (_) => "the-default" }, + new Option("--not") { DefaultValueFactory = (_) => "the-default" } }; var completions = command.Parse("m").GetCompletions(); @@ -870,7 +874,7 @@ public void It_can_provide_completions_within_quotes(string commandLine, int pos "\"nuget:Microsoft.DotNet.Interactive\"" }; - var argument = new Argument(); + var argument = new Argument("arg"); argument.CompletionSources.Add(expectedSuggestions); var r = new Command("#r") @@ -891,7 +895,7 @@ public void It_can_provide_completions_within_quotes(string commandLine, int pos [Fact] public void Default_completions_can_be_cleared_and_replaced() { - var argument = new Argument(); + var argument = new Argument("day"); argument.CompletionSources.Clear(); argument.CompletionSources.Add(new[] { "mon", "tues", "wed", "thur", "fri", "sat", "sun" }); var command = new Command("the-command") @@ -912,7 +916,7 @@ public void Default_completions_can_be_appended_to() { var command = new Command("the-command") { - new Argument + new Argument("day") { CompletionSources = { "mon", "tues", "wed", "thur", "fri", "sat", "sun" } } @@ -940,7 +944,7 @@ public void Default_completions_can_be_appended_to() public void Completions_for_options_provide_a_description() { var description = "The option before -y."; - var option = new Option("-x", description); + var option = new Option("-x") { Description = description }; var completions = new RootCommand { option }.GetCompletions(CompletionContext.Empty); @@ -987,7 +991,7 @@ public void When_option_completions_are_available_then_they_are_suggested_when_a private static Argument CreateArgumentWithAcceptOnlyFromAmong(params string[] values) { - Argument argument = new(); + Argument argument = new("arg"); argument.AcceptOnlyFromAmong(values); return argument; } diff --git a/src/System.CommandLine.Tests/DirectiveTests.cs b/src/System.CommandLine.Tests/DirectiveTests.cs index 3b9ad10a0e..76d5437ed1 100644 --- a/src/System.CommandLine.Tests/DirectiveTests.cs +++ b/src/System.CommandLine.Tests/DirectiveTests.cs @@ -143,7 +143,7 @@ public void Directives_can_be_disabled() { RootCommand rootCommand = new () { - new Argument>() + new Argument>("args") }; var configuration = new CommandLineConfiguration( diff --git a/src/System.CommandLine.Tests/GlobalOptionTests.cs b/src/System.CommandLine.Tests/GlobalOptionTests.cs index 7de39fdd0a..e099369da8 100644 --- a/src/System.CommandLine.Tests/GlobalOptionTests.cs +++ b/src/System.CommandLine.Tests/GlobalOptionTests.cs @@ -49,7 +49,7 @@ public void When_a_required_global_option_is_omitted_it_results_in_an_error() public void When_a_required_global_option_has_multiple_aliases_the_error_message_uses_longest() { var rootCommand = new RootCommand(); - var requiredOption = new Option(new[] { "-i", "--i-must-be-set" }) + var requiredOption = new Option("-i", "--i-must-be-set") { IsRequired = true, AppliesToSelfAndChildren = true diff --git a/src/System.CommandLine.Tests/Help/HelpBuilderTests.Approval.cs b/src/System.CommandLine.Tests/Help/HelpBuilderTests.Approval.cs index 77949afe8c..abf70c8e8a 100644 --- a/src/System.CommandLine.Tests/Help/HelpBuilderTests.Approval.cs +++ b/src/System.CommandLine.Tests/Help/HelpBuilderTests.Approval.cs @@ -14,57 +14,64 @@ public partial class HelpBuilderTests [UseReporter(typeof(DiffReporter))] public void Help_layout_has_not_changed() { - var command = new RootCommand(description: "Test description") + var command = new Command("the-root-command", "Test description") { new Argument("the-root-arg-no-description-no-default"), - new Argument("the-root-arg-no-description-default", - argResult => "the-root-arg-no-description-default-value", - isDefault: true), + new Argument("the-root-arg-no-description-default") + { + DefaultValueFactory = (_) => "the-root-arg-no-description-default-value", + }, new Argument("the-root-arg-no-default") { Description = "the-root-arg-no-default-description", }, - new Argument("the-root-arg", () => "the-root-arg-one-value") + new Argument("the-root-arg") { + DefaultValueFactory = (_) => "the-root-arg-one-value", Description = "the-root-arg-description" }, - new Argument("the-root-arg-enum-default", () => FileAccess.Read) + new Argument("the-root-arg-enum-default") { + DefaultValueFactory = (_) => FileAccess.Read, Description = "the-root-arg-enum-default-description" }, - new Option(aliases: new string[] {"--the-root-option-no-arg", "-trna"}) { + new Option("--the-root-option-no-arg", "-trna") + { Description = "the-root-option-no-arg-description", IsRequired = true }, - new Option( - aliases: new string[] {"--the-root-option-no-description-default-arg", "-trondda"}, - parseArgument: _ => "the-root-option--no-description-default-arg-value", - isDefault: true - ), - new Option(aliases: new string[] {"--the-root-option-no-default-arg", "-tronda"}) { + new Option("--the-root-option-no-description-default-arg", "-trondda") + { + DefaultValueFactory = (_) => "the-root-option--no-description-default-arg-value", + }, + new Option("--the-root-option-no-default-arg", "-tronda") + { Description = "the-root-option-no-default-description", - ArgumentHelpName = "the-root-option-arg-no-default-arg", + HelpName = "the-root-option-arg-no-default-arg", IsRequired = true }, - new Option(aliases: new string[] {"--the-root-option-default-arg", "-troda"}, () => "the-root-option-arg-value") + new Option("--the-root-option-default-arg", "-troda") { + DefaultValueFactory = (_) => "the-root-option-arg-value", Description = "the-root-option-default-arg-description", - ArgumentHelpName = "the-root-option-arg", + HelpName = "the-root-option-arg", }, - new Option(aliases: new string[] {"--the-root-option-enum-arg", "-troea"}, () => FileAccess.Read) + new Option("--the-root-option-enum-arg", "-troea") { + DefaultValueFactory = (_) => FileAccess.Read, Description = "the-root-option-description", }, - new Option(aliases: new string[] {"--the-root-option-required-enum-arg", "-trorea"}, () => FileAccess.Read) + new Option("--the-root-option-required-enum-arg", "-trorea") { + DefaultValueFactory = (_) => FileAccess.Read, Description = "the-root-option-description", IsRequired = true }, - new Option(aliases: new string[] {"--the-root-option-multi-line-description", "-tromld"}) { + new Option("--the-root-option-multi-line-description", "-tromld") + { Description = "the-root-option\r\nmulti-line\ndescription" - } + }, }; - command.Name = "the-root-command"; StringWriter writer = new(); GetHelpBuilder(LargeMaxWidth).Write(command, writer); diff --git a/src/System.CommandLine.Tests/Help/HelpBuilderTests.Customization.cs b/src/System.CommandLine.Tests/Help/HelpBuilderTests.Customization.cs index 04d6e53bcd..7fb3a4e6a2 100644 --- a/src/System.CommandLine.Tests/Help/HelpBuilderTests.Customization.cs +++ b/src/System.CommandLine.Tests/Help/HelpBuilderTests.Customization.cs @@ -33,7 +33,7 @@ public Customization() [Fact] public void Option_can_customize_default_value() { - var option = new Option("--the-option", defaultValueFactory: () => "not 42"); + var option = new Option("--the-option") { DefaultValueFactory = (_) => "not 42" }; var command = new Command("the-command", "command help") { option @@ -52,7 +52,7 @@ public void Option_can_customize_default_value() [Fact] public void Option_can_customize_first_column_text() { - var option = new Option("--the-option", "option description"); + var option = new Option("--the-option") { Description = "option description" }; var command = new Command("the-command", "command help") { option @@ -167,7 +167,7 @@ public void Subcommand_can_customize_first_column_text() [Fact] public void Command_arguments_can_customize_first_column_text() { - var argument = new Argument("arg-name", "arg description"); + var argument = new Argument("arg-name") { Description = "arg description" }; var command = new Command("the-command", "command help") { argument @@ -186,7 +186,11 @@ public void Command_arguments_can_customize_first_column_text() [Fact] public void Command_arguments_can_customize_second_column_text() { - var argument = new Argument("some-arg", description: "Default description", defaultValueFactory: () => "not 42"); + var argument = new Argument("some-arg") + { + Description = "Default description", + DefaultValueFactory = (_) => "not 42" + }; var command = new Command("the-command", "command help") { argument @@ -205,7 +209,10 @@ public void Command_arguments_can_customize_second_column_text() [Fact] public void Command_arguments_can_customize_default_value() { - var argument = new Argument("some-arg", defaultValueFactory: () => "not 42"); + var argument = new Argument("some-arg") + { + DefaultValueFactory = (_) => "not 42" + }; var command = new Command("the-command", "command help") { argument @@ -237,7 +244,7 @@ public void Customize_throws_when_symbol_is_null() public void Option_can_fallback_to_default_when_customizing(bool conditionA, bool conditionB, string expected) { var command = new Command("test"); - var option = new Option("--option", "description"); + var option = new Option("--option") { Description = "description" }; command.Options.Add(option); @@ -273,8 +280,11 @@ public void Argument_can_fallback_to_default_when_customizing( string expected) { var command = new Command("test"); - var argument = new Argument("arg", "description"); - argument.SetDefaultValue("default"); + var argument = new Argument("arg") + { + Description = "description", + DefaultValueFactory = _ => "default" + }; command.Arguments.Add(argument); diff --git a/src/System.CommandLine.Tests/Help/HelpBuilderTests.cs b/src/System.CommandLine.Tests/Help/HelpBuilderTests.cs index 4ee61aa4fe..e76e08fcb2 100644 --- a/src/System.CommandLine.Tests/Help/HelpBuilderTests.cs +++ b/src/System.CommandLine.Tests/Help/HelpBuilderTests.cs @@ -77,10 +77,7 @@ public void Synopsis_section_properly_wraps_description() [Fact] public void Command_name_in_synopsis_can_be_specified() { - var command = new RootCommand - { - Name = "custom-name" - }; + var command = new Command("custom-name"); var helpBuilder = GetHelpBuilder(SmallMaxWidth); helpBuilder.Write(command, _console); @@ -104,19 +101,14 @@ public void Usage_section_shows_arguments_if_there_are_arguments_for_command_whe int maxArity, string expectedArgsUsage) { - var argument = new Argument + var argument = new Argument("the-args") { - Name = "the-args", Arity = new ArgumentArity(minArity, maxArity) }; var command = new Command("the-command", "command help") { argument, - new Option(new[] - { - "-v", - "--verbosity" - }) + new Option("--verbosity", "-v") { Description = "Sets the verbosity" } @@ -144,16 +136,14 @@ public void Usage_section_shows_arguments_if_there_are_arguments_for_command_whe int maxArityForArg2, string expectedArgsUsage) { - var arg1 = new Argument + var arg1 = new Argument("arg1") { - Name = "arg1", Arity = new ArgumentArity( minArityForArg1, maxArityForArg1) }; - var arg2 = new Argument + var arg2 = new Argument("arg2") { - Name = "arg2", Arity = new ArgumentArity( minArityForArg2, maxArityForArg2) @@ -162,7 +152,7 @@ public void Usage_section_shows_arguments_if_there_are_arguments_for_command_whe { arg1, arg2, - new Option(new[] { "-v", "--verbosity" }, "Sets the verbosity") + new Option("--verbosity", "-v") { Description = "Sets the verbosity" } }; var rootCommand = new RootCommand(); @@ -185,7 +175,7 @@ public void Usage_section_for_subcommand_shows_names_of_parent_commands() outer.Subcommands.Add(inner); var innerEr = new Command("inner-er", "the inner-er command"); inner.Subcommands.Add(innerEr); - innerEr.Options.Add(new Option("--some-option", "some option")); + innerEr.Options.Add(new Option("--some-option") { Description = "some option" }); var rootCommand = new RootCommand(); rootCommand.Add(outer); @@ -203,19 +193,13 @@ public void Usage_section_for_subcommand_shows_arguments_for_subcommand_and_pare { var inner = new Command("inner", "command help") { - new Option("-v", "Sets the verbosity"), - new Argument - { - Name = "inner-args" - } + new Option("-v") {Description = "Sets the verbosity" }, + new Argument("inner-args") }; _ = new Command("outer", "command help") { inner, - new Argument - { - Name = "outer-args" - } + new Argument("outer-args") }; _helpBuilder.Write(inner, _console); @@ -234,7 +218,7 @@ public void Usage_section_does_not_show_additional_arguments_when_TreatUnmatched "some-command", "Does something"); command.Options.Add( - new Option("-x", "Indicates whether x")); + new Option("-x") { Description = "Indicates whether x" }); _helpBuilder.Write(command, _console); @@ -247,7 +231,7 @@ public void Usage_section_does_not_show_additional_arguments_when_TreatUnmatched var command = new RootCommand(); var subcommand = new Command("some-command", "Does something"); command.Subcommands.Add(subcommand); - subcommand.Options.Add(new Option("-x", "Indicates whether x")); + subcommand.Options.Add(new Option("-x") { Description = "Indicates whether x" }); subcommand.TreatUnmatchedTokensAsErrors = true; _helpBuilder.Write(subcommand, _console); @@ -261,7 +245,7 @@ public void Usage_section_shows_additional_arguments_when_TreatUnmatchedTokensAs var command = new RootCommand(); var subcommand = new Command("some-command", "Does something"); command.Subcommands.Add(subcommand); - subcommand.Options.Add(new Option("-x", "Indicates whether x")); + subcommand.Options.Add(new Option("-x") { Description = "Indicates whether x" }); subcommand.TreatUnmatchedTokensAsErrors = false; _helpBuilder.Write(subcommand, _console); @@ -274,16 +258,10 @@ public void Usage_section_keeps_added_newlines() { var outer = new Command("outer-command", "command help") { - new Argument - { - Name = $"outer args {NewLine}\r\nwith new\nlines" - }, + new Argument($"outer args {NewLine}\r\nwith new\nlines"), new Command("inner-command", "command help") { - new Argument - { - Name = "inner-args" - } + new Argument("inner-args") } }; @@ -306,16 +284,10 @@ public void Usage_section_properly_wraps_description() var outerCommand = new Command("outer-command", "command help") { - new Argument - { - Name = "outer args long enough to wrap to a new line" - }, + new Argument("outer args long enough to wrap to a new line"), new Command("inner-command", "command help") { - new Argument - { - Name = "inner-args" - } + new Argument("inner-args") } }; //NB: Using Command with a fixed name, rather than RootCommand here @@ -342,14 +314,12 @@ public void Usage_section_does_not_contain_hidden_argument() var commandName = "the-command"; var visibleArgName = "visible"; var command = new Command(commandName, "Does things"); - var hiddenArg = new Argument + var hiddenArg = new Argument("hidden") { - Name = "hidden", IsHidden = true }; - var visibleArg = new Argument + var visibleArg = new Argument(visibleArgName) { - Name = visibleArgName, IsHidden = false }; command.Arguments.Add(hiddenArg); @@ -395,9 +365,8 @@ public void Arguments_section_is_included_if_there_are_commands_with_arguments_c { var command = new Command("the-command", "command help") { - new Argument + new Argument("arg command name") { - Name = "arg command name", Description = "test" } }; @@ -412,7 +381,7 @@ public void Arguments_section_is_not_included_if_there_are_options_with_no_argum { var command = new RootCommand { - new Option(new[] { "-v", "--verbosity" }, "Sets the verbosity.") + new Option("--verbosity", "-v") { Description = "Sets the verbosity." } }; _helpBuilder.Write(command, _console); @@ -425,9 +394,10 @@ public void Arguments_section_is_not_included_if_there_are_only_options_with_arg { var command = new Command("command") { - new Option("-v", "Sets the verbosity.") + new Option("-v") { - ArgumentHelpName = "argument for options" + Description = "Sets the verbosity.", + HelpName = "argument for options" } }; @@ -441,9 +411,9 @@ public void Arguments_section_includes_configured_argument_aliases() { var command = new Command("the-command", "command help") { - new Option(new[] { "-v", "--verbosity" }) + new Option("--verbosity", "-v") { - ArgumentHelpName = "LEVEL", + HelpName = "LEVEL", Description = "Sets the verbosity." } }; @@ -468,9 +438,9 @@ public void Arguments_section_uses_name_over_suggestions_if_specified() { var command = new Command("the-command") { - new Option(new[] { "-v", "--verbosity" }) + new Option("--verbosity", "-v") { - ArgumentHelpName = "LEVEL" + HelpName = "LEVEL" } }; @@ -485,9 +455,8 @@ public void Arguments_section_uses_description_if_provided() { var command = new Command("the-command", "Help text from description") { - new Argument + new Argument("the-arg") { - Name = "the-arg", Description = "Help text from HelpDetail" } }; @@ -509,15 +478,13 @@ public void Arguments_section_does_not_contain_hidden_argument() var hiddenDesc = "the hidden desc"; var visibleArgName = "the-visible"; var visibleDesc = "the visible desc"; - var hiddenArg = new Argument + var hiddenArg = new Argument(hiddenArgName) { - Name = hiddenArgName, Description = hiddenDesc, IsHidden = true }; - var visibleArg = new Argument + var visibleArg = new Argument(visibleArgName) { - Name = visibleArgName, Description = visibleDesc, IsHidden = false }; @@ -539,9 +506,8 @@ public void Arguments_section_does_not_contain_hidden_argument() [Fact] public void Arguments_section_does_not_repeat_arguments_that_appear_on_parent_command() { - var reused = new Argument + var reused = new Argument("reused") { - Name = "reused", Description = "This argument is valid on both outer and inner commands" }; var inner = new Command("inner", "The inner command") @@ -567,17 +533,16 @@ public void Arguments_section_aligns_arguments_on_new_lines() { var inner = new Command("inner", "HelpDetail text for the inner command") { - new Argument + new Argument("the-inner-command-arg") { - Name = "the-inner-command-arg", Description = "The argument for the inner command", } }; _ = new Command("outer", "HelpDetail text for the outer command") { - new Argument + new Argument("outer-command-arg") { - Name = "outer-command-arg", Description = "The argument for the outer command" + Description = "The argument for the outer command" }, inner }; @@ -597,9 +562,8 @@ public void Arguments_section_keeps_added_newlines() { var command = new Command("outer", "Help text for the outer command") { - new Argument + new Argument("outer-command-arg") { - Name = "outer-command-arg", Description = $"The argument\r\nfor the\ninner command" } }; @@ -620,9 +584,8 @@ public void Arguments_section_keeps_added_newlines_when_width_is_very_small() { var command = new Command("outer", "Help text for the outer command") { - new Argument + new Argument("outer-command-arg") { - Name = "outer-command-arg", Description = $"The argument\r\nfor the\ninner command", } }; @@ -652,9 +615,8 @@ public void Arguments_section_properly_wraps_description() var command = new Command("outer", "Help text for the outer command") { - new Argument + new Argument("outer-command-arg") { - Name = "outer-command-arg", Description = longCmdText } }; @@ -679,9 +641,8 @@ public void Arguments_section_properly_wraps() var command = new RootCommand { - new Argument + new Argument(name) { - Name = name, Description = description } }; @@ -707,8 +668,8 @@ public void Command_argument_usage_indicates_enums_values(bool nullable) var description = "This is the argument description"; Argument argument = nullable - ? new Argument() - : new Argument(); + ? new Argument("arg") + : new Argument("arg"); argument.Description = description; var command = new Command("outer", "Help text for the outer command") @@ -735,8 +696,8 @@ public void Option_argument_usage_is_empty_for_boolean_values(bool nullable) var description = "This is the option description"; Option option = nullable - ? new Option("--opt", description) - : new Option("--opt", description); + ? new Option("--opt") { Description = description } + : new Option("--opt") { Description = description }; var command = new Command( "outer", "Help text for the outer command") @@ -756,8 +717,8 @@ public void Command_arguments_show_argument_name_in_first_column() { var command = new RootCommand { - new Argument("boolArgument", "Some value"), - new Argument("intArgument", "Another value"), + new Argument("boolArgument") { Description = "Some value" }, + new Argument("intArgument") { Description = "Another value" }, }; var helpBuilder = GetHelpBuilder(SmallMaxWidth); @@ -780,8 +741,8 @@ public void Option_argument_first_column_indicates_enums_values(bool nullable) var description = "This is the argument description"; Option option = nullable - ? new Option("--opt", description) - : new Option("--opt", description); + ? new Option("--opt") { Description = description } + : new Option("--opt") { Description = description }; var command = new Command( "outer", "Help text for the outer command") @@ -799,12 +760,11 @@ public void Option_argument_first_column_indicates_enums_values(bool nullable) [Fact] public void Help_describes_default_value_for_argument() { - var argument = new Argument + var argument = new Argument("the-arg") { - Name = "the-arg", Description = "Help text from HelpDetail", + DefaultValueFactory = (_) => "the-arg-value" }; - argument.SetDefaultValue("the-arg-value"); var command = new Command("the-command", "Help text from description") { argument }; @@ -821,8 +781,11 @@ public void Help_describes_default_value_for_argument() [Fact] public void Help_does_not_show_default_value_for_argument_when_default_value_is_empty() { - var argument = new Argument("the-arg", "The argument description"); - argument.SetDefaultValue(""); + var argument = new Argument("the-arg") + { + Description = "The argument description", + DefaultValueFactory = (_) => "" + }; var command = new Command("the-command", "The command description") { @@ -837,12 +800,15 @@ public void Help_does_not_show_default_value_for_argument_when_default_value_is_ help.Should().NotContain("[default"); } - + [Fact] public void Help_does_not_show_default_value_for_option_when_default_value_is_empty() { - var option = new Option("-x", description: "The option description"); - option.SetDefaultValue(""); + var option = new Option("-x") + { + Description = "The option description", + DefaultValueFactory = (_) => "", + }; var command = new Command("the-command", "The command description") { @@ -861,17 +827,14 @@ public void Help_does_not_show_default_value_for_option_when_default_value_is_em [Fact] public void Command_arguments_default_value_provided() { - var argument = new Argument + var argument = new Argument("the-arg") { - Name = "the-arg", + DefaultValueFactory = (_) => "the-arg-value", }; - - var otherArgument = new Argument + var otherArgument = new Argument("the-other-arg") { - Name = "the-other-arg", + DefaultValueFactory = (_) => "the-other-arg-value" }; - argument.SetDefaultValue("the-arg-value"); - otherArgument.SetDefaultValue("the-other-arg-value"); var command = new Command("the-command", "Help text from description") { @@ -898,8 +861,10 @@ public void Command_arguments_with_default_values_that_are_enumerable_display_pi { var command = new Command("the-command", "command help") { - new Argument>("filter-size", - defaultValueFactory: () => new List() { 0, 2, 4 }) + new Argument>("filter-size") + { + DefaultValueFactory = (_) => new List() { 0, 2, 4 } + } }; _helpBuilder.Write(command, _console); @@ -913,9 +878,8 @@ public void Command_arguments_with_default_values_that_are_enumerable_display_pi [Fact] public void Command_shared_arguments_with_one_or_more_arity_are_displayed_as_being_required() { - var arg = new Argument + var arg = new Argument("shared-args") { - Name = "shared-args", Arity = ArgumentArity.OneOrMore }; @@ -991,12 +955,14 @@ public void Options_section_includes_option_with_empty_description() public void Options_section_does_not_contain_option_with_HelpDefinition_that_IsHidden() { var command = new Command("the-command"); - command.Options.Add(new Option("-x", "Is Hidden") + command.Options.Add(new Option("-x") { + Description = "Is Hidden", IsHidden = true }); - command.Options.Add(new Option("-n", "Not Hidden") + command.Options.Add(new Option("-n") { + Description = "Not Hidden", IsHidden = false }); @@ -1013,15 +979,17 @@ public void Options_section_does_not_contain_option_with_HelpDefinition_that_IsH [Fact] public void Options_section_aligns_options_on_new_lines() { - var command = new Command( - "the-command", - "Help text for the command") - { - new Option(new[] { "-a", "--aaa" }, - "An option with 8 characters"), - new Option(new[] { "-b", "--bbbbbbbbbb" }, - "An option with 15 characters") - }; + var command = new Command("the-command", "Help text for the command") + { + new Option("--aaa", "-a") + { + Description = "An option with 8 characters", + }, + new Option("--bbbbbbbbbb","-b") + { + Description = "An option with 15 characters" + } + }; _helpBuilder.Write(command, _console); @@ -1040,11 +1008,9 @@ public void Options_section_aligns_options_on_new_lines() public void Retains_single_dash_on_multi_char_option() { var command = new Command("command", "Help Test") - { - new Option( - new[] { "-multi", "--alt-option" }, - "HelpDetail for option") - }; + { + new Option("-multi", "--alt-option") { Description = "HelpDetail for option" } + }; _helpBuilder.Write(command, _console); @@ -1057,11 +1023,9 @@ public void Retains_single_dash_on_multi_char_option() public void Options_section_retains_multiple_dashes_on_single_char_option() { var command = new Command("command", "Help Test") - { - new Option( - new[] { "--m", "--alt-option" }, - "HelpDetail for option") - }; + { + new Option("--m", "--alt-option") { Description = "HelpDetail for option" } + }; _helpBuilder.Write(command, _console); @@ -1076,9 +1040,10 @@ public void Options_section_keeps_added_newlines() "test-command", "Help text for the command") { - new Option( - new[] { "-a", "--aaa" }, - $"Help{NewLine}for \r\n the\noption") + new Option("--aaa", "-a") + { + Description = $"Help{NewLine}for \r\n the\noption" + } }; _helpBuilder.Write(command, _console); @@ -1101,9 +1066,9 @@ public void Options_section_properly_wraps_description() var command = new Command("test-command", "Help text for the command") { - new Option("-x", "Option with a short description"), - new Option(new[] { "-a", "--aaa" }, longOptionText), - new Option("-y", "Option with a short description"), + new Option("-x") { Description = "Option with a short description" }, + new Option("--aaa", "-a") { Description = longOptionText }, + new Option("-y") { Description = "Option with a short description" }, }; HelpBuilder helpBuilder = GetHelpBuilder(SmallMaxWidth); @@ -1126,9 +1091,13 @@ public void Options_section_properly_wraps_description_when_long_default_value_i var command = new Command("test-command", "Help text for the command") { - new Option("-x", "Option with a short description"), - new Option(new[] { "-a", "--aaa" }, description: longOptionText, defaultValueFactory: () => "the quick brown fox jumps over the lazy dog"), - new Option("-y", "Option with a short description"), + new Option("-x") { Description = "Option with a short description" }, + new Option("--aaa", "-a") + { + Description = longOptionText, + DefaultValueFactory = (_) => "the quick brown fox jumps over the lazy dog" + }, + new Option("-y") { Description = "Option with a short description" }, }; HelpBuilder helpBuilder = GetHelpBuilder(SmallMaxWidth); @@ -1150,7 +1119,7 @@ public void Options_section_properly_wraps() var command = new RootCommand { - new Option(alias, description) + new Option(alias) { Description = description } }; HelpBuilder helpBuilder = GetHelpBuilder(SmallMaxWidth); @@ -1189,10 +1158,10 @@ public void Required_options_are_indicated_when_argument_is_named() { var command = new RootCommand { - new Option(new[] {"-r", "--required" }) + new Option("--required", "-r") { IsRequired = true, - ArgumentHelpName = "ARG" + HelpName = "ARG" } }; @@ -1219,12 +1188,13 @@ public void Help_option_is_shown_in_help() .Contain($"-?, -h, --help{_columnPadding}Show help and usage information"); } + // TODO: use HiddenAliases here [Fact] public void Options_aliases_differing_only_by_prefix_are_deduplicated_favoring_dashed_prefixes() { var command = new RootCommand { - new Option(new[] { "-x", "/x" }) + new Option("-x", "/x") }; _helpBuilder.Write(command, _console); @@ -1239,7 +1209,7 @@ public void Options_aliases_differing_only_by_prefix_are_deduplicated_favoring_d { var command = new RootCommand { - new Option(new[] { "--long", "/long" }) + new Option("--long", "/long") }; _helpBuilder.Write(command, _console); @@ -1254,10 +1224,10 @@ public void Options_help_preserves_the_order_options_are_added_the_the_parent_co { var command = new RootCommand { - new Option(new[] { "--first", "-f" }), - new Option(new[] { "--second", "-s" }), - new Option(new[] { "--third" }), - new Option(new[] { "--last", "-l" }) + new Option("--first", "-f"), + new Option("--second", "-s"), + new Option("--third"), + new Option("--last", "-l") }; _helpBuilder.Write(command, _console); @@ -1278,7 +1248,7 @@ public void Option_aliases_are_shown_before_long_names_regardless_of_alphabetica { var command = new RootCommand { - new Option(new[] { "-z", "-a", "--zzz", "--aaa" }) + new Option("-z", "-a", "--zzz", "--aaa") }; _helpBuilder.Write(command, _console); @@ -1291,9 +1261,10 @@ public void Help_describes_default_value_for_option_with_argument_having_default { var command = new Command("the-command", "command help") { - new Option(new[] { "-arg"}, defaultValueFactory: () => "the-arg-value") + new Option("-arg") { - ArgumentHelpName = "the-arg" + DefaultValueFactory = (_) => "the-arg-value", + HelpName = "the-arg" } }; @@ -1305,16 +1276,16 @@ public void Help_describes_default_value_for_option_with_argument_having_default help.Should().Contain($"[default: the-arg-value]"); } - + [Fact] public void Option_arguments_with_default_values_that_are_enumerable_display_pipe_delimited_list() { var command = new Command("the-command", "command help") { - new Option>( - "--filter-size", - defaultValueFactory: () => new List { 0, 2, 4 }) - { } + new Option>("--filter-size") + { + DefaultValueFactory = (_) => new List { 0, 2, 4 } + } }; _helpBuilder.Write(command, _console); @@ -1330,10 +1301,10 @@ public void Option_arguments_with_default_values_that_are_array_display_pipe_del { var command = new Command("the-command", "command help") { - new Option( - "--prefixes", - defaultValueFactory: () => new[]{ "^(TODO|BUG)", "^HACK" }) - { } + new Option("--prefixes") + { + DefaultValueFactory = (_) => new[]{ "^(TODO|BUG)", "^HACK" } + } }; _helpBuilder.Write(command, _console); @@ -1354,22 +1325,20 @@ public void Option_arguments_with_default_values_that_are_array_display_pipe_del public void Subcommand_help_does_not_include_names_of_sibling_commands() { var inner = new Command("inner", "inner description") - { - new Command( - "inner-er", "inner-er description") - { - new Option("some-option", - "some-option description") - } - }; + { + new Command("inner-er", "inner-er description") + { + new Option("some-option") { Description = "some-option description" } + } + }; var sibling = new Command("sibling", "sibling description"); var outer = new Command("outer", "outer description") - { - sibling, - inner - }; + { + sibling, + inner + }; _helpBuilder.Write(inner, _console); @@ -1381,16 +1350,10 @@ public void Subcommands_keep_added_newlines() { var command = new Command("outer", "outer command help") { - new Argument - { - Name = "outer-args" - }, + new Argument("outer-args"), new Command("inner", $"inner{NewLine}command help \r\n with \nnewlines") { - new Argument - { - Name = "inner-args" - } + new Argument("inner-args") } }; @@ -1418,21 +1381,11 @@ public void Subcommands_properly_wraps_description() var command = new Command("outer-command", "outer command help") { - new Argument - { - Name = "outer-args" - }, + new Argument("outer-args"), new Command("inner-command", longSubcommandDescription) { - new Argument - { - Name = "inner-args" - }, - new Option(new[] - { - "-v", - "--verbosity" - }) + new Argument("inner-args"), + new Option("--verbosity", "-v") } }; @@ -1509,14 +1462,12 @@ public void Subcommand_help_does_not_contain_hidden_argument() { var command = new Command("the-command", "Does things."); var subCommand = new Command("the-subcommand"); - var hidden = new Argument() + var hidden = new Argument("the-hidden") { - Name = "the-hidden", IsHidden = true }; - var visible = new Argument() + var visible = new Argument("the-visible") { - Name = "the-visible", IsHidden = false }; subCommand.Arguments.Add(hidden); @@ -1537,32 +1488,22 @@ public void Subcommand_help_does_not_contain_hidden_argument() [Fact] public void Help_describes_default_value_for_subcommand_with_arguments_and_only_defaultable_is_shown() { - var argument = new Argument + var argument = new Argument("the-arg"); + var otherArgumentHidden = new Argument("the-other-hidden-arg") { - Name = "the-arg", - }; - var otherArgumentHidden = new Argument - { - Name = "the-other-hidden-arg", IsHidden = true }; - argument.SetDefaultValue("the-arg-value"); - otherArgumentHidden.SetDefaultValue("the-other-hidden-arg-value"); + argument.DefaultValueFactory = _ => "the-arg-value"; + otherArgumentHidden.DefaultValueFactory = _ => "the-other-hidden-arg-value"; var command = new Command("outer", "outer command help") { - new Argument - { - Name = "outer-args" - }, + new Argument("outer-args"), new Command("inner", $"inner command help") { argument, otherArgumentHidden, - new Argument - { - Name = "inner-other-arg-no-default" - } + new Argument("inner-other-arg-no-default") } }; @@ -1578,23 +1519,18 @@ public void Help_describes_default_value_for_subcommand_with_arguments_and_only_ [Fact] public void Help_describes_default_values_for_subcommand_with_multiple_defaultable_arguments() { - var argument = new Argument + var argument = new Argument("the-arg") { - Name = "the-arg", + DefaultValueFactory = (_) => "the-arg-value" }; - var otherArgument = new Argument + var otherArgument = new Argument("the-other-arg") { - Name = "the-other-arg" + DefaultValueFactory = (_) => "the-other-arg-value" }; - argument.SetDefaultValue("the-arg-value"); - otherArgument.SetDefaultValue("the-other-arg-value"); var command = new Command("outer", "outer command help") { - new Argument - { - Name = "outer-args" - }, + new Argument("outer-args"), new Command("inner", "inner command help") { argument, otherArgument @@ -1625,7 +1561,7 @@ public void Commands_without_arguments_do_not_produce_extra_newlines_between_usa { var command = new RootCommand { - new Option("-x", "the-option-description") + new Option("-x") { Description = "the-option-description" } }; var helpBuilder = GetHelpBuilder(); diff --git a/src/System.CommandLine.Tests/IdentifierSymbolTests.cs b/src/System.CommandLine.Tests/IdentifierSymbolTests.cs deleted file mode 100644 index 4c9705cd55..0000000000 --- a/src/System.CommandLine.Tests/IdentifierSymbolTests.cs +++ /dev/null @@ -1,24 +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 abstract class IdentifierSymbolTests : SymbolTests - { - [Fact] - public void When_Name_is_changed_then_old_name_is_not_among_aliases() - { - var symbol = (IdentifierSymbol) CreateSymbol("original"); - - symbol.Name = "changed"; - - symbol.HasAlias("original").Should().BeFalse(); - symbol.Aliases.Should().NotContain("original"); - symbol.Aliases.Should().NotContain("original"); - } - } -} \ No newline at end of file diff --git a/src/System.CommandLine.Tests/Invocation/TypoCorrectionTests.cs b/src/System.CommandLine.Tests/Invocation/TypoCorrectionTests.cs index 93c55270d5..c101d3197d 100644 --- a/src/System.CommandLine.Tests/Invocation/TypoCorrectionTests.cs +++ b/src/System.CommandLine.Tests/Invocation/TypoCorrectionTests.cs @@ -167,8 +167,8 @@ public async Task Suggestions_favor_matches_with_prefix() { var rootCommand = new RootCommand { - new Option(new[] { "/call", "-call", "--call" }), - new Option(new[] { "/email", "-email", "--email" }) + new Option("/call", "-call", "--call"), + new Option("/email", "-email", "--email") }; var config = new CommandLineBuilder(rootCommand) diff --git a/src/System.CommandLine.Tests/OptionTests.MultipleArgumentsPerToken.cs b/src/System.CommandLine.Tests/OptionTests.MultipleArgumentsPerToken.cs index 84c388a735..f34475c3d3 100644 --- a/src/System.CommandLine.Tests/OptionTests.MultipleArgumentsPerToken.cs +++ b/src/System.CommandLine.Tests/OptionTests.MultipleArgumentsPerToken.cs @@ -10,7 +10,7 @@ namespace System.CommandLine.Tests { - public partial class OptionTests : SymbolTests + public partial class OptionTests { public class MultipleArgumentsPerToken { @@ -26,11 +26,11 @@ public Allowed(ITestOutputHelper output) [Fact] public void When_option_is_not_respecified_but_limit_is_not_reached_then_the_following_token_is_used_as_value() { - var animalsOption = new Option(new[] { "-a", "--animals" }) + var animalsOption = new Option("-a", "--animals") { AllowMultipleArgumentsPerToken = true, }; - var vegetablesOption = new Option(new[] { "-v", "--vegetables" }); + var vegetablesOption = new Option("-v", "--vegetables"); var command = new RootCommand { @@ -63,11 +63,11 @@ public void When_option_is_not_respecified_but_limit_is_not_reached_then_the_fol [Fact] public void When_option_is_not_respecified_and_limit_is_reached_then_the_following_token_is_unmatched() { - var animalsOption = new Option(new[] { "-a", "--animals" }) + var animalsOption = new Option("-a", "--animals") { AllowMultipleArgumentsPerToken = true }; - var vegetablesOption = new Option(new[] { "-v", "--vegetables" }); + var vegetablesOption = new Option("-v", "--vegetables"); var command = new RootCommand { @@ -109,7 +109,7 @@ public void When_max_arity_is_1_then_subsequent_option_args_overwrite_previous_o var command = new Command("the-command") { option, - new Argument() + new Argument("arg") }; var result = command.Parse(commandLine); diff --git a/src/System.CommandLine.Tests/OptionTests.cs b/src/System.CommandLine.Tests/OptionTests.cs index 0c71239bd8..9fbd8d7cbb 100644 --- a/src/System.CommandLine.Tests/OptionTests.cs +++ b/src/System.CommandLine.Tests/OptionTests.cs @@ -9,30 +9,31 @@ namespace System.CommandLine.Tests { - public partial class OptionTests : SymbolTests + public partial class OptionTests { [Fact] - public void When_an_option_has_only_one_alias_then_that_alias_is_its_name() + public void When_an_option_has_only_name_then_it_has_no_aliases() { - var option = new Option(new[] { "myname" }); + var option = new Option("myname"); option.Name.Should().Be("myname"); + option.Aliases.Should().BeEmpty(); } [Fact] - public void When_an_option_has_several_aliases_then_the_longest_alias_is_its_name() + public void When_an_option_has_several_aliases_then_they_do_not_affect_its_name() { - var option = new Option(new[] { "myname", "m" }); + var option = new Option(name: "m", aliases: new[] { "longer" }); - option.Name.Should().Be("myname"); + option.Name.Should().Be("m"); } [Fact] - public void Option_names_do_not_contain_prefix_characters() + public void Option_names_can_contain_prefix_characters() { - var option = new Option(new[] { "--myname", "m" }); + var option = new Option("--myname"); - option.Name.Should().Be("myname"); + option.Name.Should().Be("--myname"); } [Fact] @@ -40,10 +41,9 @@ public void Aliases_is_aware_of_added_alias() { var option = new Option("--original"); - option.AddAlias("--added"); + option.Aliases.Add("--added"); option.Aliases.Should().Contain("--added"); - option.HasAlias("--added").Should().BeTrue(); } [Fact] @@ -51,10 +51,9 @@ public void RawAliases_is_aware_of_added_alias() { var option = new Option("--original"); - option.AddAlias("--added"); + option.Aliases.Add("--added"); option.Aliases.Should().Contain("--added"); - option.HasAlias("--added").Should().BeTrue(); } [Fact] @@ -62,60 +61,56 @@ public void A_prefixed_alias_can_be_added_to_an_option() { var option = new Option("--apple"); - option.AddAlias("-a"); + option.Aliases.Add("-a"); - option.HasAlias("-a").Should().BeTrue(); + option.Aliases.Contains("-a").Should().BeTrue(); } [Fact] public void Option_aliases_are_case_sensitive() { - var option = new Option(new[] { "-o" }); + var option = new Option("name", "o"); - option.HasAlias("O").Should().BeFalse(); + option.Aliases.Contains("O").Should().BeFalse(); } [Fact] - public void HasAlias_accepts_prefixed_short_value() + public void Aliases_accepts_prefixed_short_value() { - var option = new Option(new[] { "-o", "--option" }); + var option = new Option("--option", "-o"); - option.HasAlias("-o").Should().BeTrue(); + option.Aliases.Contains("-o").Should().BeTrue(); } [Fact] public void HasAlias_accepts_prefixed_long_value() { - var option = new Option(new[] { "-o", "--option" }); + var option = new Option("-o", "--option"); - option.HasAlias("--option").Should().BeTrue(); + option.Aliases.Contains("--option").Should().BeTrue(); } [Fact] public void It_is_not_necessary_to_specify_a_prefix_when_adding_an_option() { - var option = new Option(new[] { "o" }); + var option = new Option("o"); - option.HasAlias("o").Should().BeTrue(); + option.Name.Should().Be("o"); + option.Aliases.Should().BeEmpty(); } [Fact] - public void An_option_must_have_at_least_one_alias() + public void An_option_doesno_not_need_to_have_at_least_one_alias() { - Action create = () => new Option(Array.Empty()); + var option = new Option("justName"); - create.Should() - .Throw() - .Which - .Message - .Should() - .Contain("An option must have at least one alias"); + option.Aliases.Should().BeEmpty(); } [Fact] public void An_option_cannot_have_an_empty_alias() { - Action create = () => new Option(new[] { "" }); + Action create = () => new Option("name", ""); create.Should() .Throw() @@ -128,7 +123,7 @@ public void An_option_cannot_have_an_empty_alias() [Fact] public void An_option_cannot_have_an_alias_consisting_entirely_of_whitespace() { - Action create = () => new Option(new[] { " \t" }); + Action create = () => new Option("name", " \t"); create.Should() .Throw() @@ -141,28 +136,28 @@ public void An_option_cannot_have_an_alias_consisting_entirely_of_whitespace() [Fact] public void Raw_aliases_are_exposed_by_an_option() { - var option = new Option(new[] { "-h", "--help", "/?" }); + var option = new Option("--help", "-h", "/?"); option.Aliases .Should() - .BeEquivalentTo("-h", "--help", "/?"); + .BeEquivalentTo("-h", "/?"); } [Theory] [InlineData("-x ")] [InlineData(" -x")] [InlineData("--aa aa")] - public void When_an_option_is_created_with_an_alias_that_contains_whitespace_then_an_informative_error_is_returned( - string alias) + public void When_an_option_is_created_with_a_name_that_contains_whitespace_then_an_informative_error_is_returned( + string name) { - Action create = () => new Option(alias); + Action create = () => new Option(name); create.Should() .Throw() .Which .Message .Should() - .Contain($"Alias cannot contain whitespace: \"{alias}\""); + .Contain($"Names and aliases cannot contain whitespace: \"{name}\""); } [Theory] @@ -173,14 +168,14 @@ public void When_an_option_alias_is_added_and_contains_whitespace_then_an_inform { var option = new Option("-x"); - Action addAlias = () => option.AddAlias(alias); + Action addAlias = () => option.Aliases.Add(alias); addAlias.Should() .Throw() .Which .Message .Should() - .Contain($"Alias cannot contain whitespace: \"{alias}\""); + .Contain($"Names and aliases cannot contain whitespace: \"{alias}\""); } [Theory] @@ -210,39 +205,34 @@ public void When_options_use_different_prefixes_they_still_work(string prefix) [Fact] public void When_option_not_explicitly_provides_help_will_use_default_help() { - var option = new Option(new[] { "-o", "--option" }, "desc"); + var option = new Option("--option", "-o") + { + Description = "desc" + }; - option.Name.Should().Be("option"); + option.Name.Should().Be("--option"); option.Description.Should().Be("desc"); option.IsHidden.Should().BeFalse(); } - [Fact] - public void Argument_takes_option_alias_as_its_name_when_it_is_not_provided() - { - var command = new Option("--alias"); - - command.Name.Should().Be("alias"); - } - [Fact] public void Argument_retains_name_when_it_is_provided() { var option = new Option("-alias") { - ArgumentHelpName = "arg" + HelpName = "arg" }; - option.ArgumentHelpName.Should().Be("arg"); + option.HelpName.Should().Be("arg"); } [Fact] public void Option_T_default_value_can_be_set_via_the_constructor() { - var option = new Option( - "-x", - parseArgument: parsed => 123, - isDefault: true); + var option = new Option("-x") + { + DefaultValueFactory = _ => 123 + }; new RootCommand { option } .Parse("") @@ -255,9 +245,10 @@ public void Option_T_default_value_can_be_set_via_the_constructor() [Fact] public void Option_T_default_value_can_be_set_after_instantiation() { - var option = new Option("-x"); - - option.SetDefaultValue(123); + var option = new Option("-x") + { + DefaultValueFactory = (_) => 123 + }; new RootCommand { option } .Parse("") @@ -272,7 +263,7 @@ public void Option_T_default_value_factory_can_be_set_after_instantiation() { var option = new Option("-x"); - option.SetDefaultValueFactory(() => 123); + option.DefaultValueFactory = (_) => 123; new RootCommand { option } .Parse("") @@ -285,7 +276,7 @@ public void Option_T_default_value_factory_can_be_set_after_instantiation() [Fact] public void Option_T_default_value_is_validated() { - var option = new Option("-x", () => 123); + var option = new Option("-x") { DefaultValueFactory = (_) => 123 }; option.Validators.Add(symbol => symbol.AddError(symbol.Tokens .Select(t => t.Value) @@ -315,25 +306,12 @@ public void Option_of_string_defaults_to_null_when_not_specified() .BeNull(); } - [Fact] - public void When_Name_is_set_to_its_current_value_then_it_is_not_removed_from_aliases() - { - var option = new Option("--name"); - - option.Name = "name"; - - option.HasAlias("name").Should().BeTrue(); - option.HasAlias("--name").Should().BeTrue(); - option.Aliases.Should().Contain("--name"); - option.Aliases.Should().Contain("name"); - } - [Theory] [InlineData("-option value")] [InlineData("-option:value")] public void When_aliases_overlap_the_longer_alias_is_chosen(string parseInput) { - var option = new Option(new[] { "-o", "-option" }); + var option = new Option("-o", "-option"); var parseResult = new RootCommand { option }.Parse(parseInput); @@ -368,7 +346,5 @@ public void Option_of_enum_can_limit_enum_members_as_valid_values() .Should() .BeEquivalentTo(new[] { $"Argument 'Fuschia' not recognized. Must be one of:\n\t'Red'\n\t'Green'" }); } - - protected override Symbol CreateSymbol(string name) => new Option(name); } } diff --git a/src/System.CommandLine.Tests/ParseDiagramTests.cs b/src/System.CommandLine.Tests/ParseDiagramTests.cs index 3b5ee6ad97..d2fe692032 100644 --- a/src/System.CommandLine.Tests/ParseDiagramTests.cs +++ b/src/System.CommandLine.Tests/ParseDiagramTests.cs @@ -19,7 +19,7 @@ public void Parse_result_diagram_helps_explain_parse_operation() { new Option("-x"), new Option("-y"), - new Argument() + new Argument("arg") }; var result = command.Parse("the-command -x one -y two three"); @@ -67,9 +67,9 @@ public void Parse_diagram_identifies_options_where_default_values_have_been_appl { var rootCommand = new RootCommand { - new Option(new[] { "-h", "--height" }, () => 10), - new Option(new[] { "-w", "--width" }, () => 15), - new Option(new[] { "-c", "--color" }, () => ConsoleColor.Cyan) + new Option("-h", "--height") { DefaultValueFactory = (_) => 10 }, + new Option("-w", "--width") { DefaultValueFactory = (_) => 15 }, + new Option("-c", "--color") { DefaultValueFactory = (_) => ConsoleColor.Cyan } }; var result = rootCommand.Parse("-w 9000"); @@ -85,9 +85,9 @@ public void Parse_diagram_indicates_which_tokens_were_applied_to_which_command_a { var command = new Command("the-command") { - new Argument { Name = "first" }, - new Argument { Name = "second" }, - new Argument { Name = "third" } + new Argument("first"), + new Argument ("second"), + new Argument ("third") }; var result = command.Parse("one two three four five"); @@ -102,9 +102,9 @@ public void Parse_diagram_indicates_which_tokens_were_applied_to_which_command_a { var command = new Command("the-command") { - new Argument { Name = "first" }, - new Argument { Name = "second" }, - new Argument { Name = "third" } + new Argument ("first"), + new Argument ("second"), + new Argument ("third") }; var result = command.Parse("one two three four five"); diff --git a/src/System.CommandLine.Tests/ParseDirectiveTests.cs b/src/System.CommandLine.Tests/ParseDirectiveTests.cs index 5e467784b0..a60372d0f4 100644 --- a/src/System.CommandLine.Tests/ParseDirectiveTests.cs +++ b/src/System.CommandLine.Tests/ParseDirectiveTests.cs @@ -25,7 +25,7 @@ public async Task Parse_directive_writes_parse_diagram() var rootCommand = new RootCommand(); var subcommand = new Command("subcommand"); rootCommand.Subcommands.Add(subcommand); - var option = new Option(new[] { "-c", "--count" }); + var option = new Option("-c", "--count"); subcommand.Options.Add(option); var config = new CommandLineBuilder(rootCommand) diff --git a/src/System.CommandLine.Tests/ParseResultTests.cs b/src/System.CommandLine.Tests/ParseResultTests.cs index bd74cf03ad..0db5c03393 100644 --- a/src/System.CommandLine.Tests/ParseResultTests.cs +++ b/src/System.CommandLine.Tests/ParseResultTests.cs @@ -13,7 +13,7 @@ public class ParseResultTests [Fact] public void An_option_with_a_default_value_and_no_explicitly_provided_argument_has_an_empty_arguments_property() { - var option = new Option("-x", () => "default"); + var option = new Option("-x") { DefaultValueFactory = (_) => "default" }; var result = new RootCommand { @@ -27,7 +27,7 @@ public void An_option_with_a_default_value_and_no_explicitly_provided_argument_h [Fact] public void FindResult_can_be_used_to_check_the_presence_of_an_option() { - var option = new Option(new[] { "-h", "--help" }); + var option = new Option("-h", "--help"); var command = new Command("the-command") { @@ -42,7 +42,7 @@ public void FindResult_can_be_used_to_check_the_presence_of_an_option() [Fact] public void FindResultFor_can_be_used_to_check_the_presence_of_an_implicit_option() { - var option = new Option(new[] { "-c", "--count" }, () => 5); + var option = new Option("-c", "--count") { DefaultValueFactory = (_) => 5 }; var command = new Command("the-command") { option @@ -60,14 +60,14 @@ public void Command_will_not_accept_a_command_if_a_sibling_command_has_already_b { new Command("inner-one") { - new Argument + new Argument("arg1") { Arity = ArgumentArity.Zero } }, new Command("inner-two") { - new Argument + new Argument("arg2") { Arity = ArgumentArity.Zero } @@ -90,8 +90,8 @@ public void ParseResult_GetCompletions_returns_global_options_of_given_command_o { var leafCommand = new Command("leafCommand") { - new Option("--one", "option one"), - new Option("--two", "option two") + new Option("--one") { Description = "option one" }, + new Option("--two") { Description = "option two" } }; var midCommand1 = new Command("midCommand1") diff --git a/src/System.CommandLine.Tests/ParserTests.DoubleDash.cs b/src/System.CommandLine.Tests/ParserTests.DoubleDash.cs index 4c70908eff..bf7572c455 100644 --- a/src/System.CommandLine.Tests/ParserTests.DoubleDash.cs +++ b/src/System.CommandLine.Tests/ParserTests.DoubleDash.cs @@ -15,8 +15,8 @@ public class DefaultDoubleDashBehavior [Fact] // https://github.com/dotnet/command-line-api/issues/1238 public void Subsequent_tokens_are_parsed_as_arguments_even_if_they_match_option_identifiers() { - var option = new Option(new[] { "-o", "--one" }); - var argument = new Argument(); + var option = new Option("-o", "--one"); + var argument = new Argument("arg"); var rootCommand = new RootCommand { option, @@ -40,8 +40,8 @@ public void Subsequent_tokens_are_parsed_as_arguments_even_if_they_match_option_ [Fact] public void Unmatched_tokens_is_empty() { - var option = new Option(new[] { "-o", "--one" }); - var argument = new Argument(); + var option = new Option("-o", "--one"); + var argument = new Argument("arg"); var rootCommand = new RootCommand { option, @@ -57,8 +57,8 @@ public void Unmatched_tokens_is_empty() [Fact] // https://github.com/dotnet/command-line-api/issues/1631 public void No_errors_are_generated() { - var option = new Option(new[] { "-o", "--one" }); - var argument = new Argument(); + var option = new Option("-o", "--one"); + var argument = new Argument("arg"); var rootCommand = new RootCommand { option, @@ -74,7 +74,7 @@ public void No_errors_are_generated() [Fact] public void A_second_double_dash_is_parsed_as_an_argument() { - var argument = new Argument(); + var argument = new Argument("arg"); var rootCommand = new RootCommand { argument diff --git a/src/System.CommandLine.Tests/ParserTests.MultipleArguments.cs b/src/System.CommandLine.Tests/ParserTests.MultipleArguments.cs index 347d93e113..2f157b5731 100644 --- a/src/System.CommandLine.Tests/ParserTests.MultipleArguments.cs +++ b/src/System.CommandLine.Tests/ParserTests.MultipleArguments.cs @@ -18,16 +18,14 @@ public class MultipleArguments [Fact] public void Multiple_arguments_can_differ_by_arity() { - var multipleArityArg = new Argument> + var multipleArityArg = new Argument>("several") { Arity = new ArgumentArity(3, 3), - Name = "several" }; - var singleArityArg = new Argument> + var singleArityArg = new Argument>("one") { Arity = ArgumentArity.ZeroOrMore, - Name = "one" }; var command = new Command("the-command") @@ -49,14 +47,8 @@ public void Multiple_arguments_can_differ_by_arity() [Fact] public void Multiple_arguments_can_differ_by_type() { - var stringArg = new Argument - { - Name = "the-string" - }; - var intArg = new Argument - { - Name = "the-int" - }; + var stringArg = new Argument("the-string"); + var intArg = new Argument("the-int"); var command = new Command("the-command") { @@ -85,9 +77,9 @@ public void Multiple_arguments_can_differ_by_type() [InlineData("one two three four five --verbose true")] public void When_multiple_arguments_are_present_then_their_order_relative_to_sibling_options_is_not_significant(string commandLine) { - var first = new Argument { Name = "first" }; - var second = new Argument { Name = "second" }; - var third = new Argument { Name = "third" }; + var first = new Argument("first"); + var second = new Argument("second"); + var third = new Argument("third"); var verbose = new Option("--verbose"); var command = new Command("the-command") @@ -153,8 +145,8 @@ public void When_multiple_arguments_are_defined_but_not_provided_then_option_par var command = new Command("the-command") { option, - new Argument(), - new Argument() + new Argument("arg1"), + new Argument("arg2") }; var result = command.Parse("-e foo"); @@ -167,8 +159,8 @@ public void When_multiple_arguments_are_defined_but_not_provided_then_option_par [Fact] public void Tokens_that_cannot_be_converted_by_multiple_arity_argument_flow_to_next_multiple_arity_argument() { - var ints = new Argument(); - var strings = new Argument(); + var ints = new Argument("ints"); + var strings = new Argument("strings"); var root = new RootCommand { @@ -194,8 +186,8 @@ public void Tokens_that_cannot_be_converted_by_multiple_arity_argument_flow_to_n [Fact] public void Tokens_that_cannot_be_converted_by_multiple_arity_argument_flow_to_next_single_arity_argument() { - var ints = new Argument(); - var strings = new Argument(); + var ints = new Argument("arg1"); + var strings = new Argument("arg2"); var root = new RootCommand { @@ -227,15 +219,15 @@ public void Tokens_that_cannot_be_converted_by_multiple_arity_argument_flow_to_n [Fact] public void Unsatisfied_subsequent_argument_with_min_arity_0_parses_as_default_value() { - var arg1 = new Argument + var arg1 = new Argument("arg1") { Arity = ArgumentArity.ExactlyOne }; - var arg2 = new Argument + var arg2 = new Argument("arg2") { Arity = ArgumentArity.ZeroOrOne, + DefaultValueFactory = (_) => "the-default" }; - arg2.SetDefaultValue("the-default"); var rootCommand = new RootCommand { arg1, @@ -252,7 +244,10 @@ public void Unsatisfied_subsequent_argument_with_min_arity_0_parses_as_default_v public void Unsatisfied_subsequent_argument_with_min_arity_1_parses_as_default_value() { Argument arg1 = new(name: "arg1"); - Argument arg2 = new(name: "arg2", defaultValueFactory: () => "the-default"); + Argument arg2 = new(name: "arg2") + { + DefaultValueFactory = (_) => "the-default" + }; var rootCommand = new RootCommand { @@ -269,11 +264,11 @@ public void Unsatisfied_subsequent_argument_with_min_arity_1_parses_as_default_v [Fact] // https://github.com/dotnet/command-line-api/issues/1395 public void When_subsequent_argument_with_ZeroOrOne_arity_is_not_provided_then_parse_is_correct() { - var argument1 = new Argument(); + var argument1 = new Argument("arg1"); var rootCommand = new RootCommand { argument1, - new Argument + new Argument("arg2") { Arity = ArgumentArity.ZeroOrOne }, @@ -296,10 +291,10 @@ public void When_there_are_not_enough_tokens_for_all_arguments_then_the_correct_ { var command = new Command("command") { - new Argument(), - new Argument(), - new Argument(), - new Argument() + new Argument("arg1"), + new Argument("arg2"), + new Argument("arg3"), + new Argument("arg4") }; var result = Parser.Parse(command, providedArgs); diff --git a/src/System.CommandLine.Tests/ParserTests.MultiplePositions.cs b/src/System.CommandLine.Tests/ParserTests.MultiplePositions.cs index a915c08b4c..85739ef226 100644 --- a/src/System.CommandLine.Tests/ParserTests.MultiplePositions.cs +++ b/src/System.CommandLine.Tests/ParserTests.MultiplePositions.cs @@ -17,10 +17,7 @@ public class MultiplePositions [InlineData("outer inner xyz")] public void An_argument_can_be_specified_in_more_than_one_position(string commandLine) { - var argument = new Argument - { - Name = "the-argument" - }; + var argument = new Argument("the-argument"); var command = new Command("outer") { @@ -48,10 +45,7 @@ public void An_argument_can_be_specified_in_more_than_one_position(string comman [InlineData("outer inner xyz")] public void When_an_argument_is_shared_between_an_outer_and_inner_command_then_specifying_in_one_does_not_result_in_error_on_other(string commandLine) { - var argument = new Argument - { - Name = "the-argument" - }; + var argument = new Argument("the-argument"); var command = new Command("outer") { diff --git a/src/System.CommandLine.Tests/ParserTests.RootCommandAndArg0.cs b/src/System.CommandLine.Tests/ParserTests.RootCommandAndArg0.cs index b46fc1d62f..4d55461259 100644 --- a/src/System.CommandLine.Tests/ParserTests.RootCommandAndArg0.cs +++ b/src/System.CommandLine.Tests/ParserTests.RootCommandAndArg0.cs @@ -85,14 +85,13 @@ public void When_parsing_an_unsplit_string_then_input_a_full_path_to_an_executab [Fact] public void When_parsing_an_unsplit_string_then_a_renamed_RootCommand_can_be_omitted_from_the_parsed_args() { - var rootCommand = new RootCommand + var rootCommand = new Command("outer") { new Command("inner") { new Option("-x") } }; - rootCommand.Name = "outer"; var result1 = rootCommand.Parse("inner -x hello"); var result2 = rootCommand.Parse("outer inner -x hello"); diff --git a/src/System.CommandLine.Tests/ParserTests.cs b/src/System.CommandLine.Tests/ParserTests.cs index 16644eeef1..88bad6e0b0 100644 --- a/src/System.CommandLine.Tests/ParserTests.cs +++ b/src/System.CommandLine.Tests/ParserTests.cs @@ -38,9 +38,9 @@ public void An_option_can_be_checked_by_object_instance() [Fact] public void Two_options_are_parsed_correctly() { - var optionOne = new Option(new[] { "-o", "--one" }); + var optionOne = new Option("-o", "--one"); - var optionTwo = new Option(new[] { "-t", "--two" }); + var optionTwo = new Option("-t", "--two"); var result = new RootCommand { optionOne, optionTwo }.Parse("-o -t"); @@ -178,7 +178,7 @@ public void Options_do_not_get_unbundled_unless_all_resulting_options_would_be_v outer.Options.Add(new Option("-a")); var inner = new Command("inner") { - new Argument() + new Argument("arg") }; inner.Options.Add(new Option("-b")); inner.Options.Add(new Option("-c")); @@ -316,8 +316,8 @@ public void Invalid_char_in_bundle_causes_rest_to_be_interpreted_as_value() [Fact] public void Parser_root_Options_can_be_specified_multiple_times_and_their_arguments_are_collated() { - var animalsOption = new Option(new[] { "-a", "--animals" }); - var vegetablesOption = new Option(new[] { "-v", "--vegetables" }); + var animalsOption = new Option("-a", "--animals"); + var vegetablesOption = new Option("-v", "--vegetables"); var parser = new RootCommand { animalsOption, @@ -342,9 +342,9 @@ public void Parser_root_Options_can_be_specified_multiple_times_and_their_argume [Fact] public void Options_can_be_specified_multiple_times_and_their_arguments_are_collated() { - var animalsOption = new Option(new[] { "-a", "--animals" }); + var animalsOption = new Option("-a", "--animals"); animalsOption.AcceptOnlyFromAmong("dog", "cat", "sheep"); - var vegetablesOption = new Option(new[] { "-v", "--vegetables" }); + var vegetablesOption = new Option("-v", "--vegetables"); Command command = new Command("the-command") { animalsOption, @@ -369,16 +369,16 @@ public void Options_can_be_specified_multiple_times_and_their_arguments_are_coll [Fact] public void When_an_option_is_not_respecified_but_limit_is_reached_then_the_following_token_is_considered_an_argument_to_the_parent_command() { - var animalsOption = new Option(new[] { "-a", "--animals" }); + var animalsOption = new Option("-a", "--animals"); - var vegetablesOption = new Option(new[] { "-v", "--vegetables" }); + var vegetablesOption = new Option("-v", "--vegetables"); Command command = new Command("the-command") { animalsOption, vegetablesOption, - new Argument() + new Argument("arg") }; var result = command.Parse("the-command -a cat some-arg -v carrot"); @@ -432,7 +432,7 @@ public void Relative_order_of_arguments_and_options_within_a_command_does_not_ma { var command = new Command("move") { - new Argument(), + new Argument("arg"), new Option("-X") }; @@ -475,7 +475,7 @@ public void Original_order_of_tokens_is_preserved_in_ParseResult_Tokens(string c var command = new Command("the-command") { - new Argument(), + new Argument("arg"), new Option("--one"), new Option("--many") }; @@ -525,10 +525,10 @@ public void When_nested_commands_all_accept_arguments_then_the_nearest_captures_ var command = new Command( "outer") { - new Argument(), + new Argument("arg1"), new Command("inner") { - new Argument() + new Argument("arg2") } }; @@ -552,17 +552,17 @@ public void Nested_commands_with_colliding_names_cannot_both_be_applied() { var command = new Command("outer") { - new Argument(), + new Argument("arg1"), new Command("non-unique") { - new Argument() + new Argument("arg2") }, new Command("inner") { - new Argument(), + new Argument("arg3"), new Command("non-unique") { - new Argument() + new Argument("arg4") } } }; @@ -579,7 +579,7 @@ public void When_child_option_will_not_accept_arg_then_parent_can() var command = new Command("the-command") { option, - new Argument() + new Argument("arg") }; var result = command.Parse("the-command -x the-argument"); @@ -609,12 +609,11 @@ public void Required_arguments_on_parent_commands_do_not_create_parse_errors_whe { var child = new Command("child"); - var parent = new RootCommand + var parent = new Command("parent") { - new Argument(), + new Argument("arg"), child }; - parent.Name = "parent"; var result = parent.Parse("child"); @@ -626,15 +625,14 @@ public void Required_arguments_on_grandparent_commands_do_not_create_parse_error { var grandchild = new Command("grandchild"); - var grandparent = new RootCommand + var grandparent = new Command("grandparent") { - new Argument(), + new Argument("arg"), new Command("parent") { grandchild } }; - grandparent.Name = "grandparent"; var result = grandparent.Parse("parent grandchild"); @@ -699,10 +697,10 @@ public void Arguments_only_apply_to_the_nearest_command() { var outer = new Command("outer") { - new Argument(), + new Argument("arg1"), new Command("inner") { - new Argument() + new Argument("arg2") } }; @@ -753,7 +751,7 @@ public void Subsequent_occurrences_of_tokens_matching_command_names_are_parsed_a { new Command("complete") { - new Argument(), + new Argument("arg"), new Option("--position") } }; @@ -777,7 +775,7 @@ public void Absolute_unix_style_paths_are_lexed_correctly() Command command = new ("rm") { - new Argument() + new Argument("arg") }; var result = command.Parse(commandText); @@ -797,7 +795,7 @@ public void Absolute_Windows_style_paths_are_lexed_correctly() Command command = new("rm") { - new Argument() + new Argument("arg") }; ParseResult result = command.Parse(commandText); @@ -811,7 +809,10 @@ public void Absolute_Windows_style_paths_are_lexed_correctly() [Fact] public void Commands_can_have_default_argument_values() { - var argument = new Argument("the-arg", () => "default"); + var argument = new Argument("the-arg") + { + DefaultValueFactory = (_) => "default" + }; var command = new Command("command") { @@ -829,7 +830,10 @@ public void Commands_can_have_default_argument_values() public void When_an_option_with_a_default_value_is_not_matched_then_the_option_can_still_be_accessed_as_though_it_had_been_applied() { var command = new Command("command"); - var option = new Option(new[] { "-o", "--option" }, () => "the-default"); + var option = new Option("-o", "--option") + { + DefaultValueFactory = (_) => "the-default" + }; command.Options.Add(option); ParseResult result = command.Parse("command"); @@ -841,7 +845,10 @@ public void When_an_option_with_a_default_value_is_not_matched_then_the_option_c [Fact] public void When_an_option_with_a_default_value_is_not_matched_then_the_option_result_is_implicit() { - var option = new Option(new[]{ "-o", "--option" }, () => "the-default"); + var option = new Option("-o", "--option") + { + DefaultValueFactory = (_) => "the-default" + }; var command = new Command("command") { @@ -859,9 +866,10 @@ public void When_an_option_with_a_default_value_is_not_matched_then_the_option_r [Fact] public void When_an_option_with_a_default_value_is_not_matched_then_there_are_no_tokens() { - var option = new Option( - "-o", - () => "the-default"); + var option = new Option("-o") + { + DefaultValueFactory = (_) => "the-default" + }; var command = new Command("command") { @@ -879,9 +887,10 @@ public void When_an_option_with_a_default_value_is_not_matched_then_there_are_no [Fact] public void When_an_argument_with_a_default_value_is_not_matched_then_there_are_no_tokens() { - var argument = new Argument( - "o", - () => "the-default"); + var argument = new Argument("o") + { + DefaultValueFactory = (_) => "the-default" + }; var command = new Command("command") { @@ -898,9 +907,9 @@ public void When_an_argument_with_a_default_value_is_not_matched_then_there_are_ [Fact] public void Command_default_argument_value_does_not_override_parsed_value() { - var argument = new Argument(() => new DirectoryInfo(Directory.GetCurrentDirectory())) + var argument = new Argument("the-arg") { - Name = "the-arg" + DefaultValueFactory = (_) => new DirectoryInfo(Directory.GetCurrentDirectory()) }; var command = new Command("inner") @@ -923,7 +932,7 @@ public void Unmatched_tokens_that_look_like_options_are_not_split_into_smaller_t { new Command("inner") { - new Argument + new Argument("arg") { Arity = ArgumentArity.OneOrMore } @@ -944,7 +953,7 @@ public void The_default_behavior_of_unmatched_tokens_resulting_in_errors_can_be_ { var command = new Command("the-command") { - new Argument() + new Argument("arg") }; command.TreatUnmatchedTokensAsErrors = false; @@ -962,7 +971,7 @@ public void Option_and_Command_can_have_the_same_alias() { var innerCommand = new Command("inner") { - new Argument() + new Argument("arg1") }; var option = new Option("--inner"); @@ -971,7 +980,7 @@ public void Option_and_Command_can_have_the_same_alias() { innerCommand, option, - new Argument() + new Argument("arg2") }; outerCommand.Parse("outer inner") @@ -1006,8 +1015,8 @@ public void Option_and_Command_can_have_the_same_alias() [Fact] public void Options_can_have_the_same_alias_differentiated_only_by_prefix() { - var option1 = new Option(new[] { "-a" }); - var option2 = new Option(new[] { "--a" }); + var option1 = new Option("-a"); + var option2 = new Option("--a"); var parser = new RootCommand { @@ -1136,7 +1145,7 @@ public void Option_arguments_can_match_subcommands() [Fact] public void Arguments_can_match_subcommands() { - var argument = new Argument(); + var argument = new Argument("arg"); var subcommand = new Command("subcommand") { argument @@ -1329,7 +1338,7 @@ public void Parse_can_not_be_called_with_null_args() [Fact] public void Command_argument_arity_can_be_a_fixed_value_greater_than_1() { - var argument = new Argument + var argument = new Argument("arg") { Arity = new ArgumentArity(3, 3) }; @@ -1351,7 +1360,7 @@ public void Command_argument_arity_can_be_a_fixed_value_greater_than_1() [Fact] public void Command_argument_arity_can_be_a_range_with_a_lower_bound_greater_than_1() { - var argument = new Argument + var argument = new Argument("arg") { Arity = new ArgumentArity(3, 5) }; @@ -1385,7 +1394,7 @@ public void When_command_arguments_are_fewer_than_minimum_arity_then_an_error_is { var command = new Command("the-command") { - new Argument + new Argument("arg") { Arity = new ArgumentArity(2, 3) } @@ -1404,7 +1413,7 @@ public void When_command_arguments_are_greater_than_maximum_arity_then_an_error_ { var command = new Command("the-command") { - new Argument + new Argument("arg") { Arity = new ArgumentArity(2, 3) } @@ -1505,10 +1514,7 @@ public void When_option_arguments_are_greater_than_maximum_arity_then_an_error_i [Fact] public void Tokens_are_not_split_if_the_part_before_the_delimiter_is_not_an_option() { - var rootCommand = new RootCommand - { - Name = "jdbc" - }; + var rootCommand = new Command("jdbc"); rootCommand.Add(new Option("url")); var result = rootCommand.Parse("jdbc url \"jdbc:sqlserver://10.0.0.2;databaseName=main\""); @@ -1551,7 +1557,11 @@ public void A_subcommand_wont_overflow_when_checking_maximum_argument_capacity() [InlineData("--exec-prefix=", "")] public void Parsed_value_of_empty_string_arg_is_an_empty_string(string arg1, string arg2) { - var option = new Option("--exec-prefix", defaultValueFactory: () => "/usr/local"); + var option = new Option("--exec-prefix") + { + DefaultValueFactory = (_) => "/usr/local" + }; + var rootCommand = new RootCommand { option diff --git a/src/System.CommandLine.Tests/ParsingValidationTests.cs b/src/System.CommandLine.Tests/ParsingValidationTests.cs index 8ab17f547c..0579c9919f 100644 --- a/src/System.CommandLine.Tests/ParsingValidationTests.cs +++ b/src/System.CommandLine.Tests/ParsingValidationTests.cs @@ -76,7 +76,7 @@ public void When_FromAmong_is_used_then_the_OptionResult_ErrorMessage_is_set() [Fact] // https://github.com/dotnet/command-line-api/issues/1475 public void When_FromAmong_is_used_then_the_ArgumentResult_ErrorMessage_is_set() { - var argument = new Argument(); + var argument = new Argument("arg"); argument.AcceptOnlyFromAmong("a", "b"); var command = new Command("test") { argument }; @@ -180,7 +180,7 @@ public void When_FromAmong_is_used_for_multiple_arguments_and_invalid_input_is_p [Fact] public void When_FromAmong_is_used_and_multiple_invalid_inputs_are_provided_the_errors_mention_all_invalid_arguments() { - Option option = new(new[] { "--columns" }); + Option option = new("--columns"); option.AcceptOnlyFromAmong("author", "language", "tags", "type"); option.Arity = new ArgumentArity(1, 4); option.AllowMultipleArgumentsPerToken = true; @@ -248,7 +248,7 @@ public void When_a_required_option_has_multiple_aliases_the_error_message_uses_l { var command = new Command("command") { - new Option(new[] {"-x", "--xray" }) + new Option("-x", "--xray") { IsRequired = true } @@ -296,12 +296,11 @@ public void Required_options_on_parent_commands_do_not_create_parse_errors_when_ { var child = new Command("child"); - var parent = new RootCommand + var parent = new Command("parent") { new Option("-x") { IsRequired = true }, child }; - parent.Name = "parent"; var result = parent.Parse("child"); @@ -343,8 +342,8 @@ public void A_custom_validator_can_be_added_to_a_command() command.Validators.Add(commandResult => { - if (commandResult.Children.Any(sr => ((OptionResult)sr).Option.HasAlias("--one")) && - commandResult.Children.Any(sr => ((OptionResult)sr).Option.HasAlias("--two"))) + if (commandResult.Children.Any(sr => ((OptionResult)sr).Option.Aliases.Contains("--one")) && + commandResult.Children.Any(sr => ((OptionResult)sr).Option.Aliases.Contains("--two"))) { commandResult.AddError("Options '--one' and '--two' cannot be used together."); } @@ -529,7 +528,7 @@ public async Task A_custom_validator_added_to_a_global_option_is_checked(string public void Custom_validator_error_messages_are_not_repeated() { var errorMessage = "that's not right..."; - var argument = new Argument(); + var argument = new Argument("arg"); argument.Validators.Add(r => r.AddError(errorMessage)); var cmd = new Command("get") @@ -549,7 +548,7 @@ public void Custom_validator_error_messages_are_not_repeated() [Fact] public void The_parsed_value_of_an_argument_is_available_within_a_validator() { - var argument = new Argument(); + var argument = new Argument("arg"); var errorMessage = "The value of option '-x' must be between 1 and 100."; argument.Validators.Add(result => { @@ -599,7 +598,7 @@ public class PathValidity [Fact] public void LegalFilePathsOnly_rejects_command_arguments_containing_invalid_path_characters() { - Argument argument = new(); + Argument argument = new("arg"); argument.AcceptLegalFilePathsOnly(); var command = new Command("the-command") { @@ -643,7 +642,7 @@ public void LegalFilePathsOnly_rejects_option_arguments_containing_invalid_path_ [Fact] public void LegalFilePathsOnly_accepts_command_arguments_containing_valid_path_characters() { - Argument argument = new (); + Argument argument = new ("arg"); argument.AcceptLegalFilePathsOnly(); var command = new Command("the-command") { @@ -683,7 +682,7 @@ public class FileNameValidity [Fact] public void LegalFileNamesOnly_rejects_command_arguments_containing_invalid_file_name_characters() { - Argument argument = new(); + Argument argument = new("arg"); argument.AcceptLegalFileNamesOnly(); var command = new Command("the-command") @@ -729,7 +728,7 @@ public void LegalFileNamesOnly_rejects_option_arguments_containing_invalid_file_ [Fact] public void LegalFileNamesOnly_accepts_command_arguments_containing_valid_file_name_characters() { - Argument argument = new (); + Argument argument = new ("arg"); argument.AcceptLegalFileNamesOnly(); var command = new Command("the-command") @@ -848,7 +847,7 @@ public void A_command_argument_can_be_invalid_based_on_file_or_directory_existen { var command = new Command("move") { - new Argument().AcceptExistingOnly() + new Argument("arg").AcceptExistingOnly() }; var path = NonexistentPath(); @@ -1040,7 +1039,7 @@ public void Command_argument_does_not_return_errors_when_file_exists() { var command = new Command("move") { - new Argument().AcceptExistingOnly() + new Argument("arg").AcceptExistingOnly() }; var path = ExistingFile(); @@ -1068,7 +1067,7 @@ public void Command_argument_does_not_return_errors_when_Directory_exists() { var command = new Command("move") { - new Argument().AcceptExistingOnly() + new Argument("arg").AcceptExistingOnly() }; var path = ExistingDirectory(); @@ -1160,7 +1159,7 @@ public void A_command_with_subcommands_is_valid_to_invoke_if_it_has_a_handler() [Fact] public void When_an_option_has_a_default_value_it_is_not_valid_to_specify_the_option_without_an_argument() { - var option = new Option("-x", () => 123); + var option = new Option("-x") { DefaultValueFactory = (_) => 123 }; var result = new RootCommand { option }.Parse("-x"); @@ -1173,8 +1172,8 @@ public void When_an_option_has_a_default_value_it_is_not_valid_to_specify_the_op [Fact] public void When_an_option_has_a_default_value_then_the_default_should_apply_if_not_specified() { - var optionX = new Option("-x", () => 123); - var optionY = new Option("-y", () => 456); + var optionX = new Option("-x") { DefaultValueFactory = (_) => 123 }; + var optionY = new Option("-y") { DefaultValueFactory = (_) => 456 }; var parser = new RootCommand { @@ -1252,7 +1251,7 @@ public void Multiple_validators_on_the_same_option_do_not_report_duplicate_error [Fact] // https://github.com/dotnet/command-line-api/issues/1573 public void Multiple_validators_on_the_same_argument_do_not_report_duplicate_errors() { - var argument = new Argument(); + var argument = new Argument("arg"); argument.Validators.Add(result => result.AddError("Wrong")); argument.Validators.Add(_ => { }); diff --git a/src/System.CommandLine.Tests/ResponseFileTests.cs b/src/System.CommandLine.Tests/ResponseFileTests.cs index 47f079df57..2e2d649318 100644 --- a/src/System.CommandLine.Tests/ResponseFileTests.cs +++ b/src/System.CommandLine.Tests/ResponseFileTests.cs @@ -82,10 +82,10 @@ public void When_response_file_is_specified_it_loads_command_arguments_from_resp "three"); var result = new RootCommand - { - new Argument() - } - .Parse($"@{responseFile}"); + { + new Argument("arg") + } + .Parse($"@{responseFile}"); result.CommandResult .Tokens @@ -106,7 +106,7 @@ public void Response_file_can_provide_subcommand_arguments() { new Command("subcommand") { - new Argument() + new Argument("arg") } } .Parse($"subcommand @{responseFile}"); @@ -127,7 +127,7 @@ public void Response_file_can_provide_subcommand() { new Command("subcommand") { - new Argument() + new Argument("arg") } } .Parse($"@{responseFile} one two three"); @@ -151,7 +151,7 @@ public void When_response_file_is_specified_it_loads_subcommand_arguments_from_r { new Command("subcommand") { - new Argument() + new Argument("arg") } } .Parse($"subcommand @{responseFile}"); @@ -301,7 +301,7 @@ public void When_response_file_processing_is_disabled_then_it_returns_response_f { var command = new RootCommand { - new Argument>() + new Argument>("arg") }; var configuration = new CommandLineConfiguration( command, diff --git a/src/System.CommandLine.Tests/RootCommandTests.cs b/src/System.CommandLine.Tests/RootCommandTests.cs index 0692b4b220..664d976106 100644 --- a/src/System.CommandLine.Tests/RootCommandTests.cs +++ b/src/System.CommandLine.Tests/RootCommandTests.cs @@ -15,15 +15,5 @@ public void Root_command_name_defaults_to_executable_name() rootCommand.Name.Should().Be(RootCommand.ExecutableName); } - - [Fact] - public void When_Name_is_set_then_executable_name_is_still_an_alias() - { - var rootCommand = new RootCommand(); - rootCommand.Name = "custom"; - - rootCommand.Aliases.Should().BeEquivalentTo("custom", RootCommand.ExecutableName); - rootCommand.Aliases.Should().BeEquivalentTo("custom", RootCommand.ExecutableName); - } } } diff --git a/src/System.CommandLine.Tests/SuggestDirectiveTests.cs b/src/System.CommandLine.Tests/SuggestDirectiveTests.cs index ad8d7d830f..68778798e5 100644 --- a/src/System.CommandLine.Tests/SuggestDirectiveTests.cs +++ b/src/System.CommandLine.Tests/SuggestDirectiveTests.cs @@ -228,7 +228,7 @@ public async Task It_writes_suggestions_for_partial_option_and_subcommand_aliase new Command("child"), new Option("--option1"), new Option("--option2"), - new Argument() + new Argument("arg") }) .UseSuggestDirective() .Build(); diff --git a/src/System.CommandLine.Tests/SymbolTests.cs b/src/System.CommandLine.Tests/SymbolTests.cs deleted file mode 100644 index 728dab5982..0000000000 --- a/src/System.CommandLine.Tests/SymbolTests.cs +++ /dev/null @@ -1,23 +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 abstract class SymbolTests - { - [Fact] - public void When_Name_is_explicitly_set_then_adding_aliases_does_not_change_it() - { - var symbol = CreateSymbol("original"); - - symbol.Name = "changed"; - - symbol.Name.Should().Be("changed"); - } - - protected abstract Symbol CreateSymbol(string name); - } -} \ No newline at end of file diff --git a/src/System.CommandLine.Tests/TokenReplacementTests.cs b/src/System.CommandLine.Tests/TokenReplacementTests.cs index 07dda8d03f..6ac968111b 100644 --- a/src/System.CommandLine.Tests/TokenReplacementTests.cs +++ b/src/System.CommandLine.Tests/TokenReplacementTests.cs @@ -13,7 +13,7 @@ public class TokenReplacementTests [Fact] public void Token_replacer_receives_the_token_from_the_command_line_with_the_leading_at_symbol_removed() { - var argument = new Argument(); + var argument = new Argument("arg"); var command = new RootCommand { argument }; @@ -37,7 +37,7 @@ public void Token_replacer_receives_the_token_from_the_command_line_with_the_lea [Fact] public void Token_replacer_can_expand_argument_values() { - var argument = new Argument(); + var argument = new Argument("arg"); var command = new RootCommand { argument }; @@ -106,7 +106,7 @@ public void Custom_token_replacer_can_expand_subcommands_and_options_and_argumen [Fact] public void Expanded_tokens_containing_whitespace_are_parsed_as_single_tokens() { - var argument = new Argument(); + var argument = new Argument("arg"); var command = new RootCommand { argument }; @@ -127,7 +127,7 @@ public void Expanded_tokens_containing_whitespace_are_parsed_as_single_tokens() [Fact] public void Token_replacer_can_set_a_custom_error_message() { - var argument = new Argument(); + var argument = new Argument("arg"); var command = new RootCommand { argument }; @@ -150,7 +150,7 @@ public void Token_replacer_can_set_a_custom_error_message() [Fact] public void When_token_replacer_returns_false_without_setting_an_error_message_then_the_command_line_is_unchanged_and_no_parse_error_is_produced() { - var argument = new Argument(); + var argument = new Argument("arg"); var command = new RootCommand { argument }; @@ -173,7 +173,7 @@ public void When_token_replacer_returns_false_without_setting_an_error_message_t [Fact] public void Token_replacer_will_delete_token_when_delegate_returns_true_and_sets_tokens_to_null() { - var argument = new Argument(); + var argument = new Argument("arg"); var command = new RootCommand { argument }; @@ -196,7 +196,7 @@ public void Token_replacer_will_delete_token_when_delegate_returns_true_and_sets [Fact] public void Token_replacer_will_delete_token_when_delegate_returns_true_and_sets_tokens_to_empty_array() { - var argument = new Argument(); + var argument = new Argument("arg"); var command = new RootCommand { argument }; diff --git a/src/System.CommandLine.Tests/UseHelpTests.cs b/src/System.CommandLine.Tests/UseHelpTests.cs index f9aa48c8a2..e4ed9c2a91 100644 --- a/src/System.CommandLine.Tests/UseHelpTests.cs +++ b/src/System.CommandLine.Tests/UseHelpTests.cs @@ -260,8 +260,8 @@ public async Task UseHelp_with_custom_aliases_default_aliases_replaced(string he public void Individual_symbols_can_be_customized() { var subcommand = new Command("subcommand", "The default command description"); - var option = new Option("-x", "The default option description"); - var argument = new Argument("int-value", "The default argument description"); + var option = new Option("-x") { Description = "The default option description" }; + var argument = new Argument("int-value") { Description = "The default argument description" }; var rootCommand = new RootCommand { @@ -381,7 +381,7 @@ public void Help_default_sections_can_be_wrapped() { Command command = new("test") { - new Option("--option", "option description") + new Option("--option") { Description = "option description" } }; var config = new CommandLineBuilder(command) diff --git a/src/System.CommandLine.Tests/VersionOptionTests.cs b/src/System.CommandLine.Tests/VersionOptionTests.cs index ad3cf38eee..73ff3655b4 100644 --- a/src/System.CommandLine.Tests/VersionOptionTests.cs +++ b/src/System.CommandLine.Tests/VersionOptionTests.cs @@ -73,7 +73,10 @@ public async Task When_the_version_option_is_specified_and_there_are_default_opt { var rootCommand = new RootCommand { - new Option("-x", defaultValueFactory: () => true) + new Option("-x") + { + DefaultValueFactory = (_) => true + } }; rootCommand.SetHandler(() => { }); @@ -93,7 +96,7 @@ public async Task When_the_version_option_is_specified_and_there_are_default_arg { var rootCommand = new RootCommand { - new Argument("x", defaultValueFactory: () => true) + new Argument("x") { DefaultValueFactory =(_) => true } }; rootCommand.SetHandler(() => { }); From 4ca639777ceba65b80f7b3fdd1d0e20ca5d55102 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Fri, 3 Mar 2023 09:06:05 +0100 Subject: [PATCH 3/7] making the tests pass --- src/Common/ArgumentBuilder.cs | 22 +++++-- src/Common/OptionBuilder.cs | 14 +++-- .../CommandLineTests.cs | 8 +-- .../CommandResultExtensions.cs | 30 +++++++++- .../TableRenderingTests.cs | 2 +- src/System.CommandLine.Tests/ArgumentTests.cs | 58 +++++++++++++------ .../Binding/TypeConversionTests.cs | 2 +- .../CommandLineConfigurationTests.cs | 8 +-- src/System.CommandLine.Tests/CommandTests.cs | 15 ++--- .../GlobalOptionTests.cs | 4 +- ...s.Help_layout_has_not_changed.approved.txt | 18 +++--- .../Help/HelpBuilderTests.Customization.cs | 6 +- .../Help/HelpBuilderTests.cs | 10 ++-- src/System.CommandLine.Tests/OptionTests.cs | 4 +- .../ParseDiagramTests.cs | 4 +- .../ParseResultTests.cs | 4 +- src/System.CommandLine.Tests/ParserTests.cs | 12 ++-- .../ParsingValidationTests.cs | 26 ++++----- .../TestApps/NativeAOT/Program.cs | 4 +- .../TestApps/Trimming/Program.cs | 2 +- src/System.CommandLine.Tests/UseHelpTests.cs | 6 +- src/System.CommandLine/Argument.cs | 9 ++- src/System.CommandLine/ArgumentArity.cs | 5 +- .../Binding/ArgumentConverter.cs | 4 +- .../CommandLineConfiguration.cs | 7 ++- .../Help/HelpBuilder.Default.cs | 49 +++++----------- src/System.CommandLine/Help/HelpOption.cs | 9 +-- .../Parsing/StringExtensions.cs | 7 +-- src/System.CommandLine/Symbol.cs | 15 +++-- src/System.CommandLine/SymbolExtensions.cs | 1 - 30 files changed, 199 insertions(+), 166 deletions(-) diff --git a/src/Common/ArgumentBuilder.cs b/src/Common/ArgumentBuilder.cs index b5e01e9f60..5c24853f32 100644 --- a/src/Common/ArgumentBuilder.cs +++ b/src/Common/ArgumentBuilder.cs @@ -9,7 +9,7 @@ internal static class ArgumentBuilder static ArgumentBuilder() { - _ctor = typeof(Argument).GetConstructor(new[] { typeof(string), typeof(string) }); + _ctor = typeof(Argument).GetConstructor(new[] { typeof(string) }); } public static Argument CreateArgument(Type valueType, string name = "value") @@ -19,10 +19,10 @@ public static Argument CreateArgument(Type valueType, string name = "value") #if NET6_0_OR_GREATER var ctor = (ConstructorInfo)argumentType.GetMemberWithSameMetadataDefinitionAs(_ctor); #else - var ctor = argumentType.GetConstructor(new[] { typeof(string), typeof(string) }); + var ctor = argumentType.GetConstructor(new[] { typeof(string) }); #endif - return (Argument)ctor.Invoke(new object[] { name, null }); + return (Argument)ctor.Invoke(new object[] { name }); } internal static Argument CreateArgument(ParameterInfo argsParam) @@ -32,10 +32,20 @@ internal static Argument CreateArgument(ParameterInfo argsParam) return CreateArgument(argsParam.ParameterType, argsParam.Name); } - var argumentType = typeof(Argument<>).MakeGenericType(argsParam.ParameterType); + var argumentType = typeof(Bridge<>).MakeGenericType(argsParam.ParameterType); - var ctor = argumentType.GetConstructor(new[] { typeof(string), argsParam.ParameterType, typeof(string) }); + var ctor = argumentType.GetConstructor(new[] { typeof(string), argsParam.ParameterType }); - return (Argument)ctor.Invoke(new object[] { argsParam.Name, argsParam.DefaultValue, null }); + return (Argument)ctor.Invoke(new object[] { argsParam.Name, argsParam.DefaultValue }); + } + + private sealed class Bridge : Argument + { + public Bridge(string name, T defaultValue) + : base(name) + { + // this type exists only for an easy T => Func transformation + DefaultValueFactory = (_) => defaultValue; + } } } \ No newline at end of file diff --git a/src/Common/OptionBuilder.cs b/src/Common/OptionBuilder.cs index cd78d6b9bc..fa9ca5d232 100644 --- a/src/Common/OptionBuilder.cs +++ b/src/Common/OptionBuilder.cs @@ -11,25 +11,27 @@ internal static class OptionBuilder static OptionBuilder() { - _ctor = typeof(Option).GetConstructor(new[] { typeof(string), typeof(string) }); + _ctor = typeof(Option).GetConstructor(new[] { typeof(string), typeof(string[]) }); } - public static Option CreateOption(string name, Type valueType, string description = null) + internal static Option CreateOption(string name, Type valueType, string description = null) { var optionType = typeof(Option<>).MakeGenericType(valueType); #if NET6_0_OR_GREATER var ctor = (ConstructorInfo)optionType.GetMemberWithSameMetadataDefinitionAs(_ctor); #else - var ctor = optionType.GetConstructor(new[] { typeof(string), typeof(string) }); + var ctor = optionType.GetConstructor(new[] { typeof(string), typeof(string[]) }); #endif - var option = (Option)ctor.Invoke(new object[] { name, description }); + var option = (Option)ctor.Invoke(new object[] { name, Array.Empty() }); + + option.Description = description; return option; } - public static Option CreateOption(string name, Type valueType, string description, Func defaultValueFactory) + internal static Option CreateOption(string name, Type valueType, string description, Func defaultValueFactory) { if (defaultValueFactory == null) { @@ -45,7 +47,7 @@ public static Option CreateOption(string name, Type valueType, string descriptio return option; } - private class Bridge : Option + private sealed class Bridge : Option { public Bridge(string name, Func defaultValueFactory, string description) : base(name) diff --git a/src/System.CommandLine.DragonFruit.Tests/CommandLineTests.cs b/src/System.CommandLine.DragonFruit.Tests/CommandLineTests.cs index eba1f8a52f..ef22320fb2 100644 --- a/src/System.CommandLine.DragonFruit.Tests/CommandLineTests.cs +++ b/src/System.CommandLine.DragonFruit.Tests/CommandLineTests.cs @@ -63,7 +63,7 @@ public async Task It_shows_help_text_based_on_XML_documentation_comments() .Contain(" These are arguments") .And.Contain("Arguments:"); stdOut.Should() - .ContainAll("--name ", "Specifies the name option") + .ContainAll("--name", "Specifies the name option") .And.Contain("Options:"); stdOut.Should() .Contain($"Description:{Environment.NewLine} Normal summary"); @@ -87,7 +87,7 @@ public async Task When_XML_documentation_comment_contains_a_para_tag_then_help_i .Contain(" These are arguments") .And.Contain("Arguments:"); stdOut.Should() - .ContainAll("--name ", "Specifies the name option") + .ContainAll("--name", "Specifies the name option") .And.Contain("Options:"); stdOut.Should() .Contain($"Description:{Environment.NewLine} Help for the test program{Environment.NewLine} More help for the test program{Environment.NewLine}"); @@ -111,7 +111,7 @@ public async Task When_XML_documentation_comment_contains_a_para_tag_and_some_te .Contain(" These are arguments") .And.Contain("Arguments:"); stdOut.Should() - .ContainAll("--name ", "Specifies the name option") + .ContainAll("--name", "Specifies the name option") .And.Contain("Options:"); stdOut.Should() .Contain($"Description:{Environment.NewLine} Help for the test program{Environment.NewLine} More help for the test program{Environment.NewLine}"); @@ -132,7 +132,7 @@ public void It_synchronously_shows_help_text_based_on_XML_documentation_comments var stdOut = _terminal.Out.ToString(); stdOut.Should() - .ContainAll("--name ","name [default: Bruce]") + .ContainAll("--name","name [default: Bruce]") .And.Contain("Options:"); } diff --git a/src/System.CommandLine.NamingConventionBinder/CommandResultExtensions.cs b/src/System.CommandLine.NamingConventionBinder/CommandResultExtensions.cs index 8b96279a9d..1da0d4e9b6 100644 --- a/src/System.CommandLine.NamingConventionBinder/CommandResultExtensions.cs +++ b/src/System.CommandLine.NamingConventionBinder/CommandResultExtensions.cs @@ -18,7 +18,7 @@ internal static bool TryGetValueForArgument( { var argument = arguments[i]; - if (valueDescriptor.ValueName.IsMatch(argument.Name)) + if (valueDescriptor.ValueName.IsMatch(RemovePrefix(argument.Name))) { if (commandResult.FindResultFor(argument) is { } argumentResult) { @@ -72,7 +72,8 @@ static bool HasMatchingAlias( IValueDescriptor valueDescriptor, Option option) { - if (option.Name == valueDescriptor.ValueName || option.Aliases.Contains(valueDescriptor.ValueName)) + string nameWithoutPrefix = RemovePrefix(option.Name); + if (valueDescriptor.ValueName.Equals(nameWithoutPrefix, StringComparison.OrdinalIgnoreCase) || valueDescriptor.ValueName.IsMatch(nameWithoutPrefix)) { return true; } @@ -150,4 +151,29 @@ static int IndexAfterPrefix(string alias) return 0; } } + + private static string RemovePrefix(string name) + { + int prefixLength = GetPrefixLength(name); + return prefixLength > 0 + ? name.Substring(prefixLength) + : name; + + static int GetPrefixLength(string name) + { + if (name[0] == '-') + { + return name.Length > 1 && name[1] == '-' + ? 2 + : 1; + } + + if (name[0] == '/') + { + return 1; + } + + return 0; + } + } } \ No newline at end of file diff --git a/src/System.CommandLine.Rendering.Tests/TableRenderingTests.cs b/src/System.CommandLine.Rendering.Tests/TableRenderingTests.cs index 905897b1c8..a2319202bb 100644 --- a/src/System.CommandLine.Rendering.Tests/TableRenderingTests.cs +++ b/src/System.CommandLine.Rendering.Tests/TableRenderingTests.cs @@ -127,7 +127,7 @@ public OptionsHelpView(IEnumerable