Fix argument order in help

Closes #188
This commit is contained in:
Patrik Svensson 2021-01-01 13:31:54 +01:00 committed by Patrik Svensson
parent 5cf41725a5
commit b81739567b
7 changed files with 120 additions and 55 deletions

View File

@ -0,0 +1,22 @@
using Spectre.Console.Cli;
namespace Spectre.Console.Tests.Data
{
public sealed class ArgumentOrderSettings : CommandSettings
{
[CommandArgument(0, "[QUX]")]
public int Qux { get; set; }
[CommandArgument(3, "<CORGI>")]
public int Corgi { get; set; }
[CommandArgument(1, "<BAR>")]
public int Bar { get; set; }
[CommandArgument(2, "<BAZ>")]
public int Baz { get; set; }
[CommandArgument(0, "<FOO>")]
public int Foo { get; set; }
}
}

View File

@ -0,0 +1,12 @@
USAGE:
myapp <FOO> <BAR> <BAZ> <CORGI> [QUX] [OPTIONS]
ARGUMENTS:
<FOO>
<BAR>
<BAZ>
<CORGI>
[QUX]
OPTIONS:
-h, --help Prints help information

View File

@ -2,8 +2,8 @@ USAGE:
myapp <TEETH> [LEGS] [OPTIONS] myapp <TEETH> [LEGS] [OPTIONS]
ARGUMENTS: ARGUMENTS:
[LEGS] The number of legs
<TEETH> The number of teeth the lion has <TEETH> The number of teeth the lion has
[LEGS] The number of legs
OPTIONS: OPTIONS:
-h, --help Prints help information -h, --help Prints help information

View File

@ -5,8 +5,8 @@ EXAMPLES:
myapp 12 -c 3 myapp 12 -c 3
ARGUMENTS: ARGUMENTS:
[LEGS] The number of legs
<TEETH> The number of teeth the lion has <TEETH> The number of teeth the lion has
[LEGS] The number of legs
OPTIONS: OPTIONS:
-h, --help Prints help information -h, --help Prints help information

View File

@ -244,6 +244,24 @@ namespace Spectre.Console.Tests.Unit.Cli
// Then // Then
return Verifier.Verify(output); return Verifier.Verify(output);
} }
[Fact]
public Task Should_List_Arguments_In_Correct_Order()
{
// Given
var fixture = new CommandAppFixture();
fixture.WithDefaultCommand<GenericCommand<ArgumentOrderSettings>>();
fixture.Configure(configurator =>
{
configurator.SetApplicationName("myapp");
});
// When
var (_, output, _, _) = fixture.Run("--help");
// Then
return Verifier.Verify(output);
}
} }
} }
} }

View File

