Command line improvements (#1103)

Closes #187
Closes #203
Closes #1059
This commit is contained in:
Frank Ray
2023-04-02 21:43:21 +01:00
committed by GitHub
parent 70da3f40ff
commit 714cf179cb
24 changed files with 1052 additions and 269 deletions

View File

@ -1,7 +1,7 @@
namespace Spectre.Console.Cli;
internal sealed class CommandInfo : ICommandContainer
{
{
public string Name { get; }
public HashSet<string> Aliases { get; }
public string? Description { get; }
@ -10,14 +10,17 @@ internal sealed class CommandInfo : ICommandContainer
public Type SettingsType { get; }
public Func<CommandContext, CommandSettings, int>? Delegate { get; }
public bool IsDefaultCommand { get; }
public bool IsHidden { get; }
public CommandInfo? Parent { get; }
public IList<CommandInfo> Children { get; }
public IList<CommandParameter> Parameters { get; }
public IList<string[]> Examples { get; }
public bool IsBranch => CommandType == null && Delegate == null;
IList<CommandInfo> ICommandContainer.Commands => Children;
IList<CommandInfo> ICommandContainer.Commands => Children;
// only branches can have a default command
public CommandInfo? DefaultCommand => IsBranch ? Children.FirstOrDefault(c => c.IsDefaultCommand) : null;
public bool IsHidden { get; }
public CommandInfo(CommandInfo? parent, ConfiguredCommand prototype)
{

View File

@ -4,21 +4,20 @@ internal sealed class CommandModel : ICommandContainer
{
public string? ApplicationName { get; }
public ParsingMode ParsingMode { get; }
public CommandInfo? DefaultCommand { get; }
public IList<CommandInfo> Commands { get; }
public IList<string[]> Examples { get; }
public bool TrimTrailingPeriod { get; }
public CommandInfo? DefaultCommand => Commands.FirstOrDefault(c => c.IsDefaultCommand);
public CommandModel(
CommandAppSettings settings,
CommandInfo? defaultCommand,
IEnumerable<CommandInfo> commands,
IEnumerable<string[]> examples)
{
ApplicationName = settings.ApplicationName;
ParsingMode = settings.ParsingMode;
TrimTrailingPeriod = settings.TrimTrailingPeriod;
DefaultCommand = defaultCommand;
Commands = new List<CommandInfo>(commands ?? Array.Empty<CommandInfo>());
Examples = new List<string[]>(examples ?? Array.Empty<string[]>());
}

View File

@ -1,5 +1,5 @@
namespace Spectre.Console.Cli;
internal static class CommandModelBuilder
{
// Consider removing this in favor for value tuples at some point.
@ -25,18 +25,19 @@ internal static class CommandModelBuilder
result.Add(Build(null, command));
}
var defaultCommand = default(CommandInfo);
if (configuration.DefaultCommand != null)
{
// Add the examples from the configuration to the default command.
configuration.DefaultCommand.Examples.AddRange(configuration.Examples);
// Build the default command.
defaultCommand = Build(null, configuration.DefaultCommand);
var defaultCommand = Build(null, configuration.DefaultCommand);
result.Add(defaultCommand);
}
// Create the command model and validate it.
var model = new CommandModel(configuration.Settings, defaultCommand, result, configuration.Examples);
var model = new CommandModel(configuration.Settings, result, configuration.Examples);
CommandModelValidator.Validate(model, configuration.Settings);
return model;
@ -54,7 +55,7 @@ internal static class CommandModelBuilder
foreach (var childCommand in command.Children)
{
var child = Build(info, childCommand);
info.Children.Add(child);
info.Children.Add(child);
}
// Normalize argument positions.

View File

@ -14,7 +14,7 @@ internal static class CommandModelValidator
throw new ArgumentNullException(nameof(settings));
}
if (model.Commands.Count == 0 && model.DefaultCommand == null)
if (model.Commands.Count == 0)
{
throw CommandConfigurationException.NoCommandConfigured();
}
@ -31,7 +31,6 @@ internal static class CommandModelValidator
}
}
Validate(model.DefaultCommand);
foreach (var command in model.Commands)
{
Validate(command);
@ -147,7 +146,7 @@ internal static class CommandModelValidator
{
try
{
var parser = new CommandTreeParser(model, settings, ParsingMode.Strict);
var parser = new CommandTreeParser(model, settings.CaseSensitivity, ParsingMode.Strict);
parser.Parse(example);
}
catch (Exception ex)

View File

@ -8,5 +8,13 @@ internal interface ICommandContainer
/// <summary>
/// Gets all commands in the container.
/// </summary>
IList<CommandInfo> Commands { get; }
IList<CommandInfo> Commands { get; }
/// <summary>
/// Gets the default command for the container.
/// </summary>
/// <remarks>
/// Returns null if a default command has not been set.
/// </remarks>
CommandInfo? DefaultCommand { get; }
}