Skip to content
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

Improve spacing in HelpText #494

Merged
merged 5 commits into from
Dec 7, 2019
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
36 changes: 35 additions & 1 deletion src/CommandLine/Infrastructure/StringBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,5 +113,39 @@ public static int TrailingSpaces(this StringBuilder builder)
}
return c;
}

/// <summary>
/// Indicates whether the string value of a <see cref="System.Text.StringBuilder"/>
/// starts with the input <see cref="System.String"/> parameter. Returns false if either
/// the StringBuilder or input string is null or empty.
/// </summary>
/// <param name="builder">The <see cref="System.Text.StringBuilder"/> to test.</param>
/// <param name="s">The <see cref="System.String"/> to look for.</param>
/// <returns></returns>
public static bool SafeStartsWith(this StringBuilder builder, string s)
{
if (string.IsNullOrEmpty(s))
return false;

return builder?.Length >= s.Length
&& builder.ToString(0, s.Length) == s;
}

/// <summary>
/// Indicates whether the string value of a <see cref="System.Text.StringBuilder"/>
/// ends with the input <see cref="System.String"/> parameter. Returns false if either
/// the StringBuilder or input string is null or empty.
/// </summary>
/// <param name="builder">The <see cref="System.Text.StringBuilder"/> to test.</param>
/// <param name="s">The <see cref="System.String"/> to look for.</param>
/// <returns></returns>
public static bool SafeEndsWith(this StringBuilder builder, string s)
{
if (string.IsNullOrEmpty(s))
return false;

return builder?.Length >= s.Length
&& builder.ToString(builder.Length - s.Length, s.Length) == s;
}
}
}
}
55 changes: 45 additions & 10 deletions src/CommandLine/Text/HelpText.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ ComparableOption ToComparableOption(Specification spec, int index)
private bool addEnumValuesToHelpText;
private bool autoHelp;
private bool autoVersion;
private bool addNewLineBetweenHelpSections;

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

/// <summary>
/// Gets or sets a value indicating whether to add newlines between help sections.
/// </summary>
public bool AddNewLineBetweenHelpSections
{
get { return addNewLineBetweenHelpSections; }
set { addNewLineBetweenHelpSections = value; }
}

/// <summary>
/// Gets or sets a value indicating whether to add the values of an enum after the description of the specification.
/// </summary>
Expand Down Expand Up @@ -352,7 +362,11 @@ public static HelpText AutoBuild<T>(
{
var heading = auto.SentenceBuilder.UsageHeadingText();
if (heading.Length > 0)
{
if (auto.AddNewLineBetweenHelpSections)
heading = Environment.NewLine + heading;
auto.AddPreOptionsLine(heading);
}
}

usageAttr.Do(
Expand Down Expand Up @@ -707,19 +721,40 @@ public static IEnumerable<string> RenderUsageTextAsLines<T>(ParserResult<T> pars
public override string ToString()
{
const int ExtraLength = 10;
return
new StringBuilder(
heading.SafeLength() + copyright.SafeLength() + preOptionsHelp.SafeLength() +
optionsHelp.SafeLength() + ExtraLength).Append(heading)
.AppendWhen(!string.IsNullOrEmpty(copyright), Environment.NewLine, copyright)
.AppendWhen(preOptionsHelp.Length > 0, Environment.NewLine, preOptionsHelp.ToString())
.AppendWhen(
optionsHelp != null && optionsHelp.Length > 0,

var sbLength = heading.SafeLength() + copyright.SafeLength() + preOptionsHelp.SafeLength()
+ optionsHelp.SafeLength() + postOptionsHelp.SafeLength() + ExtraLength;
var result = new StringBuilder(sbLength);

result.Append(heading)
.AppendWhen(!string.IsNullOrEmpty(copyright),
Environment.NewLine,
copyright)
.AppendWhen(preOptionsHelp.SafeLength() > 0,
NewLineIfNeededBefore(preOptionsHelp),
Environment.NewLine,
preOptionsHelp.ToString())
.AppendWhen(optionsHelp.SafeLength() > 0,
Environment.NewLine,
Environment.NewLine,
optionsHelp.SafeToString())
.AppendWhen(postOptionsHelp.Length > 0, Environment.NewLine, postOptionsHelp.ToString())
.ToString();
.AppendWhen(postOptionsHelp.SafeLength() > 0,
NewLineIfNeededBefore(postOptionsHelp),
Environment.NewLine,
postOptionsHelp.ToString());

string NewLineIfNeededBefore(StringBuilder sb)
{
if (AddNewLineBetweenHelpSections
&& result.Length > 0
&& !result.SafeEndsWith(Environment.NewLine)
&& !sb.SafeStartsWith(Environment.NewLine))
return Environment.NewLine;
else
return null;
}

return result.ToString();
}

internal static void AddLine(StringBuilder builder, string value, int maximumLength)
Expand Down
104 changes: 104 additions & 0 deletions tests/CommandLine.Tests/Unit/StringBuilderExtensionsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xunit;
using FluentAssertions;
using CommandLine.Infrastructure;

namespace CommandLine.Tests.Unit
{
public class StringBuilderExtensionsTests
{
private static StringBuilder _sb = new StringBuilder("test string");
private static StringBuilder _emptySb = new StringBuilder();
private static StringBuilder _nullSb = null;

public static IEnumerable<object[]> GoodStartsWithData => new []
{
new object[] { "t" },
new object[] { "te" },
new object[] { "test " },
new object[] { "test string" }
};

public static IEnumerable<object[]> BadTestData => new []
{
new object[] { null },
new object[] { "" },
new object[] { "xyz" },
new object[] { "some long test string" }
};

public static IEnumerable<object[]> GoodEndsWithData => new[]
{
new object[] { "g" },
new object[] { "ng" },
new object[] { " string" },
new object[] { "test string" }
};



[Theory]
[MemberData(nameof(GoodStartsWithData))]
[MemberData(nameof(BadTestData))]
public void StartsWith_null_builder_returns_false(string input)
{
_nullSb.SafeStartsWith(input).Should().BeFalse();
}

[Theory]
[MemberData(nameof(GoodStartsWithData))]
[MemberData(nameof(BadTestData))]
public void StartsWith_empty_builder_returns_false(string input)
{
_emptySb.SafeStartsWith(input).Should().BeFalse();
}

[Theory]
[MemberData(nameof(GoodStartsWithData))]
public void StartsWith_good_data_returns_true(string input)
{
_sb.SafeStartsWith(input).Should().BeTrue();
}

[Theory]
[MemberData(nameof(BadTestData))]
public void StartsWith_bad_data_returns_false(string input)
{
_sb.SafeStartsWith(input).Should().BeFalse();
}

[Theory]
[MemberData(nameof(GoodEndsWithData))]
[MemberData(nameof(BadTestData))]
public void EndsWith_null_builder_returns_false(string input)
{
_nullSb.SafeEndsWith(input).Should().BeFalse();
}

[Theory]
[MemberData(nameof(GoodEndsWithData))]
[MemberData(nameof(BadTestData))]
public void EndsWith_empty_builder_returns_false(string input)
{
_emptySb.SafeEndsWith(input).Should().BeFalse();
}

[Theory]
[MemberData(nameof(GoodEndsWithData))]
public void EndsWith_good_data_returns_true(string input)
{
_sb.SafeEndsWith(input).Should().BeTrue();
}

[Theory]
[MemberData(nameof(BadTestData))]
public void EndsWith_bad_data_returns_false(string input)
{
_sb.SafeEndsWith(input).Should().BeFalse();
}
}
}
Loading