diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index 31439cbb..d93c4704 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -7,6 +7,9 @@
+
+
+
diff --git a/bukkit/src/main/java/co/aikar/commands/BukkitCommandCompletions.java b/bukkit/src/main/java/co/aikar/commands/BukkitCommandCompletions.java
index 8424e734..b7c3c9c7 100644
--- a/bukkit/src/main/java/co/aikar/commands/BukkitCommandCompletions.java
+++ b/bukkit/src/main/java/co/aikar/commands/BukkitCommandCompletions.java
@@ -43,12 +43,12 @@ import java.util.stream.Stream;
public class BukkitCommandCompletions extends CommandCompletions {
public BukkitCommandCompletions(BukkitCommandManager manager) {
super(manager);
- registerCompletion("mobs", c -> {
+ registerAsyncCompletion("mobs", c -> {
final Stream normal = Stream.of(EntityType.values())
.map(entityType -> ACFUtil.simplifyString(entityType.getName()));
return normal.collect(Collectors.toList());
});
- registerCompletion("chatcolors", c -> {
+ registerAsyncCompletion("chatcolors", c -> {
Stream colors = Stream.of(ChatColor.values());
if (c.hasConfig("colorsonly")) {
colors = colors.filter(color -> color.ordinal() <= 0xF);
@@ -63,7 +63,7 @@ public class BukkitCommandCompletions extends CommandCompletions ACFUtil.simplifyString(color.name())).collect(Collectors.toList());
});
- registerCompletion("dyecolors", c -> ACFUtil.enumNames(DyeColor.values()));
+ registerAsyncCompletion("dyecolors", c -> ACFUtil.enumNames(DyeColor.values()));
registerCompletion("worlds", c -> (
Bukkit.getWorlds().stream().map(World::getName).collect(Collectors.toList())
));
diff --git a/bukkit/src/main/java/co/aikar/commands/BukkitCommandIssuer.java b/bukkit/src/main/java/co/aikar/commands/BukkitCommandIssuer.java
index c1cf0fff..66110814 100644
--- a/bukkit/src/main/java/co/aikar/commands/BukkitCommandIssuer.java
+++ b/bukkit/src/main/java/co/aikar/commands/BukkitCommandIssuer.java
@@ -23,7 +23,6 @@
package co.aikar.commands;
-import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
diff --git a/bukkit/src/main/java/co/aikar/commands/BukkitRegisteredCommand.java b/bukkit/src/main/java/co/aikar/commands/BukkitRegisteredCommand.java
index bbfcec7b..29caf0ed 100644
--- a/bukkit/src/main/java/co/aikar/commands/BukkitRegisteredCommand.java
+++ b/bukkit/src/main/java/co/aikar/commands/BukkitRegisteredCommand.java
@@ -24,7 +24,6 @@
package co.aikar.commands;
import co.aikar.timings.lib.MCTiming;
-import co.aikar.timings.lib.TimingManager;
import java.lang.reflect.Method;
diff --git a/bukkit/src/main/java/co/aikar/commands/BukkitRootCommand.java b/bukkit/src/main/java/co/aikar/commands/BukkitRootCommand.java
index 400c8cb7..44919cc9 100644
--- a/bukkit/src/main/java/co/aikar/commands/BukkitRootCommand.java
+++ b/bukkit/src/main/java/co/aikar/commands/BukkitRootCommand.java
@@ -27,14 +27,11 @@ import co.aikar.commands.annotation.Description;
import co.aikar.commands.annotation.Syntax;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.SetMultimap;
-import jdk.nashorn.internal.ir.ReturnNode;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import java.util.ArrayList;
-import java.util.HashSet;
import java.util.List;
-import java.util.Set;
public class BukkitRootCommand extends Command implements RootCommand {
@@ -58,7 +55,7 @@ public class BukkitRootCommand extends Command implements RootCommand {
@Override
public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException {
- return tabComplete(manager.getCommandIssuer(sender), alias, args);
+ return getTabCompletions(manager.getCommandIssuer(sender), alias, args);
}
@Override
@@ -67,14 +64,6 @@ public class BukkitRootCommand extends Command implements RootCommand {
return true;
}
- private List tabComplete(CommandIssuer sender, String alias, String[] args) throws IllegalArgumentException {
- Set completions = new HashSet<>();
- this.children.forEach(child -> completions.addAll(child.tabComplete(sender, alias, args)));
- return new ArrayList<>(completions);
- }
-
-
-
public void addChild(BaseCommand command) {
if (this.defCommand == null || !command.subCommands.get("__default").isEmpty()) {
this.defCommand = command;
@@ -95,6 +84,11 @@ public class BukkitRootCommand extends Command implements RootCommand {
return this.subCommands;
}
+ @Override
+ public List getChildren() {
+ return children;
+ }
+
@Override
public BaseCommand getDefCommand(){
return defCommand;
diff --git a/bungee/src/main/java/co/aikar/commands/BungeeCommandCompletions.java b/bungee/src/main/java/co/aikar/commands/BungeeCommandCompletions.java
index e5c30d9c..0b94945f 100644
--- a/bungee/src/main/java/co/aikar/commands/BungeeCommandCompletions.java
+++ b/bungee/src/main/java/co/aikar/commands/BungeeCommandCompletions.java
@@ -39,7 +39,7 @@ public class BungeeCommandCompletions extends CommandCompletions {
+ registerAsyncCompletion("chatcolors", c -> {
Stream colors = Stream.of(ChatColor.values());
if (c.hasConfig("colorsonly")) {
colors = colors.filter(color -> color.ordinal() <= 0xF);
diff --git a/bungee/src/main/java/co/aikar/commands/BungeeCommandIssuer.java b/bungee/src/main/java/co/aikar/commands/BungeeCommandIssuer.java
index 2f2ba3cf..1f7d1888 100644
--- a/bungee/src/main/java/co/aikar/commands/BungeeCommandIssuer.java
+++ b/bungee/src/main/java/co/aikar/commands/BungeeCommandIssuer.java
@@ -24,7 +24,6 @@
package co.aikar.commands;
import net.md_5.bungee.api.CommandSender;
-import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import java.util.Objects;
diff --git a/bungee/src/main/java/co/aikar/commands/BungeeCommandManager.java b/bungee/src/main/java/co/aikar/commands/BungeeCommandManager.java
index 2b31f5c8..aa48f84b 100644
--- a/bungee/src/main/java/co/aikar/commands/BungeeCommandManager.java
+++ b/bungee/src/main/java/co/aikar/commands/BungeeCommandManager.java
@@ -30,9 +30,9 @@ import net.md_5.bungee.api.plugin.Plugin;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
diff --git a/bungee/src/main/java/co/aikar/commands/BungeeRootCommand.java b/bungee/src/main/java/co/aikar/commands/BungeeRootCommand.java
index e6b48e51..58e72d78 100644
--- a/bungee/src/main/java/co/aikar/commands/BungeeRootCommand.java
+++ b/bungee/src/main/java/co/aikar/commands/BungeeRootCommand.java
@@ -29,10 +29,8 @@ import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.plugin.Command;
import net.md_5.bungee.api.plugin.TabExecutor;
-import java.util.List;
import java.util.ArrayList;
-import java.util.Set;
-import java.util.HashSet;
+import java.util.List;
public class BungeeRootCommand extends Command implements RootCommand, TabExecutor {
@@ -73,6 +71,11 @@ public class BungeeRootCommand extends Command implements RootCommand, TabExecut
return subCommands;
}
+ @Override
+ public List getChildren() {
+ return children;
+ }
+
@Override
public void execute(CommandSender sender, String[] args) {
execute(manager.getCommandIssuer(sender), getName(), args);
@@ -80,13 +83,7 @@ public class BungeeRootCommand extends Command implements RootCommand, TabExecut
@Override
public Iterable onTabComplete(CommandSender commandSender, String[] strings) {
- return onTabComplete(manager.getCommandIssuer(commandSender), getName(), strings);
- }
-
- private List onTabComplete(CommandIssuer sender, String alias, String[] args) throws IllegalArgumentException {
- Set completions = new HashSet<>();
- this.children.forEach(child -> completions.addAll(child.tabComplete(sender, alias, args)));
- return new ArrayList<>(completions);
+ return getTabCompletions(manager.getCommandIssuer(commandSender), getName(), strings);
}
@Override
diff --git a/core/src/main/java/co/aikar/commands/ACFPatterns.java b/core/src/main/java/co/aikar/commands/ACFPatterns.java
index 5260e91c..25d7eaab 100644
--- a/core/src/main/java/co/aikar/commands/ACFPatterns.java
+++ b/core/src/main/java/co/aikar/commands/ACFPatterns.java
@@ -23,7 +23,6 @@
package co.aikar.commands;
-import com.google.common.collect.Maps;
import net.jodah.expiringmap.ExpirationPolicy;
import net.jodah.expiringmap.ExpiringMap;
diff --git a/core/src/main/java/co/aikar/commands/ACFUtil.java b/core/src/main/java/co/aikar/commands/ACFUtil.java
index 81462a38..97c3f6d5 100644
--- a/core/src/main/java/co/aikar/commands/ACFUtil.java
+++ b/core/src/main/java/co/aikar/commands/ACFUtil.java
@@ -25,7 +25,6 @@ package co.aikar.commands;
import co.aikar.commands.apachecommonslang.ApacheCommonsLangUtil;
-import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.math.BigDecimal;
diff --git a/core/src/main/java/co/aikar/commands/BaseCommand.java b/core/src/main/java/co/aikar/commands/BaseCommand.java
index 55c342f2..61f6d8b1 100644
--- a/core/src/main/java/co/aikar/commands/BaseCommand.java
+++ b/core/src/main/java/co/aikar/commands/BaseCommand.java
@@ -23,16 +23,37 @@
package co.aikar.commands;
-import co.aikar.commands.annotation.*;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Default;
+import co.aikar.commands.annotation.HelpCommand;
+import co.aikar.commands.annotation.PreCommand;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.UnknownHandler;
import co.aikar.commands.apachecommonslang.ApacheCommonsLangUtil;
-import com.google.common.collect.*;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.SetMultimap;
+import com.google.common.collect.Sets;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+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;
import java.util.stream.Stream;
@@ -307,7 +328,7 @@ public abstract class BaseCommand {
public void execute(CommandIssuer issuer, String commandLabel, String[] args) {
commandLabel = commandLabel.toLowerCase();
try {
- CommandOperationContext commandContext = preCommandOperation(issuer, commandLabel, args);
+ CommandOperationContext commandContext = preCommandOperation(issuer, commandLabel, args, false);
if (args.length > 0) {
CommandSearch cmd = findSubCommand(args);
@@ -319,7 +340,6 @@ public abstract class BaseCommand {
}
}
-
if (subCommands.get(DEFAULT) != null && args.length == 0) {
executeSubcommand(commandContext, DEFAULT, issuer, args);
} else if (subCommands.get(UNKNOWN) != null) {
@@ -335,6 +355,11 @@ public abstract class BaseCommand {
}
}
+ RegisteredCommand> getRegisteredCommand(String[] args) {
+ final CommandSearch cmd = findSubCommand(args);
+ return cmd != null ? cmd.cmd : null;
+ }
+
private void postCommandOperation() {
CommandManager.commandOperationContext.get().pop();
execSubcommand = null;
@@ -342,9 +367,9 @@ public abstract class BaseCommand {
origArgs = new String[]{};
}
- private CommandOperationContext preCommandOperation(CommandIssuer issuer, String commandLabel, String[] args) {
+ private CommandOperationContext preCommandOperation(CommandIssuer issuer, String commandLabel, String[] args, boolean isAsync) {
Stack contexts = CommandManager.commandOperationContext.get();
- CommandOperationContext context = this.manager.createCommandOperationContext(this, issuer, commandLabel, args);
+ CommandOperationContext context = this.manager.createCommandOperationContext(this, issuer, commandLabel, args, isAsync);
contexts.push(context);
lastCommandOperationContext = context;
execSubcommand = null;
@@ -417,37 +442,28 @@ public abstract class BaseCommand {
return true;
}
- public List tabComplete(CommandIssuer issuer, String commandLabel, String[] args)
+ public List tabComplete(CommandIssuer issuer, String commandLabel, String[] args) {
+ return tabComplete(issuer, commandLabel, args, false);
+ }
+ public List tabComplete(CommandIssuer issuer, String commandLabel, String[] args, boolean isAsync)
throws IllegalArgumentException {
commandLabel = commandLabel.toLowerCase();
+ if (args.length == 0) {
+ args = new String[]{""};
+ }
try {
- CommandOperationContext commandOperationContext = preCommandOperation(issuer, commandLabel, args);
+ CommandOperationContext commandOperationContext = preCommandOperation(issuer, commandLabel, args, isAsync);
final CommandSearch search = findSubCommand(args, true);
- String argString = ApacheCommonsLangUtil.join(args, " ").toLowerCase();
final List cmds = new ArrayList<>();
if (search != null) {
- cmds.addAll(completeCommand(commandOperationContext, issuer, search.cmd, Arrays.copyOfRange(args, search.argIndex, args.length), commandLabel));
+ cmds.addAll(completeCommand(issuer, search.cmd, Arrays.copyOfRange(args, search.argIndex, args.length), commandLabel, isAsync));
} else if (subCommands.get(UNKNOWN).size() == 1) {
- cmds.addAll(completeCommand(commandOperationContext, issuer, Iterables.getOnlyElement(subCommands.get(UNKNOWN)), args, commandLabel));
- }
-
- for (Map.Entry entry : subCommands.entries()) {
- final String key = entry.getKey();
- if (key.startsWith(argString) && !UNKNOWN.equals(key) && !DEFAULT.equals(key)) {
- final RegisteredCommand value = entry.getValue();
- if (!value.hasPermission(issuer)) {
- continue;
- }
- String prefCommand = value.prefSubCommand;
-
- final String[] psplit = ACFPatterns.SPACE.split(prefCommand);
- cmds.add(psplit[args.length - 1]);
- }
+ cmds.addAll(completeCommand(issuer, Iterables.getOnlyElement(subCommands.get(UNKNOWN)), args, commandLabel, isAsync));
}
return filterTabComplete(args[args.length - 1], cmds);
@@ -456,7 +472,25 @@ public abstract class BaseCommand {
}
}
- private List completeCommand(CommandOperationContext commandOperationContext, CommandIssuer issuer, RegisteredCommand cmd, String[] args, String commandLabel) {
+ List getCommandsForCompletion(CommandIssuer issuer, String[] args) {
+ final Set cmds = new HashSet<>();
+ String argString = ApacheCommonsLangUtil.join(args, " ").toLowerCase();
+ for (Map.Entry entry : subCommands.entries()) {
+ final String key = entry.getKey();
+ if (key.startsWith(argString) && !UNKNOWN.equals(key) && !DEFAULT.equals(key)) {
+ final RegisteredCommand value = entry.getValue();
+ if (!value.hasPermission(issuer)) {
+ continue;
+ }
+
+ String[] split = ACFPatterns.SPACE.split(value.prefSubCommand);
+ cmds.add(split[args.length - 1]);
+ }
+ }
+ return new ArrayList<>(cmds);
+ }
+
+ private List completeCommand(CommandIssuer issuer, RegisteredCommand cmd, String[] args, String commandLabel, boolean isAsync) {
if (!cmd.hasPermission(issuer) || args.length > cmd.requiredResolvers + cmd.optionalResolvers || args.length == 0
|| cmd.complete == null) {
return ImmutableList.of();
@@ -464,7 +498,7 @@ public abstract class BaseCommand {
String[] completions = ACFPatterns.SPACE.split(cmd.complete);
- List cmds = manager.getCommandCompletions().of(commandOperationContext, cmd, issuer, completions, args);
+ List cmds = manager.getCommandCompletions().of(cmd, issuer, completions, args, isAsync);
return filterTabComplete(args[args.length-1], cmds);
}
diff --git a/core/src/main/java/co/aikar/commands/CommandCompletionContext.java b/core/src/main/java/co/aikar/commands/CommandCompletionContext.java
index 076a9ee9..1b366d01 100644
--- a/core/src/main/java/co/aikar/commands/CommandCompletionContext.java
+++ b/core/src/main/java/co/aikar/commands/CommandCompletionContext.java
@@ -124,4 +124,8 @@ public class CommandCompletionContext {
public String getConfig() {
return config;
}
+
+ public boolean isAsync() {
+ return CommandManager.getCurrentCommandOperationContext().isAsync();
+ }
}
diff --git a/core/src/main/java/co/aikar/commands/CommandCompletions.java b/core/src/main/java/co/aikar/commands/CommandCompletions.java
index 699be350..986d20fa 100644
--- a/core/src/main/java/co/aikar/commands/CommandCompletions.java
+++ b/core/src/main/java/co/aikar/commands/CommandCompletions.java
@@ -42,8 +42,8 @@ public class CommandCompletions {
public CommandCompletions(CommandManager manager) {
this.manager = manager;
- registerCompletion("nothing", c -> ImmutableList.of());
- registerCompletion("range", (c) -> {
+ registerAsyncCompletion("nothing", c -> ImmutableList.of());
+ registerAsyncCompletion("range", (c) -> {
String config = c.getConfig();
if (config == null) {
return ImmutableList.of();
@@ -60,15 +60,19 @@ public class CommandCompletions {
}
return IntStream.rangeClosed(start, end).mapToObj(Integer::toString).collect(Collectors.toList());
});
- registerCompletion("timeunits", (c) -> ImmutableList.of("minutes", "hours", "days", "weeks", "months", "years"));
+ registerAsyncCompletion("timeunits", (c) -> ImmutableList.of("minutes", "hours", "days", "weeks", "months", "years"));
}
public CommandCompletionHandler registerCompletion(String id, CommandCompletionHandler handler) {
return this.completionMap.put("@" + id.toLowerCase(), handler);
}
+ public CommandCompletionHandler registerAsyncCompletion(String id, AsyncCommandCompletionHandler handler) {
+ return this.completionMap.put("@" + id.toLowerCase(), handler);
+ }
+
@NotNull
- List of(CommandOperationContext commandOperationContext, RegisteredCommand command, CommandIssuer sender, String[] completionInfo, String[] args) {
+ List of(RegisteredCommand command, CommandIssuer sender, String[] completionInfo, String[] args, boolean isAsync) {
final int argIndex = args.length - 1;
String input = args[argIndex];
@@ -77,11 +81,10 @@ public class CommandCompletions {
return ImmutableList.of(input);
}
- return getCompletionValues(command, sender, completion, args);
+ return getCompletionValues(command, sender, completion, args, isAsync);
}
- @NotNull
- List getCompletionValues(RegisteredCommand command, CommandIssuer sender, String completion, String[] args) {
+ List getCompletionValues(RegisteredCommand command, CommandIssuer sender, String completion, String[] args, boolean isAsync) {
completion = manager.getCommandReplacements().replace(completion);
List allCompletions = Lists.newArrayList();
@@ -91,6 +94,10 @@ public class CommandCompletions {
String[] complete = ACFPatterns.COLONEQUALS.split(value, 2);
CommandCompletionHandler handler = this.completionMap.get(complete[0].toLowerCase());
if (handler != null) {
+ if (isAsync && !(handler instanceof AsyncCommandCompletionHandler)) {
+ ACFUtil.sneaky(new SyncCompletionRequired());
+ return null;
+ }
String config = complete.length == 1 ? null : complete[1];
CommandCompletionContext context = manager.createCompletionContext(command, sender, input, config, args);
@@ -123,5 +130,7 @@ public class CommandCompletions {
public interface CommandCompletionHandler {
Collection getCompletions(C context) throws InvalidCommandArgument;
}
+ public interface AsyncCommandCompletionHandler extends CommandCompletionHandler {}
+ public static class SyncCompletionRequired extends Exception {}
}
diff --git a/core/src/main/java/co/aikar/commands/CommandExecutionContext.java b/core/src/main/java/co/aikar/commands/CommandExecutionContext.java
index b85357fc..25f918f6 100644
--- a/core/src/main/java/co/aikar/commands/CommandExecutionContext.java
+++ b/core/src/main/java/co/aikar/commands/CommandExecutionContext.java
@@ -31,6 +31,7 @@ import co.aikar.commands.contexts.IssuerAwareContextResolver;
import co.aikar.commands.contexts.IssuerOnlyContextResolver;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
+
import java.lang.annotation.Annotation;
import java.lang.reflect.Parameter;
import java.util.List;
diff --git a/core/src/main/java/co/aikar/commands/CommandHelp.java b/core/src/main/java/co/aikar/commands/CommandHelp.java
index 957b1dd2..943896e5 100644
--- a/core/src/main/java/co/aikar/commands/CommandHelp.java
+++ b/core/src/main/java/co/aikar/commands/CommandHelp.java
@@ -27,7 +27,12 @@ import co.aikar.locales.MessageKeyProvider;
import com.google.common.collect.SetMultimap;
import org.jetbrains.annotations.NotNull;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
import java.util.regex.Pattern;
@SuppressWarnings("WeakerAccess")
diff --git a/core/src/main/java/co/aikar/commands/CommandManager.java b/core/src/main/java/co/aikar/commands/CommandManager.java
index 29d2292e..e0034a44 100644
--- a/core/src/main/java/co/aikar/commands/CommandManager.java
+++ b/core/src/main/java/co/aikar/commands/CommandManager.java
@@ -132,7 +132,7 @@ public abstract class CommandManager passedArgs = Maps.newLinkedHashMap();
int remainingRequired = requiredResolvers;
+ CommandOperationContext opContext = CommandManager.getCurrentCommandOperationContext();
for (int i = 0; i < parameters.length && i < argLimit; i++) {
boolean isLast = i == parameters.length - 1;
boolean allowOptional = remainingRequired == 0;
@@ -222,7 +230,7 @@ public class RegisteredCommand possible = Sets.newHashSet();
for (String s : split) {
- List check = this.manager.getCommandCompletions().getCompletionValues(this, sender, s, origArgs);
+ List check = this.manager.getCommandCompletions().getCompletionValues(this, sender, s, origArgs, opContext.isAsync());
if (!check.isEmpty()) {
possible.addAll(check.stream().map(String::toLowerCase).collect(Collectors.toList()));
} else {
diff --git a/core/src/main/java/co/aikar/commands/RootCommand.java b/core/src/main/java/co/aikar/commands/RootCommand.java
index e7f6f074..88981ab2 100644
--- a/core/src/main/java/co/aikar/commands/RootCommand.java
+++ b/core/src/main/java/co/aikar/commands/RootCommand.java
@@ -26,6 +26,8 @@ package co.aikar.commands;
import co.aikar.commands.apachecommonslang.ApacheCommonsLangUtil;
import com.google.common.collect.SetMultimap;
+import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -34,6 +36,7 @@ interface RootCommand {
CommandManager getManager();
SetMultimap getSubCommands();
+ List getChildren();
String getCommandName();
default void addChildShared(List children, SetMultimap subCommands, BaseCommand command) {
@@ -59,6 +62,13 @@ interface RootCommand {
}
default BaseCommand execute(CommandIssuer sender, String commandLabel, String[] args) {
+ BaseCommand command = getBaseCommand(args);
+
+ command.execute(sender, commandLabel, args);
+ return command;
+ }
+
+ default BaseCommand getBaseCommand(String[] args) {
BaseCommand command = getDefCommand();
for (int i = args.length; i >= 0; i--) {
String checkSub = ApacheCommonsLangUtil.join(args, " ", 0, i).toLowerCase();
@@ -68,11 +78,19 @@ interface RootCommand {
break;
}
}
-
- command.execute(sender, commandLabel, args);
return command;
}
+ default List getTabCompletions(CommandIssuer sender, String alias, String[] args) throws IllegalArgumentException {
+ Set completions = new HashSet<>();
+ getChildren().forEach(child -> {
+ completions.addAll(child.tabComplete(sender, alias, args));
+ completions.addAll(child.getCommandsForCompletion(sender, args));
+ });
+ return new ArrayList<>(completions);
+ }
+
+
default RegisteredCommand getDefaultRegisteredCommand() {
BaseCommand defCommand = this.getDefCommand();
if (defCommand != null) {
diff --git a/example/src/main/java/co/aikar/acfexample/ACFExample.java b/example/src/main/java/co/aikar/acfexample/ACFExample.java
index 0409a99b..411991c5 100644
--- a/example/src/main/java/co/aikar/acfexample/ACFExample.java
+++ b/example/src/main/java/co/aikar/acfexample/ACFExample.java
@@ -60,7 +60,7 @@ public final class ACFExample extends JavaPlugin {
SomeObject.getContextResolver());
// 4: Register Command Completions - this will be accessible with @CommandCompletion("@test")
- commandManager.getCommandCompletions().registerCompletion("test", c -> (
+ commandManager.getCommandCompletions().registerAsyncCompletion("test", c -> (
Lists.newArrayList("foo", "bar", "baz")
));
diff --git a/paper/pom.xml b/paper/pom.xml
index 8e05ac2b..91cc0ef2 100644
--- a/paper/pom.xml
+++ b/paper/pom.xml
@@ -49,7 +49,7 @@
com.destroystokyo.paper
paper-api
- 1.12-R0.1-SNAPSHOT
+ 1.12.2-R0.1-SNAPSHOT
provided
diff --git a/paper/src/main/java/co/aikar/commands/PaperAsyncTabCompleteHandler.java b/paper/src/main/java/co/aikar/commands/PaperAsyncTabCompleteHandler.java
new file mode 100644
index 00000000..52934316
--- /dev/null
+++ b/paper/src/main/java/co/aikar/commands/PaperAsyncTabCompleteHandler.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2016-2017 Daniel Ennis (Aikar) - MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package co.aikar.commands;
+
+import com.destroystokyo.paper.event.server.AsyncTabCompleteEvent;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.server.TabCompleteEvent;
+
+import java.util.Arrays;
+import java.util.List;
+
+class PaperAsyncTabCompleteHandler implements Listener {
+ private final PaperCommandManager manager;
+
+ PaperAsyncTabCompleteHandler(PaperCommandManager manager) {
+ this.manager = manager;
+ manager.log(LogLevel.INFO, "Enabled Asynchronous Tab Completion Support!");
+ }
+
+ @EventHandler(ignoreCancelled = true)
+ public void onAsyncTabComplete(AsyncTabCompleteEvent event) {
+ String buffer = event.getBuffer();
+ if (!event.isCommand() && !buffer.startsWith("/")) {
+ return;
+ }
+ try {
+ //noinspection ConstantConditions,ConstantIfStatement
+ if (false) throw new CommandCompletions.SyncCompletionRequired(); // fake compiler due to SneakyThrows
+ String[] args = ACFPatterns.SPACE.split(buffer, -1);
+
+ String commandLabel = args[0];
+ if (commandLabel.startsWith("/")) {
+ commandLabel = commandLabel.substring(1);
+ }
+ args = args.length > 1 ? Arrays.copyOfRange(args, 1, args.length) : new String[]{""};
+
+ BaseCommand cmd = this.manager.getBaseCommand(commandLabel, args);
+ if (cmd == null) {
+ return;
+ }
+
+ BukkitCommandIssuer issuer = this.manager.getCommandIssuer(event.getSender());
+ List results = cmd.tabComplete(issuer, commandLabel, args, true);
+ event.setCompletions(results);
+ event.setHandled(true);
+ } catch (Exception ignored) {
+ }
+ }
+
+ @EventHandler(ignoreCancelled = true)
+ public void onTabComplete(TabCompleteEvent event) {
+ String buffer = event.getBuffer();
+ if (!event.isCommand() && !buffer.startsWith("/")) {
+ return;
+ }
+ String[] args = ACFPatterns.SPACE.split(buffer, -1);
+
+ String commandLabel = args[0];
+ if (commandLabel.startsWith("/")) {
+ commandLabel = commandLabel.substring(1);
+ }
+ args = args.length > 1 ? Arrays.copyOfRange(args, 1, args.length) : new String[]{""};
+
+ BaseCommand cmd = this.manager.getBaseCommand(commandLabel, args);
+ if (cmd != null) {
+ BukkitCommandIssuer issuer = this.manager.getCommandIssuer(event.getSender());
+ List commandsForCompletion = cmd.getCommandsForCompletion(issuer, args);
+ event.getCompletions().addAll(commandsForCompletion);
+ }
+ }
+}
diff --git a/paper/src/main/java/co/aikar/commands/PaperCommandManager.java b/paper/src/main/java/co/aikar/commands/PaperCommandManager.java
index 5dd84b1d..fe06bd32 100644
--- a/paper/src/main/java/co/aikar/commands/PaperCommandManager.java
+++ b/paper/src/main/java/co/aikar/commands/PaperCommandManager.java
@@ -23,7 +23,6 @@
package co.aikar.commands;
-import org.bukkit.command.CommandSender;
import org.bukkit.plugin.Plugin;
@SuppressWarnings("WeakerAccess")
@@ -32,6 +31,12 @@ public class PaperCommandManager extends BukkitCommandManager {
// If we get anything Paper specific
public PaperCommandManager(Plugin plugin) {
super(plugin);
+ try {
+ Class.forName("com.destroystokyo.paper.event.server.AsyncTabCompleteEvent");
+ plugin.getServer().getPluginManager().registerEvents(new PaperAsyncTabCompleteHandler(this), plugin);
+ } catch (ClassNotFoundException ignored) {
+ // Ignored
+ }
}
@Override
diff --git a/sponge/src/main/java/co/aikar/commands/ACFSpongeUtil.java b/sponge/src/main/java/co/aikar/commands/ACFSpongeUtil.java
index 02e942d9..9218f48c 100644
--- a/sponge/src/main/java/co/aikar/commands/ACFSpongeUtil.java
+++ b/sponge/src/main/java/co/aikar/commands/ACFSpongeUtil.java
@@ -1,7 +1,6 @@
package co.aikar.commands;
import com.google.common.collect.Iterables;
-import org.jetbrains.annotations.NotNull;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.entity.living.player.Player;
diff --git a/sponge/src/main/java/co/aikar/commands/SpongeCommandContexts.java b/sponge/src/main/java/co/aikar/commands/SpongeCommandContexts.java
index ebe3aa9d..8f550dbe 100644
--- a/sponge/src/main/java/co/aikar/commands/SpongeCommandContexts.java
+++ b/sponge/src/main/java/co/aikar/commands/SpongeCommandContexts.java
@@ -31,11 +31,9 @@ import org.spongepowered.api.Sponge;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.text.format.TextColor;
-import org.spongepowered.api.text.format.TextColors;
import org.spongepowered.api.text.format.TextStyle;
import org.spongepowered.api.world.World;
-import javax.swing.plaf.ActionMapUIResource;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Pattern;
diff --git a/sponge/src/main/java/co/aikar/commands/SpongeCommandIssuer.java b/sponge/src/main/java/co/aikar/commands/SpongeCommandIssuer.java
index 57fb54f2..c51aeafe 100644
--- a/sponge/src/main/java/co/aikar/commands/SpongeCommandIssuer.java
+++ b/sponge/src/main/java/co/aikar/commands/SpongeCommandIssuer.java
@@ -25,8 +25,6 @@ package co.aikar.commands;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.entity.living.player.Player;
-import org.spongepowered.api.text.Text;
-import org.spongepowered.api.text.format.TextColors;
import org.spongepowered.api.text.serializer.TextSerializers;
import java.util.Objects;
diff --git a/sponge/src/main/java/co/aikar/commands/SpongeCommandManager.java b/sponge/src/main/java/co/aikar/commands/SpongeCommandManager.java
index 0fca0315..4ed8f8db 100644
--- a/sponge/src/main/java/co/aikar/commands/SpongeCommandManager.java
+++ b/sponge/src/main/java/co/aikar/commands/SpongeCommandManager.java
@@ -172,13 +172,14 @@ public class SpongeCommandManager extends CommandManager getSuggestions(@NotNull CommandSource source, @NotNull String arguments, @Nullable Location location) throws CommandException {
String[] args = arguments.isEmpty() ? new String[0] : arguments.split(" ");
- return tabComplete(manager.getCommandIssuer(source), this.name, args);
+ return getTabCompletions(manager.getCommandIssuer(source), this.name, args);
}
@Override
@@ -93,12 +90,6 @@ public class SpongeRootCommand implements CommandCallable, RootCommand {
return Text.of();
}
- private List tabComplete(CommandIssuer sender, String alias, String[] args) throws IllegalArgumentException {
- Set completions = new HashSet<>();
- this.children.forEach(child -> completions.addAll(child.tabComplete(sender, alias, args)));
- return new ArrayList<>(completions);
- }
-
private CommandResult executeSponge(CommandIssuer sender, String commandLabel, String[] args) {
BaseCommand cmd = execute(sender, commandLabel, args);
return ((SpongeCommandOperationContext) cmd.lastCommandOperationContext).getResult();
@@ -125,4 +116,9 @@ public class SpongeRootCommand implements CommandCallable, RootCommand {
public SetMultimap getSubCommands() {
return subCommands;
}
+
+ @Override
+ public List getChildren() {
+ return children;
+ }
}