Skip to content

Added AutoHelp and AutoVersion properties to control adding of implicit 'help' and 'version' options/verbs #256

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 2 commits into from
Dec 18, 2018
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
4 changes: 3 additions & 1 deletion src/CommandLine/Core/InstanceBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ public static ParserResult<T> Build<T>(
StringComparer nameComparer,
bool ignoreValueCase,
CultureInfo parsingCulture,
bool autoHelp,
bool autoVersion,
IEnumerable<ErrorType> nonFatalErrors)
{
var typeInfo = factory.MapValueOrDefault(f => f().GetType(), typeof(T));
Expand Down Expand Up @@ -129,7 +131,7 @@ join sp in specPropsWithValue on prms.Name.ToLower() equals sp.Property.Name.ToL
};

var preprocessorErrors = arguments.Any()
? arguments.Preprocess(PreprocessorGuards.Lookup(nameComparer))
? arguments.Preprocess(PreprocessorGuards.Lookup(nameComparer, autoHelp, autoVersion))
: Enumerable.Empty<Error>();

var result = arguments.Any()
Expand Down
12 changes: 9 additions & 3 deletions src/CommandLine/Core/InstanceChooser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ public static ParserResult<object> Choose(
IEnumerable<string> arguments,
StringComparer nameComparer,
CultureInfo parsingCulture,
bool autoHelp,
bool autoVersion,
IEnumerable<ErrorType> nonFatalErrors)
{
Func<ParserResult<object>> choose = () =>
Expand All @@ -30,13 +32,13 @@ public static ParserResult<object> Choose(

var verbs = Verb.SelectFromTypes(types);

return preprocCompare("help")
return (autoHelp && preprocCompare("help"))
? MakeNotParsed(types,
MakeHelpVerbRequestedError(verbs,
arguments.Skip(1).FirstOrDefault() ?? string.Empty, nameComparer))
: preprocCompare("version")
: (autoVersion && preprocCompare("version"))
? MakeNotParsed(types, new VersionRequestedError())
: MatchVerb(tokenizer, verbs, arguments, nameComparer, parsingCulture, nonFatalErrors);
: MatchVerb(tokenizer, verbs, arguments, nameComparer, parsingCulture, autoHelp, autoVersion, nonFatalErrors);
};

return arguments.Any()
Expand All @@ -50,6 +52,8 @@ private static ParserResult<object> MatchVerb(
IEnumerable<string> arguments,
StringComparer nameComparer,
CultureInfo parsingCulture,
bool autoHelp,
bool autoVersion,
IEnumerable<ErrorType> nonFatalErrors)
{
return verbs.Any(a => nameComparer.Equals(a.Item1.Name, arguments.First()))
Expand All @@ -62,6 +66,8 @@ private static ParserResult<object> MatchVerb(
nameComparer,
false,
parsingCulture,
autoHelp,
autoVersion,
nonFatalErrors)
: MakeNotParsed(verbs.Select(v => v.Item2), new BadVerbSelectedError(arguments.First()));
}
Expand Down
13 changes: 7 additions & 6 deletions src/CommandLine/Core/PreprocessorGuards.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ namespace CommandLine.Core
static class PreprocessorGuards
{
public static IEnumerable<Func<IEnumerable<string>, IEnumerable<Error>>>
Lookup(StringComparer nameComparer)
Lookup(StringComparer nameComparer, bool autoHelp, bool autoVersion)
{
return new List<Func<IEnumerable<string>, IEnumerable<Error>>>
{
HelpCommand(nameComparer),
VersionCommand(nameComparer)
};
var list = new List<Func<IEnumerable<string>, IEnumerable<Error>>>();
if (autoHelp)
list.Add(HelpCommand(nameComparer));
if (autoVersion)
list.Add(VersionCommand(nameComparer));
return list;
}

public static Func<IEnumerable<string>, IEnumerable<Error>> HelpCommand(StringComparer nameComparer)
Expand Down
6 changes: 6 additions & 0 deletions src/CommandLine/Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ public ParserResult<T> ParseArguments<T>(IEnumerable<string> args)
settings.NameComparer,
settings.CaseInsensitiveEnumValues,
settings.ParsingCulture,
settings.AutoHelp,
settings.AutoVersion,
HandleUnknownArguments(settings.IgnoreUnknownArguments)),
settings);
}
Expand Down Expand Up @@ -128,6 +130,8 @@ public ParserResult<T> ParseArguments<T>(Func<T> factory, IEnumerable<string> ar
settings.NameComparer,
settings.CaseInsensitiveEnumValues,
settings.ParsingCulture,
settings.AutoHelp,
settings.AutoVersion,
HandleUnknownArguments(settings.IgnoreUnknownArguments)),
settings);
}
Expand Down Expand Up @@ -157,6 +161,8 @@ public ParserResult<object> ParseArguments(IEnumerable<string> args, params Type
args,
settings.NameComparer,
settings.ParsingCulture,
settings.AutoHelp,
settings.AutoVersion,
HandleUnknownArguments(settings.IgnoreUnknownArguments)),
settings);
}
Expand Down
22 changes: 22 additions & 0 deletions src/CommandLine/ParserSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ public class ParserSettings : IDisposable
private bool caseInsensitiveEnumValues;
private TextWriter helpWriter;
private bool ignoreUnknownArguments;
private bool autoHelp;
private bool autoVersion;
private CultureInfo parsingCulture;
private bool enableDashDash;
private int maximumDisplayWidth;
Expand All @@ -31,6 +33,8 @@ public ParserSettings()
{
caseSensitive = true;
caseInsensitiveEnumValues = false;
autoHelp = true;
autoVersion = true;
parsingCulture = CultureInfo.InvariantCulture;
try
{
Expand Down Expand Up @@ -118,6 +122,24 @@ public bool IgnoreUnknownArguments
set { PopsicleSetter.Set(Consumed, ref ignoreUnknownArguments, value); }
}

/// <summary>
/// Gets or sets a value indicating whether implicit option or verb 'help' should be supported.
/// </summary>
public bool AutoHelp
{
get { return autoHelp; }
set { PopsicleSetter.Set(Consumed, ref autoHelp, value); }
}

/// <summary>
/// Gets or sets a value indicating whether implicit option or verb 'version' should be supported.
/// </summary>
public bool AutoVersion
{
get { return autoVersion; }
set { PopsicleSetter.Set(Consumed, ref autoVersion, value); }
}

/// <summary>
/// Gets or sets a value indicating whether enable double dash '--' syntax,
/// that forces parsing of all subsequent tokens as values.
Expand Down
38 changes: 34 additions & 4 deletions src/CommandLine/Text/HelpText.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ public class HelpText
private StringBuilder optionsHelp;
private bool addDashesToOption;
private bool addEnumValuesToHelpText;
private bool autoHelp;
private bool autoVersion;

/// <summary>
/// Initializes a new instance of the <see cref="CommandLine.Text.HelpText"/> class.
Expand Down Expand Up @@ -113,6 +115,8 @@ public HelpText(SentenceBuilder sentenceBuilder, string heading, string copyrigh
this.sentenceBuilder = sentenceBuilder;
this.heading = heading;
this.copyright = copyright;
this.autoHelp = true;
this.autoVersion = true;
}

/// <summary>
Expand Down Expand Up @@ -183,6 +187,24 @@ public bool AddEnumValuesToHelpText
set { addEnumValuesToHelpText = value; }
}

