8000 187 Allow default command configuration for a branch by FrankRay78 · Pull Request #1101 · spectreconsole/spectre.console · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

187 Allow default command configuration for a branch #1101

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Spectre.Console.Cli/CommandApp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ public async Task<int> RunAsync(IEnumerable<string> args)
}
}

internal Configurator GetConfigurator()
internal IConfigurator GetConfigurator()
{
return _configurator;
}
Expand Down
10 changes: 10 additions & 0 deletions src/Spectre.Console.Cli/IConfigurator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@ public interface IConfigurator
/// <param name="args">The example arguments.</param>
void AddExample(string[] args);

/// <summary>
/// Adds a default command.
/// </summary>
/// <remarks>
/// This is the command that will run if the user doesn't specify one on the command line.
/// </remarks>
/// <typeparam name="TDefaultCommand">The default command type.</typeparam>
void SetDefaultCommand<TDefaultCommand>()
where TDefaultCommand : class, ICommand;

/// <summary>
/// Adds a command.
/// </summary>
Expand Down
14 changes: 13 additions & 1 deletion src/Spectre.Console.Cli/IConfiguratorOfT.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@ public interface IConfigurator<in TSettings>
/// <param name="args">The example arguments.</param>
void AddExample(string[] args);

/// <summary>
/// Adds a default command.
/// </summary>
/// <remarks>
/// This is the command that will run if the user doesn't specify one on the command line.
/// It must be able to execute successfully by itself ie. without requiring any command line
/// arguments, flags or option values.
/// </remarks>
/// <typeparam name="TDefaultCommand">The default command type.</typeparam>
void SetDefaultCommand<TDefaultCommand>()
where TDefaultCommand : class, ICommandLimiter<TSettings>;

/// <summary>
/// Marks the branch as hidden.
/// Hidden branches do not show up in help documentation or
Expand Down Expand Up @@ -52,5 +64,5 @@ ICommandConfigurator AddDelegate<TDerivedSettings>(string name, Func<CommandCont
/// <param name="name">The name of the command branch.</param>
/// <param name="action">The command branch configuration.</param>
void AddBranch<TDerivedSettings>(string name, Action<IConfigurator<TDerivedSettings>> action)
where TDerivedSettings : TSettings;
where TDerivedSettings : TSettings;
}
41 changes: 34 additions & 7 deletions src/Spectre.Console.Cli/Internal/CommandExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,10 @@ public async Task<int> Execute(IConfiguration configuration, IEnumerable<string>
}

// Parse and map the model against the arguments.
var parser = new CommandTreeParser(model, configuration.Settings);
var parsedResult = parser.Parse(args);
_registrar.RegisterInstance(typeof(CommandTreeParserResult), parsedResult);
var parsedResult = ParseCommandLineArguments(model, configuration.Settings, args);

// Currently the root?
if (parsedResult.Tree == null)
if (parsedResult?.Tree == null)
{
// Display help.
configuration.Settings.Console.SafeRender(HelpWriter.Write(model));
Expand All @@ -64,9 +62,10 @@ public async Task<int> Execute(IConfiguration configuration, IEnumerable<string>
// Branches can't be executed. Show help.
configuration.Settings.Console.SafeRender(HelpWriter.WriteCommand(model, leaf.Command));
return leaf.ShowHelp ? 0 : 1;
}

// Register the arguments with the container.
}

// Register the parsed command tree and any remaining arguments with the container.
_registrar.RegisterInstance(typeof(CommandTreeParserResult), parsedResult);
_registrar.RegisterInstance(typeof(IRemainingArguments), parsedResult.Remaining);

// Create the resolver and the context.
Expand All @@ -78,6 +77,34 @@ public async Task<int> Execute(IConfiguration configuration, IEnumerable<string>
return await Execute(leaf, parsedResult.Tree, context, resolver, configuration).ConfigureAwait(false);
}
}

