-
Notifications
You must be signed in to change notification settings - Fork 7
/
CustomUsageWriter.cs
178 lines (149 loc) · 6.35 KB
/
CustomUsageWriter.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
using Ookii.CommandLine;
using Ookii.CommandLine.Terminal;
namespace CustomUsage;
// A custom usage writer is used to change the format of the usage help.
internal class CustomUsageWriter : UsageWriter
{
// Override some defaults to suit the custom format.
public CustomUsageWriter()
{
// Only list the positional arguments in the syntax.
UseAbbreviatedSyntax = true;
// Set the indentation to work with the formatting used.
ApplicationDescriptionIndent = 2;
SyntaxIndent = 2;
// Sort the description list by short name.
ArgumentDescriptionListOrder = DescriptionListSortMode.AlphabeticalShortName;
// Customize some of the colors.
UsagePrefixColor = TextFormat.ForegroundYellow;
ArgumentDescriptionColor = TextFormat.BoldBright;
// No blank lines between arguments in the description list.
BlankLineAfterDescription = false;
}
// Add a header before the description.
protected override void WriteApplicationDescription(string description)
{
SetIndent(ApplicationDescriptionIndent);
// You must *always* check whether color is enabled before using VT sequences. It may be
// disabled, for example if the output is redirected or the terminal doesn't support VT
// sequences. WriteColor does this check for you.
WriteColor(UsagePrefixColor);
Writer.Write("DESCRIPTION:");
ResetColor();
Writer.WriteLine();
Writer.WriteLine(description);
Writer.WriteLine();
}
// Use a custom usage prefix with the string "USAGE:" in all caps and on its own line.
protected override void WriteUsageSyntaxPrefix()
{
WriteColor(UsagePrefixColor);
Writer.Write("USAGE:");
ResetColor();
Writer.WriteLine();
WriteColor(TextFormat.Underline);
Writer.Write(ExecutableName);
ResetColor();
// This application does not use subcommands, but if you do, you have to include the command
// name in this function too.
if (CommandName != null)
{
Writer.Write(' ');
Writer.Write(CommandName);
}
}
// Add some color to the argument names in the syntax.
//
// This doesn't apply to the description list because it uses WriteArgumentNameForDescription.
protected override void WriteArgumentName(string argumentName, string prefix)
{
// "bright black", aka "gray"...
WriteColor(TextFormat.BrightForegroundBlack);
base.WriteArgumentName(argumentName, prefix);
ResetColor();
}
protected override void WriteArgumentDescriptions()
{
// Calculate the amount of indentation needed based on the longest names, with two spaces
// before and after. This way the usage dynamically adapts if you change the argument
// names.
ArgumentDescriptionIndent = Parser.Arguments.Max(arg => CalculateNamesLength(arg)) + 4;
base.WriteArgumentDescriptions();
}
// Add a header before the argument description list (normally there is none).
protected override void WriteArgumentDescriptionListHeader()
{
WriteColor(UsagePrefixColor);
Writer.Write("OPTIONS:");
ResetColor();
Writer.WriteLine();
}
// Custom format for argument names and aliases.
protected override void WriteArgumentDescriptionHeader(CommandLineArgument argument)
{
// Collect all the argument's names and aliases, short names and aliases first.
var names = Enumerable.Empty<string>();
if (argument.HasShortName)
{
names = names.Append(argument.ShortNameWithPrefix!);
}
var shortPrefix = argument.Parser.ArgumentNamePrefixes[0];
names = names.Concat(argument.ShortAliases.Select(alias => shortPrefix + alias));
if (argument.HasLongName)
{
names = names.Append(argument.LongNameWithPrefix!);
}
names = names.Concat(argument.Aliases.Select(alias => argument.Parser.LongArgumentNamePrefix + alias));
// Join up all the names.
string name = string.Join('|', names);
// Unlike the default description format, we just omit the value description entirely
// if the argument is a switch.
if (!argument.IsSwitch)
{
name += " <" + argument.ValueDescription + ">";
}
// WriteArgumentDescriptions adjusts the indentation when in long/short mode, which we don't
// want here, so set it manually.
SetIndent(ArgumentDescriptionIndent);
Writer.ResetIndent();
Writer.Write(" ");
WriteColor(ArgumentDescriptionColor);
Writer.Write(name);
ResetColor();
// Pad until the indentation is reached.
WriteSpacing(ArgumentDescriptionIndent - name.Length - 2);
}
// Customize the format of the default values.
protected override void WriteDefaultValue(object defaultValue)
=> Writer.Write($" [default: {defaultValue}]");
// Calculate the length of the names and value prefix using the same logic as
// WriteArgumentDescriptionHeader.
private static int CalculateNamesLength(CommandLineArgument argument)
{
int length = 0;
if (argument.HasShortName)
{
// +1 for separator
length += argument.ShortNameWithPrefix!.Length + 1;
}
var shortPrefixLength = argument.Parser.ArgumentNamePrefixes[0].Length;
// Space for prefix, short name, separator.
length += argument.ShortAliases.Length * (shortPrefixLength + 1 + 1);
if (argument.HasLongName)
{
// +1 for separator
length += argument.LongNameWithPrefix!.Length + 1;
}
var longPrefixLength = argument.Parser.LongArgumentNamePrefix!.Length;
// Space for prefix, long name, separator.
length += argument.Aliases.Sum(alias => longPrefixLength + alias.Length + 1);
// There is one separator too many
length -= 1;
// Length of value description.
if (!argument.IsSwitch)
{
length += 3 + argument.ValueDescription.Length;
}
return length;
}
}