mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-07-04 19:58:14 +08:00
Update help output for required options
This commit is contained in:

committed by
Patrik Svensson

parent
67c3909bbb
commit
e4b5b56d93
@ -53,7 +53,7 @@ public class HelpProvider : IHelpProvider
|
|||||||
{
|
{
|
||||||
var arguments = new List<HelpArgument>();
|
var arguments = new List<HelpArgument>();
|
||||||
arguments.AddRange(command?.Parameters?.OfType<ICommandArgument>()?.Select(
|
arguments.AddRange(command?.Parameters?.OfType<ICommandArgument>()?.Select(
|
||||||
x => new HelpArgument(x.Value, x.Position, x.Required, x.Description))
|
x => new HelpArgument(x.Value, x.Position, x.IsRequired, x.Description))
|
||||||
?? Array.Empty<HelpArgument>());
|
?? Array.Empty<HelpArgument>());
|
||||||
return arguments;
|
return arguments;
|
||||||
}
|
}
|
||||||
@ -65,15 +65,20 @@ public class HelpProvider : IHelpProvider
|
|||||||
public string? Long { get; }
|
public string? Long { get; }
|
||||||
public string? Value { get; }
|
public string? Value { get; }
|
||||||
public bool? ValueIsOptional { get; }
|
public bool? ValueIsOptional { get; }
|
||||||
|
public bool IsRequired { get; }
|
||||||
public string? Description { get; }
|
public string? Description { get; }
|
||||||
public object? DefaultValue { get; }
|
public object? DefaultValue { get; }
|
||||||
|
|
||||||
private HelpOption(string? @short, string? @long, string? @value, bool? valueIsOptional, string? description, object? defaultValue)
|
private HelpOption(
|
||||||
|
string? @short, string? @long, string? @value,
|
||||||
|
bool? valueIsOptional, bool isRequired,
|
||||||
|
string? description, object? defaultValue)
|
||||||
{
|
{
|
||||||
Short = @short;
|
Short = @short;
|
||||||
Long = @long;
|
Long = @long;
|
||||||
Value = value;
|
Value = value;
|
||||||
ValueIsOptional = valueIsOptional;
|
ValueIsOptional = valueIsOptional;
|
||||||
|
IsRequired = isRequired;
|
||||||
Description = description;
|
Description = description;
|
||||||
DefaultValue = defaultValue;
|
DefaultValue = defaultValue;
|
||||||
}
|
}
|
||||||
@ -85,7 +90,8 @@ public class HelpProvider : IHelpProvider
|
|||||||
{
|
{
|
||||||
var parameters = new List<HelpOption>
|
var parameters = new List<HelpOption>
|
||||||
{
|
{
|
||||||
new HelpOption("h", "help", null, null, resources.PrintHelpDescription, null),
|
new HelpOption("h", "help", null, null, false,
|
||||||
|
resources.PrintHelpDescription, null),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Version information applies to the entire CLI application.
|
// Version information applies to the entire CLI application.
|
||||||
@ -107,7 +113,8 @@ public class HelpProvider : IHelpProvider
|
|||||||
// Only show the version option if there is an application version set.
|
// Only show the version option if there is an application version set.
|
||||||
if (model.ApplicationVersion != null)
|
if (model.ApplicationVersion != null)
|
||||||
{
|
{
|
||||||
parameters.Add(new HelpOption("v", "version", null, null, resources.PrintVersionDescription, null));
|
parameters.Add(new HelpOption("v", "version", null, null, false,
|
||||||
|
resources.PrintVersionDescription, null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -115,7 +122,7 @@ public class HelpProvider : IHelpProvider
|
|||||||
parameters.AddRange(command?.Parameters.OfType<ICommandOption>().Where(o => !o.IsHidden).Select(o =>
|
parameters.AddRange(command?.Parameters.OfType<ICommandOption>().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.IsRequired, o.Description,
|
||||||
o.IsFlag && o.DefaultValue?.Value is false ? null : o.DefaultValue?.Value))
|
o.IsFlag && o.DefaultValue?.Value is false ? null : o.DefaultValue?.Value))
|
||||||
?? Array.Empty<HelpOption>());
|
?? Array.Empty<HelpOption>());
|
||||||
return parameters;
|
return parameters;
|
||||||
@ -215,7 +222,8 @@ public class HelpProvider : IHelpProvider
|
|||||||
{
|
{
|
||||||
if (isCurrent)
|
if (isCurrent)
|
||||||
{
|
{
|
||||||
parameters.Add(NewComposer().Style(helpStyles?.Usage?.CurrentCommand ?? Style.Plain, $"{current.Name}"));
|
parameters.Add(NewComposer().Style(helpStyles?.Usage?.CurrentCommand ?? Style.Plain,
|
||||||
|
$"{current.Name}"));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -228,38 +236,44 @@ public class HelpProvider : IHelpProvider
|
|||||||
if (isCurrent)
|
if (isCurrent)
|
||||||
{
|
{
|
||||||
foreach (var argument in current.Parameters.OfType<ICommandArgument>()
|
foreach (var argument in current.Parameters.OfType<ICommandArgument>()
|
||||||
.Where(a => a.Required).OrderBy(a => a.Position).ToArray())
|
.Where(a => a.IsRequired).OrderBy(a => a.Position).ToArray())
|
||||||
{
|
{
|
||||||
parameters.Add(NewComposer().Style(helpStyles?.Usage?.RequiredArgument ?? Style.Plain, $"<{argument.Value}>"));
|
parameters.Add(NewComposer().Style(helpStyles?.Usage?.RequiredArgument ?? Style.Plain,
|
||||||
|
$"<{argument.Value}>"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var optionalArguments = current.Parameters.OfType<ICommandArgument>().Where(x => !x.Required).ToArray();
|
var optionalArguments = current.Parameters.OfType<ICommandArgument>().Where(x => !x.IsRequired)
|
||||||
|
.ToArray();
|
||||||
if (optionalArguments.Length > 0 || !isCurrent)
|
if (optionalArguments.Length > 0 || !isCurrent)
|
||||||
{
|
{
|
||||||
foreach (var optionalArgument in optionalArguments)
|
foreach (var optionalArgument in optionalArguments)
|
||||||
{
|
{
|
||||||
parameters.Add(NewComposer().Style(helpStyles?.Usage?.OptionalArgument ?? Style.Plain, $"[{optionalArgument.Value}]"));
|
parameters.Add(NewComposer().Style(helpStyles?.Usage?.OptionalArgument ?? Style.Plain,
|
||||||
|
$"[{optionalArgument.Value}]"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isCurrent)
|
if (isCurrent)
|
||||||
{
|
{
|
||||||
parameters.Add(NewComposer().Style(helpStyles?.Usage?.Options ?? Style.Plain, $"[{resources.Options}]"));
|
parameters.Add(NewComposer()
|
||||||
|
.Style(helpStyles?.Usage?.Options ?? Style.Plain, $"[{resources.Options}]"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command.IsBranch && command.DefaultCommand == null)
|
if (command.IsBranch && command.DefaultCommand == null)
|
||||||
{
|
{
|
||||||
// The user must specify the command
|
// The user must specify the command
|
||||||
parameters.Add(NewComposer().Style(helpStyles?.Usage?.Command ?? Style.Plain, $"<{resources.Command}>"));
|
parameters.Add(NewComposer()
|
||||||
|
.Style(helpStyles?.Usage?.Command ?? Style.Plain, $"<{resources.Command}>"));
|
||||||
}
|
}
|
||||||
else if (command.IsBranch && command.DefaultCommand != null && command.Commands.Count > 0)
|
else if (command.IsBranch && command.DefaultCommand != null && command.Commands.Count > 0)
|
||||||
{
|
{
|
||||||
// We are on a branch with a default command
|
// We are on a branch with a default command
|
||||||
// The user can optionally specify the command
|
// The user can optionally specify the command
|
||||||
parameters.Add(NewComposer().Style(helpStyles?.Usage?.Command ?? Style.Plain, $"[{resources.Command}]"));
|
parameters.Add(NewComposer()
|
||||||
|
.Style(helpStyles?.Usage?.Command ?? Style.Plain, $"[{resources.Command}]"));
|
||||||
}
|
}
|
||||||
else if (command.IsDefaultCommand)
|
else if (command.IsDefaultCommand)
|
||||||
{
|
{
|
||||||
@ -269,7 +283,8 @@ public class HelpProvider : IHelpProvider
|
|||||||
{
|
{
|
||||||
// Commands other than the default are present
|
// Commands other than the default are present
|
||||||
// So make these optional in the usage statement
|
// So make these optional in the usage statement
|
||||||
parameters.Add(NewComposer().Style(helpStyles?.Usage?.Command ?? Style.Plain, $"[{resources.Command}]"));
|
parameters.Add(NewComposer()
|
||||||
|
.Style(helpStyles?.Usage?.Command ?? Style.Plain, $"[{resources.Command}]"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -338,7 +353,8 @@ public class HelpProvider : IHelpProvider
|
|||||||
for (var index = 0; index < Math.Min(maxExamples, examples.Count); index++)
|
for (var index = 0; index < Math.Min(maxExamples, examples.Count); index++)
|
||||||
{
|
{
|
||||||
var args = string.Join(" ", examples[index]);
|
var args = string.Join(" ", examples[index]);
|
||||||
composer.Tab().Text(model.ApplicationName).Space().Style(helpStyles?.Examples?.Arguments ?? Style.Plain, args);
|
composer.Tab().Text(model.ApplicationName).Space()
|
||||||
|
.Style(helpStyles?.Examples?.Arguments ?? Style.Plain, args);
|
||||||
composer.LineBreak();
|
composer.LineBreak();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -364,7 +380,8 @@ public class HelpProvider : IHelpProvider
|
|||||||
|
|
||||||
var result = new List<IRenderable>
|
var result = new List<IRenderable>
|
||||||
{
|
{
|
||||||
NewComposer().LineBreak().Style(helpStyles?.Arguments?.Header ?? Style.Plain, $"{resources.Arguments}:").LineBreak(),
|
NewComposer().LineBreak().Style(helpStyles?.Arguments?.Header ?? Style.Plain, $"{resources.Arguments}:")
|
||||||
|
.LineBreak(),
|
||||||
};
|
};
|
||||||
|
|
||||||
var grid = new Grid();
|
var grid = new Grid();
|
||||||
@ -407,7 +424,8 @@ public class HelpProvider : IHelpProvider
|
|||||||
|
|
||||||
var result = new List<IRenderable>
|
var result = new List<IRenderable>
|
||||||
{
|
{
|
||||||
NewComposer().LineBreak().Style(helpStyles?.Options?.Header ?? Style.Plain, $"{resources.Options}:").LineBreak(),
|
NewComposer().LineBreak().Style(helpStyles?.Options?.Header ?? Style.Plain, $"{resources.Options}:")
|
||||||
|
.LineBreak(),
|
||||||
};
|
};
|
||||||
|
|
||||||
var helpOptions = parameters.ToArray();
|
var helpOptions = parameters.ToArray();
|
||||||
@ -439,7 +457,15 @@ public class HelpProvider : IHelpProvider
|
|||||||
columns.Add(GetDefaultValueForOption(option.DefaultValue));
|
columns.Add(GetDefaultValueForOption(option.DefaultValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
columns.Add(NewComposer().Text(NormalizeDescription(option.Description)));
|
var description = option.Description;
|
||||||
|
if (option.IsRequired)
|
||||||
|
{
|
||||||
|
description = string.IsNullOrWhiteSpace(description)
|
||||||
|
? "[i]Required[/]"
|
||||||
|
: description.TrimEnd('.') + ". [i]Required[/]";
|
||||||
|
}
|
||||||
|
|
||||||
|
columns.Add(NewComposer().Text(NormalizeDescription(description)));
|
||||||
|
|
||||||
grid.AddRow(columns.ToArray());
|
grid.AddRow(columns.ToArray());
|
||||||
}
|
}
|
||||||
@ -470,7 +496,8 @@ public class HelpProvider : IHelpProvider
|
|||||||
|
|
||||||
var result = new List<IRenderable>
|
var result = new List<IRenderable>
|
||||||
{
|
{
|
||||||
NewComposer().LineBreak().Style(helpStyles?.Commands?.Header ?? Style.Plain, $"{resources.Commands}:").LineBreak(),
|
NewComposer().LineBreak().Style(helpStyles?.Commands?.Header ?? Style.Plain, $"{resources.Commands}:")
|
||||||
|
.LineBreak(),
|
||||||
};
|
};
|
||||||
|
|
||||||
var grid = new Grid();
|
var grid = new Grid();
|
||||||
@ -546,11 +573,11 @@ public class HelpProvider : IHelpProvider
|
|||||||
composer.Text(" ");
|
composer.Text(" ");
|
||||||
if (option.ValueIsOptional ?? false)
|
if (option.ValueIsOptional ?? false)
|
||||||
{
|
{
|
||||||
composer.Style(helpStyles?.Options?.OptionalOption ?? Style.Plain, $"[{option.Value}]");
|
composer.Style(helpStyles?.Options?.OptionalOptionValue ?? Style.Plain, $"[{option.Value}]");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
composer.Style(helpStyles?.Options?.RequiredOption ?? Style.Plain, $"<{option.Value}>");
|
composer.Style(helpStyles?.Options?.RequiredOptionValue ?? Style.Plain, $"<{option.Value}>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -564,8 +591,12 @@ public class HelpProvider : IHelpProvider
|
|||||||
null => NewComposer().Text(" "),
|
null => NewComposer().Text(" "),
|
||||||
"" => NewComposer().Text(" "),
|
"" => NewComposer().Text(" "),
|
||||||
Array { Length: 0 } => NewComposer().Text(" "),
|
Array { Length: 0 } => NewComposer().Text(" "),
|
||||||
Array array => NewComposer().Join(", ", array.Cast<object>().Select(o => NewComposer().Style(helpStyles?.Options?.DefaultValue ?? Style.Plain, o.ToString() ?? string.Empty))),
|
Array array => NewComposer().Join(", ",
|
||||||
_ => NewComposer().Style(helpStyles?.Options?.DefaultValue ?? Style.Plain, defaultValue?.ToString() ?? string.Empty),
|
array.Cast<object>().Select(o =>
|
||||||
|
NewComposer().Style(helpStyles?.Options?.DefaultValue ?? Style.Plain,
|
||||||
|
o.ToString() ?? string.Empty))),
|
||||||
|
_ => NewComposer().Style(helpStyles?.Options?.DefaultValue ?? Style.Plain,
|
||||||
|
defaultValue?.ToString() ?? string.Empty),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,8 +76,8 @@ public sealed class HelpProviderStyle
|
|||||||
Header = "yellow",
|
Header = "yellow",
|
||||||
DefaultValueHeader = "lime",
|
DefaultValueHeader = "lime",
|
||||||
DefaultValue = "bold",
|
DefaultValue = "bold",
|
||||||
RequiredOption = "silver",
|
RequiredOptionValue = "silver",
|
||||||
OptionalOption = "grey",
|
OptionalOptionValue = "grey",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -212,8 +212,13 @@ public sealed class OptionStyle
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public Style? RequiredOption { get; set; }
|
public Style? RequiredOption { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the style for required option values.
|
||||||
|
/// </summary>
|
||||||
|
public Style? RequiredOptionValue { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the style for optional options.
|
/// Gets or sets the style for optional options.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Style? OptionalOption { get; set; }
|
public Style? OptionalOptionValue { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ public interface ICommandParameter
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value indicating whether the parameter is required.
|
/// Gets a value indicating whether the parameter is required.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool Required { get; }
|
bool IsRequired { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the description of the parameter.
|
/// Gets the description of the parameter.
|
||||||
|
@ -1,6 +1,13 @@
|
|||||||
namespace Spectre.Console.Tests.Data;
|
namespace Spectre.Console.Tests.Data;
|
||||||
|
|
||||||
public class RequiredOptionsSettings : CommandSettings
|
public class RequiredOptionsSettings : CommandSettings
|
||||||
|
{
|
||||||
|
[CommandOption("--foo <VALUE>", true)]
|
||||||
|
[Description("Foos the bars")]
|
||||||
|
public string Foo { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RequiredOptionsWithoutDescriptionSettings : CommandSettings
|
||||||
{
|
{
|
||||||
[CommandOption("--foo <VALUE>", true)]
|
[CommandOption("--foo <VALUE>", true)]
|
||||||
public string Foo { get; set; }
|
public string Foo { get; set; }
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
USAGE:
|
||||||
|
myapp [OPTIONS]
|
||||||
|
|
||||||
|
OPTIONS:
|
||||||
|
-h, --help Prints help information
|
||||||
|
--foo <VALUE> Foos the bars. Required
|
@ -0,0 +1,6 @@
|
|||||||
|
USAGE:
|
||||||
|
myapp [OPTIONS]
|
||||||
|
|
||||||
|
OPTIONS:
|
||||||
|
-h, --help Prints help information
|
||||||
|
--foo <VALUE> Foos the bars. Required
|
@ -0,0 +1,6 @@
|
|||||||
|
USAGE:
|
||||||
|
myapp [OPTIONS]
|
||||||
|
|
||||||
|
OPTIONS:
|
||||||
|
-h, --help Prints help information
|
||||||
|
--foo <VALUE> Required
|
@ -0,0 +1,6 @@
|
|||||||
|
USAGE:
|
||||||
|
myapp [OPTIONS]
|
||||||
|
|
||||||
|
OPTIONS:
|
||||||
|
-h, --help Prints help information
|
||||||
|
--foo <VALUE> Required
|
@ -1061,5 +1061,43 @@ public sealed partial class CommandAppTests
|
|||||||
// Then
|
// Then
|
||||||
return Verifier.Verify(result.Output);
|
return Verifier.Verify(result.Output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Expectation("Required_Options")]
|
||||||
|
public Task Should_Show_Required_Options()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var fixture = new CommandAppTester();
|
||||||
|
fixture.SetDefaultCommand<GenericCommand<RequiredOptionsSettings>>();
|
||||||
|
fixture.Configure(configurator =>
|
||||||
|
{
|
||||||
|
configurator.SetApplicationName("myapp");
|
||||||
|
});
|
||||||
|
|
||||||
|
// When
|
||||||
|
var result = fixture.Run("--help");
|
||||||
|
|
||||||
|
// Then
|
||||||
|
return Verifier.Verify(result.Output);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Expectation("Required_Options_No_Description")]
|
||||||
|
public Task Should_Show_Required_Options_Without_Description()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var fixture = new CommandAppTester();
|
||||||
|
fixture.SetDefaultCommand<GenericCommand<RequiredOptionsWithoutDescriptionSettings>>();
|
||||||
|
fixture.Configure(configurator =>
|
||||||
|
{
|
||||||
|
configurator.SetApplicationName("myapp");
|
||||||
|
});
|
||||||
|
|
||||||
|
// When
|
||||||
|
var result = fixture.Run("--help");
|
||||||
|
|
||||||
|
// Then
|
||||||
|
return Verifier.Verify(result.Output);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
namespace Spectre.Console.Cli.Tests.Unit.Testing;
|
namespace Spectre.Console.Tests.Unit.Cli;
|
||||||
|
|
||||||
public sealed class InteractiveCommandTests
|
public sealed class InteractiveCommandTests
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user