Skip to content

Name and aliases separation #2073

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Mar 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions src/Common/ArgumentBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ internal static class ArgumentBuilder

static ArgumentBuilder()
{
_ctor = typeof(Argument<string>).GetConstructor(new[] { typeof(string), typeof(string) });
_ctor = typeof(Argument<string>).GetConstructor(new[] { typeof(string) });
}

public static Argument CreateArgument(Type valueType, string name = "value")
Expand All @@ -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)
Expand All @@ -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<T> : Argument<T>
{
public Bridge(string name, T defaultValue)
: base(name)
{
// this type exists only for an easy T => Func<ArgumentResult, T> transformation
DefaultValueFactory = (_) => defaultValue;
}
}
}
21 changes: 12 additions & 9 deletions src/Common/OptionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,27 @@ internal static class OptionBuilder

static OptionBuilder()
{
_ctor = typeof(Option<string>).GetConstructor(new[] { typeof(string), typeof(string) });
_ctor = typeof(Option<string>).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<string>() });

option.Description = description;

return option;
}

public static Option CreateOption(string name, Type valueType, string description, Func<object> defaultValueFactory)
internal static Option CreateOption(string name, Type valueType, string description, Func<object> defaultValueFactory)
{
if (defaultValueFactory == null)
{
Expand All @@ -45,13 +47,14 @@ public static Option CreateOption(string name, Type valueType, string descriptio
return option;
}

private class Bridge<T> : Option<T>
private sealed class Bridge<T> : Option<T>
{
public Bridge(string name, Func<object> defaultValueFactory, string description)
: base(name,
() => (T)defaultValueFactory(), // this type exists only for an easy Func<object> => Func<T> transformation
description)
: base(name)
{
// this type exists only for an easy Func<object> => Func<T> transformation
DefaultValueFactory = (_) => (T)defaultValueFactory();
Description = description;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,14 @@ System.CommandLine
public System.Object GetDefaultValue()
public System.String ToString()
public class Argument<T> : Argument, IValueDescriptor<T>, System.CommandLine.Binding.IValueDescriptor
.ctor()
.ctor(System.String name, System.String description = null)
.ctor(System.String name, Func<T> defaultValueFactory, System.String description = null)
.ctor(System.String name, T defaultValue, System.String description = null)
.ctor(Func<T> defaultValueFactory)
.ctor(System.String name, Func<System.CommandLine.Parsing.ArgumentResult,T> parse, System.Boolean isDefault = False, System.String description = null)
.ctor(Func<System.CommandLine.Parsing.ArgumentResult,T> parse, System.Boolean isDefault = False)
.ctor(System.String name)
public Func<System.CommandLine.Parsing.ArgumentResult,T> CustomParser { get; set; }
public Func<System.CommandLine.Parsing.ArgumentResult,T> 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<T> defaultValueFactory)
public System.Void SetDefaultValueFactory(Func<System.CommandLine.Parsing.ArgumentResult,T> defaultValueFactory)
public struct ArgumentArity : System.ValueType, System.IEquatable<ArgumentArity>
public static ArgumentArity ExactlyOne { get; }
public static ArgumentArity OneOrMore { get; }
Expand All @@ -42,8 +35,9 @@ System.CommandLine
public static Argument<System.IO.DirectoryInfo> AcceptExistingOnly(this Argument<System.IO.DirectoryInfo> argument)
public static Argument<System.IO.FileSystemInfo> AcceptExistingOnly(this Argument<System.IO.FileSystemInfo> argument)
public static Argument<T> AcceptExistingOnly<T>(this Argument<T> argument)
public class Command : IdentifierSymbol, System.Collections.Generic.IEnumerable<Symbol>, System.Collections.IEnumerable
public class Command : Symbol, System.Collections.Generic.IEnumerable<Symbol>, System.Collections.IEnumerable
.ctor(System.String name, System.String description = null)
public System.Collections.Generic.ICollection<System.String> Aliases { get; }
public System.Collections.Generic.IList<Argument> Arguments { get; }
public System.Collections.Generic.IEnumerable<Symbol> Children { get; }
public ICommandHandler Handler { get; set; }
Expand Down Expand Up @@ -75,7 +69,7 @@ System.CommandLine
public CommandLineBuilder UseEnvironmentVariableDirective()
public CommandLineBuilder UseExceptionHandler(System.Action<System.Exception,System.CommandLine.Invocation.InvocationContext> onException = null, System.Nullable<System.Int32> errorExitCode = null)
public CommandLineBuilder UseHelp(System.Nullable<System.Int32> maxWidth = null)
public CommandLineBuilder UseHelp(System.String[] helpAliases)
public CommandLineBuilder UseHelp(System.String name, System.String[] helpAliases)
public CommandLineBuilder UseHelp(System.Action<System.CommandLine.Help.HelpContext> customize, System.Nullable<System.Int32> maxWidth = null)
public CommandLineBuilder UseHelpBuilder(System.Func<System.CommandLine.Binding.BindingContext,System.CommandLine.Help.HelpBuilder> getHelpBuilder)
public CommandLineBuilder UseParseDirective(System.Int32 errorExitCode = 1)
Expand All @@ -84,7 +78,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<System.CommandLine.Invocation.InvocationMiddleware> middlewarePipeline = null, System.Func<System.CommandLine.Binding.BindingContext,System.CommandLine.Help.HelpBuilder> helpBuilderFactory = null, System.CommandLine.Parsing.TryReplaceToken tokenReplacer = null)
Expand Down Expand Up @@ -137,32 +131,24 @@ System.CommandLine
public System.Int32 Invoke(System.CommandLine.Invocation.InvocationContext context)
public System.Threading.Tasks.Task<System.Int32> 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<System.String> Aliases { get; }
public System.Void AddAlias(System.String alias)
public System.Boolean HasAlias(System.String alias)
public abstract class Option : IdentifierSymbol, System.CommandLine.Binding.IValueDescriptor
public abstract class Option : Symbol, System.CommandLine.Binding.IValueDescriptor
public System.Collections.Generic.ICollection<System.String> Aliases { get; }
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<System.Func<System.CommandLine.Completions.CompletionContext,System.Collections.Generic.IEnumerable<System.CommandLine.Completions.CompletionItem>>> CompletionSources { get; }
public System.String HelpName { get; set; }
public System.Boolean IsRequired { get; set; }
public System.Collections.Generic.List<System.Action<System.CommandLine.Parsing.OptionResult>> Validators { get; }
public System.Type ValueType { get; }
public System.Collections.Generic.IEnumerable<System.CommandLine.Completions.CompletionItem> GetCompletions(System.CommandLine.Completions.CompletionContext context)
public class Option<T> : Option, IValueDescriptor<T>, 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<System.CommandLine.Parsing.ArgumentResult,T> parseArgument, System.Boolean isDefault = False, System.String description = null)
.ctor(System.String[] aliases, Func<System.CommandLine.Parsing.ArgumentResult,T> parseArgument, System.Boolean isDefault = False, System.String description = null)
.ctor(System.String name, Func<T> defaultValueFactory, System.String description = null)
.ctor(System.String[] aliases, Func<T> defaultValueFactory, System.String description = null)
.ctor(System.String name, System.String[] aliases)
public Func<System.CommandLine.Parsing.ArgumentResult,T> CustomParser { get; set; }
public Func<System.CommandLine.Parsing.ArgumentResult,T> 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<T> defaultValueFactory)
public static class OptionValidation
public static Option<System.IO.FileInfo> AcceptExistingOnly(this Option<System.IO.FileInfo> option)
public static Option<System.IO.DirectoryInfo> AcceptExistingOnly(this Option<System.IO.DirectoryInfo> option)
Expand Down Expand Up @@ -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<Symbol> Parents { get; }
public System.Collections.Generic.IEnumerable<System.CommandLine.Completions.CompletionItem> GetCompletions(System.CommandLine.Completions.CompletionContext context)
public System.String ToString()
Expand Down Expand Up @@ -258,9 +244,9 @@ System.CommandLine.Help
public static System.String GetArgumentDefaultValue(System.CommandLine.Argument argument)
public static System.String GetArgumentDescription(System.CommandLine.Argument argument)
public static System.String GetArgumentUsageLabel(System.CommandLine.Argument argument)
public static System.String GetIdentifierSymbolDescription(System.CommandLine.IdentifierSymbol symbol)
public static System.String GetIdentifierSymbolUsageLabel(System.CommandLine.IdentifierSymbol symbol, HelpContext context)
public static System.String GetCommandUsageLabel(System.CommandLine.Command symbol)
public static System.Collections.Generic.IEnumerable<System.Action<HelpContext>> GetLayout()
public static System.String GetOptionUsageLabel(System.CommandLine.Option symbol)
public static System.Action<HelpContext> OptionsSection()
public static System.Action<HelpContext> SubcommandsSection()
public static System.Action<HelpContext> SynopsisSection()
Expand Down Expand Up @@ -350,12 +336,14 @@ System.CommandLine.Parsing
public System.Collections.Generic.IEnumerable<SymbolResult> Children { get; }
public System.CommandLine.Command Command { get; }
public Token Token { get; }
public System.String ToString()
public class OptionResult : SymbolResult
public System.Boolean IsImplicit { get; }
public System.CommandLine.Option Option { get; }
public Token Token { get; }
public System.Object GetValueOrDefault()
public T GetValueOrDefault<T>()
public System.String ToString()
public class ParseError
public System.String Message { get; }
public SymbolResult SymbolResult { get; }
Expand All @@ -374,7 +362,6 @@ System.CommandLine.Parsing
public OptionResult FindResultFor(System.CommandLine.Option option)
public T GetValue<T>(Argument<T> argument)
public T GetValue<T>(Option<T> option)
public System.String ToString()
public class Token, System.IEquatable<Token>
public static System.Boolean op_Equality(Token left, Token right)
public static System.Boolean op_Inequality(Token left, Token right)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public void ErrorMessages_AreLocalized(string cultureName, string expectedMessag

Command command = new(CommandName)
{
new Argument<string>()
new Argument<string>("arg")
};

ParseResult parseResult = command.Parse(CommandName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public void SetupOneOptWithNestedCommand()
{
_rootCommand = new Command("root_command");
var nestedCommand = new Command("nested_command");
var option = new Option<int>("-opt1", () => 123);
var option = new Option<int>("-opt1") { DefaultValueFactory = (_) => 123 };
nestedCommand.Options.Add(option);
_rootCommand.Subcommands.Add(nestedCommand);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ public class Perf_Parser_Simple

private static RootCommand BuildCommand()
{
Option<bool> boolOption = new(new[] { "--bool", "-b" }, "Bool option");
Option<string> stringOption = new(new[] { "--string", "-s" }, "String option");
Option<bool> boolOption = new("--bool", "-b") { Description = "Bool option" };
Option<string> stringOption = new("--string", "-s") { Description = "String option" };

RootCommand command = new()
{
Expand Down
8 changes: 4 additions & 4 deletions src/System.CommandLine.DragonFruit.Tests/CommandLineTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public async Task It_shows_help_text_based_on_XML_documentation_comments()
.Contain("<args> These are arguments")
.And.Contain("Arguments:");
stdOut.Should()
.ContainAll("--name <name>", "Specifies the name option")
.ContainAll("--name", "Specifies the name option")
.And.Contain("Options:");
stdOut.Should()
.Contain($"Description:{Environment.NewLine} Normal summary");
Expand All @@ -87,7 +87,7 @@ public async Task When_XML_documentation_comment_contains_a_para_tag_then_help_i
.Contain("<args> These are arguments")
.And.Contain("Arguments:");
stdOut.Should()
.ContainAll("--name <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}");
Expand All @@ -111,7 +111,7 @@ public async Task When_XML_documentation_comment_contains_a_para_tag_and_some_te
.Contain("<args> These are arguments")
.And.Contain("Arguments:");
stdOut.Should()
.ContainAll("--name <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}");
Expand All @@ -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>","name [default: Bruce]")
.ContainAll("--name","name [default: Bruce]")
.And.Contain("Options:");
}

Expand Down
5 changes: 5 additions & 0 deletions src/System.CommandLine.DragonFruit/CommandLine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,11 @@ private static bool HasAliasIgnoringPrefix(Option option, string alias)
{
ReadOnlySpan<char> 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))
Expand Down
Loading