private CommandTreeParserResult? ParseCommandLineArguments(CommandModel model, CommandAppSettings settings, IEnumerable<string> args)
{
var parser = new CommandTreeParser(model, settings.CaseSensitivity);

var parserContext = new CommandTreeParserContext(args, settings.ParsingMode);
var tokenizerResult = CommandTreeTokenizer.Tokenize(args);
var parsedResult = parser.Parse(parserContext, tokenizerResult);

var lastParsedLeaf = parsedResult?.Tree?.GetLeafCommand();
var lastParsedCommand = lastParsedLeaf?.Command;
if (lastParsedLeaf != null && lastParsedCommand != null &&
lastParsedCommand.IsBranch && !lastParsedLeaf.ShowHelp &&
lastParsedCommand.DefaultCommand != null)
{
// Insert this branch's default command into the command line
// arguments and try again to see if it will parse.
var argsWithDefaultCommand = new List<string>(args);

argsWithDefaultCommand.Insert(tokenizerResult.Tokens.Position, lastParsedCommand.DefaultCommand.Name);

parserContext = new CommandTreeParserContext(argsWithDefaultCommand, settings.ParsingMode);
tokenizerResult = CommandTreeTokenizer.Tokenize(argsWithDefaultCommand);
parsedResult = parser.Parse(parserContext, tokenizerResult);
}

return parsedResult;
}

private static string ResolveApplicationVersion(IConfiguration configuration)
{
Expand Down
18 changes: 9 additions & 9 deletions src/Spectre.Console.Cli/Internal/Configuration/Configurator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,19 @@ public Configurator(ITypeRegistrar registrar)
public void AddExample(string[] args)
{
Examples.Add(args);
}

public void SetDefaultCommand<TDefaultCommand>()
where TDefaultCommand : class, ICommand
{
DefaultCommand = ConfiguredCommand.FromType<TDefaultCommand>(
CliConstants.DefaultCommandName, isDefaultCommand: true);
}
}
public void SetDefaultCommand<TDefaultCommand>()
where TDefaultCommand : class, ICommand
{
DefaultCommand = ConfiguredCommand.FromType<TDefaultCommand>(
CliConstants.DefaultCommandName, isDefaultCommand: true);
}

public ICommandConfigurator AddCommand<TCommand>(string name)
where TCommand : class, ICommand
{
var command = Commands.AddAndReturn(ConfiguredCommand.FromType<TCommand>(name, false));
var command = Commands.AddAndReturn(ConfiguredCommand.FromType<TCommand>(name, isDefaultCommand: false));
return new CommandConfigurator(command);
}

