diff --git a/src/CommandLine/BaseAttribute.cs b/src/CommandLine/BaseAttribute.cs index a009111d..f91a17a2 100644 --- a/src/CommandLine/BaseAttribute.cs +++ b/src/CommandLine/BaseAttribute.cs @@ -123,5 +123,14 @@ public string MetaValue metaValue = value; } } + + /// + /// Gets or sets a value indicating whether a command line option is visible in the help text. + /// + public bool Hidden + { + get; + set; + } } } diff --git a/src/CommandLine/Core/OptionSpecification.cs b/src/CommandLine/Core/OptionSpecification.cs index e519ffb9..0bbbbb06 100644 --- a/src/CommandLine/Core/OptionSpecification.cs +++ b/src/CommandLine/Core/OptionSpecification.cs @@ -16,8 +16,8 @@ sealed class OptionSpecification : Specification public OptionSpecification(string shortName, string longName, bool required, string setName, Maybe min, Maybe max, char separator, Maybe defaultValue, string helpText, string metaValue, IEnumerable enumValues, - Type conversionType, TargetType targetType) - : base(SpecificationType.Option, required, min, max, defaultValue, helpText, metaValue, enumValues, conversionType, targetType) + Type conversionType, TargetType targetType, bool hidden = false) + : base(SpecificationType.Option, required, min, max, defaultValue, helpText, metaValue, enumValues, conversionType, targetType, hidden) { this.shortName = shortName; this.longName = longName; @@ -40,13 +40,14 @@ public static OptionSpecification FromAttribute(OptionAttribute attribute, Type attribute.MetaValue, enumValues, conversionType, - conversionType.ToTargetType()); + conversionType.ToTargetType(), + attribute.Hidden); } - public static OptionSpecification NewSwitch(string shortName, string longName, bool required, string helpText, string metaValue) + public static OptionSpecification NewSwitch(string shortName, string longName, bool required, string helpText, string metaValue, bool hidden = false) { return new OptionSpecification(shortName, longName, required, string.Empty, Maybe.Nothing(), Maybe.Nothing(), - '\0', Maybe.Nothing(), helpText, metaValue, Enumerable.Empty(), typeof(bool), TargetType.Switch); + '\0', Maybe.Nothing(), helpText, metaValue, Enumerable.Empty(), typeof(bool), TargetType.Switch, hidden); } public string ShortName diff --git a/src/CommandLine/Core/Specification.cs b/src/CommandLine/Core/Specification.cs index 74a09190..cefae158 100644 --- a/src/CommandLine/Core/Specification.cs +++ b/src/CommandLine/Core/Specification.cs @@ -25,6 +25,7 @@ abstract class Specification { private readonly SpecificationType tag; private readonly bool required; + private readonly bool hidden; private readonly Maybe min; private readonly Maybe max; private readonly Maybe defaultValue; @@ -37,7 +38,7 @@ abstract class Specification protected Specification(SpecificationType tag, bool required, Maybe min, Maybe max, Maybe defaultValue, string helpText, string metaValue, IEnumerable enumValues, - Type conversionType, TargetType targetType) + Type conversionType, TargetType targetType, bool hidden = false) { this.tag = tag; this.required = required; @@ -49,6 +50,7 @@ protected Specification(SpecificationType tag, bool required, Maybe min, Ma this.helpText = helpText; this.metaValue = metaValue; this.enumValues = enumValues; + this.hidden = hidden; } public SpecificationType Tag @@ -101,6 +103,11 @@ public TargetType TargetType get { return targetType; } } + public bool Hidden + { + get { return hidden; } + } + public static Specification FromProperty(PropertyInfo property) { var attrs = property.GetCustomAttributes(true); diff --git a/src/CommandLine/Core/SpecificationExtensions.cs b/src/CommandLine/Core/SpecificationExtensions.cs index cad4187f..64e6475a 100644 --- a/src/CommandLine/Core/SpecificationExtensions.cs +++ b/src/CommandLine/Core/SpecificationExtensions.cs @@ -33,7 +33,8 @@ public static OptionSpecification WithLongName(this OptionSpecification specific specification.MetaValue, specification.EnumValues, specification.ConversionType, - specification.TargetType); + specification.TargetType, + specification.Hidden); } public static string UniqueName(this OptionSpecification specification) diff --git a/src/CommandLine/Core/ValueSpecification.cs b/src/CommandLine/Core/ValueSpecification.cs index d898b6db..bd90252e 100644 --- a/src/CommandLine/Core/ValueSpecification.cs +++ b/src/CommandLine/Core/ValueSpecification.cs @@ -13,8 +13,8 @@ sealed class ValueSpecification : Specification public ValueSpecification(int index, string metaName, bool required, Maybe min, Maybe max, Maybe defaultValue, string helpText, string metaValue, IEnumerable enumValues, - Type conversionType, TargetType targetType) - : base(SpecificationType.Value, required, min, max, defaultValue, helpText, metaValue, enumValues, conversionType, targetType) + Type conversionType, TargetType targetType, bool hidden = false) + : base(SpecificationType.Value, required, min, max, defaultValue, helpText, metaValue, enumValues, conversionType, targetType, hidden) { this.index = index; this.metaName = metaName; @@ -33,7 +33,8 @@ public static ValueSpecification FromAttribute(ValueAttribute attribute, Type co attribute.MetaValue, enumValues, conversionType, - conversionType.ToTargetType()); + conversionType.ToTargetType(), + attribute.Hidden); } public int Index diff --git a/src/CommandLine/Core/Verb.cs b/src/CommandLine/Core/Verb.cs index c2c00351..d6c71afc 100644 --- a/src/CommandLine/Core/Verb.cs +++ b/src/CommandLine/Core/Verb.cs @@ -10,14 +10,16 @@ sealed class Verb { private readonly string name; private readonly string helpText; + private readonly bool hidden; - public Verb(string name, string helpText) + public Verb(string name, string helpText, bool hidden = false) { if (name == null) throw new ArgumentNullException("name"); if (helpText == null) throw new ArgumentNullException("helpText"); this.name = name; this.helpText = helpText; + this.hidden = hidden; } public string Name @@ -30,11 +32,17 @@ public string HelpText get { return helpText; } } + public bool Hidden + { + get { return hidden; } + } + public static Verb FromAttribute(VerbAttribute attribute) { return new Verb( attribute.Name, - attribute.HelpText + attribute.HelpText, + attribute.Hidden ); } diff --git a/src/CommandLine/Text/HelpText.cs b/src/CommandLine/Text/HelpText.cs index 037216a0..9d29caa6 100644 --- a/src/CommandLine/Text/HelpText.cs +++ b/src/CommandLine/Text/HelpText.cs @@ -683,7 +683,8 @@ private IEnumerable AdaptVerbsToSpecifications(IEnumerable verbTuple.Item1.Name, false, verbTuple.Item1.HelpText, - string.Empty)).Concat(new[] { MakeHelpEntry(), MakeVersionEntry() }); + string.Empty, + verbTuple.Item1.Hidden)).Concat(new[] { MakeHelpEntry(), MakeVersionEntry() }); } private HelpText AddOptionsImpl( @@ -711,7 +712,8 @@ private OptionSpecification MakeHelpEntry() "help", false, sentenceBuilder.HelpCommandText(AddDashesToOption), - string.Empty); + string.Empty, + false); } private OptionSpecification MakeVersionEntry() @@ -721,7 +723,8 @@ private OptionSpecification MakeVersionEntry() "version", false, sentenceBuilder.VersionCommandText(AddDashesToOption), - string.Empty); + string.Empty, + false); } private HelpText AddPreOptionsLine(string value, int maximumLength) @@ -733,6 +736,9 @@ private HelpText AddPreOptionsLine(string value, int maximumLength) private HelpText AddOption(string requiredWord, int maxLength, Specification specification, int widthOfHelpText) { + if (specification.Hidden) + return this; + optionsHelp.Append(" "); var name = new StringBuilder(maxLength) .BimapIf( @@ -841,13 +847,15 @@ private int GetMaxLength(IEnumerable specifications) { return specifications.Aggregate(0, (length, spec) => - { - var specLength = spec.Tag == SpecificationType.Option + { + if (spec.Hidden) + return length; + var specLength = spec.Tag == SpecificationType.Option ? GetMaxOptionLength((OptionSpecification)spec) : GetMaxValueLength((ValueSpecification)spec); - return Math.Max(length, specLength); - }); + return Math.Max(length, specLength); + }); } diff --git a/src/CommandLine/VerbAttribute.cs b/src/CommandLine/VerbAttribute.cs index 3172f210..af66ac12 100644 --- a/src/CommandLine/VerbAttribute.cs +++ b/src/CommandLine/VerbAttribute.cs @@ -34,6 +34,15 @@ public string Name get { return name; } } + /// + /// Gets or sets a value indicating whether a command line verb is visible in the help text. + /// + public bool Hidden + { + get; + set; + } + /// /// Gets or sets a short description of this command line option. Usually a sentence summary. /// diff --git a/tests/CommandLine.Tests/Fakes/Help_Fakes.cs b/tests/CommandLine.Tests/Fakes/Help_Fakes.cs index 8b7af30a..a35e8599 100644 --- a/tests/CommandLine.Tests/Fakes/Help_Fakes.cs +++ b/tests/CommandLine.Tests/Fakes/Help_Fakes.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using CommandLine.Text; +using Microsoft.FSharp.Collections; namespace CommandLine.Tests.Fakes { @@ -12,6 +13,9 @@ class Simple_Options_Without_HelpText [Option("input-file")] public string FileName { get; set; } + + [Option("secert-option", Hidden = true, HelpText = "This is a description for a secert hidden option that should never be visibile to the user via help text.")] + public string SecertOption { get; set; } } class Simple_Options_With_HelpText_Set @@ -21,6 +25,9 @@ class Simple_Options_With_HelpText_Set [Option('i', "input-file", Required = true, HelpText = "Specify input file to be processed.")] public string FileName { get; set; } + + [Option("secert-option", Hidden = true, HelpText = "This is a description for a secert hidden option that should never be visibile to the user via help text.")] + public string SecertOption { get; set; } } class Simple_Options_With_HelpText_Set_To_Long_Description @@ -30,6 +37,9 @@ class Simple_Options_With_HelpText_Set_To_Long_Description [Option("input-file", HelpText = "This is a very long description of the Input File argument that gets passed in. It should be passed in as a string.")] public string FileName { get; set; } + + [Option("secert-option", Hidden = true, HelpText = "This is a description for a secert hidden option that should never be visibile to the user via help text.")] + public string SecertOption { get; set; } } class Simple_Options_With_HelpText_Set_To_Long_Description_Without_Spaces @@ -39,6 +49,9 @@ class Simple_Options_With_HelpText_Set_To_Long_Description_Without_Spaces [Option("input-file", HelpText = "Before 012345678901234567890123456789 After")] public string FileName { get; set; } + + [Option("secert-option", Hidden = true, HelpText = "This is a description for a secert hidden option that should never be visibile to the user via help text.")] + public string SecertOption { get; set; } } class Options_With_Usage_Attribute @@ -64,6 +77,9 @@ class Options_With_Usage_Attribute [Value(0, HelpText = "Value.")] public string Value { get; set; } + [Option("secert-option", Hidden = true, HelpText = "This is a description for a secert hidden option that should never be visibile to the user via help text.")] + public string SecertOption { get; set; } + [Usage(ApplicationAlias = "mono testapp.exe")] public static IEnumerable Examples { @@ -78,6 +94,16 @@ public static IEnumerable Examples } } + [Verb("secert", Hidden = true, HelpText = "This is a secert hidden verb that should never be visible to the user via help text.")] + public class Secert_Verb + { + [Option('f', "force", SetName = "mode-f", HelpText = "Allow adding otherwise ignored files.")] + public bool Force { get; set; } + + [Option("secert-option", Hidden = true, HelpText = "This is a description for a secert hidden option that should never be visibile to the user via help text.")] + public string SecertOption { get; set; } + } + [Verb("add", HelpText = "Add file contents to the index.")] public class Add_Verb_With_Usage_Attribute { @@ -92,6 +118,9 @@ public class Add_Verb_With_Usage_Attribute [Value(0)] public string FileName { get; set; } + [Option("secert-option", Hidden = true, HelpText = "This is a description for a secert hidden option that should never be visibile to the user via help text.")] + public string SecertOption { get; set; } + [Usage(ApplicationAlias = "git")] public static IEnumerable Examples { @@ -112,6 +141,9 @@ public class Commit_Verb_With_Usage_Attribute [Option("amend", HelpText = "Used to amend the tip of the current branch.")] public bool Amend { get; set; } + [Option("secert-option", Hidden = true, HelpText = "This is a description for a secert hidden option that should never be visibile to the user via help text.")] + public string SecertOption { get; set; } + [Usage(ApplicationAlias = "git")] public static IEnumerable Examples { @@ -133,6 +165,9 @@ public class Clone_Verb_With_Usage_Attribute HelpText = "Suppress summary message.")] public bool Quiet { get; set; } + [Option("secert-option", Hidden = true, HelpText = "This is a description for a secert hidden option that should never be visibile to the user via help text.")] + public string SecertOption { get; set; } + [Value(0, MetaName = "URLS", HelpText = "A list of url(s) to clone.")] public IEnumerable Urls { get; set; } diff --git a/tests/CommandLine.Tests/Unit/ParserTests.cs b/tests/CommandLine.Tests/Unit/ParserTests.cs index 0989f32c..f058aa05 100644 --- a/tests/CommandLine.Tests/Unit/ParserTests.cs +++ b/tests/CommandLine.Tests/Unit/ParserTests.cs @@ -492,6 +492,91 @@ public void Properly_formatted_help_screen_is_displayed_when_usage_is_defined_in // Teardown } + [Fact] + public void Properly_formatted_help_screen_is_displayed_when_there_is_a_hidden_verb() + { + // Fixture setup + var help = new StringWriter(); + var sut = new Parser(config => config.HelpWriter = help); + + // Exercize system + sut.ParseArguments(new string[] { }); + var result = help.ToString(); + + // Verify outcome + var lines = result.ToNotEmptyLines().TrimStringArray(); + lines[0].Should().StartWithEquivalent("CommandLine"); + lines[1].ShouldBeEquivalentTo("Copyright (c) 2005 - 2015 Giacomo Stelluti Scala"); + lines[2].ShouldBeEquivalentTo("ERROR(S):"); + lines[3].ShouldBeEquivalentTo("No verb selected."); + lines[4].ShouldBeEquivalentTo("add Add file contents to the index."); + lines[5].ShouldBeEquivalentTo("help Display more information on a specific command."); + lines[6].ShouldBeEquivalentTo("version Display version information."); + + // Teardown + } + + [Fact] + public void Properly_formatted_help_screen_is_displayed_when_there_is_a_hidden_verb_selected_usage_displays_with_hidden_option() + { + // Fixture setup + var help = new StringWriter(); + var sut = new Parser(config => config.HelpWriter = help); + + // Exercize system + sut.ParseArguments(new string[] { "secert", "--help" }); + var result = help.ToString(); + + // Verify outcome + var lines = result.ToNotEmptyLines().TrimStringArray(); + lines[0].Should().StartWithEquivalent("CommandLine"); + lines[1].ShouldBeEquivalentTo("Copyright (c) 2005 - 2015 Giacomo Stelluti Scala"); + lines[2].ShouldBeEquivalentTo("-f, --force Allow adding otherwise ignored files."); + lines[3].ShouldBeEquivalentTo("--help Display this help screen."); + lines[4].ShouldBeEquivalentTo("--version Display version information."); + + // Teardown + } + + [Fact] + public void Parse_options_when_given_hidden_verb() + { + // Fixture setup + var expectedOptions = new Secert_Verb { Force = true, SecertOption = null}; + var help = new StringWriter(); + var sut = new Parser(config => config.HelpWriter = help); + + // Exercize system + var result = sut.ParseArguments(new string[] { "secert", "--force" }); + + + // Verify outcome + result.Tag.ShouldBeEquivalentTo(ParserResultType.Parsed); + result.GetType().Should().Be>(); + result.TypeInfo.Current.Should().Be(); + ((Parsed)result).Value.ShouldBeEquivalentTo(expectedOptions, o => o.RespectingRuntimeTypes()); + // Teardown + } + + [Fact] + public void Parse_options_when_given_hidden_verb_with_hidden_option() + { + // Fixture setup + var expectedOptions = new Secert_Verb { Force = true, SecertOption = "shhh" }; + var help = new StringWriter(); + var sut = new Parser(config => config.HelpWriter = help); + + // Exercize system + var result = sut.ParseArguments(new string[] { "secert", "--force", "--secert-option", "shhh" }); + + // Verify outcome + result.Tag.ShouldBeEquivalentTo(ParserResultType.Parsed); + result.GetType().Should().Be>(); + result.TypeInfo.Current.Should().Be(); + ((Parsed)result).Value.ShouldBeEquivalentTo(expectedOptions, o => o.RespectingRuntimeTypes()); + // Teardown + } + [Fact] public void Specific_verb_help_screen_should_be_displayed_regardless_other_argument() { diff --git a/tests/CommandLine.Tests/Unit/Text/HelpTextTests.cs b/tests/CommandLine.Tests/Unit/Text/HelpTextTests.cs index 4c5defe0..f0403982 100644 --- a/tests/CommandLine.Tests/Unit/Text/HelpTextTests.cs +++ b/tests/CommandLine.Tests/Unit/Text/HelpTextTests.cs @@ -152,7 +152,7 @@ public void When_help_text_is_longer_than_width_it_will_wrap_around_as_if_in_a_c lines[6].ShouldBeEquivalentTo(" Help Text."); // Teardown } - + [Fact] @@ -161,7 +161,6 @@ public void When_help_text_is_longer_than_width_it_will_wrap_around_as_if_in_a_c // Fixture setup // Exercize system var sut = new HelpText(new HeadingInfo("CommandLine.Tests.dll", "1.9.4.131")) { MaximumDisplayWidth = 100} ; - //sut.MaximumDisplayWidth = 100; sut.AddOptions( new NotParsed( TypeInfo.Create(typeof(Simple_Options_With_HelpText_Set_To_Long_Description)), @@ -175,6 +174,25 @@ public void When_help_text_is_longer_than_width_it_will_wrap_around_as_if_in_a_c // Teardown } + [Fact] + public void When_help_text_has_hidden_option_it_should_not_be_added_to_help_text_output() + { + // Fixture setup + // Exercize system + var sut = new HelpText(new HeadingInfo("CommandLine.Tests.dll", "1.9.4.131")); + sut.AddOptions( + new NotParsed( + TypeInfo.Create(typeof(Simple_Options_With_HelpText_Set_To_Long_Description)), + Enumerable.Empty())); + + // Verify outcome + var lines = sut.ToString().Split(new[] { Environment.NewLine }, StringSplitOptions.None); + lines[2].ShouldBeEquivalentTo(" v, verbose This is the description of the verbosity to test out the "); //"The first line should have the arguments and the start of the Help Text."); + //string formattingMessage = "Beyond the second line should be formatted as though it's in a column."; + lines[3].ShouldBeEquivalentTo(" wrapping capabilities of the Help Text."); + // Teardown + } + [Fact] public void Long_help_text_without_spaces() { @@ -458,7 +476,7 @@ public void Invoke_AutoBuild_for_Options_with_Usage_returns_appropriate_formatte var helpText = HelpText.AutoBuild(fakeResult); // Verify outcome - var text = helpText.ToString(); + var text = helpText.ToString(); var lines = text.ToNotEmptyLines().TrimStringArray(); lines[0].Should().StartWithEquivalent("CommandLine"); lines[1].Should().StartWithEquivalent("Copyright (c)");