mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-04-14 16:02:50 +08:00
Automatically display default values of options in the help page (#1032)
Fixes #973
This commit is contained in:
parent
4a8a4ab048
commit
3e6e0990c5
@ -40,6 +40,23 @@ public static class ConfiguratorExtensions
|
||||
return configurator;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hides the <c>DEFAULT</c> column that lists default values coming from the
|
||||
/// <see cref="DefaultValueAttribute"/> in the options help text.
|
||||
/// </summary>
|
||||
/// <param name="configurator">The configurator.</param>
|
||||
/// <returns>A configurator that can be used to configure the application further.</returns>
|
||||
public static IConfigurator HideOptionDefaultValues(this IConfigurator configurator)
|
||||
{
|
||||
if (configurator == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configurator));
|
||||
}
|
||||
|
||||
configurator.Settings.ShowOptionDefaultValues = false;
|
||||
return configurator;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures the console.
|
||||
/// </summary>
|
||||
|
@ -15,6 +15,11 @@ public interface ICommandAppSettings
|
||||
/// </summary>
|
||||
string? ApplicationVersion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether any default values for command options are shown in the help text.
|
||||
/// </summary>
|
||||
bool ShowOptionDefaultValues { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="IAnsiConsole"/>.
|
||||
/// </summary>
|
||||
@ -34,11 +39,11 @@ public interface ICommandAppSettings
|
||||
/// <summary>
|
||||
/// Gets or sets case sensitivity.
|
||||
/// </summary>
|
||||
CaseSensitivity CaseSensitivity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether trailing period of a description is trimmed.
|
||||
/// </summary>
|
||||
CaseSensitivity CaseSensitivity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether trailing period of a description is trimmed.
|
||||
/// </summary>
|
||||
bool TrimTrailingPeriod { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
@ -53,7 +53,7 @@ internal sealed class CommandExecutor
|
||||
if (parsedResult.Tree == null)
|
||||
{
|
||||
// Display help.
|
||||
configuration.Settings.Console.SafeRender(HelpWriter.Write(model));
|
||||
configuration.Settings.Console.SafeRender(HelpWriter.Write(model, configuration.Settings.ShowOptionDefaultValues));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -62,7 +62,7 @@ internal sealed class CommandExecutor
|
||||
if (leaf.Command.IsBranch || leaf.ShowHelp)
|
||||
{
|
||||
// Branches can't be executed. Show help.
|
||||
configuration.Settings.Console.SafeRender(HelpWriter.WriteCommand(model, leaf.Command));
|
||||
configuration.Settings.Console.SafeRender(HelpWriter.WriteCommand(model, leaf.Command, configuration.Settings.ShowOptionDefaultValues));
|
||||
return leaf.ShowHelp ? 0 : 1;
|
||||
}
|
||||
|
||||
@ -70,7 +70,7 @@ internal sealed class CommandExecutor
|
||||
if (leaf.Command.IsDefaultCommand && args.Count() == 0 && leaf.Command.Parameters.Any(p => p.Required))
|
||||
{
|
||||
// Display help for default command.
|
||||
configuration.Settings.Console.SafeRender(HelpWriter.WriteCommand(model, leaf.Command));
|
||||
configuration.Settings.Console.SafeRender(HelpWriter.WriteCommand(model, leaf.Command, configuration.Settings.ShowOptionDefaultValues));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ internal sealed class CommandAppSettings : ICommandAppSettings
|
||||
{
|
||||
public string? ApplicationName { get; set; }
|
||||
public string? ApplicationVersion { get; set; }
|
||||
public bool ShowOptionDefaultValues { get; set; }
|
||||
public IAnsiConsole? Console { get; set; }
|
||||
public ICommandInterceptor? Interceptor { get; set; }
|
||||
public ITypeRegistrarFrontend Registrar { get; set; }
|
||||
@ -22,6 +23,7 @@ internal sealed class CommandAppSettings : ICommandAppSettings
|
||||
{
|
||||
Registrar = new TypeRegistrar(registrar);
|
||||
CaseSensitivity = CaseSensitivity.All;
|
||||
ShowOptionDefaultValues = true;
|
||||
}
|
||||
|
||||
public bool IsTrue(Func<CommandAppSettings, bool> func, string environmentVariableName)
|
||||
|
@ -34,42 +34,45 @@ internal static class HelpWriter
|
||||
public string? Value { get; }
|
||||
public bool? ValueIsOptional { get; }
|
||||
public string? Description { get; }
|
||||
public object? DefaultValue { get; }
|
||||
|
||||
public HelpOption(string? @short, string? @long, string? @value, bool? valueIsOptional, string? description)
|
||||
public HelpOption(string? @short, string? @long, string? @value, bool? valueIsOptional, string? description, object? defaultValue)
|
||||
{
|
||||
Short = @short;
|
||||
Long = @long;
|
||||
Value = value;
|
||||
ValueIsOptional = valueIsOptional;
|
||||
Description = description;
|
||||
DefaultValue = defaultValue;
|
||||
}
|
||||
|
||||
public static IReadOnlyList<HelpOption> Get(CommandModel model, CommandInfo? command)
|
||||
{
|
||||
var parameters = new List<HelpOption>();
|
||||
parameters.Add(new HelpOption("h", "help", null, null, "Prints help information"));
|
||||
parameters.Add(new HelpOption("h", "help", null, null, "Prints help information", null));
|
||||
|
||||
// At the root and no default command?
|
||||
if (command == null && model?.DefaultCommand == null)
|
||||
{
|
||||
parameters.Add(new HelpOption("v", "version", null, null, "Prints version information"));
|
||||
parameters.Add(new HelpOption("v", "version", null, null, "Prints version information", null));
|
||||
}
|
||||
|
||||
parameters.AddRange(command?.Parameters.OfType<CommandOption>().Where(o => !o.IsHidden).Select(o =>
|
||||
new HelpOption(
|
||||
o.ShortNames.FirstOrDefault(), o.LongNames.FirstOrDefault(),
|
||||
o.ValueName, o.ValueIsOptional, o.Description))
|
||||
o.ValueName, o.ValueIsOptional, o.Description,
|
||||
o.ParameterKind == ParameterKind.Flag && o.DefaultValue?.Value is false ? null : o.DefaultValue?.Value))
|
||||
?? Array.Empty<HelpOption>());
|
||||
return parameters;
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<IRenderable> Write(CommandModel model)
|
||||
public static IEnumerable<IRenderable> Write(CommandModel model, bool writeOptionsDefaultValues)
|
||||
{
|
||||
return WriteCommand(model, null);
|
||||
return WriteCommand(model, null, writeOptionsDefaultValues);
|
||||
}
|
||||
|
||||
public static IEnumerable<IRenderable> WriteCommand(CommandModel model, CommandInfo? command)
|
||||
public static IEnumerable<IRenderable> WriteCommand(CommandModel model, CommandInfo? command, bool writeOptionsDefaultValues)
|
||||
{
|
||||
var container = command as ICommandContainer ?? model;
|
||||
var isDefaultCommand = command?.IsDefaultCommand ?? false;
|
||||
@ -79,7 +82,7 @@ internal static class HelpWriter
|
||||
result.AddRange(GetUsage(model, command));
|
||||
result.AddRange(GetExamples(model, command));
|
||||
result.AddRange(GetArguments(command));
|
||||
result.AddRange(GetOptions(model, command));
|
||||
result.AddRange(GetOptions(model, command, writeOptionsDefaultValues));
|
||||
result.AddRange(GetCommands(model, container, isDefaultCommand));
|
||||
|
||||
return result;
|
||||
@ -266,7 +269,7 @@ internal static class HelpWriter
|
||||
return result;
|
||||
}
|
||||
|
||||
private static IEnumerable<IRenderable> GetOptions(CommandModel model, CommandInfo? command)
|
||||
private static IEnumerable<IRenderable> GetOptions(CommandModel model, CommandInfo? command, bool writeDefaultValues)
|
||||
{
|
||||
// Collect all options into a single structure.
|
||||
var parameters = HelpOption.Get(model, command);
|
||||
@ -282,8 +285,16 @@ internal static class HelpWriter
|
||||
new Markup(Environment.NewLine),
|
||||
};
|
||||
|
||||
var helpOptions = parameters.ToArray();
|
||||
var defaultValueColumn = writeDefaultValues && helpOptions.Any(e => e.DefaultValue != null);
|
||||
|
||||
var grid = new Grid();
|
||||
grid.AddColumn(new GridColumn { Padding = new Padding(4, 4), NoWrap = true });
|
||||
if (defaultValueColumn)
|
||||
{
|
||||
grid.AddColumn(new GridColumn { Padding = new Padding(0, 0, 4, 0) });
|
||||
}
|
||||
|
||||
grid.AddColumn(new GridColumn { Padding = new Padding(0, 0) });
|
||||
|
||||
static string GetOptionParts(HelpOption option)
|
||||
@ -327,11 +338,22 @@ internal static class HelpWriter
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
foreach (var option in parameters.ToArray())
|
||||
if (defaultValueColumn)
|
||||
{
|
||||
grid.AddRow(
|
||||
GetOptionParts(option),
|
||||
option.Description?.TrimEnd('.') ?? " ");
|
||||
grid.AddRow(" ", "[lime]DEFAULT[/]", " ");
|
||||
}
|
||||
|
||||
foreach (var option in helpOptions)
|
||||
{
|
||||
var columns = new List<string> { GetOptionParts(option) };
|
||||
if (defaultValueColumn)
|
||||
{
|
||||
columns.Add(option.DefaultValue == null ? " " : $"[bold]{option.DefaultValue.ToString().EscapeMarkup()}[/]");
|
||||
}
|
||||
|
||||
columns.Add(option.Description?.TrimEnd('.') ?? " ");
|
||||
|
||||
grid.AddRow(columns.ToArray());
|
||||
}
|
||||
|
||||
result.Add(grid);
|
||||
@ -373,19 +395,19 @@ internal static class HelpWriter
|
||||
{
|
||||
arguments.Style("silver", $"<{argument.Name.EscapeMarkup()}>");
|
||||
arguments.Space();
|
||||
}
|
||||
}
|
||||
|
||||
if (model.TrimTrailingPeriod)
|
||||
{
|
||||
if (model.TrimTrailingPeriod)
|
||||
{
|
||||
grid.AddRow(
|
||||
arguments.ToString().TrimEnd(),
|
||||
child.Description?.TrimEnd('.') ?? " ");
|
||||
}
|
||||
else
|
||||
{
|
||||
grid.AddRow(
|
||||
arguments.ToString().TrimEnd(),
|
||||
child.Description ?? " ");
|
||||
}
|
||||
else
|
||||
{
|
||||
grid.AddRow(
|
||||
arguments.ToString().TrimEnd(),
|
||||
child.Description ?? " ");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,10 +8,11 @@ ARGUMENTS:
|
||||
[LEGS] The number of legs
|
||||
|
||||
OPTIONS:
|
||||
-h, --help Prints help information
|
||||
-a, --alive Indicates whether or not the animal is alive
|
||||
DEFAULT
|
||||
-h, --help Prints help information
|
||||
-a, --alive Indicates whether or not the animal is alive
|
||||
-n, --name <VALUE>
|
||||
--agility <VALUE> The agility between 0 and 100
|
||||
--agility <VALUE> 10 The agility between 0 and 100
|
||||
|
||||
COMMANDS:
|
||||
lion <TEETH> The lion command
|
@ -0,0 +1,17 @@
|
||||
DESCRIPTION:
|
||||
Contains settings for a cat.
|
||||
|
||||
USAGE:
|
||||
myapp cat [LEGS] [OPTIONS] <COMMAND>
|
||||
|
||||
ARGUMENTS:
|
||||
[LEGS] The number of legs
|
||||
|
||||
OPTIONS:
|
||||
-h, --help Prints help information
|
||||
-a, --alive Indicates whether or not the animal is alive
|
||||
-n, --name <VALUE>
|
||||
--agility <VALUE> The agility between 0 and 100
|
||||
|
||||
COMMANDS:
|
||||
lion <TEETH> The lion command
|
@ -9,8 +9,9 @@ ARGUMENTS:
|
||||
[LEGS] The number of legs
|
||||
|
||||
OPTIONS:
|
||||
-h, --help Prints help information
|
||||
-a, --alive Indicates whether or not the animal is alive
|
||||
DEFAULT
|
||||
-h, --help Prints help information
|
||||
-a, --alive Indicates whether or not the animal is alive
|
||||
-n, --name <VALUE>
|
||||
--agility <VALUE> The agility between 0 and 100
|
||||
-c <CHILDREN> The number of children the lion has
|
||||
--agility <VALUE> 10 The agility between 0 and 100
|
||||
-c <CHILDREN> The number of children the lion has
|
@ -12,8 +12,9 @@ ARGUMENTS:
|
||||
[LEGS] The number of legs
|
||||
|
||||
OPTIONS:
|
||||
-h, --help Prints help information
|
||||
-a, --alive Indicates whether or not the animal is alive
|
||||
DEFAULT
|
||||
-h, --help Prints help information
|
||||
-a, --alive Indicates whether or not the animal is alive
|
||||
-n, --name <VALUE>
|
||||
--agility <VALUE> The agility between 0 and 100
|
||||
-c <CHILDREN> The number of children the lion has
|
||||
--agility <VALUE> 10 The agility between 0 and 100
|
||||
-c <CHILDREN> The number of children the lion has
|
@ -1,16 +1,17 @@
|
||||
DESCRIPTION:
|
||||
The lion command.
|
||||
|
||||
USAGE:
|
||||
myapp <TEETH> [LEGS] [OPTIONS]
|
||||
|
||||
ARGUMENTS:
|
||||
<TEETH> The number of teeth the lion has
|
||||
[LEGS] The number of legs
|
||||
|
||||
OPTIONS:
|
||||
-h, --help Prints help information
|
||||
-a, --alive Indicates whether or not the animal is alive
|
||||
-n, --name <VALUE>
|
||||
--agility <VALUE> The agility between 0 and 100
|
||||
-c <CHILDREN> The number of children the lion has
|
||||
DESCRIPTION:
|
||||
The lion command.
|
||||
|
||||
USAGE:
|
||||
myapp <TEETH> [LEGS] [OPTIONS]
|
||||
|
||||
ARGUMENTS:
|
||||
<TEETH> The number of teeth the lion has
|
||||
[LEGS] The number of legs
|
||||
|
||||
OPTIONS:
|
||||
DEFAULT
|
||||
-h, --help Prints help information
|
||||
-a, --alive Indicates whether or not the animal is alive
|
||||
-n, --name <VALUE>
|
||||
--agility <VALUE> 10 The agility between 0 and 100
|
||||
-c <CHILDREN> The number of children the lion has
|
@ -1,4 +1,4 @@
|
||||
DESCRIPTION:
|
||||
DESCRIPTION:
|
||||
The lion command.
|
||||
|
||||
USAGE:
|
||||
@ -9,11 +9,12 @@ ARGUMENTS:
|
||||
[LEGS] The number of legs
|
||||
|
||||
OPTIONS:
|
||||
-h, --help Prints help information
|
||||
-a, --alive Indicates whether or not the animal is alive
|
||||
DEFAULT
|
||||
-h, --help Prints help information
|
||||
-a, --alive Indicates whether or not the animal is alive
|
||||
-n, --name <VALUE>
|
||||
--agility <VALUE> The agility between 0 and 100
|
||||
-c <CHILDREN> The number of children the lion has
|
||||
--agility <VALUE> 10 The agility between 0 and 100
|
||||
-c <CHILDREN> The number of children the lion has
|
||||
|
||||
COMMANDS:
|
||||
giraffe <LENGTH> The giraffe command
|
@ -99,6 +99,30 @@ public sealed partial class CommandAppTests
|
||||
return Verifier.Verify(result.Output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Expectation("Command_Hide_Default")]
|
||||
public Task Should_Not_Print_Default_Column()
|
||||
{
|
||||
// Given
|
||||
var fixture = new CommandAppTester();
|
||||
fixture.Configure(configurator =>
|
||||
{
|
||||
configurator.SetApplicationName("myapp");
|
||||
configurator.AddBranch<CatSettings>("cat", animal =>
|
||||
{
|
||||
animal.SetDescription("Contains settings for a cat.");
|
||||
animal.AddCommand<LionCommand>("lion");
|
||||
});
|
||||
configurator.HideOptionDefaultValues();
|
||||
});
|
||||
|
||||
// When
|
||||
var result = fixture.Run("cat", "--help");
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(result.Output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Expectation("Leaf")]
|
||||
public Task Should_Output_Leaf_Correctly()
|
||||
|
Loading…
x
Reference in New Issue
Block a user