Skip to content
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

Choice menu for all the registered commands #1654

Open
flupkede opened this issue Sep 23, 2024 · 0 comments
Open

Choice menu for all the registered commands #1654

flupkede opened this issue Sep 23, 2024 · 0 comments

Comments

@flupkede
Copy link

Description
When we build console programs we often want to invoke them with direct commands and parameters from a commandline, as well as to run them interactively from some kind of menu system to select a command from that you want to run. To achieve this I added a default command "Menu" that shows a numbered list of all the commands and uses a selectionprompt to chose the command. I know the menu system wouldn't handle the command arguments, however we use appsettings a lot.

Describe the solution you'd like
It would be nice to have such a command OOB, and if that is a to specific request to have at least the possibility to have somewhere a list of the registered commands available in the context in a way we can also Run them.

Describe alternatives you've considered
An awfull solution I quickly put together, however it works for me.
It was (at least to my understanding) rather difficult to achieve this currently and I had to do a lot of workaround to make it work, the execute method of the default Menu command:
public override int Execute(CommandContext context)
{
var type = GetType().Assembly.GetName();
_con.MarkupLine($"[yellow]{type.Name} v.{type.Version.Major}.{type.Version.Minor}[/] dotnetcore REST Client");
_con.MarkupLine("[blue]--help for info[/]");

    var commandAppService = _provider.GetService(typeof(Spectre.Console.Cli.ICommandApp));
    var commandApp = commandAppService.GetType().GetField("_app", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(commandAppService);
    var configurator = commandApp.GetType().GetField("_configurator", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(commandApp);
    var commands = configurator.GetType().GetProperty("Commands").GetValue(configurator) as IList;

    var prompt = new SelectionPrompt<SelectionCommandItem>() { SearchEnabled = true }
        .Title("Select command to run:")
        .PageSize(20)
        .MoreChoicesText("[yellow][[more]][/]");
    prompt.Converter = item => { return $"[[{item.Sequence + 1:000}]] - {item.CommandTitle}"; };

    string commandTitle;
    for (byte count = 0; count < commands.Count - 1; count++)
    {
        var configuredCommandType = commands[count].GetType();
        var commandType = configuredCommandType.GetProperty("CommandType").GetValue(commands[count]) as Type;
        var executeType = (commandType.BaseType.Name?.StartsWith("Async") == true) ? "Async" : string.Empty;
        var settingsType = configuredCommandType.GetProperty("SettingsType").GetValue(commands[count]) as Type;
        commandTitle = configuredCommandType.GetProperty("Name").GetValue(commands[count]) as string;
        prompt.AddChoice(new SelectionCommandItem(count, commandTitle, commandType, settingsType, executeType));
    }

    prompt.AddChoice(new SelectionCommandItem(commands.Count, "QUIT", null, null, null));

    var selection = _con.Prompt(prompt);

    if (selection.CommandTitle.Equals("QUIT"))
        return 0;

    _con.MarkupLine($"Executing command: [yellow]{selection.CommandTitle}[/]");

    var command = ActivatorUtilities.CreateInstance(_provider, selection.CommandType);
    var settings = ActivatorUtilities.CreateInstance(_provider, selection.SettingsType);
    
    selection.CommandType.InvokeMember($"Execute{selection.ExecuteType}", BindingFlags.InvokeMethod, null, command, [context, settings]);

    return 0;
}

and

public sealed class SelectionCommandItem(byte sequence, string commandTitle, Type commandType, Type settingsType, string executeType)
{
public byte Sequence { get; } = sequence;
public string CommandTitle { get; } = commandTitle;
public Type CommandType { get; } = commandType;
public Type SettingsType { get; } = settingsType;
public string ExecuteType { get; } = executeType;
}


Please upvote 👍 this issue if you are interested in it.

@flupkede flupkede changed the title Choice menu for all the command Choice menu for all the registered commands Sep 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Todo 🕑
Development

No branches or pull requests

1 participant