diff --git a/src/CommandLine/Text/CopyrightInfo.cs b/src/CommandLine/Text/CopyrightInfo.cs
index 3fd2b6a8..c8bc3593 100644
--- a/src/CommandLine/Text/CopyrightInfo.cs
+++ b/src/CommandLine/Text/CopyrightInfo.cs
@@ -27,11 +27,11 @@ public class CopyrightInfo
///
/// An empty object used for initialization.
///
- public static CopyrightInfo Empty
+ public static CopyrightInfo Empty
{
get
{
- return new CopyrightInfo("author", 1);
+ return new CopyrightInfo("author", DateTime.Now.Year);
}
}
@@ -115,12 +115,13 @@ public static CopyrightInfo Default
case MaybeType.Just:
return new CopyrightInfo(copyrightAttr.FromJustOrFail());
default:
- // if no copyright attribute exist but a company attribute does, use it as copyright holder
- return new CopyrightInfo(
- ReflectionHelper.GetAttribute().FromJustOrFail(
- new InvalidOperationException("CopyrightInfo::Default requires that you define AssemblyCopyrightAttribute or AssemblyCompanyAttribute.")
- ).Company,
- DateTime.Now.Year);
+ var companyAttr = ReflectionHelper.GetAttribute();
+ return companyAttr.IsNothing()
+ //if both copyrightAttr and companyAttr aren't available in Assembly,don't fire Exception
+ ? Empty
+ // if no copyright attribute exist but a company attribute does, use it as copyright holder
+ : new CopyrightInfo(companyAttr.FromJust().Company, DateTime.Now.Year);
+
}
}
}
@@ -192,4 +193,4 @@ protected virtual string FormatYears(int[] years)
return yearsPart.ToString();
}
}
-}
\ No newline at end of file
+}
diff --git a/src/CommandLine/Text/HelpText.cs b/src/CommandLine/Text/HelpText.cs
index f5fd8065..94b7e4f3 100644
--- a/src/CommandLine/Text/HelpText.cs
+++ b/src/CommandLine/Text/HelpText.cs
@@ -266,7 +266,7 @@ public bool AdditionalNewLineAfterOption
///
public bool AddNewLineBetweenHelpSections
{
- get { return addNewLineBetweenHelpSections; }
+ get { return addNewLineBetweenHelpSections; }
set { addNewLineBetweenHelpSections = value; }
}
@@ -389,7 +389,7 @@ public static HelpText AutoBuild(
}
///
- /// Creates a new instance of the class,
+ /// Creates a default instance of the class,
/// automatically handling verbs or options scenario.
///
/// The containing the instance that collected command line arguments parsed with class.
@@ -400,6 +400,23 @@ public static HelpText AutoBuild(
/// 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)
+ {
+ return AutoBuild(parserResult, h => h, maxDisplayWidth);
+ }
+
+ ///
+ /// Creates a custom instance of the class,
+ /// automatically handling verbs or options scenario.
+ ///
+ /// 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.
+ ///
+ /// 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)
{
if (parserResult.Tag != ParserResultType.NotParsed)
throw new ArgumentException("Excepting NotParsed type.", "parserResult");
@@ -410,13 +427,25 @@ public static HelpText AutoBuild(ParserResult parserResult, int maxDisplay
return new HelpText($"{HeadingInfo.Default}{Environment.NewLine}") { MaximumDisplayWidth = maxDisplayWidth }.AddPreOptionsLine(Environment.NewLine);
if (!errors.Any(e => e.Tag == ErrorType.HelpVerbRequestedError))
- return AutoBuild(parserResult, current => DefaultParsingErrorsHandler(parserResult, current), e => e, maxDisplayWidth: maxDisplayWidth);
+ return AutoBuild(parserResult, current =>
+ {
+ onError?.Invoke(current);
+ return DefaultParsingErrorsHandler(parserResult, current);
+ }, e => e, maxDisplayWidth: maxDisplayWidth);
var err = errors.OfType().Single();
- var pr = new NotParsed(TypeInfo.Create(err.Type), Enumerable.Empty());
+ var pr = new NotParsed(TypeInfo.Create(err.Type), new Error[] { err });
return err.Matched
- ? AutoBuild(pr, current => DefaultParsingErrorsHandler(pr, current), e => e, maxDisplayWidth: maxDisplayWidth)
- : AutoBuild(parserResult, current => DefaultParsingErrorsHandler(parserResult, current), e => e, true, maxDisplayWidth);
+ ? AutoBuild(pr, current =>
+ {
+ onError?.Invoke(current);
+ return DefaultParsingErrorsHandler(pr, current);
+ }, e => e, maxDisplayWidth: maxDisplayWidth)
+ : AutoBuild(parserResult, current =>
+ {
+ onError?.Invoke(current);
+ return DefaultParsingErrorsHandler(parserResult, current);
+ }, e => e, true, maxDisplayWidth);
}
///
@@ -431,7 +460,6 @@ public static HelpText DefaultParsingErrorsHandler(ParserResult parserResu
if (((NotParsed)parserResult).Errors.OnlyMeaningfulOnes().Empty())
return current;
-
var errors = RenderParsingErrorsTextAsLines(parserResult,
current.SentenceBuilder.FormatError,
current.SentenceBuilder.FormatMutuallyExclusiveSetErrors,
@@ -732,8 +760,8 @@ public override string ToString()
var result = new StringBuilder(sbLength);
result.Append(heading)
- .AppendWhen(!string.IsNullOrEmpty(copyright),
- Environment.NewLine,
+ .AppendWhen(!string.IsNullOrEmpty(copyright),
+ Environment.NewLine,
copyright)
.AppendWhen(preOptionsHelp.SafeLength() > 0,
NewLineIfNeededBefore(preOptionsHelp),
@@ -743,15 +771,15 @@ public override string ToString()
Environment.NewLine,
Environment.NewLine,
optionsHelp.SafeToString())
- .AppendWhen(postOptionsHelp.SafeLength() > 0,
+ .AppendWhen(postOptionsHelp.SafeLength() > 0,
NewLineIfNeededBefore(postOptionsHelp),
Environment.NewLine,
postOptionsHelp.ToString());
string NewLineIfNeededBefore(StringBuilder sb)
{
- if (AddNewLineBetweenHelpSections
- && result.Length > 0
+ if (AddNewLineBetweenHelpSections
+ && result.Length > 0
&& !result.SafeEndsWith(Environment.NewLine)
&& !sb.SafeStartsWith(Environment.NewLine))
return Environment.NewLine;
@@ -952,7 +980,7 @@ specification is OptionSpecification optionSpecification &&
if (optionGroupSpecification != null)
{
- optionHelpText = "({0}: {1}) ".FormatInvariant(optionGroupWord, optionGroupSpecification.Group) + optionHelpText;
+ optionHelpText = "({0}: {1}) ".FormatInvariant(optionGroupWord, optionGroupSpecification.Group) + optionHelpText;
}
//note that we need to indent trim the start of the string because it's going to be
diff --git a/tests/CommandLine.Tests/Unit/Text/HelpTextTests.cs b/tests/CommandLine.Tests/Unit/Text/HelpTextTests.cs
index 1364c982..29e64431 100644
--- a/tests/CommandLine.Tests/Unit/Text/HelpTextTests.cs
+++ b/tests/CommandLine.Tests/Unit/Text/HelpTextTests.cs
@@ -191,7 +191,7 @@ 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(headingInfo) { MaximumDisplayWidth = 100} ;
+ var sut = new HelpText(headingInfo) { MaximumDisplayWidth = 100 };
sut.AddOptions(
new NotParsed(
TypeInfo.Create(typeof(Simple_Options_With_HelpText_Set_To_Long_Description)),
@@ -270,7 +270,7 @@ public void Long_pre_and_post_lines_without_spaces()
// Teardown
}
-
+
[Fact]
public void Invoking_RenderParsingErrorsText_returns_appropriate_formatted_text()
{
@@ -296,32 +296,32 @@ public void Invoking_RenderParsingErrorsText_returns_appropriate_formatted_text(
new MissingGroupOptionError("bad-option-group", optionsInGroup),
});
Func fakeRenderer = err =>
+ {
+ switch (err.Tag)
{
- switch (err.Tag)
- {
- case ErrorType.BadFormatTokenError:
- return "ERR " + ((BadFormatTokenError)err).Token;
- case ErrorType.MissingValueOptionError:
- return "ERR " + ((MissingValueOptionError)err).NameInfo.NameText;
- case ErrorType.UnknownOptionError:
- return "ERR " + ((UnknownOptionError)err).Token;
- case ErrorType.MissingRequiredOptionError:
- return "ERR " + ((MissingRequiredOptionError)err).NameInfo.NameText;
- case ErrorType.SequenceOutOfRangeError:
- return "ERR " + ((SequenceOutOfRangeError)err).NameInfo.NameText;
- case ErrorType.NoVerbSelectedError:
- return "ERR no-verb-selected";
- case ErrorType.BadVerbSelectedError:
- return "ERR " + ((BadVerbSelectedError)err).Token;
- case ErrorType.MissingGroupOptionError:
+ case ErrorType.BadFormatTokenError:
+ return "ERR " + ((BadFormatTokenError)err).Token;
+ case ErrorType.MissingValueOptionError:
+ return "ERR " + ((MissingValueOptionError)err).NameInfo.NameText;
+ case ErrorType.UnknownOptionError:
+ return "ERR " + ((UnknownOptionError)err).Token;
+ case ErrorType.MissingRequiredOptionError:
+ return "ERR " + ((MissingRequiredOptionError)err).NameInfo.NameText;
+ case ErrorType.SequenceOutOfRangeError:
+ return "ERR " + ((SequenceOutOfRangeError)err).NameInfo.NameText;
+ case ErrorType.NoVerbSelectedError:
+ return "ERR no-verb-selected";
+ case ErrorType.BadVerbSelectedError:
+ return "ERR " + ((BadVerbSelectedError)err).Token;
+ case ErrorType.MissingGroupOptionError:
{
var groupErr = (MissingGroupOptionError)err;
return "ERR " + groupErr.Group + ": " + string.Join("---", groupErr.Names.Select(n => n.NameText));
}
- default:
- throw new InvalidOperationException();
- }
- };
+ default:
+ throw new InvalidOperationException();
+ }
+ };
Func, string> fakeMutExclRenderer =
_ => string.Empty;
@@ -420,12 +420,12 @@ public void Invoke_AutoBuild_for_Verbs_with_specific_verb_returns_appropriate_fo
});
// Exercize system
- var helpText = HelpText.AutoBuild(fakeResult, maxDisplayWidth: 100);
+ var helpText = HelpText.AutoBuild(fakeResult, maxDisplayWidth: 100);
// Verify outcome
var lines = helpText.ToString().ToNotEmptyLines().TrimStringArray();
lines[0].Should().Be(HeadingInfo.Default.ToString());
- lines[1].Should().Be(CopyrightInfo.Default.ToString());
+ lines[1].Should().Be(CopyrightInfo.Default.ToString());
lines[2].Should().BeEquivalentTo("-p, --patch Use the interactive patch selection interface to chose which changes to commit.");
lines[3].Should().BeEquivalentTo("--amend Used to amend the tip of the current branch.");
lines[4].Should().BeEquivalentTo("-m, --message Use the given message as the commit message.");
@@ -451,7 +451,7 @@ public void Invoke_AutoBuild_for_Verbs_with_unknown_verb_returns_appropriate_for
var lines = helpText.ToString().ToNotEmptyLines().TrimStringArray();
lines[0].Should().Be(HeadingInfo.Default.ToString());
- lines[1].Should().Be(CopyrightInfo.Default.ToString());
+ lines[1].Should().Be(CopyrightInfo.Default.ToString());
lines[2].Should().BeEquivalentTo("add Add file contents to the index.");
lines[3].Should().BeEquivalentTo("commit Record changes to the repository.");
lines[4].Should().BeEquivalentTo("clone Clone a repository into a new directory.");
@@ -495,7 +495,7 @@ public static void RenderUsageText_returns_properly_formatted_text()
ParserResult result =
new NotParsed(
TypeInfo.Create(typeof(Options_With_Usage_Attribute)), Enumerable.Empty());
-
+
// Exercize system
var text = HelpText.RenderUsageText(result);
@@ -584,7 +584,7 @@ public void Invoke_AutoBuild_for_Options_with_Usage_returns_appropriate_formatte
var text = helpText.ToString();
var lines = text.ToLines().TrimStringArray();
-
+
lines.Should().StartWith(expected);
}
@@ -667,23 +667,17 @@ public void Default_set_to_sequence_should_be_properly_printed()
[Fact]
public void AutoBuild_when_no_assembly_attributes()
{
- string expectedCopyright = "Copyright (C) 1 author";
+ string expectedCopyright = $"Copyright (C) {DateTime.Now.Year} author";
ReflectionHelper.SetAttributeOverride(new Attribute[0]);
ParserResult fakeResult = new NotParsed(
- TypeInfo.Create(typeof (Simple_Options)), new Error[0]);
- bool onErrorCalled = false;
- HelpText actualResult = HelpText.AutoBuild(fakeResult, ht =>
- {
- onErrorCalled = true;
- return ht;
- }, ex => ex);
-
- onErrorCalled.Should().BeTrue();
+ TypeInfo.Create(typeof(Simple_Options)), new Error[0]);
+ HelpText actualResult = HelpText.AutoBuild(fakeResult, ht => ht, ex => ex);
actualResult.Copyright.Should().Be(expectedCopyright);
}
+
[Fact]
public void AutoBuild_with_assembly_title_and_version_attributes_only()
{
@@ -697,15 +691,8 @@ public void AutoBuild_with_assembly_title_and_version_attributes_only()
});
ParserResult fakeResult = new NotParsed(
- TypeInfo.Create(typeof (Simple_Options)), new Error[0]);
- bool onErrorCalled = false;
- HelpText actualResult = HelpText.AutoBuild(fakeResult, ht =>
- {
- onErrorCalled = true;
- return ht;
- }, ex => ex);
-
- onErrorCalled.Should().BeTrue();
+ TypeInfo.Create(typeof(Simple_Options)), new Error[0]);
+ HelpText actualResult = HelpText.AutoBuild(fakeResult, ht => ht, ex => ex);
actualResult.Heading.Should().Be(string.Format("{0} {1}", expectedTitle, expectedVersion));
}
@@ -720,7 +707,7 @@ public void AutoBuild_with_assembly_company_attribute_only()
});
ParserResult fakeResult = new NotParsed(
- TypeInfo.Create(typeof (Simple_Options)), new Error[0]);
+ TypeInfo.Create(typeof(Simple_Options)), new Error[0]);
bool onErrorCalled = false;
HelpText actualResult = HelpText.AutoBuild(fakeResult, ht =>
{
@@ -748,7 +735,7 @@ public void HelpTextHonoursLineBreaks()
{
// Fixture setup
// Exercize system
- var sut = new HelpText {AddDashesToOption = true}
+ var sut = new HelpText { AddDashesToOption = true }
.AddOptions(new NotParsed(TypeInfo.Create(typeof(HelpTextWithLineBreaks_Options)),
Enumerable.Empty()));
@@ -758,7 +745,7 @@ public void HelpTextHonoursLineBreaks()
lines[0].Should().BeEquivalentTo(" --stringvalue This is a help text description.");
lines[1].Should().BeEquivalentTo(" It has multiple lines.");
lines[2].Should().BeEquivalentTo(" We also want to ensure that indentation is correct.");
-
+
// Teardown
}
@@ -767,7 +754,7 @@ public void HelpTextHonoursIndentationAfterLineBreaks()
{
// Fixture setup
// Exercize system
- var sut = new HelpText {AddDashesToOption = true}
+ var sut = new HelpText { AddDashesToOption = true }
.AddOptions(new NotParsed(TypeInfo.Create(typeof(HelpTextWithLineBreaks_Options)),
Enumerable.Empty()));
@@ -777,7 +764,7 @@ public void HelpTextHonoursIndentationAfterLineBreaks()
lines[3].Should().BeEquivalentTo(" --stringvalu2 This is a help text description where we want");
lines[4].Should().BeEquivalentTo(" the left pad after a linebreak to be honoured so that");
lines[5].Should().BeEquivalentTo(" we can sub-indent within a description.");
-
+
// Teardown
}
@@ -786,7 +773,7 @@ public void HelpTextPreservesIndentationAcrossWordWrap()
{
// Fixture setup
// Exercise system
- var sut = new HelpText {AddDashesToOption = true,MaximumDisplayWidth = 60}
+ var sut = new HelpText { AddDashesToOption = true, MaximumDisplayWidth = 60 }
.AddOptions(new NotParsed(TypeInfo.Create(typeof(HelpTextWithLineBreaksAndSubIndentation_Options)),
Enumerable.Empty()));
@@ -809,7 +796,7 @@ public void HelpTextIsConsitentRegardlessOfCompileTimeLineStyle()
{
// Fixture setup
// Exercize system
- var sut = new HelpText {AddDashesToOption = true}
+ var sut = new HelpText { AddDashesToOption = true }
.AddOptions(new NotParsed(TypeInfo.Create(typeof(HelpTextWithMixedLineBreaks_Options)),
Enumerable.Empty()));
@@ -819,7 +806,7 @@ public void HelpTextIsConsitentRegardlessOfCompileTimeLineStyle()
lines[0].Should().BeEquivalentTo(" --stringvalue This is a help text description");
lines[1].Should().BeEquivalentTo(" It has multiple lines.");
lines[2].Should().BeEquivalentTo(" Third line");
-
+
// Teardown
}
@@ -828,14 +815,14 @@ public void HelpTextPreservesIndentationAcrossWordWrapWithSmallMaximumDisplayWid
{
// Fixture setup
// Exercise system
- var sut = new HelpText {AddDashesToOption = true,MaximumDisplayWidth = 10}
+ var sut = new HelpText { AddDashesToOption = true, MaximumDisplayWidth = 10 }
.AddOptions(new NotParsed(TypeInfo.Create(typeof(HelpTextWithLineBreaksAndSubIndentation_Options)),
Enumerable.Empty()));
// Verify outcome
-
- Assert.True(sut.ToString().Length>0);
-
+
+ Assert.True(sut.ToString().Length > 0);
+
// Teardown
}
@@ -926,5 +913,117 @@ public void Options_Should_Render_Multiple_OptionGroups_When_Available()
lines[6].Should().BeEquivalentTo("--help Display this help screen.");
lines[7].Should().BeEquivalentTo("--version Display version information.");
}
+
+ #region Custom Help
+
+ [Fact]
+ [Trait("Category", "CustomHelp")]
+ public void AutoBuild_with_custom_copyright_using_onError_action()
+ {
+ string expectedCopyright = "Copyright (c) 2019 Global.com";
+ var expectedHeading = "MyApp 2.0.0-beta";
+ ParserResult fakeResult = new NotParsed(
+ TypeInfo.Create(typeof(Simple_Options)),
+ new Error[] { new HelpRequestedError() });
+ bool onErrorCalled = false;
+ HelpText actualResult = HelpText.AutoBuild(fakeResult, ht =>
+ {
+ ht.AdditionalNewLineAfterOption = false;
+ ht.Heading = "MyApp 2.0.0-beta";
+ ht.Copyright = "Copyright (c) 2019 Global.com";
+ return ht;
+ });
+ actualResult.Copyright.Should().Be(expectedCopyright);
+ actualResult.Heading.Should().Be(expectedHeading);
+ }
+
+ [Fact]
+ [Trait("Category", "CustomHelp")]
+ public void AutoBuild_with_custom_help_and_version_request()
+ {
+ string expectedTitle = "Title";
+ string expectedVersion = "1.2.3.4";
+
+ ReflectionHelper.SetAttributeOverride(new Attribute[]
+ {
+ new AssemblyTitleAttribute(expectedTitle),
+ new AssemblyInformationalVersionAttribute(expectedVersion)
+ });
+
+ ParserResult fakeResult = new NotParsed(
+ TypeInfo.Create(typeof(Simple_Options)),
+ new Error[] { new VersionRequestedError() });
+
+ HelpText helpText = HelpText.AutoBuild(fakeResult, ht => ht);
+ helpText.ToString().Trim().Should().Be($"{expectedTitle} {expectedVersion}");
+ }
+ [Fact]
+ [Trait("Category", "CustomHelp")]
+ public void Invoke_Custom_AutoBuild_for_Verbs_with_specific_verb_and_no_AdditionalNewLineAfterOption_returns_appropriate_formatted_text()
+ {
+ // Fixture setup
+ var fakeResult = new NotParsed(
+ TypeInfo.Create(typeof(NullInstance)),
+ new Error[]
+ {
+ new HelpVerbRequestedError("commit", typeof(Commit_Verb), true)
+ });
+
+ // Exercize system
+ var helpText = HelpText.AutoBuild(fakeResult, h => {
+ h.AdditionalNewLineAfterOption = false;
+ return h;
+ });
+
+ // Verify outcome
+ var lines = helpText.ToString().ToLines().TrimStringArray();
+ var i = 0;
+ lines[i++].Should().Be(HeadingInfo.Default.ToString());
+ lines[i++].Should().Be(CopyrightInfo.Default.ToString());
+ lines[i++].Should().BeEmpty();
+ lines[i++].Should().BeEquivalentTo("-p, --patch Use the interactive patch selection interface to chose which");
+ lines[i++].Should().BeEquivalentTo("changes to commit.");
+ lines[i++].Should().BeEquivalentTo("--amend Used to amend the tip of the current branch.");
+ lines[i++].Should().BeEquivalentTo("-m, --message Use the given message as the commit message.");
+ lines[i++].Should().BeEquivalentTo("--help Display this help screen.");
+
+ }
+ [Fact]
+ [Trait("Category", "CustomHelp")]
+ public void Invoke_AutoBuild_for_Options_with_custom_help_returns_appropriate_formatted_text()
+ {
+ // Fixture setup
+ var fakeResult = new NotParsed(
+ TypeInfo.Create(typeof(Simple_Options)),
+ new Error[]
+ {
+ new BadFormatTokenError("badtoken"),
+ new SequenceOutOfRangeError(new NameInfo("i", ""))
+ });
+
+ // Exercize system
+ var helpText = HelpText.AutoBuild(fakeResult, h => h);
+
+ // Verify outcome
+ var lines = helpText.ToString().ToLines().TrimStringArray();
+ lines[0].Should().Be(HeadingInfo.Default.ToString());
+ lines[1].Should().Be(CopyrightInfo.Default.ToString());
+ lines[2].Should().BeEmpty();
+ lines[3].Should().BeEquivalentTo("ERROR(S):");
+ lines[4].Should().BeEquivalentTo("Token 'badtoken' is not recognized.");
+ lines[5].Should().BeEquivalentTo("A sequence option 'i' is defined with fewer or more items than required.");
+ lines[6].Should().BeEmpty();
+ lines[7].Should().BeEquivalentTo("--stringvalue Define a string value here.");
+ lines[8].Should().BeEmpty();
+ lines[9].Should().BeEquivalentTo("-s, --shortandlong Example with both short and long name.");
+ lines[10].Should().BeEmpty();
+ lines[11].Should().BeEquivalentTo("-i Define a int sequence here.");
+ lines[12].Should().BeEmpty();
+ lines[13].Should().BeEquivalentTo("-x Define a boolean or switch value here.");
+ lines[14].Should().BeEmpty();
+ lines[15].Should().BeEquivalentTo("--help Display this help screen.");
+
+ }
+ #endregion
}
}