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]
ARGUMENTS:
[LEGS] The number of legs
<TEETH> The number of teeth the lion has
[LEGS] The number of legs
OPTIONS:
-h, --help Prints help information

View File

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

View File

@ -244,6 +244,24 @@ namespace Spectre.Console.Tests.Unit.Cli
// Then
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
{
public string Name { get; }
public int Position { get; set; }
public bool Required { 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;
Position = position;
Required = required;
Description = description;
}
@ -25,7 +27,7 @@ namespace Spectre.Console.Cli.Internal
{
var arguments = new List<HelpArgument>();
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>());
return arguments;
}
@ -94,63 +96,61 @@ namespace Spectre.Console.Cli.Internal
composer.Style("yellow", "USAGE:").LineBreak();
composer.Tab().Text(model.GetApplicationName());
var parameters = new Stack<string>();
var parameters = new List<string>();
if (command == null)
{
parameters.Push("[aqua]<COMMAND>[/]");
parameters.Push("[grey][[OPTIONS]][/]");
parameters.Add("[grey][[OPTIONS]][/]");
parameters.Add("[aqua]<COMMAND>[/]");
}
else
{
var current = command;
if (command.IsBranch)
{
parameters.Push("[aqua]<COMMAND>[/]");
}
while (current != null)
foreach (var current in command.Flatten())
{
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 (isCurrent)
{
parameters.Push($"[underline]{current.Name.EscapeMarkup()}[/]");
parameters.Add($"[underline]{current.Name.EscapeMarkup()}[/]");
}
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(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()}>[/]",
argument.Description?.TrimEnd('.') ?? " ");
}
else
{
grid.AddRow(
$"[grey][[{argument.Name.EscapeMarkup()}]][/]",
argument.Description?.TrimEnd('.') ?? " ");
}
grid.AddRow(
$"[silver]<{argument.Name.EscapeMarkup()}>[/]",
argument.Description?.TrimEnd('.') ?? " ");
}
foreach (var argument in arguments.Where(x => !x.Required).OrderBy(x => x.Position))
{
grid.AddRow(
$"[grey][[{argument.Name.EscapeMarkup()}]][/]",
argument.Description?.TrimEnd('.') ?? " ");
}
grid.AddEmptyRow();

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
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();
}
}
}