Skip to content

Commit 0148bda

Browse files
asherbermoh-hassan
authored andcommitted
Improve spacing in HelpText (#494)
* Improve spacing in HelpText * Make new spacing an option * Refactor * Extend logic to PostOptions * Add XML docs
1 parent ce3011c commit 0148bda

File tree

4 files changed

+378
-96
lines changed

4 files changed

+378
-96
lines changed

Diff for: src/CommandLine/Infrastructure/StringBuilderExtensions.cs

+35-1
Original file line numberDiff line numberDiff line change
@@ -113,5 +113,39 @@ public static int TrailingSpaces(this StringBuilder builder)
113113
}
114114
return c;
115115
}
116+
117+
/// <summary>
118+
/// Indicates whether the string value of a <see cref="System.Text.StringBuilder"/>
119+
/// starts with the input <see cref="System.String"/> parameter. Returns false if either
120+
/// the StringBuilder or input string is null or empty.
121+
/// </summary>
122+
/// <param name="builder">The <see cref="System.Text.StringBuilder"/> to test.</param>
123+
/// <param name="s">The <see cref="System.String"/> to look for.</param>
124+
/// <returns></returns>
125+
public static bool SafeStartsWith(this StringBuilder builder, string s)
126+
{
127+
if (string.IsNullOrEmpty(s))
128+
return false;
129+
130+
return builder?.Length >= s.Length
131+
&& builder.ToString(0, s.Length) == s;
132+
}
133+
134+
/// <summary>
135+
/// Indicates whether the string value of a <see cref="System.Text.StringBuilder"/>
136+
/// ends with the input <see cref="System.String"/> parameter. Returns false if either
137+
/// the StringBuilder or input string is null or empty.
138+
/// </summary>
139+
/// <param name="builder">The <see cref="System.Text.StringBuilder"/> to test.</param>
140+
/// <param name="s">The <see cref="System.String"/> to look for.</param>
141+
/// <returns></returns>
142+
public static bool SafeEndsWith(this StringBuilder builder, string s)
143+
{
144+
if (string.IsNullOrEmpty(s))
145+
return false;
146+
147+
return builder?.Length >= s.Length
148+
&& builder.ToString(builder.Length - s.Length, s.Length) == s;
149+
}
116150
}
117-
}
151+
}

Diff for: src/CommandLine/Text/HelpText.cs

+45-10
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ ComparableOption ToComparableOption(Specification spec, int index)
109109
private bool addEnumValuesToHelpText;
110110
private bool autoHelp;
111111
private bool autoVersion;
112+
private bool addNewLineBetweenHelpSections;
112113

113114
/// <summary>
114115
/// Initializes a new instance of the <see cref="CommandLine.Text.HelpText"/> class.
@@ -258,6 +259,15 @@ public bool AdditionalNewLineAfterOption
258259
set { additionalNewLineAfterOption = value; }
259260
}
260261

