diff --git a/Chroma-Core/pom.xml b/Chroma-Core/pom.xml index 32357d5..c8d0e79 100755 --- a/Chroma-Core/pom.xml +++ b/Chroma-Core/pom.xml @@ -194,7 +194,7 @@ me.lucko commodore - 1.7 + 1.8 compile diff --git a/Chroma-Core/src/main/java/buttondevteam/core/ChromaCommand.java b/Chroma-Core/src/main/java/buttondevteam/core/ChromaCommand.java index 499818f..a9d8699 100644 --- a/Chroma-Core/src/main/java/buttondevteam/core/ChromaCommand.java +++ b/Chroma-Core/src/main/java/buttondevteam/core/ChromaCommand.java @@ -4,18 +4,28 @@ import buttondevteam.lib.architecture.ButtonPlugin; import buttondevteam.lib.chat.Command2; import buttondevteam.lib.chat.CommandClass; import buttondevteam.lib.chat.ICommand2MC; +import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.plugin.Plugin; +import java.util.Arrays; +import java.util.Optional; + @CommandClass 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 - public void reload(CommandSender sender, @Command2.OptionalArg Plugin plugin) { + public void reload(CommandSender sender, @Command2.OptionalArg ButtonPlugin plugin) { if (plugin == null) plugin = MainPlugin.Instance; - if (!(plugin instanceof ButtonPlugin)) //Probably not a good idea to allow reloading any plugin's config - sender.sendMessage("§c" + plugin.getName() + " doesn't support this."); - else if (((ButtonPlugin) plugin).tryReloadConfig()) + if (plugin.tryReloadConfig()) sender.sendMessage("§b" + plugin.getName() + " config reloaded."); else sender.sendMessage("§cFailed to reload config. Check console."); diff --git a/Chroma-Core/src/main/java/buttondevteam/core/ComponentCommand.java b/Chroma-Core/src/main/java/buttondevteam/core/ComponentCommand.java index d2b8bfd..4c601d8 100644 --- a/Chroma-Core/src/main/java/buttondevteam/core/ComponentCommand.java +++ b/Chroma-Core/src/main/java/buttondevteam/core/ComponentCommand.java @@ -67,9 +67,10 @@ public class ComponentCommand extends ICommand2MC { return getPluginComponents(plugin).map(c -> c.getClass().getSimpleName())::iterator; } - @CustomTabCompleteMethod(param = "plugin") - public Iterable list() { - return Arrays.stream(Bukkit.getPluginManager().getPlugins()).map(Plugin::getName)::iterator; + @CustomTabCompleteMethod(param = "plugin", subcommand = {"list", "enable", "disable"}, ignoreTypeCompletion = true) + public Iterable pluginTabcomplete() { + 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) { diff --git a/Chroma-Core/src/main/java/buttondevteam/lib/chat/Command2MC.java b/Chroma-Core/src/main/java/buttondevteam/lib/chat/Command2MC.java index bb2eccf..49875db 100644 --- a/Chroma-Core/src/main/java/buttondevteam/lib/chat/Command2MC.java +++ b/Chroma-Core/src/main/java/buttondevteam/lib/chat/Command2MC.java @@ -19,9 +19,7 @@ import org.bukkit.Location; import org.bukkit.OfflinePlayer; import org.bukkit.command.*; import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; -import org.bukkit.event.server.TabCompleteEvent; import org.bukkit.permissions.Permission; import org.bukkit.permissions.PermissionDefault; import org.javatuples.Triplet; @@ -33,6 +31,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Optional; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -70,9 +69,9 @@ public class Command2MC extends Command2 implemen } String pg = permGroup(command, method); if (pg.length() == 0) continue; - perm = "chroma." + pg; - if (Bukkit.getPluginManager().getPermission(perm) == null) //It may occur multiple times - Bukkit.getPluginManager().addPermission(new Permission(perm, + String permGroup = "chroma." + pg; + if (Bukkit.getPluginManager().getPermission(permGroup) == null) //It may occur multiple times + Bukkit.getPluginManager().addPermission(new Permission(permGroup, PermissionDefault.OP)); //Do not allow any commands that belong to a group } @@ -172,12 +171,16 @@ public class Command2MC extends Command2 implemen .map(comp -> component.getClass().getSimpleName().equals(comp.getClass().getSimpleName())).orElse(false)); } - @EventHandler + /*@EventHandler public void onTabComplete(TabCompleteEvent event) { - //System.out.println("Tabcomplete: " + event.getBuffer()); - //System.out.println("First completion: " + event.getCompletions().stream().findFirst().orElse("no completions")); - event.getCompletions().clear(); - } + try { + event.getCompletions().clear(); //Remove player names + } catch (UnsupportedOperationException e) { + //System.out.println("Tabcomplete: " + event.getBuffer()); + //System.out.println("First completion: " + event.getCompletions().stream().findFirst().orElse("no completions")); + //System.out.println("Listeners: " + Arrays.toString(event.getHandlers().getRegisteredListeners())); + } + }*/ @Override public boolean handleCommand(Command2MCSender sender, String commandline) { @@ -231,11 +234,13 @@ public class Command2MC extends Command2 implemen @Override public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + //System.out.println("Correct tabcomplete queried"); return Collections.emptyList(); } @Override public List tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException { + //System.out.println("Correct tabcomplete queried"); return Collections.emptyList(); } } @@ -245,22 +250,33 @@ public class Command2MC extends Command2 implemen private static LiteralCommandNode appendSubcommand(String path, CommandNode parent, SubcommandData subcommand) { + LiteralCommandNode scmd; + if ((scmd = (LiteralCommandNode) parent.getChild(path)) != null) + return scmd; var scmdBuilder = LiteralArgumentBuilder.literal(path); if (subcommand != null) scmdBuilder.requires(o -> { var sender = commodore.getBukkitSender(o); return ButtonPlugin.getCommand2MC().hasPermission(sender, subcommand.command, subcommand.method); }); - var scmd = scmdBuilder.build(); + scmd = scmdBuilder.build(); parent.addChild(scmd); return scmd; } private static void registerTabcomplete(ICommand2MC command2MC, List> subcmds, Command bukkitCommand) { - if (commodore == null) + if (commodore == null) { 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(" "); - 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) node).findAny() + .orElseGet(() -> LiteralArgumentBuilder.literal(path[0]).build()); //Commodore 1.8 removes previous nodes var cmd = maincmd; for (int i = 1; i < path.length; i++) { var scmd = subcmds.stream().filter(sd -> sd.method.getName().equals("def")).findAny().orElse(null); @@ -274,7 +290,7 @@ public class Command2MC extends Command2 implemen .orElseGet(() -> new String[]{ 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()); for (SubcommandData subcmd : subcmds) { String subpathAsOne = ButtonPlugin.getCommand2MC().getCommandPath(subcmd.method.getName(), ' ').trim(); @@ -323,20 +339,24 @@ public class Command2MC extends Command2 implemen val param = subcmd.parameters[i - 1]; val customTC = Optional.ofNullable(parameter.getAnnotation(CustomTabComplete.class)) .map(CustomTabComplete::value); - final Optional customTCmethod = customTCmethods.stream().filter(t -> subpathAsOne.equalsIgnoreCase(t.getValue0())) - .filter(t -> param.replaceAll("[\\[\\]<>]", "").equalsIgnoreCase(t.getValue1())) - .map(Triplet::getValue2).findAny(); + var customTCmethod = customTCmethods.stream().filter(t -> subpathAsOne.equalsIgnoreCase(t.getValue0())) + .filter(t -> param.replaceAll("[\\[\\]<>]", "").equalsIgnoreCase(t.getValue1().param())) + .findAny(); var argb = RequiredArgumentBuilder.argument(param, type) .suggests((SuggestionProvider) (context, builder) -> { if (parameter.isVarArgs()) { //Do it before the builder is used - int x = context.getInput().lastIndexOf(' ') + 1; - builder = builder.createOffset(x); + int nextTokenStart = context.getInput().lastIndexOf(' ') + 1; + builder = builder.createOffset(nextTokenStart); } if (customTC.isPresent()) for (val ctc : customTC.get()) builder.suggest(ctc); + boolean ignoreCustomParamType = false; 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 args = new Object[params.length]; for (int j = 0, k = 0; j < args.length && k < subcmd.parameters.length; j++) { @@ -382,7 +402,7 @@ public class Command2MC extends Command2 implemen } } } - if (customParamType) { + if (!ignoreCustomParamType && customParamType) { val converter = ButtonPlugin.getCommand2MC().paramConverters.get(ptype); if (converter == null) TBMCCoreAPI.SendException("Could not find a suitable converter for type " + ptype.getSimpleName(), @@ -395,19 +415,27 @@ public class Command2MC extends Command2 implemen } if (ptype == boolean.class || ptype == Boolean.class) builder.suggest("true").suggest("false"); + final String loweredInput = builder.getRemaining().toLowerCase(); 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 at the end after that + s.getList().add(s.getList().remove(0))) //So we need to put the 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(); scmd.addChild(arg); scmd = arg; } } - commodore.register(maincmd); - 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()) - prefixedcmd.addChild(child); - commodore.register(prefixedcmd); + if (shouldRegister.get()) { + 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()); + for (var child : maincmd.getChildren()) + prefixedcmd.addChild(child); + commodore.register(prefixedcmd); + } } } } diff --git a/Chroma-Core/src/main/java/buttondevteam/lib/chat/CustomTabCompleteMethod.java b/Chroma-Core/src/main/java/buttondevteam/lib/chat/CustomTabCompleteMethod.java index 74c95aa..4c425c1 100644 --- a/Chroma-Core/src/main/java/buttondevteam/lib/chat/CustomTabCompleteMethod.java +++ b/Chroma-Core/src/main/java/buttondevteam/lib/chat/CustomTabCompleteMethod.java @@ -21,4 +21,9 @@ public @interface CustomTabCompleteMethod { * The subcommand(s) which have the parameter, by default the method's name */ String[] subcommand() default {}; + + /** + * Parameter types can provide tab completions. This allows disabling that. + */ + boolean ignoreTypeCompletion() default false; }