From 113a21c66a1ebbedfd633a32f4d25bccdc99a1ee Mon Sep 17 00:00:00 2001 From: b3b00 Date: Tue, 23 Jul 2019 11:43:39 +0200 Subject: [PATCH] HelpText fluent API, initial --- src/CommandLine/Text/HelpText.cs | 196 ++++++++++++++---- tests/CommandLine.Tests/Unit/Issue482Tests.cs | 55 +++++ 2 files changed, 215 insertions(+), 36 deletions(-) diff --git a/src/CommandLine/Text/HelpText.cs b/src/CommandLine/Text/HelpText.cs index acc67b41..0762eac9 100644 --- a/src/CommandLine/Text/HelpText.cs +++ b/src/CommandLine/Text/HelpText.cs @@ -17,9 +17,9 @@ namespace CommandLine.Text /// Provides means to format an help screen. /// You can assign it in place of a instance. /// - - - + + + public struct ComparableOption { public bool Required; @@ -28,7 +28,125 @@ public struct ComparableOption public string LongName; public int Index; } - + + public class HelpTextBuilder + { + + private Func onError; + private ParserResult parserResult; + private Func onExample; + + private bool verbsIndex = false; + + private int maxDisplayWidth = HelpText.DefaultMaximumLength; + + private Comparison optionComparison; + + internal HelpTextBuilder(ParserResult result) + { + parserResult = result; + } + + public HelpTextBuilder OnError(Func error) + { + onError = error; + return this; + } + public HelpTextBuilder OnExample(Func example) + { + onExample = example; + return this; + } + + public HelpTextBuilder WithMaxDisplayWidth(int width) { + maxDisplayWidth = width; + return this; + } + + public HelpTextBuilder WithVerbsIndex(bool index) { + verbsIndex = index; + return this; + } + + public HelpTextBuilder WithComparison(Comparison comparison) + { + optionComparison = comparison; + return this; + } + + public string Build() + { + + //TODO : call HelpText.AutoBuild according to set parameters. + + // if (onError) + // HelpText.AutoBuild(parserResult,onError,onExample,verbsIndex,maxDisplayWidth); + // HelpText.AutoBuild(parserResult,maxDisplayWidth); + + var auto = new HelpText + { + Heading = HeadingInfo.Empty, + Copyright = CopyrightInfo.Empty, + AdditionalNewLineAfterOption = true, + AddDashesToOption = !verbsIndex, + MaximumDisplayWidth = maxDisplayWidth, + OptionComparison = optionComparison + }; + + try + { + auto.Heading = HeadingInfo.Default; + auto.Copyright = CopyrightInfo.Default; + } + catch (Exception) + { + auto = onError(auto); + } + + var errors = Enumerable.Empty(); + + if (onError != null && parserResult.Tag == ParserResultType.NotParsed) + { + errors = ((NotParsed)parserResult).Errors; + + if (errors.OnlyMeaningfulOnes().Any()) + auto = onError(auto); + } + + ReflectionHelper.GetAttribute() + .Do(license => license.AddToHelpText(auto, true)); + + var usageAttr = ReflectionHelper.GetAttribute(); + var usageLines = HelpText.RenderUsageTextAsLines(parserResult, onExample).ToMaybe(); + + if (usageAttr.IsJust() || usageLines.IsJust()) + { + var heading = auto.SentenceBuilder.UsageHeadingText(); + if (heading.Length > 0) + auto.AddPreOptionsLine(heading); + } + + usageAttr.Do( + usage => usage.AddToHelpText(auto, true)); + + usageLines.Do( + lines => auto.AddPreOptionsLines(lines)); + + if ((verbsIndex && parserResult.TypeInfo.Choices.Any()) + || errors.Any(e => e.Tag == ErrorType.NoVerbSelectedError)) + { + auto.AddDashesToOption = false; + auto.AddVerbs(parserResult.TypeInfo.Choices.ToArray()); + } + else + auto.AddOptions(parserResult); + + return auto; + } + } + + + public class HelpText { @@ -48,46 +166,46 @@ ComparableOption ToComparableOption(Specification spec, int index) LongName = name, Index = index }; - + } - public static Comparison OptionComparison = null; + public Comparison OptionComparison {get; set;} = null; + + public static Comparison RequiredThenAlphaComparison = (ComparableOption attr1, ComparableOption attr2) => + { + if (attr1.IsOption && attr2.IsOption) + { + if (attr1.Required && !attr2.Required) + { + return -1; + } + else if (!attr1.Required && attr2.Required) + { + return 1; + } + else + { + int t = String.Compare(attr1.LongName, attr2.LongName, StringComparison.CurrentCulture); + return t; + } + } + else if (attr1.IsOption && attr2.IsValue) + { + return -1; + } + else + { + return 1; + } + }; - public static Comparison RequiredThenAlphaComparison = (ComparableOption attr1, ComparableOption attr2) => - { - if (attr1.IsOption && attr2.IsOption) - { - if (attr1.Required && !attr2.Required) - { - return -1; - } - else if (!attr1.Required && attr2.Required) - { - return 1; - } - else - { - int t = String.Compare(attr1.LongName, attr2.LongName, StringComparison.CurrentCulture); - return t; - } - } - else if (attr1.IsOption && attr2.IsValue) - { - return -1; - } - else - { - return 1; - } - }; - private const int BuilderCapacity = 128; - private const int DefaultMaximumLength = 80; // default console width + public const int DefaultMaximumLength = 80; // default console width /// /// The number of spaces between an option and its associated help text /// - private const int OptionToHelpTextSeparatorWidth = 4; + private const int OptionToHelpTextSeparatorWidth = 4; /// /// The width of the option prefix (either "--" or " " /// @@ -304,6 +422,12 @@ public SentenceBuilder SentenceBuilder /// If true the output style is consistent with verb commands (no dashes), otherwise it outputs options. /// The maximum width of the display. /// The parameter is not ontly a metter of formatting, it controls whether to handle verbs or options. + + public static HelpTextBuilder CreateWith(ParserResult parserResult) + { + return new HelpTextBuilder(parserResult); + } + public static HelpText AutoBuild( ParserResult parserResult, Func onError, diff --git a/tests/CommandLine.Tests/Unit/Issue482Tests.cs b/tests/CommandLine.Tests/Unit/Issue482Tests.cs index fb7290a6..6c3aded0 100644 --- a/tests/CommandLine.Tests/Unit/Issue482Tests.cs +++ b/tests/CommandLine.Tests/Unit/Issue482Tests.cs @@ -62,6 +62,61 @@ public void AutoBuild_with_ordering() ; } + + [Fact] + public void AutoBuild_with_ordering_fluent() + { + string expectedCompany = "Company"; + + + var parser = Parser.Default; + var parseResult = parser.ParseArguments( + new[] {"verb1", "--alpha", "alpaga", "--alpha2", "alala", "--charlie", "charlot"}) + .WithNotParsed(errors => { throw new InvalidOperationException("Must be parsed."); }) + .WithParsed(args => { ; }); + + Comparison comparison = HelpText.RequiredThenAlphaComparison; + + + var message = HelpText.CreateWith(parseResult) + .WithComparison(HelpText.RequiredThenAlphaComparison) + .OnError(error => { + throw new InvalidOperationException($"help text build failed. {error.ToString()}"); + }) + .OnExample(ex => + { + return null; + }) + .Build(); + + + + + + string helpMessage = message.ToString(); + var helps = helpMessage.Split(new[] {'\r', '\n'}, StringSplitOptions.RemoveEmptyEntries).Skip(2).ToList(); + List expected = new List() + { + " -a, --alpha Required.", + " -b, --alpha2 Required.", + " -c, --bravo", + " -d, --charlie", + "-e, --echo", + "-f, --foxtrot", + "--help Display this help screen.", + "--version Display version information.", + "value pos. 0" + }; + Assert.Equal(expected.Count,helps.Count); + int i = 0; + foreach (var expect in expected) + { + Assert.Equal(expect.Trim(),helps[i].Trim()); + i++; + } + + ; + } }