@ -11,12 +11,14 @@ namespace Spectre.Console.Cli.Internal
private sealed class HelpArgument private sealed class HelpArgument
{ {
public string Name { get; } public string Name { get; }
public int Position { get; set; }
public bool Required { get; } public bool Required { get; }
public string? Description { get; } public string? Description { get; }
public HelpArgument(string name, bool required, string? description) public HelpArgument(string name, int position, bool required, string? description)
{ {
Name = name; Name = name;
Position = position;
Required = required; Required = required;
Description = description; Description = description;
} }
@ -25,7 +27,7 @@ namespace Spectre.Console.Cli.Internal
{ {
var arguments = new List<HelpArgument>(); var arguments = new List<HelpArgument>();
arguments.AddRange(command?.Parameters?.OfType<CommandArgument>()?.Select( arguments.AddRange(command?.Parameters?.OfType<CommandArgument>()?.Select(
x => new HelpArgument(x.Value, x.Required, x.Description)) x => new HelpArgument(x.Value, x.Position, x.Required, x.Description))
?? Array.Empty<HelpArgument>()); ?? Array.Empty<HelpArgument>());
return arguments; return arguments;
} }
@ -94,63 +96,61 @@ namespace Spectre.Console.Cli.Internal
composer.Style("yellow", "USAGE:").LineBreak(); composer.Style("yellow", "USAGE:").LineBreak();
composer.Tab().Text(model.GetApplicationName()); composer.Tab().Text(model.GetApplicationName());
var parameters = new Stack<string>(); var parameters = new List<string>();
if (command == null) if (command == null)
{ {
parameters.Push("[aqua]<COMMAND>[/]"); parameters.Add("[grey][[OPTIONS]][/]");
parameters.Push("[grey][[OPTIONS]][/]"); parameters.Add("[aqua]<COMMAND>[/]");
} }
else else
{ {
var current = command; foreach (var current in command.Flatten())
if (command.IsBranch)
{
parameters.Push("[aqua]<COMMAND>[/]");
}
while (current != null)
{ {
var isCurrent = current == command; var isCurrent = current == command;
if (isCurrent)
{
parameters.Push("[grey][[OPTIONS]][/]");
}
if (current.Parameters.OfType<CommandArgument>().Any())
{
var optionalArguments = current.Parameters.OfType<CommandArgument>().Where(x => !x.Required).ToArray();
if (optionalArguments.Length > 0 || !isCurrent)
{
foreach (var optionalArgument in optionalArguments)
{
parameters.Push($"[silver][[{optionalArgument.Value.EscapeMarkup()}]][/]");
}
}
if (isCurrent)
{
foreach (var argument in current.Parameters.OfType<CommandArgument>()
.Where(a => a.Required).OrderBy(a => a.Position).ToArray())
{
parameters.Push($"[aqua]<{argument.Value.EscapeMarkup()}>[/]");
}
}
}
if (!current.IsDefaultCommand) if (!current.IsDefaultCommand)
{ {
if (isCurrent) if (isCurrent)
{ {
parameters.Push($"[underline]{current.Name.EscapeMarkup()}[/]"); parameters.Add($"[underline]{current.Name.EscapeMarkup()}[/]");
} }
else else
{ {
parameters.Push($"{current.Name.EscapeMarkup()}"); parameters.Add($"{current.Name.EscapeMarkup()}");
} }
} }
current = current.Parent; if (current.Parameters.OfType<CommandArgument>().Any())
{
if (isCurrent)
{
foreach (var argument in current.Parameters.OfType<CommandArgument>()
.Where(a => a.Required).OrderBy(a => a.Position).ToArray())
{
parameters.Add($"[aqua]<{argument.Value.EscapeMarkup()}>[/]");
}
}
var optionalArguments = current.Parameters.OfType<CommandArgument>().Where(x => !x.Required).ToArray();
if (optionalArguments.Length > 0 || !isCurrent)
{
foreach (var optionalArgument in optionalArguments)
{
parameters.Add($"[silver][[{optionalArgument.Value.EscapeMarkup()}]][/]");
}
}
}
if (isCurrent)
{
parameters.Add("[grey][[OPTIONS]][/]");
}
}
if (command.IsBranch)
{
parameters.Add("[aqua]<COMMAND>[/]");
} }
} }
@ -238,20 +238,18 @@ namespace Spectre.Console.Cli.Internal
grid.AddColumn(new GridColumn { Padding = new Padding(4, 4), NoWrap = true }); grid.AddColumn(new GridColumn { Padding = new Padding(4, 4), NoWrap = true });
grid.AddColumn(new GridColumn { Padding = new Padding(0, 0) }); grid.AddColumn(new GridColumn { Padding = new Padding(0, 0) });
foreach (var argument in arguments) foreach (var argument in arguments.Where(x => x.Required).OrderBy(x => x.Position))
{ {
if (argument.Required) grid.AddRow(
{ $"[silver]<{argument.Name.EscapeMarkup()}>[/]",
grid.AddRow( argument.Description?.TrimEnd('.') ?? " ");
$"[silver]<{argument.Name.EscapeMarkup()}>[/]", }
argument.Description?.TrimEnd('.') ?? " ");
} foreach (var argument in arguments.Where(x => !x.Required).OrderBy(x => x.Position))
else {
{ grid.AddRow(
grid.AddRow( $"[grey][[{argument.Name.EscapeMarkup()}]][/]",
$"[grey][[{argument.Name.EscapeMarkup()}]][/]", argument.Description?.TrimEnd('.') ?? " ");
argument.Description?.TrimEnd('.') ?? " ");
}
} }
grid.AddEmptyRow(); grid.AddEmptyRow();

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Linq;
using System.Reflection; using System.Reflection;
namespace Spectre.Console.Cli.Internal namespace Spectre.Console.Cli.Internal
@ -51,5 +52,19 @@ namespace Spectre.Console.Cli.Internal
} }
} }
} }
public List<CommandInfo> Flatten()
{
var result = new Stack<CommandInfo>();
var current = this;
while (current != null)
{
result.Push(current);
current = current.Parent;
}
return result.ToList();
}
} }
} }