Add a greedy "command argument"
willkroboth opened this issue · comments
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.
The Brigadier methods only use Minecraft-registered commands. It may be possible to use the TabCompleteEvent
to generate plugin command completions and shove them into the argument if tab completion produces any results.
Merged to dev/dev
for 8.6.0's release.