Skip to content

Commit 1fe68f0

Browse files
committed
Add general help text. Display group name in option details
1 parent f442e9a commit 1fe68f0

File tree

3 files changed

+86
-61
lines changed

3 files changed

+86
-61
lines changed

src/CommandLine/OptionAttribute.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ public char Separator
106106
set { separator = value; }
107107
}
108108

109+
/// <summary>
110+
/// Gets or sets the option group name. When one or more options are grouped, at least one of them should have value.
111+
/// </summary>
109112
public string Group
110113
{
111114
get { return group; }

src/CommandLine/Text/HelpText.cs

Lines changed: 73 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,27 @@
11
// Copyright 2005-2015 Giacomo Stelluti Scala & Contributors. All rights reserved. See License.md in the project root for license information.
22

3+
using CommandLine.Core;
4+
using CommandLine.Infrastructure;
5+
6+
using CSharpx;
7+
38
using System;
49
using System.Collections;
510
using System.Collections.Generic;
611
using System.IO;
7-
using System.Text;
812
using System.Linq;
913
using System.Reflection;
10-
using CommandLine.Infrastructure;
11-
using CommandLine.Core;
12-
using CSharpx;
14+
using System.Text;
1315

1416
namespace CommandLine.Text
1517
{
1618
/// <summary>
1719
/// Provides means to format an help screen.
1820
/// You can assign it in place of a <see cref="System.String"/> instance.
1921
/// </summary>
20-
21-
22-
22+
23+
24+
2325
public struct ComparableOption
2426
{
2527
public bool Required;
@@ -29,7 +31,7 @@ public struct ComparableOption
2931
public string ShortName;
3032
public int Index;
3133
}
32-
34+
3335
public class HelpText
3436
{
3537

@@ -55,40 +57,40 @@ ComparableOption ToComparableOption(Specification spec, int index)
5557

5658
public Comparison<ComparableOption> OptionComparison { get; set; } = null;
5759

58-
public static Comparison<ComparableOption> RequiredThenAlphaComparison = (ComparableOption attr1, ComparableOption attr2) =>
59-
{
60-
if (attr1.IsOption && attr2.IsOption)
61-
{
62-
if (attr1.Required && !attr2.Required)
63-
{
64-
return -1;
65-
}
66-
else if (!attr1.Required && attr2.Required)
67-
{
68-
return 1;
69-
}
70-
71-
return String.Compare(attr1.LongName, attr2.LongName, StringComparison.Ordinal);
72-
73-
}
74-
else if (attr1.IsOption && attr2.IsValue)
75-
{
76-
return -1;
77-
}
78-
else
79-
{
80-
return 1;
81-
}
82-
};
83-
60+
public static Comparison<ComparableOption> RequiredThenAlphaComparison = (ComparableOption attr1, ComparableOption attr2) =>
61+
{
62+
if (attr1.IsOption && attr2.IsOption)
63+
{
64+
if (attr1.Required && !attr2.Required)
65+
{
66+
return -1;
67+
}
68+
else if (!attr1.Required && attr2.Required)
69+
{
70+
return 1;
71+
}
72+
73+
return String.Compare(attr1.LongName, attr2.LongName, StringComparison.Ordinal);
74+
75+
}
76+
else if (attr1.IsOption && attr2.IsValue)
77+
{
78+
return -1;
79+
}
80+
else
81+
{
82+
return 1;
83+
}
84+
};
85+
8486
#endregion
85-
87+
8688
private const int BuilderCapacity = 128;
8789
private const int DefaultMaximumLength = 80; // default console width
8890
/// <summary>
8991
/// The number of spaces between an option and its associated help text
9092
/// </summary>
91-
private const int OptionToHelpTextSeparatorWidth = 4;
93+
private const int OptionToHelpTextSeparatorWidth = 4;
9294
/// <summary>
9395
/// The width of the option prefix (either "--" or " "
9496
/// </summary>
@@ -334,7 +336,7 @@ public static HelpText AutoBuild<T>(
334336

335337
var errors = Enumerable.Empty<Error>();
336338

337-
339+
338340
if (onError != null && parserResult.Tag == ParserResultType.NotParsed)
339341
{
340342
errors = ((NotParsed<T>)parserResult).Errors;
@@ -392,7 +394,7 @@ public static HelpText AutoBuild<T>(ParserResult<T> parserResult, int maxDisplay
392394
var errors = ((NotParsed<T>)parserResult).Errors;
393395

394396
if (errors.Any(e => e.Tag == ErrorType.VersionRequestedError))
395-
return new HelpText($"{HeadingInfo.Default}{Environment.NewLine}"){MaximumDisplayWidth = maxDisplayWidth }.AddPreOptionsLine(Environment.NewLine);
397+
return new HelpText($"{HeadingInfo.Default}{Environment.NewLine}") { MaximumDisplayWidth = maxDisplayWidth }.AddPreOptionsLine(Environment.NewLine);
396398

397399
if (!errors.Any(e => e.Tag == ErrorType.HelpVerbRequestedError))
398400
return AutoBuild(parserResult, current => DefaultParsingErrorsHandler(parserResult, current), e => e, maxDisplayWidth: maxDisplayWidth);
@@ -520,6 +522,7 @@ public HelpText AddOptions<T>(ParserResult<T> result)
520522
return AddOptionsImpl(
521523
GetSpecificationsFromType(result.TypeInfo.Current),
522524
SentenceBuilder.RequiredWord(),
525+
SentenceBuilder.OptionGroupWord(),
523526
MaximumDisplayWidth);
524527
}
525528

@@ -537,6 +540,7 @@ public HelpText AddVerbs(params Type[] types)
537540
return AddOptionsImpl(
538541
AdaptVerbsToSpecifications(types),
539542
SentenceBuilder.RequiredWord(),
543+
SentenceBuilder.OptionGroupWord(),
540544
MaximumDisplayWidth);
541545
}
542546

@@ -553,6 +557,7 @@ public HelpText AddOptions<T>(int maximumLength, ParserResult<T> result)
553557
return AddOptionsImpl(
554558
GetSpecificationsFromType(result.TypeInfo.Current),
555559
SentenceBuilder.RequiredWord(),
560+
SentenceBuilder.OptionGroupWord(),
556561
maximumLength);
557562
}
558563

@@ -571,6 +576,7 @@ public HelpText AddVerbs(int maximumLength, params Type[] types)
571576
return AddOptionsImpl(
572577
AdaptVerbsToSpecifications(types),
573578
SentenceBuilder.RequiredWord(),
579+
SentenceBuilder.OptionGroupWord(),
574580
maximumLength);
575581
}
576582

@@ -614,7 +620,7 @@ public static IEnumerable<string> RenderParsingErrorsTextAsLines<T>(
614620
if (meaningfulErrors.Empty())
615621
yield break;
616622

617-
foreach(var error in meaningfulErrors
623+
foreach (var error in meaningfulErrors
618624
.Where(e => e.Tag != ErrorType.MutuallyExclusiveSetError))
619625
{
620626
var line = new StringBuilder(indent.Spaces())
@@ -751,9 +757,9 @@ private IEnumerable<Specification> GetSpecificationsFromType(Type type)
751757
var optionSpecs = specs
752758
.OfType<OptionSpecification>();
753759
if (autoHelp)
754-
optionSpecs = optionSpecs.Concat(new [] { MakeHelpEntry() });
760+
optionSpecs = optionSpecs.Concat(new[] { MakeHelpEntry() });
755761
if (autoVersion)
756-
optionSpecs = optionSpecs.Concat(new [] { MakeVersionEntry() });
762+
optionSpecs = optionSpecs.Concat(new[] { MakeVersionEntry() });
757763
var valueSpecs = specs
758764
.OfType<ValueSpecification>()
759765
.OrderBy(v => v.Index);
@@ -780,29 +786,30 @@ private static Maybe<Tuple<UsageAttribute, IEnumerable<Example>>> GetUsageFromTy
780786
private IEnumerable<Specification> AdaptVerbsToSpecifications(IEnumerable<Type> types)
781787
{
782788
var optionSpecs = from verbTuple in Verb.SelectFromTypes(types)
783-
select
784-
OptionSpecification.NewSwitch(
785-
string.Empty,
786-
verbTuple.Item1.Name,
787-
false,
788-
verbTuple.Item1.HelpText,
789-
string.Empty,
790-
verbTuple.Item1.Hidden);
789+
select
790+
OptionSpecification.NewSwitch(
791+
string.Empty,
792+
verbTuple.Item1.Name,
793+
false,
794+
verbTuple.Item1.HelpText,
795+
string.Empty,
796+
verbTuple.Item1.Hidden);
791797
if (autoHelp)
792-
optionSpecs = optionSpecs.Concat(new [] { MakeHelpEntry() });
798+
optionSpecs = optionSpecs.Concat(new[] { MakeHelpEntry() });
793799
if (autoVersion)
794-
optionSpecs = optionSpecs.Concat(new [] { MakeVersionEntry() });
800+
optionSpecs = optionSpecs.Concat(new[] { MakeVersionEntry() });
795801
return optionSpecs;
796802
}
797803

798804
private HelpText AddOptionsImpl(
799805
IEnumerable<Specification> specifications,
800806
string requiredWord,
807+
string optionGroupWord,
801808
int maximumLength)
802809
{
803810
var maxLength = GetMaxLength(specifications);
804-
805-
811+
812+
806813

807814
optionsHelp = new StringBuilder(BuilderCapacity);
808815

@@ -822,14 +829,14 @@ private HelpText AddOptionsImpl(
822829
foreach (var comparable in comparables)
823830
{
824831
Specification spec = specifications.ElementAt(comparable.Index);
825-
AddOption(requiredWord, maxLength, spec, remainingSpace);
832+
AddOption(requiredWord, optionGroupWord, maxLength, spec, remainingSpace);
826833
}
827834
}
828835
else
829836
{
830837
specifications.ForEach(
831838
option =>
832-
AddOption(requiredWord, maxLength, option, remainingSpace));
839+
AddOption(requiredWord, optionGroupWord, maxLength, option, remainingSpace));
833840

834841
}
835842

@@ -865,7 +872,7 @@ private HelpText AddPreOptionsLine(string value, int maximumLength)
865872
return this;
866873
}
867874

868-
private HelpText AddOption(string requiredWord, int maxLength, Specification specification, int widthOfHelpText)
875+
private HelpText AddOption(string requiredWord, string optionGroupWord, int maxLength, Specification specification, int widthOfHelpText)
869876
{
870877
if (specification.Hidden)
871878
return this;
@@ -891,11 +898,16 @@ private HelpText AddOption(string requiredWord, int maxLength, Specification spe
891898

892899
if (specification.Required)
893900
optionHelpText = "{0} ".FormatInvariant(requiredWord) + optionHelpText;
894-
901+
902+
if (specification.Tag == SpecificationType.Option && specification is OptionSpecification optionSpecification && optionSpecification.Group.IsJust())
903+
{
904+
optionHelpText = "({0}: {1})".FormatInvariant(optionGroupWord, optionSpecification.Group.GetValueOrDefault(null)) + optionHelpText;
905+
}
906+
895907
//note that we need to indent trim the start of the string because it's going to be
896908
//appended to an existing line that is as long as the indent-level
897-
var indented = TextWrapper.WrapAndIndentText(optionHelpText, maxLength+TotalOptionPadding, widthOfHelpText).TrimStart();
898-
909+
var indented = TextWrapper.WrapAndIndentText(optionHelpText, maxLength + TotalOptionPadding, widthOfHelpText).TrimStart();
910+
899911
optionsHelp
900912
.Append(indented)
901913
.Append(Environment.NewLine)
@@ -989,7 +1001,7 @@ private int GetMaxOptionLength(OptionSpecification spec)
9891001
}
9901002

9911003
if (hasShort && hasLong)
992-
specLength += OptionPrefixWidth;
1004+
specLength += OptionPrefixWidth;
9931005

9941006
return specLength;
9951007
}
@@ -1037,7 +1049,7 @@ private static string FormatDefaultValue<T>(T value)
10371049
: string.Empty;
10381050
}
10391051

1040-
1052+
10411053

10421054
}
10431055
}

src/CommandLine/Text/SentenceBuilder.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ public static SentenceBuilder Create()
3434
/// </summary>
3535
public abstract Func<string> RequiredWord { get; }
3636

37+
/// <summary>
38+
/// Gets a delegate that returns the word 'group'.
39+
/// </summary>
40+
public abstract Func<string> OptionGroupWord { get; }
41+
3742
/// <summary>
3843
/// Gets a delegate that returns that errors block heading text.
3944
/// </summary>
@@ -85,6 +90,11 @@ public override Func<string> UsageHeadingText
8590
get { return () => "USAGE:"; }
8691
}
8792

93+
public override Func<string> OptionGroupWord
94+
{
95+
get { return () => "Group"; }
96+
}
97+
8898
public override Func<bool, string> HelpCommandText
8999
{
90100
get

0 commit comments

Comments
 (0)