Improve parameter tab completion
Fixed bad permissions being used for subcommands (older change) Updated Commodore and made it only register a command node once - this fixes main command argument handling as Bukkit's handler is removed Added option to ignore the tab completion provided by the parameter type (param converter) Only showing matching completions (it has to start with the text typed in) #82
This commit is contained in:
parent
82858b0a41
commit
9dae442950
5 changed files with 79 additions and 35 deletions
|
@ -194,7 +194,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>me.lucko</groupId>
|
<groupId>me.lucko</groupId>
|
||||||
<artifactId>commodore</artifactId>
|
<artifactId>commodore</artifactId>
|
||||||
<version>1.7</version>
|
<version>1.8</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|
|
@ -4,18 +4,28 @@ import buttondevteam.lib.architecture.ButtonPlugin;
|
||||||
import buttondevteam.lib.chat.Command2;
|
import buttondevteam.lib.chat.Command2;
|
||||||
import buttondevteam.lib.chat.CommandClass;
|
import buttondevteam.lib.chat.CommandClass;
|
||||||
import buttondevteam.lib.chat.ICommand2MC;
|
import buttondevteam.lib.chat.ICommand2MC;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
import org.bukkit.plugin.Plugin;
|
import org.bukkit.plugin.Plugin;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
@CommandClass
|
@CommandClass
|
||||||
public class ChromaCommand extends ICommand2MC {
|
public class ChromaCommand extends ICommand2MC {
|
||||||
|
public ChromaCommand() {
|
||||||
|
getManager().addParamConverter(ButtonPlugin.class, name ->
|
||||||
|
(ButtonPlugin) Optional.ofNullable(Bukkit.getPluginManager().getPlugin(name))
|
||||||
|
.filter(plugin -> plugin instanceof ButtonPlugin).orElse(null),
|
||||||
|
"No Chroma plugin found by that name.", () -> Arrays.stream(Bukkit.getPluginManager().getPlugins())
|
||||||
|
.filter(plugin -> plugin instanceof ButtonPlugin).map(Plugin::getName)::iterator);
|
||||||
|
}
|
||||||
|
|
||||||
@Command2.Subcommand
|
@Command2.Subcommand
|
||||||
public void reload(CommandSender sender, @Command2.OptionalArg Plugin plugin) {
|
public void reload(CommandSender sender, @Command2.OptionalArg ButtonPlugin plugin) {
|
||||||
if (plugin == null)
|
if (plugin == null)
|
||||||
plugin = MainPlugin.Instance;
|
plugin = MainPlugin.Instance;
|
||||||
if (!(plugin instanceof ButtonPlugin)) //Probably not a good idea to allow reloading any plugin's config
|
if (plugin.tryReloadConfig())
|
||||||
sender.sendMessage("§c" + plugin.getName() + " doesn't support this.");
|
|
||||||
else if (((ButtonPlugin) plugin).tryReloadConfig())
|
|
||||||
sender.sendMessage("§b" + plugin.getName() + " config reloaded.");
|
sender.sendMessage("§b" + plugin.getName() + " config reloaded.");
|
||||||
else
|
else
|
||||||
sender.sendMessage("§cFailed to reload config. Check console.");
|
sender.sendMessage("§cFailed to reload config. Check console.");
|
||||||
|
|
|
@ -67,9 +67,10 @@ public class ComponentCommand extends ICommand2MC {
|
||||||
return getPluginComponents(plugin).map(c -> c.getClass().getSimpleName())::iterator;
|
return getPluginComponents(plugin).map(c -> c.getClass().getSimpleName())::iterator;
|
||||||
}
|
}
|
||||||
|
|
||||||
@CustomTabCompleteMethod(param = "plugin")
|
@CustomTabCompleteMethod(param = "plugin", subcommand = {"list", "enable", "disable"}, ignoreTypeCompletion = true)
|
||||||
public Iterable<String> list() {
|
public Iterable<String> pluginTabcomplete() {
|
||||||
return Arrays.stream(Bukkit.getPluginManager().getPlugins()).map(Plugin::getName)::iterator;
|
return Component.getComponents().values().stream().map(Component::getPlugin)
|
||||||
|
.distinct().map(Plugin::getName)::iterator;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean enable_disable(CommandSender sender, Plugin plugin, String component, boolean enable, boolean permanent) {
|
private boolean enable_disable(CommandSender sender, Plugin plugin, String component, boolean enable, boolean permanent) {
|
||||||
|
|
|
@ -19,9 +19,7 @@ import org.bukkit.Location;
|
||||||
import org.bukkit.OfflinePlayer;
|
import org.bukkit.OfflinePlayer;
|
||||||
import org.bukkit.command.*;
|
import org.bukkit.command.*;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.EventHandler;
|
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import org.bukkit.event.server.TabCompleteEvent;
|
|
||||||
import org.bukkit.permissions.Permission;
|
import org.bukkit.permissions.Permission;
|
||||||
import org.bukkit.permissions.PermissionDefault;
|
import org.bukkit.permissions.PermissionDefault;
|
||||||
import org.javatuples.Triplet;
|
import org.javatuples.Triplet;
|
||||||
|
@ -33,6 +31,7 @@ import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
@ -70,9 +69,9 @@ public class Command2MC extends Command2<ICommand2MC, Command2MCSender> implemen
|
||||||
}
|
}
|
||||||
String pg = permGroup(command, method);
|
String pg = permGroup(command, method);
|
||||||
if (pg.length() == 0) continue;
|
if (pg.length() == 0) continue;
|
||||||
perm = "chroma." + pg;
|
String permGroup = "chroma." + pg;
|
||||||
if (Bukkit.getPluginManager().getPermission(perm) == null) //It may occur multiple times
|
if (Bukkit.getPluginManager().getPermission(permGroup) == null) //It may occur multiple times
|
||||||
Bukkit.getPluginManager().addPermission(new Permission(perm,
|
Bukkit.getPluginManager().addPermission(new Permission(permGroup,
|
||||||
PermissionDefault.OP)); //Do not allow any commands that belong to a group
|
PermissionDefault.OP)); //Do not allow any commands that belong to a group
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,12 +171,16 @@ public class Command2MC extends Command2<ICommand2MC, Command2MCSender> implemen
|
||||||
.map(comp -> component.getClass().getSimpleName().equals(comp.getClass().getSimpleName())).orElse(false));
|
.map(comp -> component.getClass().getSimpleName().equals(comp.getClass().getSimpleName())).orElse(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
/*@EventHandler
|
||||||
public void onTabComplete(TabCompleteEvent event) {
|
public void onTabComplete(TabCompleteEvent event) {
|
||||||
|
try {
|
||||||
|
event.getCompletions().clear(); //Remove player names
|
||||||
|
} catch (UnsupportedOperationException e) {
|
||||||
//System.out.println("Tabcomplete: " + event.getBuffer());
|
//System.out.println("Tabcomplete: " + event.getBuffer());
|
||||||
//System.out.println("First completion: " + event.getCompletions().stream().findFirst().orElse("no completions"));
|
//System.out.println("First completion: " + event.getCompletions().stream().findFirst().orElse("no completions"));
|
||||||
event.getCompletions().clear();
|
//System.out.println("Listeners: " + Arrays.toString(event.getHandlers().getRegisteredListeners()));
|
||||||
}
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean handleCommand(Command2MCSender sender, String commandline) {
|
public boolean handleCommand(Command2MCSender sender, String commandline) {
|
||||||
|
@ -231,11 +234,13 @@ public class Command2MC extends Command2<ICommand2MC, Command2MCSender> implemen
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException {
|
public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException {
|
||||||
|
//System.out.println("Correct tabcomplete queried");
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException {
|
public List<String> tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException {
|
||||||
|
//System.out.println("Correct tabcomplete queried");
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -245,22 +250,33 @@ public class Command2MC extends Command2<ICommand2MC, Command2MCSender> implemen
|
||||||
|
|
||||||
private static LiteralCommandNode<Object> appendSubcommand(String path, CommandNode<Object> parent,
|
private static LiteralCommandNode<Object> appendSubcommand(String path, CommandNode<Object> parent,
|
||||||
SubcommandData<ICommand2MC> subcommand) {
|
SubcommandData<ICommand2MC> subcommand) {
|
||||||
|
LiteralCommandNode<Object> scmd;
|
||||||
|
if ((scmd = (LiteralCommandNode<Object>) parent.getChild(path)) != null)
|
||||||
|
return scmd;
|
||||||
var scmdBuilder = LiteralArgumentBuilder.literal(path);
|
var scmdBuilder = LiteralArgumentBuilder.literal(path);
|
||||||
if (subcommand != null)
|
if (subcommand != null)
|
||||||
scmdBuilder.requires(o -> {
|
scmdBuilder.requires(o -> {
|
||||||
var sender = commodore.getBukkitSender(o);
|
var sender = commodore.getBukkitSender(o);
|
||||||
return ButtonPlugin.getCommand2MC().hasPermission(sender, subcommand.command, subcommand.method);
|
return ButtonPlugin.getCommand2MC().hasPermission(sender, subcommand.command, subcommand.method);
|
||||||
});
|
});
|
||||||
var scmd = scmdBuilder.build();
|
scmd = scmdBuilder.build();
|
||||||
parent.addChild(scmd);
|
parent.addChild(scmd);
|
||||||
return scmd;
|
return scmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void registerTabcomplete(ICommand2MC command2MC, List<SubcommandData<ICommand2MC>> subcmds, Command bukkitCommand) {
|
private static void registerTabcomplete(ICommand2MC command2MC, List<SubcommandData<ICommand2MC>> subcmds, Command bukkitCommand) {
|
||||||
if (commodore == null)
|
if (commodore == null) {
|
||||||
commodore = CommodoreProvider.getCommodore(MainPlugin.Instance); //Register all to the Core, it's easier
|
commodore = CommodoreProvider.getCommodore(MainPlugin.Instance); //Register all to the Core, it's easier
|
||||||
|
commodore.register(LiteralArgumentBuilder.literal("un").redirect(RequiredArgumentBuilder.argument("unsomething",
|
||||||
|
StringArgumentType.word()).suggests((context, builder) -> builder.suggest("untest").buildFuture()).build()));
|
||||||
|
}
|
||||||
String[] path = command2MC.getCommandPath().split(" ");
|
String[] path = command2MC.getCommandPath().split(" ");
|
||||||
var maincmd = LiteralArgumentBuilder.literal(path[0]).build();
|
var shouldRegister = new AtomicBoolean(true);
|
||||||
|
@SuppressWarnings("unchecked") var maincmd = commodore.getRegisteredNodes().stream()
|
||||||
|
.filter(node -> node.getLiteral().equalsIgnoreCase(path[0]))
|
||||||
|
.filter(node -> { shouldRegister.set(false); return true; })
|
||||||
|
.map(node -> (LiteralCommandNode<Object>) node).findAny()
|
||||||
|
.orElseGet(() -> LiteralArgumentBuilder.literal(path[0]).build()); //Commodore 1.8 removes previous nodes
|
||||||
var cmd = maincmd;
|
var cmd = maincmd;
|
||||||
for (int i = 1; i < path.length; i++) {
|
for (int i = 1; i < path.length; i++) {
|
||||||
var scmd = subcmds.stream().filter(sd -> sd.method.getName().equals("def")).findAny().orElse(null);
|
var scmd = subcmds.stream().filter(sd -> sd.method.getName().equals("def")).findAny().orElse(null);
|
||||||
|
@ -274,7 +290,7 @@ public class Command2MC extends Command2<ICommand2MC, Command2MCSender> implemen
|
||||||
.orElseGet(() -> new String[]{
|
.orElseGet(() -> new String[]{
|
||||||
ButtonPlugin.getCommand2MC().getCommandPath(method.getName(), ' ').trim()
|
ButtonPlugin.getCommand2MC().getCommandPath(method.getName(), ' ').trim()
|
||||||
});
|
});
|
||||||
return Arrays.stream(paths).map(name -> new Triplet<>(name, ctcm.param(), method));
|
return Arrays.stream(paths).map(name -> new Triplet<>(name, ctcm, method));
|
||||||
})).collect(Collectors.toList());
|
})).collect(Collectors.toList());
|
||||||
for (SubcommandData<ICommand2MC> subcmd : subcmds) {
|
for (SubcommandData<ICommand2MC> subcmd : subcmds) {
|
||||||
String subpathAsOne = ButtonPlugin.getCommand2MC().getCommandPath(subcmd.method.getName(), ' ').trim();
|
String subpathAsOne = ButtonPlugin.getCommand2MC().getCommandPath(subcmd.method.getName(), ' ').trim();
|
||||||
|
@ -323,20 +339,24 @@ public class Command2MC extends Command2<ICommand2MC, Command2MCSender> implemen
|
||||||
val param = subcmd.parameters[i - 1];
|
val param = subcmd.parameters[i - 1];
|
||||||
val customTC = Optional.ofNullable(parameter.getAnnotation(CustomTabComplete.class))
|
val customTC = Optional.ofNullable(parameter.getAnnotation(CustomTabComplete.class))
|
||||||
.map(CustomTabComplete::value);
|
.map(CustomTabComplete::value);
|
||||||
final Optional<Method> customTCmethod = customTCmethods.stream().filter(t -> subpathAsOne.equalsIgnoreCase(t.getValue0()))
|
var customTCmethod = customTCmethods.stream().filter(t -> subpathAsOne.equalsIgnoreCase(t.getValue0()))
|
||||||
.filter(t -> param.replaceAll("[\\[\\]<>]", "").equalsIgnoreCase(t.getValue1()))
|
.filter(t -> param.replaceAll("[\\[\\]<>]", "").equalsIgnoreCase(t.getValue1().param()))
|
||||||
.map(Triplet::getValue2).findAny();
|
.findAny();
|
||||||
var argb = RequiredArgumentBuilder.argument(param, type)
|
var argb = RequiredArgumentBuilder.argument(param, type)
|
||||||
.suggests((SuggestionProvider<Object>) (context, builder) -> {
|
.suggests((SuggestionProvider<Object>) (context, builder) -> {
|
||||||
if (parameter.isVarArgs()) { //Do it before the builder is used
|
if (parameter.isVarArgs()) { //Do it before the builder is used
|
||||||
int x = context.getInput().lastIndexOf(' ') + 1;
|
int nextTokenStart = context.getInput().lastIndexOf(' ') + 1;
|
||||||
builder = builder.createOffset(x);
|
builder = builder.createOffset(nextTokenStart);
|
||||||
}
|
}
|
||||||
if (customTC.isPresent())
|
if (customTC.isPresent())
|
||||||
for (val ctc : customTC.get())
|
for (val ctc : customTC.get())
|
||||||
builder.suggest(ctc);
|
builder.suggest(ctc);
|
||||||
|
boolean ignoreCustomParamType = false;
|
||||||
if (customTCmethod.isPresent()) {
|
if (customTCmethod.isPresent()) {
|
||||||
final var method = customTCmethod.get();
|
var tr = customTCmethod.get();
|
||||||
|
if (tr.getValue1().ignoreTypeCompletion())
|
||||||
|
ignoreCustomParamType = true;
|
||||||
|
final var method = tr.getValue2();
|
||||||
val params = method.getParameters();
|
val params = method.getParameters();
|
||||||
val args = new Object[params.length];
|
val args = new Object[params.length];
|
||||||
for (int j = 0, k = 0; j < args.length && k < subcmd.parameters.length; j++) {
|
for (int j = 0, k = 0; j < args.length && k < subcmd.parameters.length; j++) {
|
||||||
|
@ -382,7 +402,7 @@ public class Command2MC extends Command2<ICommand2MC, Command2MCSender> implemen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (customParamType) {
|
if (!ignoreCustomParamType && customParamType) {
|
||||||
val converter = ButtonPlugin.getCommand2MC().paramConverters.get(ptype);
|
val converter = ButtonPlugin.getCommand2MC().paramConverters.get(ptype);
|
||||||
if (converter == null)
|
if (converter == null)
|
||||||
TBMCCoreAPI.SendException("Could not find a suitable converter for type " + ptype.getSimpleName(),
|
TBMCCoreAPI.SendException("Could not find a suitable converter for type " + ptype.getSimpleName(),
|
||||||
|
@ -395,19 +415,27 @@ public class Command2MC extends Command2<ICommand2MC, Command2MCSender> implemen
|
||||||
}
|
}
|
||||||
if (ptype == boolean.class || ptype == Boolean.class)
|
if (ptype == boolean.class || ptype == Boolean.class)
|
||||||
builder.suggest("true").suggest("false");
|
builder.suggest("true").suggest("false");
|
||||||
|
final String loweredInput = builder.getRemaining().toLowerCase();
|
||||||
return builder.suggest(param).buildFuture().whenComplete((s, e) -> //The list is automatically ordered
|
return builder.suggest(param).buildFuture().whenComplete((s, e) -> //The list is automatically ordered
|
||||||
s.getList().add(s.getList().remove(0))); //So we need to put the <param> at the end after that
|
s.getList().add(s.getList().remove(0))) //So we need to put the <param> at the end after that
|
||||||
|
.whenComplete((ss, e) -> ss.getList().removeIf(s -> {
|
||||||
|
String text = s.getText();
|
||||||
|
return !text.startsWith("<") && !text.startsWith("[") && !text.toLowerCase().startsWith(loweredInput);
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
var arg = argb.build();
|
var arg = argb.build();
|
||||||
scmd.addChild(arg);
|
scmd.addChild(arg);
|
||||||
scmd = arg;
|
scmd = arg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (shouldRegister.get()) {
|
||||||
commodore.register(maincmd);
|
commodore.register(maincmd);
|
||||||
|
//MinecraftArgumentTypes.getByKey(NamespacedKey.minecraft(""))
|
||||||
var prefixedcmd = new LiteralCommandNode<>(command2MC.getPlugin().getName().toLowerCase() + ":" + path[0], maincmd.getCommand(), maincmd.getRequirement(), maincmd.getRedirect(), maincmd.getRedirectModifier(), maincmd.isFork());
|
var prefixedcmd = new LiteralCommandNode<>(command2MC.getPlugin().getName().toLowerCase() + ":" + path[0], maincmd.getCommand(), maincmd.getRequirement(), maincmd.getRedirect(), maincmd.getRedirectModifier(), maincmd.isFork());
|
||||||
for (var child : maincmd.getChildren())
|
for (var child : maincmd.getChildren())
|
||||||
prefixedcmd.addChild(child);
|
prefixedcmd.addChild(child);
|
||||||
commodore.register(prefixedcmd);
|
commodore.register(prefixedcmd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,4 +21,9 @@ public @interface CustomTabCompleteMethod {
|
||||||
* The subcommand(s) which have the parameter, by default the method's name
|
* The subcommand(s) which have the parameter, by default the method's name
|
||||||
*/
|
*/
|
||||||
String[] subcommand() default {};
|
String[] subcommand() default {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parameter types can provide tab completions. This allows disabling that.
|
||||||
|
*/
|
||||||
|
boolean ignoreTypeCompletion() default false;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue