diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index 163a1c5d..b70af4c1 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -1,6 +1,9 @@ \ No newline at end of file diff --git a/bukkit/src/main/java/co/aikar/commands/BukkitRootCommand.java b/bukkit/src/main/java/co/aikar/commands/BukkitRootCommand.java index 98f195ac..8c0ce9d5 100644 --- a/bukkit/src/main/java/co/aikar/commands/BukkitRootCommand.java +++ b/bukkit/src/main/java/co/aikar/commands/BukkitRootCommand.java @@ -52,8 +52,9 @@ public class BukkitRootCommand extends Command implements RootCommand { } @Override - public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { - return getTabCompletions(manager.getCommandIssuer(sender), alias, args); + public List tabComplete(CommandSender sender, String commandLabel, String[] args) throws IllegalArgumentException { + if (commandLabel.contains(":")) commandLabel = ACFPatterns.COLON.split(commandLabel, 2)[1]; + return getTabCompletions(manager.getCommandIssuer(sender), commandLabel, args); } @Override @@ -71,8 +72,6 @@ public class BukkitRootCommand extends Command implements RootCommand { public void addChild(BaseCommand command) { if (this.defCommand == null || !command.subCommands.get(BaseCommand.DEFAULT).isEmpty()) { this.defCommand = command; - //this.setDescription(command.getDescription()); - //this.setUsage(command.getUsage()); } addChildShared(this.children, this.subCommands, command); setPermission(getUniquePermission()); diff --git a/core/src/main/java/co/aikar/commands/BaseCommand.java b/core/src/main/java/co/aikar/commands/BaseCommand.java index 71b8b5aa..16572049 100644 --- a/core/src/main/java/co/aikar/commands/BaseCommand.java +++ b/core/src/main/java/co/aikar/commands/BaseCommand.java @@ -23,6 +23,7 @@ package co.aikar.commands; +import co.aikar.commands.CommandRouter.RouteSearch; import co.aikar.commands.annotation.CatchAll; import co.aikar.commands.annotation.CatchUnknown; import co.aikar.commands.annotation.CommandAlias; @@ -50,7 +51,6 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Optional; import java.util.Set; import java.util.Stack; import java.util.stream.Collectors; @@ -67,7 +67,6 @@ import java.util.stream.Stream; * then each actionable command is a sub command */ -@SuppressWarnings("unused") public abstract class BaseCommand { /** @@ -310,7 +309,6 @@ public abstract class BaseCommand { */ private void registerSubcommands() { final Annotations annotations = manager.getAnnotations(); - boolean foundDefault = false; boolean foundCatchUnknown = false; boolean isParentEmpty = parentSubcommand == null || parentSubcommand.isEmpty(); @@ -318,22 +316,14 @@ public abstract class BaseCommand { method.setAccessible(true); String sublist = null; String sub = getSubcommandValue(method); - final boolean def = annotations.hasAnnotation(method, Default.class); final String helpCommand = annotations.getAnnotationValue(method, HelpCommand.class, Annotations.NOTHING); final String commandAliases = annotations.getAnnotationValue(method, CommandAlias.class, Annotations.NOTHING); - if (!isParentEmpty && def) { - sub = parentSubcommand; - } - if (isParentEmpty && (def || (!foundDefault && helpCommand != null))) { - if (!foundDefault) { - if (def) { - this.subCommands.get(DEFAULT).clear(); - foundDefault = true; - } - registerSubcommand(method, DEFAULT); + if (annotations.hasAnnotation(method, Default.class)) { + if (!isParentEmpty) { + sub = parentSubcommand; } else { - ACFUtil.sneaky(new IllegalStateException("Multiple @Default/@HelpCommand commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName())); + registerSubcommand(method, DEFAULT); } } @@ -359,7 +349,7 @@ public abstract class BaseCommand { } registerSubcommand(method, CATCHUNKNOWN); } else { - ACFUtil.sneaky(new IllegalStateException("Multiple @UnknownHandler/@HelpCommand commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName())); + ACFUtil.sneaky(new IllegalStateException("Multiple @CatchUnknown/@HelpCommand commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName())); } } else if (preCommand) { if (this.preCommandHandler == null) { @@ -506,48 +496,16 @@ public abstract class BaseCommand { } } - public void execute(CommandIssuer issuer, String commandLabel, String[] args) { - commandLabel = commandLabel.toLowerCase(); + void execute(CommandIssuer issuer, CommandRouter.CommandRouteResult command) { try { - CommandOperationContext commandContext = preCommandOperation(issuer, commandLabel, args, false); - - if (args.length > 0) { - CommandSearch cmd = findSubCommand(args); - if (cmd != null) { - execSubcommand = cmd.getCheckSub(); - final String[] execargs = Arrays.copyOfRange(args, cmd.argIndex, args.length); - executeCommand(commandContext, issuer, execargs, cmd.cmd); - return; - } - } - - Set defaultCommands = subCommands.get(DEFAULT); - RegisteredCommand defCommand = ACFUtil.getFirstElement(defaultCommands); - if (defCommand != null && (args.length == 0 || defCommand.consumeInputResolvers > 0)) { - findAndExecuteCommand(commandContext, DEFAULT, issuer, args); - } else if (subCommands.get(CATCHUNKNOWN) != null) { - if (!findAndExecuteCommand(commandContext, CATCHUNKNOWN, issuer, args)) { - help(issuer, args); - } - } - + CommandOperationContext commandContext = preCommandOperation(issuer, command.commandLabel, command.args, false); + execSubcommand = command.subcommand; + executeCommand(commandContext, issuer, command.args, command.cmd); } finally { postCommandOperation(); } } - /** - * Gets the registered command of the given arguments. - * - * @param args The arguments given by the user. - * @return The subcommand or null if none were found. - * @see #findSubCommand(String[]) - */ - RegisteredCommand getRegisteredCommand(String[] args) { - final CommandSearch cmd = findSubCommand(args); - return cmd != null ? cmd.cmd : null; - } - /** * This is ran after any command operation has been performed. */ @@ -598,60 +556,6 @@ public abstract class BaseCommand { return CommandManager.getCurrentCommandManager(); } - /** - * Finds a subcommand of the given arguments. - * - * @param args The arguments the user input. - * @return The identified subcommand. - * @see #findSubCommand(String[], boolean) - */ - private CommandSearch findSubCommand(String[] args) { - return findSubCommand(args, false); - } - - /** - * Finds a subcommand of the given arguments. - * - * @param args The arguments the user input. - * @param completion Whether or not completion of arguments should kick in. This may end up with worse than wanted results. - * @return The identified subcommand. - */ - private CommandSearch findSubCommand(String[] args, boolean completion) { - for (int i = args.length; i >= 0; i--) { - String checkSub = ApacheCommonsLangUtil.join(args, " ", 0, i).toLowerCase(); - Set cmds = subCommands.get(checkSub); - - final int extraArgs = args.length - i; - if (!cmds.isEmpty()) { - RegisteredCommand cmd = null; - if (cmds.size() == 1) { - cmd = ACFUtil.getFirstElement(cmds); - } else { - Optional optCmd = cmds.stream().filter(c -> { - int required = c.requiredResolvers; - int optional = c.optionalResolvers; - return extraArgs <= required + optional && (completion || extraArgs >= required); - }).min((c1, c2) -> { - int a = c1.consumeInputResolvers; - int b = c2.consumeInputResolvers; - - if (a == b) { - return 0; - } - return a < b ? 1 : -1; - }); - if (optCmd.isPresent()) { - cmd = optCmd.get(); - } - } - if (cmd != null) { - return new CommandSearch(cmd, i, checkSub); - } - } - } - return null; - } - private void executeCommand(CommandOperationContext commandOperationContext, CommandIssuer issuer, String[] args, RegisteredCommand cmd) { if (cmd.hasPermission(issuer)) { @@ -711,19 +615,17 @@ public abstract class BaseCommand { args = new String[]{""}; } try { - CommandOperationContext commandOperationContext = preCommandOperation(issuer, commandLabel, args, isAsync); - - final CommandSearch search = findSubCommand(args, true); + CommandRouter router = manager.getRouter(); + preCommandOperation(issuer, commandLabel, args, isAsync); + final RouteSearch search = router.routeCommand(commandLabel, args, true); final List cmds = new ArrayList<>(); - if (search != null) { - cmds.addAll(completeCommand(issuer, search.cmd, Arrays.copyOfRange(args, search.argIndex, args.length), commandLabel, isAsync)); - } else if (subCommands.get(CATCHUNKNOWN).size() == 1) { - cmds.addAll(completeCommand(issuer, ACFUtil.getFirstElement(subCommands.get(CATCHUNKNOWN)), args, commandLabel, isAsync)); - } else if (subCommands.get(DEFAULT).size() == 1) { - cmds.addAll(completeCommand(issuer, ACFUtil.getFirstElement(subCommands.get(DEFAULT)), args, commandLabel, isAsync)); + CommandRouter.CommandRouteResult result = router.matchCommand(search, true); + if (result != null) { + cmds.addAll(completeCommand(issuer, result.cmd, result.args, commandLabel, isAsync)); + } } return filterTabComplete(args[args.length - 1], cmds); @@ -792,55 +694,6 @@ public abstract class BaseCommand { .collect(Collectors.toList()); } - /** - * Gets a registered command under the given subcommand name. - * - * @param subcommand The name of the subcommand requested. - * @return The subcommand found or null if none. - */ - private RegisteredCommand getCommandBySubcommand(String subcommand) { - return getCommandBySubcommand(subcommand, false); - } - - /** - * Gets a registered command under the given name. - * If requireOne is true, it won't accept more than a single matching subcommand. - * - * @param subcommand Name of the subcommand wanted. - * @param requireOne Whether to only accept 1 result. - * @return The subcommand found, or null if none/too many. - */ - private RegisteredCommand getCommandBySubcommand(String subcommand, boolean requireOne) { - final Set commands = subCommands.get(subcommand); - if (!commands.isEmpty() && (!requireOne || commands.size() == 1)) { - return commands.iterator().next(); - } - return null; - } - - /** - * Internally calls {@link #executeCommand(CommandOperationContext, CommandIssuer, String[], RegisteredCommand)} - * and gets through {@link #getCommandBySubcommand(String)}. - * - * @param commandContext The command context to use. - * @param subcommand The subcommand to find the executor of. - * @param issuer The issuer who executed the subcommand. - * @param args All arguments given by the issuer. - * @return Whether it found a command or not. - * @see #executeCommand(CommandOperationContext, CommandIssuer, String[], RegisteredCommand) - * @see #getCommandBySubcommand(String) - * @see RegisteredCommand#invoke(CommandIssuer, List, CommandOperationContext) - */ - private boolean findAndExecuteCommand(CommandOperationContext commandContext, String subcommand, CommandIssuer issuer, String... args) { - final RegisteredCommand cmd = this.getCommandBySubcommand(subcommand); - if (cmd != null) { - executeCommand(commandContext, issuer, args, cmd); - return true; - } - - return false; - } - /** * Executes the precommand and sees whether something is wrong. Ideally, you get false from this. * @@ -951,7 +804,7 @@ public abstract class BaseCommand { } public RegisteredCommand getDefaultRegisteredCommand() { - return this.getCommandBySubcommand(DEFAULT); + return ACFUtil.getFirstElement(this.subCommands.get(DEFAULT)); } public String setContextFlags(Class cls, String flags) { @@ -967,35 +820,4 @@ public abstract class BaseCommand { registeredCommands.addAll(this.subCommands.values()); return registeredCommands; } - - private static class CommandSearch { - RegisteredCommand cmd; - int argIndex; - String checkSub; - - CommandSearch(RegisteredCommand cmd, int argIndex, String checkSub) { - this.cmd = cmd; - this.argIndex = argIndex; - this.checkSub = checkSub; - } - - String getCheckSub() { - return this.checkSub; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - CommandSearch that = (CommandSearch) o; - return argIndex == that.argIndex && - Objects.equals(cmd, that.cmd) && - Objects.equals(checkSub, that.checkSub); - } - - @Override - public int hashCode() { - return Objects.hash(cmd, argIndex, checkSub); - } - } } diff --git a/core/src/main/java/co/aikar/commands/CommandContexts.java b/core/src/main/java/co/aikar/commands/CommandContexts.java index 48ac63f9..895f15e1 100644 --- a/core/src/main/java/co/aikar/commands/CommandContexts.java +++ b/core/src/main/java/co/aikar/commands/CommandContexts.java @@ -177,8 +177,6 @@ public class CommandContexts { String val; - // Go home IDEA, you're drunk - //noinspection unchecked List args = c.getArgs(); if (c.isLastArg() && !c.hasAnnotation(Single.class)) { val = ACFUtil.join(args); @@ -195,7 +193,7 @@ public class CommandContexts { private final RegisteredCommand cmd; private final CommandParameter param; diff --git a/core/src/main/java/co/aikar/commands/CommandHelp.java b/core/src/main/java/co/aikar/commands/CommandHelp.java index 693c12e8..8cd85328 100644 --- a/core/src/main/java/co/aikar/commands/CommandHelp.java +++ b/core/src/main/java/co/aikar/commands/CommandHelp.java @@ -43,7 +43,7 @@ public class CommandHelp { private int page; private int perPage; List search; - private HelpEntry selectedEntry; + private Set selectedEntry = new HashSet<>(); private int totalResults; private int totalPages; private boolean lastPage; @@ -117,14 +117,14 @@ public class CommandHelp { return manager; } - public boolean isExactMatch(String command) { + public boolean testExactMatch(String command) { + selectedEntry.clear(); for (HelpEntry helpEntry : helpEntries) { if (helpEntry.getCommand().endsWith(" " + command)) { - selectedEntry = helpEntry; - return true; + selectedEntry.add(helpEntry); } } - return false; + return !selectedEntry.isEmpty(); } public void showHelp() { @@ -133,8 +133,15 @@ public class CommandHelp { public void showHelp(CommandIssuer issuer) { CommandHelpFormatter formatter = manager.getHelpFormatter(); - if (selectedEntry != null) { - formatter.showDetailedHelp(this, selectedEntry); + if (!selectedEntry.isEmpty()) { + HelpEntry first = ACFUtil.getFirstElement(selectedEntry); + formatter.printDetailedHelpHeader(this, issuer, first); + + for (HelpEntry helpEntry : selectedEntry) { + formatter.showDetailedHelp(this, helpEntry); + } + + formatter.printDetailedHelpFooter(this, issuer, first); return; } @@ -224,7 +231,7 @@ public class CommandHelp { return search; } - public HelpEntry getSelectedEntry() { + public Set getSelectedEntry() { return selectedEntry; } diff --git a/core/src/main/java/co/aikar/commands/CommandHelpFormatter.java b/core/src/main/java/co/aikar/commands/CommandHelpFormatter.java index 49906f6a..4ac1f883 100644 --- a/core/src/main/java/co/aikar/commands/CommandHelpFormatter.java +++ b/core/src/main/java/co/aikar/commands/CommandHelpFormatter.java @@ -56,8 +56,6 @@ public class CommandHelpFormatter { public void showDetailedHelp(CommandHelp commandHelp, HelpEntry entry) { CommandIssuer issuer = commandHelp.getIssuer(); - // header - printDetailedHelpHeader(commandHelp, issuer, entry); // normal help line printDetailedHelpCommand(commandHelp, issuer, entry); @@ -69,9 +67,6 @@ public class CommandHelpFormatter { printDetailedParameter(commandHelp, issuer, entry, param); } } - - // footer - printDetailedHelpFooter(commandHelp, issuer, entry); } // ######## diff --git a/core/src/main/java/co/aikar/commands/CommandManager.java b/core/src/main/java/co/aikar/commands/CommandManager.java index 6386b499..f82b0cb8 100644 --- a/core/src/main/java/co/aikar/commands/CommandManager.java +++ b/core/src/main/java/co/aikar/commands/CommandManager.java @@ -86,6 +86,7 @@ public abstract class CommandManager < private Set unstableAPIs = new HashSet<>(); private Annotations annotations = new Annotations<>(this); + private CommandRouter router = new CommandRouter(this); public static CommandOperationContext getCurrentCommandOperationContext() { return commandOperationContext.get().peek(); @@ -199,6 +200,10 @@ public abstract class CommandManager < return helpFormatter; } + CommandRouter getRouter() { + return router; + } + /** * Registers a command with ACF * @@ -270,14 +275,6 @@ public abstract class CommandManager < return true; } - BaseCommand getBaseCommand(String commandLabel, @NotNull String[] args) { - RootCommand rootCommand = obtainRootCommand(commandLabel); - if (rootCommand == null) { - return null; - } - return rootCommand.getBaseCommand(args); - } - public synchronized RootCommand getRootCommand(@NotNull String cmd) { return rootCommands.get(ACFPatterns.SPACE.split(cmd.toLowerCase(), 2)[0]); } diff --git a/core/src/main/java/co/aikar/commands/CommandParameter.java b/core/src/main/java/co/aikar/commands/CommandParameter.java index 029ff776..f13bcb51 100644 --- a/core/src/main/java/co/aikar/commands/CommandParameter.java +++ b/core/src/main/java/co/aikar/commands/CommandParameter.java @@ -28,6 +28,7 @@ import co.aikar.commands.annotation.Default; import co.aikar.commands.annotation.Description; import co.aikar.commands.annotation.Flags; import co.aikar.commands.annotation.Optional; +import co.aikar.commands.annotation.Single; import co.aikar.commands.annotation.Syntax; import co.aikar.commands.annotation.Values; import co.aikar.commands.contexts.ContextResolver; @@ -58,12 +59,12 @@ public class CommandParameter flags; private boolean canConsumeInput; private boolean optionalResolver; + boolean consumesRest; - public CommandParameter(RegisteredCommand command, Parameter param, int paramIndex) { + public CommandParameter(RegisteredCommand command, Parameter param, int paramIndex, boolean isLast) { this.parameter = param; this.type = param.getType(); this.name = param.getName(); // do we care for an annotation to supply name? - //noinspection unchecked this.manager = command.manager; this.paramIndex = paramIndex; Annotations annotations = manager.getAnnotations(); @@ -80,12 +81,13 @@ public class CommandParameter cmds = search.commands; + String[] args = search.args; + if (!cmds.isEmpty()) { + if (cmds.size() == 1) { + return new CommandRouteResult(ACFUtil.getFirstElement(cmds), search); + } else { + Optional optCmd = cmds.stream() + .filter(c -> isProbableMatch(c, args, completion)) + .min((c1, c2) -> { + int a = c1.consumeInputResolvers; + int b = c2.consumeInputResolvers; + + if (a == b) { + return 0; + } + return a < b ? 1 : -1; + }); + if (optCmd.isPresent()) { + return new CommandRouteResult(optCmd.get(), search); + } + } + } + return null; + } + + /** + * @param c + * @param args + * @param completion + * @return + * @TODO: Improve this to be more accurate like @Default handling. + */ + private boolean isProbableMatch(RegisteredCommand c, String[] args, boolean completion) { + int required = c.requiredResolvers; + int optional = c.optionalResolvers; + return args.length <= required + optional && (completion || args.length >= required); + } + + RouteSearch routeCommand(RootCommand command, String commandLabel, String[] args, boolean completion) { + SetMultimap subCommands = command.getSubCommands(); + int argLength = args.length; + for (int i = argLength; i >= 0; i--) { + String subcommand = ApacheCommonsLangUtil.join(args, " ", 0, i).toLowerCase(); + Set cmds = subCommands.get(subcommand); + + if (!cmds.isEmpty()) { + return new RouteSearch(cmds, Arrays.copyOfRange(args, i, argLength), commandLabel, subcommand, completion); + } + } + + Set defaultCommands = subCommands.get(DEFAULT); + Set unknownCommands = subCommands.get(CATCHUNKNOWN); + if (!defaultCommands.isEmpty()) { + Set matchedDefault = new HashSet<>(); + for (RegisteredCommand c : defaultCommands) { + int required = c.requiredResolvers; + int optional = c.optionalResolvers; + CommandParameter lastParam = c.parameters.length > 0 ? c.parameters[c.parameters.length - 1] : null; + if (argLength <= required + optional || ( + lastParam != null && ( + lastParam.getType() == String[].class + || + (argLength >= required && lastParam.consumesRest) + ) + )) { + matchedDefault.add(c); + } + } + if (!matchedDefault.isEmpty()) { + return new RouteSearch(matchedDefault, args, commandLabel, null, completion); + } + } + + if (!unknownCommands.isEmpty()) { + return new RouteSearch(unknownCommands, args, commandLabel, null, completion); + } + + return null; + } + + RouteSearch routeCommand(String commandLabel, String[] args, boolean completion) { + return routeCommand(manager.getRootCommand(commandLabel), commandLabel, args, completion); + } + + + static class CommandRouteResult { + final RegisteredCommand cmd; + final String[] args; + final String commandLabel; + final String subcommand; + + CommandRouteResult(RegisteredCommand cmd, RouteSearch search) { + this(cmd, search.args, search.subcommand, search.commandLabel); + } + + CommandRouteResult(CommandRouteResult result, String[] args) { + this(result.cmd, args, result.subcommand, result.commandLabel); + } + + CommandRouteResult(RegisteredCommand cmd, String[] args, String subcommand, String commandLabel) { + this.cmd = cmd; + this.args = args; + this.commandLabel = commandLabel; + this.subcommand = subcommand; + } + + } + + static class RouteSearch { + final String[] args; + final Set commands; + final String commandLabel; + final String subcommand; + + RouteSearch(Set commands, String[] args, String commandLabel, String subcommand, boolean completion) { + this.commands = commands; + this.args = args; + this.commandLabel = commandLabel.toLowerCase(); + this.subcommand = subcommand; + } + } + +} diff --git a/core/src/main/java/co/aikar/commands/ForwardingCommand.java b/core/src/main/java/co/aikar/commands/ForwardingCommand.java index 3045c4eb..6274c791 100644 --- a/core/src/main/java/co/aikar/commands/ForwardingCommand.java +++ b/core/src/main/java/co/aikar/commands/ForwardingCommand.java @@ -78,8 +78,9 @@ public class ForwardingCommand extends BaseCommand { } @Override - public void execute(CommandIssuer issuer, String commandLabel, String[] args) { - command.execute(issuer, commandLabel, ApacheCommonsLangUtil.addAll(baseArgs, args)); + public void execute(CommandIssuer issuer, CommandRouter.CommandRouteResult result) { + result = new CommandRouter.CommandRouteResult(result, ApacheCommonsLangUtil.addAll(baseArgs, result.args)); + command.execute(issuer, result); } BaseCommand getCommand() { diff --git a/core/src/main/java/co/aikar/commands/RegisteredCommand.java b/core/src/main/java/co/aikar/commands/RegisteredCommand.java index 0030402d..9c519f51 100644 --- a/core/src/main/java/co/aikar/commands/RegisteredCommand.java +++ b/core/src/main/java/co/aikar/commands/RegisteredCommand.java @@ -106,7 +106,7 @@ public class RegisteredCommand parameter = this.parameters[i] = new CommandParameter<>(this, parameters[i], i); + CommandParameter parameter = this.parameters[i] = new CommandParameter<>(this, parameters[i], i, i == parameters.length - 1); if (!parameter.isCommandIssuer()) { if (!parameter.requiresInput()) { optionalResolvers++; diff --git a/core/src/main/java/co/aikar/commands/RootCommand.java b/core/src/main/java/co/aikar/commands/RootCommand.java index 39234124..962738ce 100644 --- a/core/src/main/java/co/aikar/commands/RootCommand.java +++ b/core/src/main/java/co/aikar/commands/RootCommand.java @@ -23,7 +23,8 @@ package co.aikar.commands; -import co.aikar.commands.apachecommonslang.ApacheCommonsLangUtil; +import co.aikar.commands.CommandRouter.CommandRouteResult; +import co.aikar.commands.CommandRouter.RouteSearch; import com.google.common.collect.SetMultimap; import java.util.ArrayList; @@ -31,9 +32,6 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -import static co.aikar.commands.BaseCommand.CATCHUNKNOWN; -import static co.aikar.commands.BaseCommand.DEFAULT; - public interface RootCommand { void addChild(BaseCommand command); @@ -47,21 +45,7 @@ public interface RootCommand { default void addChildShared(List children, SetMultimap subCommands, BaseCommand command) { command.subCommands.entries().forEach(e -> { - String key = e.getKey(); - RegisteredCommand registeredCommand = e.getValue(); - if (key.equals(DEFAULT) || key.equals(BaseCommand.CATCHUNKNOWN)) { - return; - } - Set registered = subCommands.get(key); - if (!registered.isEmpty()) { - BaseCommand prevBase = registered.iterator().next().scope; - if (prevBase != registeredCommand.scope) { - this.getManager().log(LogLevel.ERROR, "ACF Error: " + command.getName() + " registered subcommand " + key + " for root command " + getCommandName() + " - but it is already defined in " + prevBase.getName()); - this.getManager().log(LogLevel.ERROR, "2 subcommands of the same prefix may not be spread over 2 different classes. Ignoring this."); - return; - } - } - subCommands.put(key, registeredCommand); + subCommands.put(e.getKey(), e.getValue()); }); children.add(command); @@ -105,35 +89,25 @@ public interface RootCommand { } default BaseCommand execute(CommandIssuer sender, String commandLabel, String[] args) { - BaseCommand command = getBaseCommand(args); + CommandRouter router = getManager().getRouter(); + RouteSearch search = router.routeCommand(this, commandLabel, args, false); + BaseCommand defCommand = getDefCommand(); + if (search != null) { + CommandRouteResult result = router.matchCommand(search, false); + if (result != null) { + BaseCommand scope = result.cmd.scope; + scope.execute(sender, result); + return scope; + } - command.execute(sender, commandLabel, args); - return command; - } - - default BaseCommand getBaseCommand(String[] args) { - SetMultimap subCommands = getSubCommands(); - RegisteredCommand command; - for (int i = args.length; i >= 0; i--) { - String checkSub = ApacheCommonsLangUtil.join(args, " ", 0, i).toLowerCase(); - command = ACFUtil.getFirstElement(subCommands.get(checkSub)); - if (command != null) { - return command.scope; + RegisteredCommand firstElement = ACFUtil.getFirstElement(search.commands); + if (firstElement != null) { + defCommand = firstElement.scope; } } - command = ACFUtil.getFirstElement(subCommands.get(DEFAULT)); - if (command != null) { - if (args.length == 0 || command.consumeInputResolvers > 0) { - return command.scope; - } - } - - command = ACFUtil.getFirstElement(subCommands.get(CATCHUNKNOWN)); - if (command != null) { - return command.scope; - } - return getDefCommand(); + defCommand.help(sender, args); + return defCommand; } default List getTabCompletions(CommandIssuer sender, String alias, String[] args) { diff --git a/example/src/main/java/co/aikar/acfexample/SomeOtherCommand.java b/example/src/main/java/co/aikar/acfexample/SomeOtherCommand.java index 56d93bdb..86f386a6 100644 --- a/example/src/main/java/co/aikar/acfexample/SomeOtherCommand.java +++ b/example/src/main/java/co/aikar/acfexample/SomeOtherCommand.java @@ -1,9 +1,11 @@ package co.aikar.acfexample; import co.aikar.commands.BaseCommand; +import co.aikar.commands.CommandHelp; import co.aikar.commands.annotation.CatchUnknown; import co.aikar.commands.annotation.CommandAlias; import co.aikar.commands.annotation.Default; +import co.aikar.commands.annotation.HelpCommand; import co.aikar.commands.annotation.Single; import co.aikar.commands.annotation.Subcommand; import org.bukkit.command.CommandSender; @@ -31,4 +33,9 @@ public class SomeOtherCommand extends BaseCommand { public void test(Player player, String string, @Default("1") int integer) { player.sendMessage("Hi " + string + " - " + integer); } + + @HelpCommand + public void onHelp(CommandSender sender, CommandHelp help) { + help.showHelp(); + } }