mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-07-01 18:38:16 +08:00
Move Spectre.Console.Cli to it's own package
This commit is contained in:

committed by
Patrik Svensson

parent
b600832e00
commit
36ca22ffac
264
src/Spectre.Console.Cli/Internal/Commands/ExplainCommand.cs
Normal file
264
src/Spectre.Console.Cli/Internal/Commands/ExplainCommand.cs
Normal file
@ -0,0 +1,264 @@
|
||||
namespace Spectre.Console.Cli;
|
||||
|
||||
[Description("Displays diagnostics about CLI configurations")]
|
||||
[SuppressMessage("Performance", "CA1812: Avoid uninstantiated internal classes")]
|
||||
internal sealed class ExplainCommand : Command<ExplainCommand.Settings>
|
||||
{
|
||||
private readonly CommandModel _commandModel;
|
||||
private readonly IAnsiConsole _writer;
|
||||
|
||||
public ExplainCommand(IConfiguration configuration, CommandModel commandModel)
|
||||
{
|
||||
_commandModel = commandModel ?? throw new ArgumentNullException(nameof(commandModel));
|
||||
_writer = configuration.Settings.Console.GetConsole();
|
||||
}
|
||||
|
||||
public sealed class Settings : CommandSettings
|
||||
{
|
||||
public Settings(string[]? commands, bool? detailed, bool includeHidden)
|
||||
{
|
||||
Commands = commands;
|
||||
Detailed = detailed;
|
||||
IncludeHidden = includeHidden;
|
||||
}
|
||||
|
||||
[CommandArgument(0, "[command]")]
|
||||
public string[]? Commands { get; }
|
||||
|
||||
[Description("Include detailed information about the commands.")]
|
||||
[CommandOption("-d|--detailed")]
|
||||
public bool? Detailed { get; }
|
||||
|
||||
[Description("Include hidden commands and options.")]
|
||||
[CommandOption("--hidden")]
|
||||
public bool IncludeHidden { get; }
|
||||
}
|
||||
|
||||
public override int Execute([NotNull] CommandContext context, [NotNull] Settings settings)
|
||||
{
|
||||
var tree = new Tree("CLI Configuration");
|
||||
tree.AddNode(ValueMarkup("Application Name", _commandModel.ApplicationName, "no application name"));
|
||||
tree.AddNode(ValueMarkup("Parsing Mode", _commandModel.ParsingMode.ToString()));
|
||||
|
||||
if (settings.Commands == null || settings.Commands.Length == 0)
|
||||
{
|
||||
// If there is a default command we'll want to include it in the list too.
|
||||
var commands = _commandModel.DefaultCommand != null
|
||||
? new[] { _commandModel.DefaultCommand }.Concat(_commandModel.Commands)
|
||||
: _commandModel.Commands;
|
||||
|
||||
AddCommands(
|
||||
tree.AddNode(ParentMarkup("Commands")),
|
||||
commands,
|
||||
settings.Detailed ?? false,
|
||||
settings.IncludeHidden);
|
||||
}
|
||||
else
|
||||
{
|
||||
var currentCommandTier = _commandModel.Commands;
|
||||
CommandInfo? currentCommand = null;
|
||||
foreach (var command in settings.Commands)
|
||||
{
|
||||
currentCommand = currentCommandTier
|
||||
.SingleOrDefault(i =>
|
||||
i.Name.Equals(command, StringComparison.CurrentCultureIgnoreCase) ||
|
||||
i.Aliases
|
||||
.Any(alias => alias.Equals(command, StringComparison.CurrentCultureIgnoreCase)));
|
||||
|
||||
if (currentCommand == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
currentCommandTier = currentCommand.Children;
|
||||
}
|
||||
|
||||
if (currentCommand == null)
|
||||
{
|
||||
throw new Exception($"Command {string.Join(" ", settings.Commands)} not found");
|
||||
}
|
||||
|
||||
AddCommands(
|
||||
tree.AddNode(ParentMarkup("Commands")),
|
||||
new[] { currentCommand },
|
||||
settings.Detailed ?? true,
|
||||
settings.IncludeHidden);
|
||||
}
|
||||
|
||||
_writer.Write(tree);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private IRenderable ValueMarkup(string key, string value)
|
||||
{
|
||||
return new Markup($"{key}: [blue]{value.EscapeMarkup()}[/]");
|
||||
}
|
||||
|
||||
private IRenderable ValueMarkup(string key, string? value, string noValueText)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
return new Markup($"{key}: [grey]({noValueText.EscapeMarkup()})[/]");
|
||||
}
|
||||
|
||||
var table = new Table().NoBorder().HideHeaders().AddColumns("key", "value");
|
||||
table.AddRow($"{key}", $"[blue]{value.EscapeMarkup()}[/]");
|
||||
return table;
|
||||
}
|
||||
|
||||
private string ParentMarkup(string description)
|
||||
{
|
||||
return $"[yellow]{description.EscapeMarkup()}[/]";
|
||||
}
|
||||
|
||||
private void AddStringList(TreeNode node, string description, IList<string>? strings)
|
||||
{
|
||||
if (strings == null || strings.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var parentNode = node.AddNode(ParentMarkup(description));
|
||||
foreach (var s in strings)
|
||||
{
|
||||
parentNode.AddNode(s);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddCommands(TreeNode node, IEnumerable<CommandInfo> commands, bool detailed, bool includeHidden)
|
||||
{
|
||||
foreach (var command in commands)
|
||||
{
|
||||
if (!includeHidden && command.IsHidden)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var commandName = $"[green]{command.Name}[/]";
|
||||
if (command.IsDefaultCommand)
|
||||
{
|
||||
commandName += " (default)";
|
||||
}
|
||||
|
||||
var commandNode = node.AddNode(commandName);
|
||||
commandNode.AddNode(ValueMarkup("Description", command.Description, "no description"));
|
||||
if (command.IsHidden)
|
||||
{
|
||||
commandNode.AddNode(ValueMarkup("IsHidden", command.IsHidden.ToString()));
|
||||
}
|
||||
|
||||
if (!command.IsBranch)
|
||||
{
|
||||
commandNode.AddNode(ValueMarkup("Type", command.CommandType?.ToString(), "no command type"));
|
||||
commandNode.AddNode(ValueMarkup("Settings Type", command.SettingsType.ToString()));
|
||||
}
|
||||
|
||||
if (command.Parameters.Count > 0)
|
||||
{
|
||||
var parametersNode = commandNode.AddNode(ParentMarkup("Parameters"));
|
||||
foreach (var parameter in command.Parameters)
|
||||
{
|
||||
AddParameter(parametersNode, parameter, detailed, includeHidden);
|
||||
}
|
||||
}
|
||||
|
||||
AddStringList(commandNode, "Aliases", command.Aliases.ToList());
|
||||
AddStringList(commandNode, "Examples", command.Examples.Select(i => string.Join(" ", i)).ToList());
|
||||
|
||||
if (command.Children.Count > 0)
|
||||
{
|
||||
var childNode = commandNode.AddNode(ParentMarkup("Child Commands"));
|
||||
AddCommands(childNode, command.Children, detailed, includeHidden);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddParameter(TreeNode parametersNode, CommandParameter parameter, bool detailed, bool includeHidden)
|
||||
{
|
||||
if (!includeHidden && parameter.IsHidden)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!detailed)
|
||||
{
|
||||
parametersNode.AddNode(
|
||||
$"{parameter.PropertyName} [purple]{GetShortOptions(parameter)}[/] [grey]{parameter.Property.PropertyType.ToString().EscapeMarkup()}[/]");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var parameterNode = parametersNode.AddNode(
|
||||
$"{parameter.PropertyName} [grey]{parameter.Property.PropertyType.ToString().EscapeMarkup()}[/]");
|
||||
|
||||
parameterNode.AddNode(ValueMarkup("Description", parameter.Description, "no description"));
|
||||
parameterNode.AddNode(ValueMarkup("Parameter Kind", parameter.ParameterKind.ToString()));
|
||||
|
||||
if (parameter is CommandOption commandOptionParameter)
|
||||
{
|
||||
if (commandOptionParameter.IsShadowed)
|
||||
{
|
||||
parameterNode.AddNode(ValueMarkup("IsShadowed", commandOptionParameter.IsShadowed.ToString()));
|
||||
}
|
||||
|
||||
if (commandOptionParameter.LongNames.Count > 0)
|
||||
{
|
||||
parameterNode.AddNode(ValueMarkup(
|
||||
"Long Names",
|
||||
string.Join("|", commandOptionParameter.LongNames.Select(i => $"--{i}"))));
|
||||
|
||||
parameterNode.AddNode(ValueMarkup(
|
||||
"Short Names",
|
||||
string.Join("|", commandOptionParameter.ShortNames.Select(i => $"-{i}"))));
|
||||
}
|
||||
}
|
||||
else if (parameter is CommandArgument commandArgumentParameter)
|
||||
{
|
||||
parameterNode.AddNode(ValueMarkup("Position", commandArgumentParameter.Position.ToString()));
|
||||
parameterNode.AddNode(ValueMarkup("Value", commandArgumentParameter.Value));
|
||||
}
|
||||
|
||||
parameterNode.AddNode(ValueMarkup("Required", parameter.Required.ToString()));
|
||||
|
||||
if (parameter.Converter != null)
|
||||
{
|
||||
parameterNode.AddNode(ValueMarkup(
|
||||
"Converter", $"\"{parameter.Converter.ConverterTypeName}\""));
|
||||
}
|
||||
|
||||
if (parameter.DefaultValue != null)
|
||||
{
|
||||
parameterNode.AddNode(ValueMarkup(
|
||||
"Default Value", $"\"{parameter.DefaultValue.Value}\""));
|
||||
}
|
||||
|
||||
if (parameter.PairDeconstructor != null)
|
||||
{
|
||||
parameterNode.AddNode(ValueMarkup("Pair Deconstructor", parameter.PairDeconstructor.Type.ToString()));
|
||||
}
|
||||
|
||||
AddStringList(
|
||||
parameterNode,
|
||||
"Validators",
|
||||
parameter.Validators.Select(i => i.GetType().ToString()).ToList());
|
||||
}
|
||||
|
||||
private static string GetShortOptions(CommandParameter parameter)
|
||||
{
|
||||
if (parameter is CommandOption commandOptionParameter)
|
||||
{
|
||||
return string.Join(
|
||||
" | ",
|
||||
commandOptionParameter.LongNames.Select(i => $"--{i}")
|
||||
.Concat(commandOptionParameter.ShortNames.Select(i => $"-{i}")));
|
||||
}
|
||||
|
||||
if (parameter is CommandArgument commandArgumentParameter)
|
||||
{
|
||||
return $"{commandArgumentParameter.Value} position {commandArgumentParameter.Position}";
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
30
src/Spectre.Console.Cli/Internal/Commands/VersionCommand.cs
Normal file
30
src/Spectre.Console.Cli/Internal/Commands/VersionCommand.cs
Normal file
@ -0,0 +1,30 @@
|
||||
namespace Spectre.Console.Cli;
|
||||
|
||||
[Description("Displays the CLI library version")]
|
||||
[SuppressMessage("Performance", "CA1812: Avoid uninstantiated internal classes")]
|
||||
internal sealed class VersionCommand : Command<VersionCommand.Settings>
|
||||
{
|
||||
private readonly IAnsiConsole _writer;
|
||||
|
||||
public VersionCommand(IConfiguration configuration)
|
||||
{
|
||||
_writer = configuration.Settings.Console.GetConsole();
|
||||
}
|
||||
|
||||
public sealed class Settings : CommandSettings
|
||||
{
|
||||
}
|
||||
|
||||
public override int Execute([NotNull] CommandContext context, [NotNull] Settings settings)
|
||||
{
|
||||
_writer.MarkupLine(
|
||||
"[yellow]Spectre.Cli[/] version [aqua]{0}[/]",
|
||||
VersionHelper.GetVersion(typeof(VersionCommand)?.Assembly));
|
||||
|
||||
_writer.MarkupLine(
|
||||
"[yellow]Spectre.Console[/] version [aqua]{0}[/]",
|
||||
VersionHelper.GetVersion(typeof(IAnsiConsole)?.Assembly));
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
175
src/Spectre.Console.Cli/Internal/Commands/XmlDocCommand.cs
Normal file
175
src/Spectre.Console.Cli/Internal/Commands/XmlDocCommand.cs
Normal file
@ -0,0 +1,175 @@
|
||||
namespace Spectre.Console.Cli;
|
||||
|
||||
[Description("Generates an XML representation of the CLI configuration.")]
|
||||
[SuppressMessage("Performance", "CA1812: Avoid uninstantiated internal classes")]
|
||||
internal sealed class XmlDocCommand : Command<XmlDocCommand.Settings>
|
||||
{
|
||||
private readonly CommandModel _model;
|
||||
private readonly IAnsiConsole _writer;
|
||||
|
||||
public XmlDocCommand(IConfiguration configuration, CommandModel model)
|
||||
{
|
||||
_model = model ?? throw new ArgumentNullException(nameof(model));
|
||||
_writer = configuration.Settings.Console.GetConsole();
|
||||
}
|
||||
|
||||
public sealed class Settings : CommandSettings
|
||||
{
|
||||
}
|
||||
|
||||
public override int Execute([NotNull] CommandContext context, [NotNull] Settings settings)
|
||||
{
|
||||
_writer.Write(Serialize(_model), Style.Plain);
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static string Serialize(CommandModel model)
|
||||
{
|
||||
var settings = new XmlWriterSettings
|
||||
{
|
||||
Indent = true,
|
||||
IndentChars = " ",
|
||||
NewLineChars = "\n",
|
||||
OmitXmlDeclaration = false,
|
||||
Encoding = Encoding.UTF8,
|
||||
};
|
||||
|
||||
using (var buffer = new StringWriterWithEncoding(Encoding.UTF8))
|
||||
using (var xmlWriter = XmlWriter.Create(buffer, settings))
|
||||
{
|
||||
SerializeModel(model).WriteTo(xmlWriter);
|
||||
xmlWriter.Flush();
|
||||
return buffer.GetStringBuilder().ToString();
|
||||
}
|
||||
}
|
||||
|
||||
private static XmlDocument SerializeModel(CommandModel model)
|
||||
{
|
||||
var document = new XmlDocument();
|
||||
var root = document.CreateElement("Model");
|
||||
|
||||
if (model.DefaultCommand != null)
|
||||
{
|
||||
root.AppendChild(document.CreateComment("DEFAULT COMMAND"));
|
||||
root.AppendChild(CreateCommandNode(document, model.DefaultCommand, isDefaultCommand: true));
|
||||
}
|
||||
|
||||
foreach (var command in model.Commands.Where(x => !x.IsHidden))
|
||||
{
|
||||
root.AppendChild(document.CreateComment(command.Name.ToUpperInvariant()));
|
||||
root.AppendChild(CreateCommandNode(document, command));
|
||||
}
|
||||
|
||||
document.AppendChild(root);
|
||||
return document;
|
||||
}
|
||||
|
||||
private static XmlNode CreateCommandNode(XmlDocument doc, CommandInfo command, bool isDefaultCommand = false)
|
||||
{
|
||||
var node = doc.CreateElement("Command");
|
||||
|
||||
// Attributes
|
||||
node.SetNullableAttribute("Name", command.Name);
|
||||
node.SetBooleanAttribute("IsBranch", command.IsBranch);
|
||||
|
||||
if (isDefaultCommand)
|
||||
{
|
||||
node.SetBooleanAttribute("IsDefault", true);
|
||||
}
|
||||
|
||||
if (command.CommandType != null)
|
||||
{
|
||||
node.SetNullableAttribute("ClrType", command.CommandType?.FullName);
|
||||
}
|
||||
|
||||
node.SetNullableAttribute("Settings", command.SettingsType?.FullName);
|
||||
|
||||
// Parameters
|
||||
if (command.Parameters.Count > 0)
|
||||
{
|
||||
var parameterRootNode = doc.CreateElement("Parameters");
|
||||
foreach (var parameter in CreateParameterNodes(doc, command))
|
||||
{
|
||||
parameterRootNode.AppendChild(parameter);
|
||||
}
|
||||
|
||||
node.AppendChild(parameterRootNode);
|
||||
}
|
||||
|
||||
// Commands
|
||||
foreach (var childCommand in command.Children)
|
||||
{
|
||||
node.AppendChild(doc.CreateComment(childCommand.Name.ToUpperInvariant()));
|
||||
node.AppendChild(CreateCommandNode(doc, childCommand));
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
private static IEnumerable<XmlNode> CreateParameterNodes(XmlDocument document, CommandInfo command)
|
||||
{
|
||||
// Arguments
|
||||
foreach (var argument in command.Parameters.OfType<CommandArgument>().OrderBy(x => x.Position))
|
||||
{
|
||||
var node = document.CreateElement("Argument");
|
||||
node.SetNullableAttribute("Name", argument.Value);
|
||||
node.SetAttribute("Position", argument.Position.ToString(CultureInfo.InvariantCulture));
|
||||
node.SetBooleanAttribute("Required", argument.Required);
|
||||
node.SetEnumAttribute("Kind", argument.ParameterKind);
|
||||
node.SetNullableAttribute("ClrType", argument.ParameterType?.FullName);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(argument.Description))
|
||||
{
|
||||
var descriptionNode = document.CreateElement("Description");
|
||||
descriptionNode.InnerText = argument.Description;
|
||||
node.AppendChild(descriptionNode);
|
||||
}
|
||||
|
||||
if (argument.Validators.Count > 0)
|
||||
{
|
||||
var validatorRootNode = document.CreateElement("Validators");
|
||||
foreach (var validator in argument.Validators.OrderBy(x => x.GetType().FullName))
|
||||
{
|
||||
var validatorNode = document.CreateElement("Validator");
|
||||
validatorNode.SetNullableAttribute("ClrType", validator.GetType().FullName);
|
||||
validatorNode.SetNullableAttribute("Message", validator.ErrorMessage);
|
||||
validatorRootNode.AppendChild(validatorNode);
|
||||
}
|
||||
|
||||
node.AppendChild(validatorRootNode);
|
||||
}
|
||||
|
||||
yield return node;
|
||||
}
|
||||
|
||||
// Options
|
||||
foreach (var option in command.Parameters.OfType<CommandOption>()
|
||||
.Where(x => !x.IsHidden)
|
||||
.OrderBy(x => string.Join(",", x.LongNames))
|
||||
.ThenBy(x => string.Join(",", x.ShortNames)))
|
||||
{
|
||||
var node = document.CreateElement("Option");
|
||||
|
||||
if (option.IsShadowed)
|
||||
{
|
||||
node.SetBooleanAttribute("Shadowed", true);
|
||||
}
|
||||
|
||||
node.SetNullableAttribute("Short", option.ShortNames);
|
||||
node.SetNullableAttribute("Long", option.LongNames);
|
||||
node.SetNullableAttribute("Value", option.ValueName);
|
||||
node.SetBooleanAttribute("Required", option.Required);
|
||||
node.SetEnumAttribute("Kind", option.ParameterKind);
|
||||
node.SetNullableAttribute("ClrType", option.ParameterType?.FullName);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(option.Description))
|
||||
{
|
||||
var descriptionNode = document.CreateElement("Description");
|
||||
descriptionNode.InnerText = option.Description;
|
||||
node.AppendChild(descriptionNode);
|
||||
}
|
||||
|
||||
yield return node;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user