diff --git a/src/CommandLine/Core/GetoptTokenizer.cs b/src/CommandLine/Core/GetoptTokenizer.cs index b8c97fc2..92bbacf9 100644 --- a/src/CommandLine/Core/GetoptTokenizer.cs +++ b/src/CommandLine/Core/GetoptTokenizer.cs @@ -16,7 +16,13 @@ public static Result, Error> Tokenize( IEnumerable arguments, Func nameLookup) { - return GetoptTokenizer.Tokenize(arguments, nameLookup, ignoreUnknownArguments:false, allowDashDash:true, posixlyCorrect:false); + return GetoptTokenizer.Tokenize( + arguments, + nameLookup, + ignoreUnknownArguments: false, + allowDashDash: true, + posixlyCorrect: false, + optionsParseMode: OptionsParseMode.Default); } public static Result, Error> Tokenize( @@ -24,7 +30,8 @@ public static Result, Error> Tokenize( Func nameLookup, bool ignoreUnknownArguments, bool allowDashDash, - bool posixlyCorrect) + bool posixlyCorrect, + OptionsParseMode optionsParseMode) { var errors = new List(); Action onBadFormatToken = arg => errors.Add(new BadFormatTokenError(arg)); @@ -70,11 +77,31 @@ public static Result, Error> Tokenize( break; case string arg when arg.StartsWith("--"): + + if (optionsParseMode == OptionsParseMode.SingleDashOnly) + { + onBadFormatToken(arg); + continue; + } + tokens.AddRange(TokenizeLongName(arg, nameLookup, onBadFormatToken, onUnknownOption, onConsumeNext)); break; case string arg when arg.StartsWith("-"): - tokens.AddRange(TokenizeShortName(arg, nameLookup, onUnknownOption, onConsumeNext)); + switch(optionsParseMode) + { + case OptionsParseMode.Default: + tokens.AddRange(TokenizeShortName(arg, nameLookup, onUnknownOption, onConsumeNext)); + break; + + case OptionsParseMode.SingleOrDoubleDash: + case OptionsParseMode.SingleDashOnly: + tokens.AddRange(TokenizeLongName(arg, nameLookup, onBadFormatToken, onUnknownOption, onConsumeNext, dashCount: 1)); + break; + + default: + throw new ArgumentOutOfRangeException(nameof(optionsParseMode), optionsParseMode, null); + } break; case string arg: @@ -126,12 +153,23 @@ public static Func< StringComparer nameComparer, bool ignoreUnknownArguments, bool enableDashDash, - bool posixlyCorrect) + bool posixlyCorrect, + OptionsParseMode optionsParseMode) { return (arguments, optionSpecs) => { - var tokens = GetoptTokenizer.Tokenize(arguments, name => NameLookup.Contains(name, optionSpecs, nameComparer), ignoreUnknownArguments, enableDashDash, posixlyCorrect); - var explodedTokens = GetoptTokenizer.ExplodeOptionList(tokens, name => NameLookup.HavingSeparator(name, optionSpecs, nameComparer)); + var tokens = GetoptTokenizer.Tokenize( + arguments, + name => NameLookup.Contains(name, optionSpecs, nameComparer), + ignoreUnknownArguments, + enableDashDash, + posixlyCorrect, + optionsParseMode); + + var explodedTokens = GetoptTokenizer.ExplodeOptionList( + tokens, + name => NameLookup.HavingSeparator(name, optionSpecs, nameComparer)); + return explodedTokens; }; } @@ -190,9 +228,10 @@ private static IEnumerable TokenizeLongName( Func nameLookup, Action onBadFormatToken, Action onUnknownOption, - Action onConsumeNext) + Action onConsumeNext, + int dashCount = 2) { - string[] parts = arg.Substring(2).Split(new char[] { '=' }, 2); + string[] parts = arg.Substring(dashCount).Split(new char[] { '=' }, 2); string name = parts[0]; string value = (parts.Length > 1) ? parts[1] : null; // A parameter like "--stringvalue=" is acceptable, and makes stringvalue be the empty string diff --git a/src/CommandLine/Core/InstanceBuilder.cs b/src/CommandLine/Core/InstanceBuilder.cs index f48127b1..05c4056a 100644 --- a/src/CommandLine/Core/InstanceBuilder.cs +++ b/src/CommandLine/Core/InstanceBuilder.cs @@ -22,6 +22,7 @@ public static ParserResult Build( CultureInfo parsingCulture, bool autoHelp, bool autoVersion, + OptionsParseMode optionsParseMode, IEnumerable nonFatalErrors) { return Build( @@ -34,6 +35,7 @@ public static ParserResult Build( autoHelp, autoVersion, false, + optionsParseMode, nonFatalErrors); } @@ -47,6 +49,7 @@ public static ParserResult Build( bool autoHelp, bool autoVersion, bool allowMultiInstance, + OptionsParseMode optionsParseMode, IEnumerable nonFatalErrors) { var typeInfo = factory.MapValueOrDefault(f => f().GetType(), typeof(T)); @@ -137,7 +140,7 @@ public static ParserResult Build( var preprocessorErrors = ( argumentsList.Any() - ? arguments.Preprocess(PreprocessorGuards.Lookup(nameComparer, autoHelp, autoVersion)) + ? arguments.Preprocess(PreprocessorGuards.Lookup(nameComparer, autoHelp, autoVersion, optionsParseMode)) : Enumerable.Empty() ).Memoize(); diff --git a/src/CommandLine/Core/InstanceChooser.cs b/src/CommandLine/Core/InstanceChooser.cs index 72307bf2..02209e44 100644 --- a/src/CommandLine/Core/InstanceChooser.cs +++ b/src/CommandLine/Core/InstanceChooser.cs @@ -21,6 +21,7 @@ public static ParserResult Choose( CultureInfo parsingCulture, bool autoHelp, bool autoVersion, + OptionsParseMode optionsParseMode, IEnumerable nonFatalErrors) { return Choose( @@ -33,6 +34,7 @@ public static ParserResult Choose( autoHelp, autoVersion, false, + optionsParseMode, nonFatalErrors); } @@ -46,6 +48,7 @@ public static ParserResult Choose( bool autoHelp, bool autoVersion, bool allowMultiInstance, + OptionsParseMode optionsParseMode, IEnumerable nonFatalErrors) { var verbs = Verb.SelectFromTypes(types); @@ -62,8 +65,9 @@ ParserResult choose() var firstArg = arguments.First(); bool preprocCompare(string command) => - nameComparer.Equals(command, firstArg) || - nameComparer.Equals(string.Concat("--", command), firstArg); + nameComparer.Equals(command, firstArg) + || optionsParseMode != OptionsParseMode.SingleDashOnly && nameComparer.Equals(string.Concat("--", command), firstArg) + || optionsParseMode != OptionsParseMode.Default && nameComparer.Equals(string.Concat("-", command), firstArg); return (autoHelp && preprocCompare("help")) ? MakeNotParsed(types, @@ -71,13 +75,13 @@ bool preprocCompare(string command) => arguments.Skip(1).FirstOrDefault() ?? string.Empty, nameComparer)) : (autoVersion && preprocCompare("version")) ? MakeNotParsed(types, new VersionRequestedError()) - : MatchVerb(tokenizer, verbs, defaultVerb, arguments, nameComparer, ignoreValueCase, parsingCulture, autoHelp, autoVersion, allowMultiInstance, nonFatalErrors); + : MatchVerb(tokenizer, verbs, defaultVerb, arguments, nameComparer, ignoreValueCase, parsingCulture, autoHelp, autoVersion, allowMultiInstance, optionsParseMode, nonFatalErrors); } return arguments.Any() ? choose() : (defaultVerbCount == 1 - ? MatchDefaultVerb(tokenizer, verbs, defaultVerb, arguments, nameComparer, ignoreValueCase, parsingCulture, autoHelp, autoVersion, nonFatalErrors) + ? MatchDefaultVerb(tokenizer, verbs, defaultVerb, arguments, nameComparer, ignoreValueCase, parsingCulture, autoHelp, autoVersion, optionsParseMode, nonFatalErrors) : MakeNotParsed(types, new NoVerbSelectedError())); } @@ -91,6 +95,7 @@ private static ParserResult MatchDefaultVerb( CultureInfo parsingCulture, bool autoHelp, bool autoVersion, + OptionsParseMode optionsParseMode, IEnumerable nonFatalErrors) { return !(defaultVerb is null) @@ -103,6 +108,7 @@ private static ParserResult MatchDefaultVerb( parsingCulture, autoHelp, autoVersion, + optionsParseMode, nonFatalErrors) : MakeNotParsed(verbs.Select(v => v.Item2), new BadVerbSelectedError(arguments.First())); } @@ -118,6 +124,7 @@ private static ParserResult MatchVerb( bool autoHelp, bool autoVersion, bool allowMultiInstance, + OptionsParseMode optionsParseMode, IEnumerable nonFatalErrors) { string firstArg = arguments.First(); @@ -129,7 +136,7 @@ private static ParserResult MatchVerb( if (verbUsed == default) { - return MatchDefaultVerb(tokenizer, verbs, defaultVerb, arguments, nameComparer, ignoreValueCase, parsingCulture, autoHelp, autoVersion, nonFatalErrors); + return MatchDefaultVerb(tokenizer, verbs, defaultVerb, arguments, nameComparer, ignoreValueCase, parsingCulture, autoHelp, autoVersion, optionsParseMode, nonFatalErrors); } return InstanceBuilder.Build( Maybe.Just>( @@ -141,7 +148,8 @@ private static ParserResult MatchVerb( parsingCulture, autoHelp, autoVersion, - allowMultiInstance, + allowMultiInstance, + optionsParseMode, nonFatalErrors); } diff --git a/src/CommandLine/Core/PreprocessorGuards.cs b/src/CommandLine/Core/PreprocessorGuards.cs index 8d6fb5be..3d8a7fac 100644 --- a/src/CommandLine/Core/PreprocessorGuards.cs +++ b/src/CommandLine/Core/PreprocessorGuards.cs @@ -9,30 +9,31 @@ namespace CommandLine.Core static class PreprocessorGuards { public static IEnumerable, IEnumerable>> - Lookup(StringComparer nameComparer, bool autoHelp, bool autoVersion) + Lookup(StringComparer nameComparer, bool autoHelp, bool autoVersion, OptionsParseMode optionsParseMode) { var list = new List, IEnumerable>>(); if (autoHelp) - list.Add(HelpCommand(nameComparer)); + list.Add(HelpCommand(nameComparer, optionsParseMode)); if (autoVersion) - list.Add(VersionCommand(nameComparer)); + list.Add(VersionCommand(nameComparer, optionsParseMode)); return list; } - - public static Func, IEnumerable> HelpCommand(StringComparer nameComparer) + public static Func, IEnumerable> HelpCommand(StringComparer nameComparer, OptionsParseMode optionsParseMode) { return arguments => - nameComparer.Equals("--help", arguments.First()) + optionsParseMode != OptionsParseMode.SingleDashOnly && nameComparer.Equals("--help", arguments.First()) + || optionsParseMode != OptionsParseMode.Default && nameComparer.Equals("-help", arguments.First()) ? new Error[] { new HelpRequestedError() } : Enumerable.Empty(); } - public static Func, IEnumerable> VersionCommand(StringComparer nameComparer) + public static Func, IEnumerable> VersionCommand(StringComparer nameComparer, OptionsParseMode optionsParseMode) { return arguments => - nameComparer.Equals("--version", arguments.First()) + optionsParseMode != OptionsParseMode.SingleDashOnly && nameComparer.Equals("--version", arguments.First()) + || optionsParseMode != OptionsParseMode.Default && nameComparer.Equals("-version", arguments.First()) ? new Error[] { new VersionRequestedError() } : Enumerable.Empty(); } diff --git a/src/CommandLine/Core/Tokenizer.cs b/src/CommandLine/Core/Tokenizer.cs index fe94fc61..3628e162 100644 --- a/src/CommandLine/Core/Tokenizer.cs +++ b/src/CommandLine/Core/Tokenizer.cs @@ -22,21 +22,41 @@ public static Result, Error> Tokenize( public static Result, Error> Tokenize( IEnumerable arguments, Func nameLookup, - Func, IEnumerable> normalize) + Func, IEnumerable> normalize, + OptionsParseMode optionsParseMode = OptionsParseMode.Default) { + var tokens = new List(); var errors = new List(); + Action onError = errors.Add; - var tokens = (from arg in arguments - from token in !arg.StartsWith("-", StringComparison.Ordinal) - ? new[] { Token.Value(arg) } - : arg.StartsWith("--", StringComparison.Ordinal) - ? TokenizeLongName(arg, onError) - : TokenizeShortName(arg, nameLookup) - select token) - .Memoize(); + foreach (var argument in arguments) + { + if (!argument.StartsWith("-", StringComparison.Ordinal)) + { + tokens.Add(Token.Value(argument)); + } + else if (argument.StartsWith("--", StringComparison.Ordinal)) + { + if (optionsParseMode == OptionsParseMode.SingleDashOnly) + { + onError(new BadFormatTokenError(argument)); + continue; + } + + tokens.AddRange(TokenizeLongName(argument, onError)); + } + else if (optionsParseMode == OptionsParseMode.SingleOrDoubleDash || optionsParseMode == OptionsParseMode.SingleDashOnly) + { + tokens.AddRange(TokenizeLongName(argument, onError, dashCount: 1)); + } + else + { + tokens.AddRange(TokenizeShortName(argument, nameLookup)); + } + } - var normalized = normalize(tokens).Memoize(); + var normalized = normalize(tokens.Memoize()).Memoize(); var unkTokens = (from t in normalized where t.IsName() && nameLookup(t.Text) == NameLookupResult.NoOptionFound select t).Memoize(); @@ -123,7 +143,8 @@ public static Func< ConfigureTokenizer( StringComparer nameComparer, bool ignoreUnknownArguments, - bool enableDashDash) + bool enableDashDash, + OptionsParseMode optionsParseMode = OptionsParseMode.Default) { return (arguments, optionSpecs) => { @@ -136,8 +157,8 @@ public static Func< ? Tokenizer.PreprocessDashDash( arguments, args => - Tokenizer.Tokenize(args, name => NameLookup.Contains(name, optionSpecs, nameComparer), normalize)) - : Tokenizer.Tokenize(arguments, name => NameLookup.Contains(name, optionSpecs, nameComparer), normalize); + Tokenizer.Tokenize(args, name => NameLookup.Contains(name, optionSpecs, nameComparer), normalize, optionsParseMode)) + : Tokenizer.Tokenize(arguments, name => NameLookup.Contains(name, optionSpecs, nameComparer), normalize, optionsParseMode); var explodedTokens = Tokenizer.ExplodeOptionList(tokens, name => NameLookup.HavingSeparator(name, optionSpecs, nameComparer)); return explodedTokens; }; @@ -191,11 +212,12 @@ private static IEnumerable TokenizeShortName( private static IEnumerable TokenizeLongName( string value, - Action onError) + Action onError, + int dashCount = 2) { - if (value.Length > 2 && value.StartsWith("--", StringComparison.Ordinal)) + if (value.Length > dashCount && value.StartsWith(new string('-', dashCount), StringComparison.Ordinal)) { - var text = value.Substring(2); + var text = value.Substring(dashCount); var equalIndex = text.IndexOf('='); if (equalIndex <= 0) { diff --git a/src/CommandLine/OptionsParseMode.cs b/src/CommandLine/OptionsParseMode.cs new file mode 100644 index 00000000..0db15594 --- /dev/null +++ b/src/CommandLine/OptionsParseMode.cs @@ -0,0 +1,27 @@ +// Copyright 2005-2015 Giacomo Stelluti Scala & Contributors. All rights reserved. See License.md in the project root for license information. + +namespace CommandLine +{ + /// + /// Defines how commandline options are being parsed. + /// + public enum OptionsParseMode + { + /// + /// Options that start with a double dash must be defined using its full name. E.g. git rebase --interactive + /// Options that start with a single dash are interpreted as list of short named options. E.g. git clean -xdf + /// + Default, + + /// + /// Options that start with a single or double dash are interpreted as short or full named option. + /// + SingleOrDoubleDash, + + /// + /// Options that start with a single dash are interpreted as short or full named option. + /// Options that start with a double dash are considered an invalid input. + /// + SingleDashOnly + } +} diff --git a/src/CommandLine/Parser.cs b/src/CommandLine/Parser.cs index 4301aa52..ca542917 100644 --- a/src/CommandLine/Parser.cs +++ b/src/CommandLine/Parser.cs @@ -102,6 +102,7 @@ public ParserResult ParseArguments(IEnumerable args) settings.AutoHelp, settings.AutoVersion, settings.AllowMultiInstance, + settings.OptionsParseMode, HandleUnknownArguments(settings.IgnoreUnknownArguments)), settings); } @@ -133,6 +134,7 @@ public ParserResult ParseArguments(Func factory, IEnumerable ar settings.AutoHelp, settings.AutoVersion, settings.AllowMultiInstance, + settings.OptionsParseMode, HandleUnknownArguments(settings.IgnoreUnknownArguments)), settings); } @@ -166,6 +168,7 @@ public ParserResult ParseArguments(IEnumerable args, params Type settings.AutoHelp, settings.AutoVersion, settings.AllowMultiInstance, + settings.OptionsParseMode, HandleUnknownArguments(settings.IgnoreUnknownArguments)), settings); } @@ -190,11 +193,13 @@ private static Result, Error> Tokenize( settings.NameComparer, settings.IgnoreUnknownArguments, settings.EnableDashDash, - settings.PosixlyCorrect)(arguments, optionSpecs) + settings.PosixlyCorrect, + settings.OptionsParseMode)(arguments, optionSpecs) : Tokenizer.ConfigureTokenizer( settings.NameComparer, settings.IgnoreUnknownArguments, - settings.EnableDashDash)(arguments, optionSpecs); + settings.EnableDashDash, + settings.OptionsParseMode)(arguments, optionSpecs); } private static ParserResult MakeParserResult(ParserResult parserResult, ParserSettings settings) @@ -202,15 +207,16 @@ private static ParserResult MakeParserResult(ParserResult parserResult, return DisplayHelp( parserResult, settings.HelpWriter, - settings.MaximumDisplayWidth); + settings.MaximumDisplayWidth, + settings.OptionsParseMode != OptionsParseMode.Default); } - private static ParserResult DisplayHelp(ParserResult parserResult, TextWriter helpWriter, int maxDisplayWidth) + private static ParserResult DisplayHelp(ParserResult parserResult, TextWriter helpWriter, int maxDisplayWidth, bool useSingleDashForOptions) { parserResult.WithNotParsed( errors => Maybe.Merge(errors.ToMaybe(), helpWriter.ToMaybe()) - .Do((_, writer) => writer.Write(HelpText.AutoBuild(parserResult, maxDisplayWidth))) + .Do((_, writer) => writer.Write(HelpText.AutoBuild(parserResult, maxDisplayWidth, useSingleDashForOptions))) ); return parserResult; diff --git a/src/CommandLine/ParserSettings.cs b/src/CommandLine/ParserSettings.cs index 5ed73f30..8e6fc147 100644 --- a/src/CommandLine/ParserSettings.cs +++ b/src/CommandLine/ParserSettings.cs @@ -25,6 +25,7 @@ public class ParserSettings : IDisposable private bool autoVersion; private CultureInfo parsingCulture; private Maybe enableDashDash; + private OptionsParseMode optionsParseMode; private int maximumDisplayWidth; private Maybe allowMultiInstance; private bool getoptMode; @@ -174,6 +175,15 @@ public bool EnableDashDash set => PopsicleSetter.Set(Consumed, ref enableDashDash, Maybe.Just(value)); } + /// + /// Gets or sets a value indicating how commandline options are being parsed. + /// + public OptionsParseMode OptionsParseMode + { + get => optionsParseMode; + set => PopsicleSetter.Set(Consumed, ref optionsParseMode, value); + } + /// /// Gets or sets the maximum width of the display. This determines word wrap when displaying the text. /// diff --git a/src/CommandLine/Text/HelpText.cs b/src/CommandLine/Text/HelpText.cs index f5e9a7b9..fe16aa0c 100644 --- a/src/CommandLine/Text/HelpText.cs +++ b/src/CommandLine/Text/HelpText.cs @@ -111,6 +111,7 @@ ComparableOption ToComparableOption(Specification spec, int index) private bool addEnumValuesToHelpText; private bool autoHelp; private bool autoVersion; + private bool useSingleDashForOptions; private bool addNewLineBetweenHelpSections; /// @@ -296,6 +297,15 @@ public bool AutoVersion get { return autoVersion; } set { autoVersion = value; } } + + /// + /// Gets or sets a value indicating whether a single dash is used for options. + /// + public bool UseSingleDashForOptions + { + get { return useSingleDashForOptions; } + set { useSingleDashForOptions = value; } + } /// /// Gets the instance specified in constructor. @@ -316,13 +326,15 @@ public SentenceBuilder SentenceBuilder /// A delegate used to customize model used to render text block of usage examples. /// If true the output style is consistent with verb commands (no dashes), otherwise it outputs options. /// The maximum width of the display. + /// Indicating whether a single dash is used for options. /// The parameter is not ontly a metter of formatting, it controls whether to handle verbs or options. public static HelpText AutoBuild( ParserResult parserResult, Func onError, Func onExample, bool verbsIndex = false, - int maxDisplayWidth = DefaultMaximumLength) + int maxDisplayWidth = DefaultMaximumLength, + bool useSingleDashForOptions = false) { var auto = new HelpText { @@ -330,7 +342,8 @@ public static HelpText AutoBuild( Copyright = CopyrightInfo.Empty, AdditionalNewLineAfterOption = true, AddDashesToOption = !verbsIndex, - MaximumDisplayWidth = maxDisplayWidth + MaximumDisplayWidth = maxDisplayWidth, + UseSingleDashForOptions = useSingleDashForOptions }; try @@ -394,14 +407,15 @@ public static HelpText AutoBuild( /// /// The containing the instance that collected command line arguments parsed with class. /// The maximum width of the display. + /// Indicating whether a single dash is used for options. /// /// An instance of class. /// /// This feature is meant to be invoked automatically by the parser, setting the HelpWriter property /// of . - public static HelpText AutoBuild(ParserResult parserResult, int maxDisplayWidth = DefaultMaximumLength) + public static HelpText AutoBuild(ParserResult parserResult, int maxDisplayWidth = DefaultMaximumLength, bool useSingleDashForOptions = false) { - return AutoBuild(parserResult, h => h, maxDisplayWidth); + return AutoBuild(parserResult, h => h, maxDisplayWidth, useSingleDashForOptions); } /// @@ -411,12 +425,13 @@ public static HelpText AutoBuild(ParserResult parserResult, int maxDisplay /// The containing the instance that collected command line arguments parsed with class. /// A delegate used to customize the text block of reporting parsing errors text block. /// The maximum width of the display. + /// Indicating whether a single dash is used for options. /// /// An instance of class. /// /// This feature is meant to be invoked automatically by the parser, setting the HelpWriter property /// of . - public static HelpText AutoBuild(ParserResult parserResult, Func onError, int maxDisplayWidth = DefaultMaximumLength) + public static HelpText AutoBuild(ParserResult parserResult, Func onError, int maxDisplayWidth = DefaultMaximumLength, bool useSingleDashForOptions = false) { if (parserResult.Tag != ParserResultType.NotParsed) throw new ArgumentException("Excepting NotParsed type.", "parserResult"); @@ -424,14 +439,14 @@ public static HelpText AutoBuild(ParserResult parserResult, Func)parserResult).Errors; if (errors.Any(e => e.Tag == ErrorType.VersionRequestedError)) - return new HelpText($"{HeadingInfo.Default}{Environment.NewLine}") { MaximumDisplayWidth = maxDisplayWidth }.AddPreOptionsLine(Environment.NewLine); + return new HelpText($"{HeadingInfo.Default}{Environment.NewLine}") { MaximumDisplayWidth = maxDisplayWidth, UseSingleDashForOptions = useSingleDashForOptions }.AddPreOptionsLine(Environment.NewLine); if (!errors.Any(e => e.Tag == ErrorType.HelpVerbRequestedError)) return AutoBuild(parserResult, current => { onError?.Invoke(current); return DefaultParsingErrorsHandler(parserResult, current); - }, e => e, maxDisplayWidth: maxDisplayWidth); + }, e => e, maxDisplayWidth: maxDisplayWidth, useSingleDashForOptions: useSingleDashForOptions); var err = errors.OfType().Single(); var pr = new NotParsed(TypeInfo.Create(err.Type), new Error[] { err }); @@ -1010,7 +1025,7 @@ private string AddOptionName(int maxLength, OptionSpecification specification) .MapIf( specification.LongName.Length > 0, it => it - .AppendWhen(addDashesToOption, "--") + .AppendWhen(addDashesToOption, useSingleDashForOptions ? "-" : "--") .AppendFormat("{0}", specification.LongName) .AppendFormatWhen(specification.MetaValue.Length > 0, "={0}", specification.MetaValue)) .ToString(); diff --git a/tests/CommandLine.Tests/Unit/Core/InstanceBuilderTests.cs b/tests/CommandLine.Tests/Unit/Core/InstanceBuilderTests.cs index 2f8d02b7..8bd3c830 100644 --- a/tests/CommandLine.Tests/Unit/Core/InstanceBuilderTests.cs +++ b/tests/CommandLine.Tests/Unit/Core/InstanceBuilderTests.cs @@ -19,7 +19,7 @@ namespace CommandLine.Tests.Unit.Core { public class InstanceBuilderTests { - private static ParserResult InvokeBuild(string[] arguments, bool autoHelp = true, bool autoVersion = true, bool multiInstance = false) + private static ParserResult InvokeBuild(string[] arguments, bool autoHelp = true, bool autoVersion = true, bool multiInstance = false, OptionsParseMode optionsParseMode = OptionsParseMode.Default) where T : new() { return InstanceBuilder.Build( @@ -32,6 +32,7 @@ private static ParserResult InvokeBuild(string[] arguments, bool autoHelp autoHelp, autoVersion, multiInstance, + optionsParseMode, Enumerable.Empty()); } @@ -47,6 +48,7 @@ private static ParserResult InvokeBuildEnumValuesCaseIgnore(string[] argum CultureInfo.InvariantCulture, true, true, + OptionsParseMode.Default, Enumerable.Empty()); } @@ -61,6 +63,7 @@ private static ParserResult InvokeBuildImmutable(string[] arguments) CultureInfo.InvariantCulture, true, true, + OptionsParseMode.Default, Enumerable.Empty()); } @@ -421,6 +424,7 @@ public void Double_dash_force_subsequent_arguments_as_values() CultureInfo.InvariantCulture, true, true, + OptionsParseMode.Default, Enumerable.Empty()); // Verify outcome @@ -1263,6 +1267,43 @@ public void Parse_int_sequence_with_multi_instance() ((Parsed)result).Value.IntSequence.Should().BeEquivalentTo(expected); } + + [Theory] + [InlineData("-help", OptionsParseMode.SingleDashOnly)] + [InlineData("-help", OptionsParseMode.SingleOrDoubleDash)] + [InlineData("--help", OptionsParseMode.Default)] + [InlineData("--help", OptionsParseMode.SingleOrDoubleDash)] + public void Parse_Built_In_Help_Argument(string argument, OptionsParseMode optionsParseMode) + { + var result = InvokeBuild(new[] { argument }, optionsParseMode: optionsParseMode); + + result.Errors.Single().Should().BeOfType(); + } + + [Theory] + [InlineData("-version", OptionsParseMode.SingleDashOnly)] + [InlineData("-version", OptionsParseMode.SingleOrDoubleDash)] + [InlineData("--version", OptionsParseMode.Default)] + [InlineData("--version", OptionsParseMode.SingleOrDoubleDash)] + public void Parse_Built_In_Version_Argument(string argument, OptionsParseMode optionsParseMode) + { + var result = InvokeBuild(new[] { argument }, optionsParseMode: optionsParseMode); + + result.Errors.Single().Should().BeOfType(); + } + + [Theory] + [InlineData("-help", OptionsParseMode.Default)] + [InlineData("--help", OptionsParseMode.SingleDashOnly)] + [InlineData("-version", OptionsParseMode.Default)] + [InlineData("--version", OptionsParseMode.SingleDashOnly)] + public void Parse_Invalid_Built_In_Argument(string argument, OptionsParseMode optionsParseMode) + { + var result = InvokeBuild(new[] { argument }, optionsParseMode: optionsParseMode); + + result.Errors.Should().NotBeOfType() + .And.Should().NotBeOfType(); + } #region custom types diff --git a/tests/CommandLine.Tests/Unit/Core/InstanceChooserTests.cs b/tests/CommandLine.Tests/Unit/Core/InstanceChooserTests.cs index d5cb9a21..0518842f 100644 --- a/tests/CommandLine.Tests/Unit/Core/InstanceChooserTests.cs +++ b/tests/CommandLine.Tests/Unit/Core/InstanceChooserTests.cs @@ -16,7 +16,8 @@ public class InstanceChooserTests private static ParserResult InvokeChoose( IEnumerable types, IEnumerable arguments, - bool multiInstance = false) + bool multiInstance = false, + OptionsParseMode optionsParseMode = OptionsParseMode.Default) { return InstanceChooser.Choose( (args, optionSpecs) => Tokenizer.ConfigureTokenizer(StringComparer.Ordinal, false, false)(args, optionSpecs), @@ -28,6 +29,7 @@ private static ParserResult InvokeChoose( true, true, multiInstance, + optionsParseMode, Enumerable.Empty()); } @@ -183,5 +185,60 @@ public void Parse_sequence_verb_with_multi_instance_returns_verb_instance() Assert.IsType(((Parsed)result).Value); expected.Should().BeEquivalentTo(((Parsed)result).Value); } + + [Theory] + [InlineData("help", OptionsParseMode.Default)] + [InlineData("help", OptionsParseMode.SingleDashOnly)] + [InlineData("help", OptionsParseMode.SingleOrDoubleDash)] + [InlineData("-help", OptionsParseMode.SingleDashOnly)] + [InlineData("-help", OptionsParseMode.SingleOrDoubleDash)] + [InlineData("--help", OptionsParseMode.Default)] + [InlineData("--help", OptionsParseMode.SingleOrDoubleDash)] + public void Parse_Built_In_Help_Argument(string argument, OptionsParseMode optionsParseMode) + { + var result = InvokeChoose( + Type.EmptyTypes, + new[] { argument }, + true, + optionsParseMode); + + result.Errors.Single().Should().BeOfType(); + } + + [Theory] + [InlineData("version", OptionsParseMode.Default)] + [InlineData("version", OptionsParseMode.SingleDashOnly)] + [InlineData("version", OptionsParseMode.SingleOrDoubleDash)] + [InlineData("-version", OptionsParseMode.SingleDashOnly)] + [InlineData("-version", OptionsParseMode.SingleOrDoubleDash)] + [InlineData("--version", OptionsParseMode.Default)] + [InlineData("--version", OptionsParseMode.SingleOrDoubleDash)] + public void Parse_Built_In_Version_Argument(string argument, OptionsParseMode optionsParseMode) + { + var result = InvokeChoose( + Type.EmptyTypes, + new[] { argument }, + true, + optionsParseMode); + + result.Errors.Single().Should().BeOfType(); + } + + [Theory] + [InlineData("-help", OptionsParseMode.Default)] + [InlineData("--help", OptionsParseMode.SingleDashOnly)] + [InlineData("-version", OptionsParseMode.Default)] + [InlineData("--version", OptionsParseMode.SingleDashOnly)] + public void Parse_Invalid_Built_In_Argument(string argument, OptionsParseMode optionsParseMode) + { + var result = InvokeChoose( + Type.EmptyTypes, + new[] { argument }, + true, + optionsParseMode); + + result.Errors.Single().Should().NotBeOfType() + .And.Should().NotBeOfType(); + } } } diff --git a/tests/CommandLine.Tests/Unit/ParserTests.cs b/tests/CommandLine.Tests/Unit/ParserTests.cs index b079ce0f..7be2cc4b 100644 --- a/tests/CommandLine.Tests/Unit/ParserTests.cs +++ b/tests/CommandLine.Tests/Unit/ParserTests.cs @@ -1025,5 +1025,32 @@ public void When_HelpWriter_is_null_it_should_not_fire_exception() //Assert sut.Settings.MaximumDisplayWidth.Should().BeGreaterThan(1); } + + [Theory] + [InlineData(OptionsParseMode.SingleOrDoubleDash, ParserResultType.Parsed, new[]{ "-s", "value" })] + [InlineData(OptionsParseMode.SingleOrDoubleDash, ParserResultType.Parsed, new[]{ "-shortandlong", "value" })] + [InlineData(OptionsParseMode.SingleOrDoubleDash, ParserResultType.Parsed, new[]{ "--s", "value" })] + [InlineData(OptionsParseMode.SingleOrDoubleDash, ParserResultType.Parsed, new[]{ "--shortandlong", "value" })] + [InlineData(OptionsParseMode.SingleDashOnly, ParserResultType.Parsed, new[]{ "-s", "value" })] + [InlineData(OptionsParseMode.SingleDashOnly, ParserResultType.Parsed, new[]{ "-shortandlong", "value" })] + [InlineData(OptionsParseMode.SingleDashOnly, ParserResultType.NotParsed, new[]{ "--s", "value" })] + [InlineData(OptionsParseMode.SingleDashOnly, ParserResultType.NotParsed, new[]{ "--shortanlong", "value" })] + public void Parse_Options_With_Custom_OptionsParseMode(OptionsParseMode mode, ParserResultType result, string[] arguments) + { + // Arrange + var sut = new Parser(with => + { + with.OptionsParseMode = mode; + }); + + // Act + var options = sut.ParseArguments(arguments); + + // Assert + options.Tag.Should().Be(result); + + if (options.Tag == ParserResultType.Parsed) + options.Value.ShortAndLong.Should().Be("value"); + } } }