mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-04-16 08:52: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;
|
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>
|
/// <summary>
|
||||||
/// Configures the console.
|
/// Configures the console.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -15,6 +15,11 @@ public interface ICommandAppSettings
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
string? ApplicationVersion { get; set; }
|
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>
|
/// <summary>
|
||||||
/// Gets or sets the <see cref="IAnsiConsole"/>.
|
/// Gets or sets the <see cref="IAnsiConsole"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -53,7 +53,7 @@ internal sealed class CommandExecutor
|
|||||||
if (parsedResult.Tree == null)
|
if (parsedResult.Tree == null)
|
||||||
{
|
{
|
||||||
// Display help.
|
// Display help.
|
||||||
configuration.Settings.Console.SafeRender(HelpWriter.Write(model));
|
configuration.Settings.Console.SafeRender(HelpWriter.Write(model, configuration.Settings.ShowOptionDefaultValues));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,7 +62,7 @@ internal sealed class CommandExecutor
|
|||||||
if (leaf.Command.IsBranch || leaf.ShowHelp)
|
if (leaf.Command.IsBranch || leaf.ShowHelp)
|
||||||
{
|
{
|
||||||
// Branches can't be executed. Show help.
|
// 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;
|
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))
|
if (leaf.Command.IsDefaultCommand && args.Count() == 0 && leaf.Command.Parameters.Any(p => p.Required))
|
||||||
{
|
{
|
||||||
// Display help for default command.
|
// 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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ internal sealed class CommandAppSettings : ICommandAppSettings
|
|||||||
{
|
{
|
||||||
public string? ApplicationName { get; set; }
|
public string? ApplicationName { get; set; }
|
||||||
public string? ApplicationVersion { get; set; }
|
public string? ApplicationVersion { get; set; }
|
||||||
|
public bool ShowOptionDefaultValues { get; set; }
|
||||||
public IAnsiConsole? Console { get; set; }
|
public IAnsiConsole? Console { get; set; }
|
||||||
public ICommandInterceptor? Interceptor { get; set; }
|
public ICommandInterceptor? Interceptor { get; set; }
|
||||||
public ITypeRegistrarFrontend Registrar { get; set; }
|
public ITypeRegistrarFrontend Registrar { get; set; }
|
||||||
@ -22,6 +23,7 @@ internal sealed class CommandAppSettings : ICommandAppSettings
|
|||||||
{
|
{
|
||||||
Registrar = new TypeRegistrar(registrar);
|
Registrar = new TypeRegistrar(registrar);
|
||||||
CaseSensitivity = CaseSensitivity.All;
|
CaseSensitivity = CaseSensitivity.All;
|
||||||
|
ShowOptionDefaultValues = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsTrue(Func<CommandAppSettings, bool> func, string environmentVariableName)
|
public bool IsTrue(Func<CommandAppSettings, bool> func, string environmentVariableName)
|
||||||
|
@ -34,42 +34,45 @@ internal static class HelpWriter
|
|||||||
public string? Value { get; }
|
public string? Value { get; }
|
||||||
public bool? ValueIsOptional { get; }
|
public bool? ValueIsOptional { get; }
|
||||||
public string? Description { 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;
|
Short = @short;
|
||||||
Long = @long;
|
Long = @long;
|
||||||
Value = value;
|
Value = value;
|
||||||
ValueIsOptional = valueIsOptional;
|
ValueIsOptional = valueIsOptional;
|
||||||
Description = description;
|
Description = description;
|
||||||
|
DefaultValue = defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IReadOnlyList<HelpOption> Get(CommandModel model, CommandInfo? command)
|
public static IReadOnlyList<HelpOption> Get(CommandModel model, CommandInfo? command)
|
||||||
{
|
{
|
||||||
var parameters = new List<HelpOption>();
|
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?
|
// At the root and no default command?
|
||||||
if (command == null && model?.DefaultCommand == null)
|
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 =>
|
parameters.AddRange(command?.Parameters.OfType<CommandOption>().Where(o => !o.IsHidden).Select(o =>
|
||||||
new HelpOption(
|
new HelpOption(
|
||||||
o.ShortNames.FirstOrDefault(), o.LongNames.FirstOrDefault(),
|
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>());
|
?? Array.Empty<HelpOption>());
|
||||||
return parameters;
|
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 container = command as ICommandContainer ?? model;
|
||||||
var isDefaultCommand = command?.IsDefaultCommand ?? false;
|
var isDefaultCommand = command?.IsDefaultCommand ?? false;
|
||||||
@ -79,7 +82,7 @@ internal static class HelpWriter
|
|||||||
result.AddRange(GetUsage(model, command));
|
result.AddRange(GetUsage(model, command));
|
||||||
result.AddRange(GetExamples(model, command));
|
result.AddRange(GetExamples(model, command));
|
||||||
result.AddRange(GetArguments(command));
|
result.AddRange(GetArguments(command));
|
||||||
result.AddRange(GetOptions(model, command));
|
result.AddRange(GetOptions(model, command, writeOptionsDefaultValues));
|
||||||
result.AddRange(GetCommands(model, container, isDefaultCommand));
|
result.AddRange(GetCommands(model, container, isDefaultCommand));
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -266,7 +269,7 @@ internal static class HelpWriter
|
|||||||
return result;
|
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.
|
// Collect all options into a single structure.
|
||||||
var parameters = HelpOption.Get(model, command);
|
var parameters = HelpOption.Get(model, command);
|
||||||
@ -282,8 +285,16 @@ internal static class HelpWriter
|
|||||||
new Markup(Environment.NewLine),
|
new Markup(Environment.NewLine),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var helpOptions = parameters.ToArray();
|
||||||
|
var defaultValueColumn = writeDefaultValues && helpOptions.Any(e => e.DefaultValue != null);
|
||||||
|
|
||||||
var grid = new Grid();
|
var grid = new Grid();
|
||||||
grid.AddColumn(new GridColumn { Padding = new Padding(4, 4), NoWrap = true });
|
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) });
|
grid.AddColumn(new GridColumn { Padding = new Padding(0, 0) });
|
||||||
|
|
||||||
static string GetOptionParts(HelpOption option)
|
static string GetOptionParts(HelpOption option)
|
||||||
@ -327,11 +338,22 @@ internal static class HelpWriter
|
|||||||
return builder.ToString();
|
return builder.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var option in parameters.ToArray())
|
if (defaultValueColumn)
|
||||||
{
|
{
|
||||||
grid.AddRow(
|
grid.AddRow(" ", "[lime]DEFAULT[/]", " ");
|
||||||
GetOptionParts(option),
|
}
|
||||||
option.Description?.TrimEnd('.') ?? " ");
|
|
||||||
|
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);
|
result.Add(grid);
|
||||||
|
@ -8,10 +8,11 @@ ARGUMENTS:
|
|||||||
[LEGS] The number of legs
|
[LEGS] The number of legs
|
||||||
|
|
||||||
OPTIONS:
|
OPTIONS:
|
||||||
|
DEFAULT
|
||||||
-h, --help Prints help information
|
-h, --help Prints help information
|
||||||
-a, --alive Indicates whether or not the animal is alive
|
-a, --alive Indicates whether or not the animal is alive
|
||||||
-n, --name <VALUE>
|
-n, --name <VALUE>
|
||||||
--agility <VALUE> The agility between 0 and 100
|
--agility <VALUE> 10 The agility between 0 and 100
|
||||||
|
|
||||||
COMMANDS:
|
COMMANDS:
|
||||||
lion <TEETH> The lion command
|
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
|
[LEGS] The number of legs
|
||||||
|
|
||||||
OPTIONS:
|
OPTIONS:
|
||||||
|
DEFAULT
|
||||||
-h, --help Prints help information
|
-h, --help Prints help information
|
||||||
-a, --alive Indicates whether or not the animal is alive
|
-a, --alive Indicates whether or not the animal is alive
|
||||||
-n, --name <VALUE>
|
-n, --name <VALUE>
|
||||||
--agility <VALUE> The agility between 0 and 100
|
--agility <VALUE> 10 The agility between 0 and 100
|
||||||
-c <CHILDREN> The number of children the lion has
|
-c <CHILDREN> The number of children the lion has
|
@ -12,8 +12,9 @@ ARGUMENTS:
|
|||||||
[LEGS] The number of legs
|
[LEGS] The number of legs
|
||||||
|
|
||||||
OPTIONS:
|
OPTIONS:
|
||||||
|
DEFAULT
|
||||||
-h, --help Prints help information
|
-h, --help Prints help information
|
||||||
-a, --alive Indicates whether or not the animal is alive
|
-a, --alive Indicates whether or not the animal is alive
|
||||||
-n, --name <VALUE>
|
-n, --name <VALUE>
|
||||||
--agility <VALUE> The agility between 0 and 100
|
--agility <VALUE> 10 The agility between 0 and 100
|
||||||
-c <CHILDREN> The number of children the lion has
|
-c <CHILDREN> The number of children the lion has
|
@ -9,8 +9,9 @@ ARGUMENTS:
|
|||||||
[LEGS] The number of legs
|
[LEGS] The number of legs
|
||||||
|
|
||||||
OPTIONS:
|
OPTIONS:
|
||||||
|
DEFAULT
|
||||||
-h, --help Prints help information
|
-h, --help Prints help information
|
||||||
-a, --alive Indicates whether or not the animal is alive
|
-a, --alive Indicates whether or not the animal is alive
|
||||||
-n, --name <VALUE>
|
-n, --name <VALUE>
|
||||||
--agility <VALUE> The agility between 0 and 100
|
--agility <VALUE> 10 The agility between 0 and 100
|
||||||
-c <CHILDREN> The number of children the lion has
|
-c <CHILDREN> The number of children the lion has
|
@ -1,4 +1,4 @@
|
|||||||
DESCRIPTION:
|
DESCRIPTION:
|
||||||
The lion command.
|
The lion command.
|
||||||
|
|
||||||
USAGE:
|
USAGE:
|
||||||
@ -9,10 +9,11 @@ ARGUMENTS:
|
|||||||
[LEGS] The number of legs
|
[LEGS] The number of legs
|
||||||
|
|
||||||
OPTIONS:
|
OPTIONS:
|
||||||
|
DEFAULT
|
||||||
-h, --help Prints help information
|
-h, --help Prints help information
|
||||||
-a, --alive Indicates whether or not the animal is alive
|
-a, --alive Indicates whether or not the animal is alive
|
||||||
-n, --name <VALUE>
|
-n, --name <VALUE>
|
||||||
--agility <VALUE> The agility between 0 and 100
|
--agility <VALUE> 10 The agility between 0 and 100
|
||||||
-c <CHILDREN> The number of children the lion has
|
-c <CHILDREN> The number of children the lion has
|
||||||
|
|
||||||
COMMANDS:
|
COMMANDS:
|
||||||
|
@ -99,6 +99,30 @@ public sealed partial class CommandAppTests
|
|||||||
return Verifier.Verify(result.Output);
|
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]
|
[Fact]
|
||||||
[Expectation("Leaf")]
|
[Expectation("Leaf")]
|
||||||
public Task Should_Output_Leaf_Correctly()
|
public Task Should_Output_Leaf_Correctly()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user