Expand Down
15 changes: 12 additions & 3 deletions src/Spectre.Console.Cli/Internal/Configuration/ConfiguratorOfT.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ internal sealed class Configurator<TSettings> : IUnsafeBranchConfigurator, IConf
where TSettings : CommandSettings
{
private readonly ConfiguredCommand _command;
private readonly ITypeRegistrar? _registrar;
private readonly ITypeRegistrar? _registrar;

public Configurator(ConfiguredCommand command, ITypeRegistrar? registrar)
{
Expand All @@ -22,6 +22,15 @@ public void AddExample(string[] args)
_command.Examples.Add(args);
}

public void SetDefaultCommand<TDefaultCommand>()
where TDefaultCommand : class, ICommandLimiter<TSettings>
{
var defaultCommand = ConfiguredCommand.FromType<TDefaultCommand>(
CliConstants.DefaultCommandName, isDefaultCommand: true);

_command.Children.Add(defaultCommand);
}

public void HideBranch()
{
_command.IsHidden = true;
Expand All @@ -30,7 +39,7 @@ public void HideBranch()
public ICommandConfigurator AddCommand<TCommand>(string name)
where TCommand : class, ICommandLimiter<TSettings>
{
var command = ConfiguredCommand.FromType<TCommand>(name);
var command = ConfiguredCommand.FromType<TCommand>(name, isDefaultCommand: false);
var configurator = new CommandConfigurator(command);

_command.Children.Add(command);
Expand Down Expand Up @@ -86,5 +95,5 @@ void IUnsafeConfigurator.AddBranch(string name, Type settings, Action<IUnsafeBra

action(configurator);
_command.Children.Add(command);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ private ConfiguredCommand(
CommandType = commandType;
SettingsType = settingsType;
Delegate = @delegate;
IsDefaultCommand = isDefaultCommand;
IsDefaultCommand = isDefaultCommand;

// Default commands are always created as hidden.
IsHidden = IsDefaultCommand;

Children = new List<ConfiguredCommand>();
Examples = new List<string[]>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@ public static void RegisterDependencies(this ITypeRegistrar registrar, CommandMo
{
var stack = new Stack<CommandInfo>();
model.Commands.ForEach(c => stack.Push(c));
if (model.DefaultCommand != null)
{
stack.Push(model.DefaultCommand);
}

while (stack.Count > 0)
{
Expand Down
9 changes: 6 additions & 3 deletions src/Spectre.Console.Cli/Internal/Modelling/CommandInfo.cs
Original file line number Diff line number Diff line change
@@ -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; }
Expand All @@ -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)
{
Expand Down
5 changes: 2 additions & 3 deletions src/Spectre.Console.Cli/Internal/Modelling/CommandModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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[]>());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("Spectre.Console.Cli.Tests")]

namespace Spectre.Console.Cli;

internal static class CommandModelBuilder
{
// Consider removing this in favor for value tuples at some point.
Expand All @@ -25,18 +29,19 @@ public static CommandModel Build(IConfiguration configuration)
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;
Expand All @@ -54,7 +59,7 @@ private static CommandInfo Build(CommandInfo? parent, ConfiguredCommand command)
foreach (var childCommand in command.Children)
{
var child = Build(info, childCommand);
info.Children.Add(child);
info.Children.Add(child);
}

// Normalize argument positions.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public static void Validate(CommandModel model, CommandAppSettings settings)
throw new ArgumentNullException(nameof(settings));
}

if (model.Commands.Count == 0 && model.DefaultCommand == null)
if (model.Commands.Count == 0)
{
throw CommandConfigurationException.NoCommandConfigured();
}
Expand All @@ -31,7 +31,6 @@ public static void Validate(CommandModel model, CommandAppSettings settings)
}
}

Validate(model.DefaultCommand);
foreach (var command in model.Commands)
{
Validate(command);
Expand Down Expand Up @@ -147,7 +146,7 @@ private static void ValidateExamples(CommandModel model, CommandAppSettings sett
{
try
{
var parser = new CommandTreeParser(model, settings, ParsingMode.Strict);
var parser = new CommandTreeParser(model, settings.CaseSensitivity, ParsingMode.Strict);
parser.Parse(example);
}
catch (Exception ex)
Expand Down
10 changes: 9 additions & 1 deletion src/Spectre.Console.Cli/Internal/Modelling/ICommandContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
}
28 changes: 15 additions & 13 deletions src/Spectre.Console.Cli/Internal/Parsing/CommandTreeParser.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using static Spectre.Console.Cli.CommandTreeTokenizer;

namespace Spectre.Console.Cli;

internal class CommandTreeParser
Expand All @@ -14,25 +16,25 @@ public enum State
Remaining = 1,
}

public CommandTreeParser(CommandModel configuration, ICommandAppSettings settings, ParsingMode? parsingMode = null)
public CommandTreeParser(CommandModel configuration, CaseSensitivity caseSensitivity, ParsingMode? parsingMode = null)
{
if (settings is null)
{
throw new ArgumentNullException(nameof(settings));
}

_configuration = configuration;
_configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
_parsingMode = parsingMode ?? _configuration.ParsingMode;
_help = new CommandOptionAttribute("-h|--help");

CaseSensitivity = settings.CaseSensitivity;
}

CaseSensitivity = caseSensitivity;
}
public CommandTreeParserResult Parse(IEnumerable<string> args)
{
var parserContext = new CommandTreeParserContext(args, _parsingMode);
var tokenizerResult = CommandTreeTokenizer.Tokenize(args);

return Parse(parserContext, tokenizerResult);
}

public CommandTreeParserResult Parse(CommandTreeParserContext context, CommandTreeTokenizerResult tokenizerResult)
{
var context = new CommandTreeParserContext(args, _parsingMode);

var tokenizerResult = CommandTreeTokenizer.Tokenize(context.Arguments);
var tokens = tokenizerResult.Tokens;
var rawRemaining = tokenizerResult.Remaining;

Expand Down
Loading
0