/// <summary>
/// Gets or sets a value indicating whether implicit option or verb 'help' should be supported.
/// </summary>
public bool AutoHelp
{
get { return autoHelp; }
set { autoHelp = value; }
}

/// <summary>
/// Gets or sets a value indicating whether implicit option or verb 'version' should be supported.
/// </summary>
public bool AutoVersion
{
get { return autoVersion; }
set { autoVersion = value; }
}

/// <summary>
/// Gets the <see cref="SentenceBuilder"/> instance specified in constructor.
/// </summary>
Expand Down Expand Up @@ -676,8 +698,11 @@ private IEnumerable<Specification> GetSpecificationsFromType(Type type)
{
var specs = type.GetSpecifications(Specification.FromProperty);
var optionSpecs = specs
.OfType<OptionSpecification>()
.Concat(new[] { MakeHelpEntry(), MakeVersionEntry() });
.OfType<OptionSpecification>();
if (autoHelp)
optionSpecs = optionSpecs.Concat(new [] { MakeHelpEntry() });
if (autoVersion)
optionSpecs = optionSpecs.Concat(new [] { MakeVersionEntry() });
var valueSpecs = specs
.OfType<ValueSpecification>()
.OrderBy(v => v.Index);
Expand Down Expand Up @@ -707,15 +732,20 @@ private static Maybe<Tuple<UsageAttribute, IEnumerable<Example>>> GetUsageFromTy

private IEnumerable<Specification> AdaptVerbsToSpecifications(IEnumerable<Type> types)
{
return (from verbTuple in Verb.SelectFromTypes(types)
var optionSpecs = from verbTuple in Verb.SelectFromTypes(types)
select
OptionSpecification.NewSwitch(
string.Empty,
verbTuple.Item1.Name,
false,
verbTuple.Item1.HelpText,
string.Empty,
verbTuple.Item1.Hidden)).Concat(new[] { MakeHelpEntry(), MakeVersionEntry() });
verbTuple.Item1.Hidden);
if (autoHelp)
optionSpecs = optionSpecs.Concat(new [] { MakeHelpEntry() });
if (autoVersion)
optionSpecs = optionSpecs.Concat(new [] { MakeVersionEntry() });
return optionSpecs;
}