262+
/// <summary>
263+
/// Gets or sets a value indicating whether to add newlines between help sections.
264+
/// </summary>
265+
public bool AddNewLineBetweenHelpSections
266+
{
267+
get { return addNewLineBetweenHelpSections; }
268+
set { addNewLineBetweenHelpSections = value; }
269+
}
270+
261271
/// <summary>
262272
/// Gets or sets a value indicating whether to add the values of an enum after the description of the specification.
263273
/// </summary>
@@ -352,7 +362,11 @@ public static HelpText AutoBuild<T>(
352362
{
353363
var heading = auto.SentenceBuilder.UsageHeadingText();
354364
if (heading.Length > 0)
365+
{
366+
if (auto.AddNewLineBetweenHelpSections)
367+
heading = Environment.NewLine + heading;
355368
auto.AddPreOptionsLine(heading);
369+
}
356370
}
357371

358372
usageAttr.Do(
@@ -707,19 +721,40 @@ public static IEnumerable<string> RenderUsageTextAsLines<T>(ParserResult<T> pars
707721
public override string ToString()
708722
{
709723
const int ExtraLength = 10;
710-
return
711-
new StringBuilder(
712-
heading.SafeLength() + copyright.SafeLength() + preOptionsHelp.SafeLength() +
713-
optionsHelp.SafeLength() + ExtraLength).Append(heading)
714-
.AppendWhen(!string.IsNullOrEmpty(copyright), Environment.NewLine, copyright)
715-
.AppendWhen(preOptionsHelp.Length > 0, Environment.NewLine, preOptionsHelp.ToString())
716-
.AppendWhen(
717-
optionsHelp != null && optionsHelp.Length > 0,
724+
725+
var sbLength = heading.SafeLength() + copyright.SafeLength() + preOptionsHelp.SafeLength()
726+
+ optionsHelp.SafeLength() + postOptionsHelp.SafeLength() + ExtraLength;
727+
var result = new StringBuilder(sbLength);
728+
729+
result.Append(heading)
730+
.AppendWhen(!string.IsNullOrEmpty(copyright),
731+
Environment.NewLine,
732+
copyright)
733+
.AppendWhen(preOptionsHelp.SafeLength() > 0,
734+
NewLineIfNeededBefore(preOptionsHelp),
735+
Environment.NewLine,
736+
preOptionsHelp.ToString())
737+
.AppendWhen(optionsHelp.SafeLength() > 0,
718738
Environment.NewLine,
719739
Environment.NewLine,
720740
optionsHelp.SafeToString())
721-
.AppendWhen(postOptionsHelp.Length > 0, Environment.NewLine, postOptionsHelp.ToString())
722-
.ToString();
741+
.AppendWhen(postOptionsHelp.SafeLength() > 0,
742+
NewLineIfNeededBefore(postOptionsHelp),
743+
Environment.NewLine,
744+
postOptionsHelp.ToString());
745+
746+
string NewLineIfNeededBefore(StringBuilder sb)
747+
{
748+
if (AddNewLineBetweenHelpSections
749+
&& result.Length > 0
750+
&& !result.SafeEndsWith(Environment.NewLine)
751+
&& !sb.SafeStartsWith(Environment.NewLine))
752+
return Environment.NewLine;
753+
else
754+
return null;
755+
}
756+
757+
return result.ToString();
723758
}
724759

725760
internal static void AddLine(StringBuilder builder, string value, int maximumLength)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using Xunit;
7+
using FluentAssertions;
8+
using CommandLine.Infrastructure;
9+
10+
namespace CommandLine.Tests.Unit
11+
{
12+
public class StringBuilderExtensionsTests
13+
{
14+
private static StringBuilder _sb = new StringBuilder("test string");
15+
private static StringBuilder _emptySb = new StringBuilder();
16+
private static StringBuilder _nullSb = null;
17+
18+
public static IEnumerable<object[]> GoodStartsWithData => new []
19+
{
20+
new object[] { "t" },
21+
new object[] { "te" },
22+
new object[] { "test " },
23+
new object[] { "test string" }
24+
};
25+
26+
public static IEnumerable<object[]> BadTestData => new []
27+
{
28+
new object[] { null },
29+
new object[] { "" },
30+
new object[] { "xyz" },
31+
new object[] { "some long test string" }
32+
};
33+
34+
public static IEnumerable<object[]> GoodEndsWithData => new[]
35+
{
36+
new object[] { "g" },
37+
new object[] { "ng" },
38+
new object[] { " string" },
39+
new object[] { "test string" }
40+
};
41+
42+
43+
44+
[Theory]
45+
[MemberData(nameof(GoodStartsWithData))]
46+
[MemberData(nameof(BadTestData))]
47+
public void StartsWith_null_builder_returns_false(string input)
48+
{
49+
_nullSb.SafeStartsWith(input).Should().BeFalse();
50+
}
51+
52+
[Theory]
53+
[MemberData(nameof(GoodStartsWithData))]
54+
[MemberData(nameof(BadTestData))]
55+
public void StartsWith_empty_builder_returns_false(string input)
56+
{
57+
_emptySb.SafeStartsWith(input).Should().BeFalse();
58+
}
59+
60+
[Theory]
61+
[MemberData(nameof(GoodStartsWithData))]
62+
public void StartsWith_good_data_returns_true(string input)
63+
{
64+
_sb.SafeStartsWith(input).Should().BeTrue();
65+
}
66+
67+
[Theory]
68+
[MemberData(nameof(BadTestData))]
69+
public void StartsWith_bad_data_returns_false(string input)
70+
{
71+
_sb.SafeStartsWith(input).Should().BeFalse();
72+
}
73+
74+
[Theory]
75+
[MemberData(nameof(GoodEndsWithData))]
76+
[MemberData(nameof(BadTestData))]
77+
public void EndsWith_null_builder_returns_false(string input)
78+
{
79+
_nullSb.SafeEndsWith(input).Should().BeFalse();
80+
}
81+
82+
[Theory]
83+
[MemberData(nameof(GoodEndsWithData))]
84+
[MemberData(nameof(BadTestData))]
85+
public void EndsWith_empty_builder_returns_false(string input)
86+
{
87+
_emptySb.SafeEndsWith(input).Should().BeFalse();
88+
}
89+
90+
[Theory]
91+
[MemberData(nameof(GoodEndsWithData))]
92+
public void EndsWith_good_data_returns_true(string input)
93+
{
94+
_sb.SafeEndsWith(input).Should().BeTrue();
95+
}
96+
97+
[Theory]
98+
[MemberData(nameof(BadTestData))]
99+
public void EndsWith_bad_data_returns_false(string input)
100+
{
101+
_sb.SafeEndsWith(input).Should().BeFalse();
102+
}
103+
}
104+
}

0 commit comments

Comments
 (0)