usage method
Return a string telling the user how to use your application from the command line.
Implementation
String usage() {
List<String?> lines = [];
if (_isNotNull(_app?.description)) {
lines.add(_app!.description);
lines.add('');
}
List<String> helpKeys = [];
List<Group?> helpGroups = [];
List<List<String>> helpDescriptions = [];
final arguments =
_mirrorParameterPairs.where((v) => _isFalse(v.argument is Command));
final commands = _mirrorParameterPairs.where((v) => v.argument is Command);
if (arguments.isNotEmpty) {
for (var mpp in arguments) {
List<String?> keys = [];
keys.addAll(mpp.keys(_app).map((v) => v!.startsWith('-') ? v : '--$v'));
helpKeys.add(keys.join(', '));
helpGroups.add(mpp.group);
List<String> helpLines = [mpp.argument.help ?? 'no help available'];
if (mpp.argument.isRequired ?? false) {
helpLines.add('[REQUIRED]');
}
String? envVar = mpp.argument.environmentVariable;
if (_isNotBlank(envVar)) {
helpLines.add('[Environment Variable: \$$envVar]');
}
helpLines.addAll(mpp.argument.additionalHelpLines);
helpDescriptions.add(helpLines);
}
}
const lineWidth = 78; // TODO: Can we get this from the terminal?
const lineIndent = 2;
const maxKeyLenAllowed = 25; // Will include indent
final linePrefix = ' ' * lineIndent;
final maxKeyLen = helpKeys.fold<int>(
0,
(a, b) => (b.length + lineIndent) > a &&
(b.length + lineIndent) < maxKeyLenAllowed
? (b.length + lineIndent)
: a);
final keyPadWidth = min(maxKeyLenAllowed, maxKeyLen + 1);
{
void trailingHelp(Group? group) {
if (_isNotNull(group?.afterHelp)) {
lines.add('');
lines.add(indent(
hardWrap(group!.afterHelp!, lineWidth - lineIndent),
lineIndent,
));
}
}
Group? currentGroup;
for (var i = 0; i < helpKeys.length; i++) {
final thisGroup = helpGroups[i];
if (thisGroup != currentGroup) {
trailingHelp(currentGroup);
if (_isNotNull(currentGroup)) {
lines.add('');
}
lines.add(thisGroup!.name);
if (_isNotNull(thisGroup.beforeHelp)) {
lines.add(indent(
hardWrap(thisGroup.beforeHelp!, lineWidth - lineIndent),
lineIndent));
lines.add('');
}
}
var keyDisplay =
linePrefix + helpKeys[i].padRight(keyPadWidth - lineIndent);
var thisHelpDescriptions = helpDescriptions[i].join('\n');
thisHelpDescriptions =
hardWrap(thisHelpDescriptions, lineWidth - keyPadWidth);
thisHelpDescriptions = indent(thisHelpDescriptions, keyPadWidth);
if (keyDisplay.length == keyPadWidth) {
thisHelpDescriptions =
thisHelpDescriptions.replaceRange(0, keyPadWidth, keyDisplay);
} else {
lines.add(keyDisplay);
}
lines.add(thisHelpDescriptions);
currentGroup = helpGroups[i] ?? currentGroup;
}
trailingHelp(currentGroup);
}
if (commands.isNotEmpty) {
lines.add('');
lines.add('COMMANDS');
final maxCommandLength =
commands.fold(0, (int a, b) => max(a, b.displayKey!.length));
for (var mpp in commands) {
final key = mpp.displayKey!.padRight(maxCommandLength + 1);
final help = mpp.argument.help ?? '';
final displayString = '$key $help';
lines.add(indent(hardWrap(displayString, lineWidth), 2));
}
}
if (_isNotNull(_app?.extendedHelp)) {
for (final eh in _app!.extendedHelp!) {
if (_isNull(eh.help)) {
throw StateError('Help.help must be set');
}
lines.add('');
if (_isNotNull(eh.header)) {
lines.add(hardWrap(eh.header!, lineWidth));
lines.add(
indent(hardWrap(eh.help!, lineWidth - lineIndent), lineIndent));
} else {
lines.add(hardWrap(eh.help!, lineWidth));
}
}
}
return lines.join('\n');
}