private HelpText AddOptionsImpl(
Expand Down
2 changes: 2 additions & 0 deletions tests/CommandLine.Tests/CommandLine.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="CultureInfoExtensions.cs" />
<Compile Include="Fakes\Options_With_Custom_Help_Option.cs" />
<Compile Include="Fakes\Options_With_Custom_Version_Option.cs" />
<Compile Include="Fakes\Options_With_Default_Set_To_Sequence.cs" />
<Compile Include="Fakes\Options_With_Guid.cs" />
<Compile Include="Fakes\Options_With_Option_And_Value_Of_String_Type.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace CommandLine.Tests.Fakes
{
class Options_With_Custom_Help_Option : Simple_Options
{
[Option('h', "help")]
public bool Help { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace CommandLine.Tests.Fakes
{
class Options_With_Custom_Version_Option : Simple_Options
{
[Option('v', "version")]
public bool MyVersion { get; set; }
}
}
80 changes: 79 additions & 1 deletion tests/CommandLine.Tests/Unit/Core/InstanceBuilderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace CommandLine.Tests.Unit.Core
{
public class InstanceBuilderTests
{
private static ParserResult<T> InvokeBuild<T>(string[] arguments)
private static ParserResult<T> InvokeBuild<T>(string[] arguments, bool autoHelp = true, bool autoVersion = true)
where T : new()
{
return InstanceBuilder.Build(
Expand All @@ -29,6 +29,8 @@ private static ParserResult<T> InvokeBuild<T>(string[] arguments)
StringComparer.Ordinal,
false,
CultureInfo.InvariantCulture,
autoHelp,
autoVersion,
Enumerable.Empty<ErrorType>());
}

Expand All @@ -42,6 +44,8 @@ private static ParserResult<T> InvokeBuildEnumValuesCaseIgnore<T>(string[] argum
StringComparer.Ordinal,
true,
CultureInfo.InvariantCulture,
true,
true,
Enumerable.Empty<ErrorType>());
}

Expand All @@ -54,6 +58,8 @@ private static ParserResult<T> InvokeBuildImmutable<T>(string[] arguments)
StringComparer.Ordinal,
false,
CultureInfo.InvariantCulture,
true,
true,
Enumerable.Empty<ErrorType>());
}

Expand Down Expand Up @@ -451,6 +457,8 @@ public void Double_dash_force_subsequent_arguments_as_values()
StringComparer.Ordinal,
false,
CultureInfo.InvariantCulture,
true,
true,
Enumerable.Empty<ErrorType>());

// Verify outcome
Expand Down Expand Up @@ -1007,6 +1015,76 @@ public void Parse_string_with_dashes_except_in_beginning(string[] arguments, str
// Teardown
}

[Theory]
[InlineData(new[] { "--help" }, ErrorType.UnknownOptionError)]
public void Parse_without_auto_help_should_not_recognize_help_option(string[] arguments, ErrorType errorType)
{
// Fixture setup in attributes

// Exercize system
var result = InvokeBuild<Simple_Options>(arguments, autoHelp: false);

// Verify outcome
result.Should().BeOfType<NotParsed<Simple_Options>>()
.Which.Errors.Should().ContainSingle()
.Which.Tag.Should().Be(errorType);

// Teardown
}

[Theory]
[InlineData(new[] { "--help" }, true)]
[InlineData(new[] { "-h" }, true)]
[InlineData(new[] { "-x" }, false)]
public void Parse_with_custom_help_option(string[] arguments, bool isHelp)
{
// Fixture setup in attributes

// Exercize system
var result = InvokeBuild<Options_With_Custom_Help_Option>(arguments, autoHelp: false);

// Verify outcome
result.Should().BeOfType<Parsed<Options_With_Custom_Help_Option>>()
.Which.Value.Help.Should().Be(isHelp);

// Teardown
}

[Theory]
[InlineData(new[] { "--version" }, ErrorType.UnknownOptionError)]
public void Parse_without_auto_version_should_not_recognize_version_option(string[] arguments, ErrorType errorType)
{
// Fixture setup in attributes

// Exercize system
var result = InvokeBuild<Simple_Options>(arguments, autoVersion: false);

// Verify outcome
result.Should().BeOfType<NotParsed<Simple_Options>>()
.Which.Errors.Should().ContainSingle()
.Which.Tag.Should().Be(errorType);

// Teardown
}

[Theory]
[InlineData(new[] { "--version" }, true)]
[InlineData(new[] { "-v" }, true)]
[InlineData(new[] { "-s", "s" }, false)]
public void Parse_with_custom_version_option(string[] arguments, bool isVersion)
{
// Fixture setup in attributes

// Exercize system
var result = InvokeBuild<Options_With_Custom_Version_Option>(arguments, autoVersion: false);

// Verify outcome
result.Should().BeOfType<Parsed<Options_With_Custom_Version_Option>>()
.Which.Value.MyVersion.Should().Be(isVersion);

// Teardown
}

[Theory]
[MemberData("GuidData")]
public void Parse_Guid(string[] arguments, Options_With_Guid expected)
Expand Down
2 changes: 2 additions & 0 deletions tests/CommandLine.Tests/Unit/Core/InstanceChooserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ private static ParserResult<object> InvokeChoose(
arguments,
StringComparer.Ordinal,
CultureInfo.InvariantCulture,
true,
true,
Enumerable.Empty<ErrorType>());
}

Expand Down