Skip to content

Add a greedy "command argument" #307

@willkroboth

Description

@willkroboth

Description

It is currently possible to import Brigadier and create a GreedyStringArgument that lets users input a Minecraft command as an argument like so:

From https://commandapi.jorel.dev/8.4.0/brigadiersuggestions.html

ArgumentSuggestions commandSuggestions = (info, builder) -> {
    // The current argument, which is a full command
    String arg = info.currentArg();

    // Identify the position of the current argument
    int start;
    if(arg.contains(" ")) {
        // Current argument contains spaces - it starts after the last space and after the start of this argument.
        start = builder.getStart() + arg.lastIndexOf(' ') + 1;
    } else {
        // Input starts at the start of this argument
        start = builder.getStart();
    }
    
    // Parse command using brigadier
    ParseResults<?> parseResults = Brigadier.getCommandDispatcher()
        .parse(info.currentArg(), Brigadier.getBrigadierSourceFromCommandSender(info.sender()));
    
    // Intercept any parsing errors indicating an invalid command
    for(CommandSyntaxException exception : parseResults.getExceptions().values()) {
        // Raise the error, with the cursor offset to line up with the argument
        throw new CommandSyntaxException(exception.getType(), exception.getRawMessage(), exception.getInput(), exception.getCursor() + start);
    }

    return Brigadier
        .getCommandDispatcher()
        .getCompletionSuggestions(parseResults)
        .thenApply((suggestionsObject) -> {
            // Brigadier's suggestions
            Suggestions suggestions = (Suggestions) suggestionsObject;

            return new Suggestions(
                // Offset the index range of the suggestions by the start of the current argument
                new StringRange(start, start + suggestions.getRange().getLength()),
                // Copy the suggestions
                suggestions.getList()
            );
        });
};

new CommandAPICommand("commandargument")
    .withArguments(new GreedyStringArgument("command").replaceSuggestions(commandSuggestions))
    .executes((sender, args) -> {
        // Run the command using Bukkit.dispatchCommand()
        Bukkit.dispatchCommand(sender, (String) args[0]);
    }).register();

Similar to how the ListArgument is a GreedyStringArgument that automatically applies a special ArgumentSuggesstions rule (#275), it would be useful if there was a CommandArgument that implemented this special ArgumentSuggestions for you. This would let users create commands like the vanilla /execute run [another command] without having to import Brigadier or figuring out the complicated ArgumentSuggestions.

Expected code

The same example from before, but using the CommandArgument (much simpler)

new CommandAPICommand("commandargument")
                .withArguments(new CommandArgument("command"))
                .executes((sender, args) -> {
                    // Run the command using Bukkit.dispatchCommand()
                    Bukkit.dispatchCommand(sender, (String) args[0]);
                }).register();

A sudo command for running a command as another player

new CommandAPICommand("sudo")
                .withArguments(
                        new PlayerArgument("target"),
                        new CommandArgument("command")
                ).executes(
                        (sender, args) -> {
                            Player target = (Player) args[0];
                            String command = (String) args[1];
                            Bukkit.dispatchCommand(target, command);
                        }
                ).register();

Extra details

I don't know if the Brigadier methods used already do this, but it would be nice if the CommandArgument could also receive plugin commands. Maybe this can only work after converting the plugin commands using the CommandAPI.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestimplemented for next releaseThis has been implemented in the current dev build for the next public release

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions