mirror of
https://github.com/aikar/commands.git
synced 2026-05-31 06:11:55 +00:00
Add Proper support for Default Completion Handlers
This commit is contained in:
@@ -23,6 +23,7 @@
|
||||
|
||||
package co.aikar.commands;
|
||||
|
||||
import co.aikar.commands.bukkit.contexts.OnlinePlayer;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
@@ -65,7 +66,7 @@ public class BukkitCommandCompletions extends CommandCompletions<BukkitCommandCo
|
||||
});
|
||||
registerAsyncCompletion("dyecolors", c -> ACFUtil.enumNames(DyeColor.values()));
|
||||
registerCompletion("worlds", c -> (
|
||||
Bukkit.getWorlds().stream().map(World::getName).collect(Collectors.toList())
|
||||
Bukkit.getWorlds().stream().map(World::getName).collect(Collectors.toList())
|
||||
));
|
||||
|
||||
registerCompletion("players", c -> {
|
||||
@@ -86,6 +87,9 @@ public class BukkitCommandCompletions extends CommandCompletions<BukkitCommandCo
|
||||
matchedPlayers.sort(String.CASE_INSENSITIVE_ORDER);
|
||||
return matchedPlayers;
|
||||
});
|
||||
|
||||
setDefaultCompletion("players", OnlinePlayer.class, co.aikar.commands.contexts.OnlinePlayer.class, Player.class);
|
||||
setDefaultCompletion("worlds", World.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -680,7 +680,7 @@ public abstract class BaseCommand {
|
||||
* @return All results to complete the command.
|
||||
*/
|
||||
private List<String> completeCommand(CommandIssuer issuer, RegisteredCommand cmd, String[] args, String commandLabel, boolean isAsync) {
|
||||
if (!cmd.hasPermission(issuer) || args.length > cmd.consumeInputResolvers || args.length == 0 || cmd.complete == null) {
|
||||
if (!cmd.hasPermission(issuer) || args.length > cmd.consumeInputResolvers || args.length == 0) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,9 @@ import java.util.stream.IntStream;
|
||||
|
||||
@SuppressWarnings({"WeakerAccess", "UnusedReturnValue"})
|
||||
public class CommandCompletions<C extends CommandCompletionContext> {
|
||||
private static final String DEFAULT_ENUM_ID = "@__defaultenum__";
|
||||
private final CommandManager manager;
|
||||
// TODO: use a CompletionProvider that can return a delegated Id or provide values such as enum support
|
||||
private Map<String, CommandCompletionHandler> completionMap = new HashMap<>();
|
||||
private Map<Class, String> defaultCompletions = new HashMap<>();
|
||||
|
||||
@@ -75,7 +77,7 @@ public class CommandCompletions<C extends CommandCompletionContext> {
|
||||
* @return
|
||||
*/
|
||||
public CommandCompletionHandler registerCompletion(String id, CommandCompletionHandler<C> handler) {
|
||||
return this.completionMap.put("@" + id.toLowerCase(), handler);
|
||||
return this.completionMap.put(prepareCompletionId(id), handler);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,7 +96,7 @@ public class CommandCompletions<C extends CommandCompletionContext> {
|
||||
* @return
|
||||
*/
|
||||
public CommandCompletionHandler registerAsyncCompletion(String id, AsyncCommandCompletionHandler<C> handler) {
|
||||
return this.completionMap.put("@" + id.toLowerCase(), handler);
|
||||
return this.completionMap.put(prepareCompletionId(id), handler);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,14 +148,16 @@ public class CommandCompletions<C extends CommandCompletionContext> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a completion handler such as @players to default apply to all command parameters of the specified types
|
||||
* <p>
|
||||
* This enables automatic completion support for parameters without manually defining it for custom objects
|
||||
*
|
||||
* @param id
|
||||
* @param classes
|
||||
* @return
|
||||
* @deprecated Feature Not done yet
|
||||
*/
|
||||
CommandCompletionHandler setDefaultCompletion(String id, Class... classes) {
|
||||
public void setDefaultCompletion(String id, Class... classes) {
|
||||
// get completion with specified id
|
||||
id = id.toLowerCase();
|
||||
id = prepareCompletionId(id);
|
||||
CommandCompletionHandler completion = completionMap.get(id);
|
||||
|
||||
if (completion == null) {
|
||||
@@ -164,8 +168,11 @@ public class CommandCompletions<C extends CommandCompletionContext> {
|
||||
for (Class clazz : classes) {
|
||||
defaultCompletions.put(clazz, id);
|
||||
}
|
||||
}
|
||||
|
||||
return completion;
|
||||
@NotNull
|
||||
private static String prepareCompletionId(String id) {
|
||||
return (id.startsWith("@") ? "" : "@") + id.toLowerCase();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -176,6 +183,10 @@ public class CommandCompletions<C extends CommandCompletionContext> {
|
||||
String input = args[argIndex];
|
||||
|
||||
String completion = argIndex < completions.length ? completions[argIndex] : null;
|
||||
if (completion == null || "*".equals(completion)) {
|
||||
completion = findDefaultCompletion(cmd, args);
|
||||
}
|
||||
|
||||
if (completion == null && completions.length > 0) {
|
||||
String last = completions[completions.length - 1];
|
||||
if (last.startsWith("repeat@")) {
|
||||
@@ -190,7 +201,35 @@ public class CommandCompletions<C extends CommandCompletionContext> {
|
||||
return getCompletionValues(cmd, sender, completion, args, isAsync);
|
||||
}
|
||||
|
||||
String findDefaultCompletion(RegisteredCommand cmd, String[] args) {
|
||||
int i = 0;
|
||||
for (CommandParameter param : cmd.parameters) {
|
||||
if (param.canConsumeInput() && ++i == args.length) {
|
||||
Class type = param.getType();
|
||||
while (type != null) {
|
||||
String completion = this.defaultCompletions.get(type);
|
||||
if (completion != null) {
|
||||
return completion;
|
||||
}
|
||||
type = type.getSuperclass();
|
||||
}
|
||||
if (param.getType().isEnum()) {
|
||||
CommandOperationContext ctx = CommandManager.getCurrentCommandOperationContext();
|
||||
//noinspection unchecked
|
||||
ctx.enumCompletionValues = ACFUtil.enumNames((Class<? extends Enum<?>>) param.getType());
|
||||
return DEFAULT_ENUM_ID;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
List<String> getCompletionValues(RegisteredCommand command, CommandIssuer sender, String completion, String[] args, boolean isAsync) {
|
||||
if (DEFAULT_ENUM_ID.equals(completion)) {
|
||||
CommandOperationContext<?> ctx = CommandManager.getCurrentCommandOperationContext();
|
||||
return ctx.enumCompletionValues;
|
||||
}
|
||||
if (completion.startsWith("repeat@")) {
|
||||
completion = completion.substring(6);
|
||||
}
|
||||
|
||||
@@ -205,7 +205,7 @@ public class CommandContexts<R extends CommandExecutionContext<?, ? extends Comm
|
||||
Enum<?> match = ACFUtil.simpleMatch(enumCls, first);
|
||||
if (match == null) {
|
||||
List<String> names = ACFUtil.enumNames(enumCls);
|
||||
throw new InvalidCommandArgument(MessageKeys.PLEASE_SPECIFY_ONE_OF, "{valid}", ACFUtil.join(names));
|
||||
throw new InvalidCommandArgument(MessageKeys.PLEASE_SPECIFY_ONE_OF, "{valid}", ACFUtil.join(names, ", "));
|
||||
}
|
||||
return match;
|
||||
});
|
||||
|
||||
@@ -24,11 +24,12 @@
|
||||
package co.aikar.commands;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Holds information about the currently executing command on this thread
|
||||
*/
|
||||
public class CommandOperationContext <I extends CommandIssuer> {
|
||||
public class CommandOperationContext<I extends CommandIssuer> {
|
||||
|
||||
private final CommandManager manager;
|
||||
private final I issuer;
|
||||
@@ -37,6 +38,7 @@ public class CommandOperationContext <I extends CommandIssuer> {
|
||||
private final String[] args;
|
||||
private final boolean isAsync;
|
||||
private RegisteredCommand registeredCommand;
|
||||
List<String> enumCompletionValues;
|
||||
|
||||
CommandOperationContext(CommandManager manager, I issuer, BaseCommand command, String commandLabel, String[] args, boolean isAsync) {
|
||||
this.manager = manager;
|
||||
@@ -81,6 +83,7 @@ public class CommandOperationContext <I extends CommandIssuer> {
|
||||
|
||||
/**
|
||||
* This method will not support annotation processors!! use getAnnotationValue or hasAnnotation
|
||||
*
|
||||
* @deprecated Use {@link #getAnnotationValue(Class)}
|
||||
*/
|
||||
@Deprecated
|
||||
|
||||
@@ -88,7 +88,7 @@ public class RegisteredCommand<CEC extends CommandExecutionContext<CEC, ? extend
|
||||
this.prefSubCommand = prefSubCommand;
|
||||
|
||||
this.permission = annotations.getAnnotationValue(method, CommandPermission.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
|
||||
this.complete = annotations.getAnnotationValue(method, CommandCompletion.class);
|
||||
this.complete = annotations.getAnnotationValue(method, CommandCompletion.class, Annotations.DEFAULT_EMPTY); // no replacements as it should be per-issuer
|
||||
this.helpText = annotations.getAnnotationValue(method, Description.class, Annotations.REPLACEMENTS | Annotations.DEFAULT_EMPTY);
|
||||
this.conditions = annotations.getAnnotationValue(method, Conditions.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
|
||||
this.helpSearchTags = annotations.getAnnotationValue(method, HelpSearchTags.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY);
|
||||
@@ -262,6 +262,9 @@ public class RegisteredCommand<CEC extends CommandExecutionContext<CEC, ? extend
|
||||
Set<String> possible = new HashSet<>();
|
||||
CommandCompletions commandCompletions = this.manager.getCommandCompletions();
|
||||
for (String s : parameter.getValues()) {
|
||||
if ("*".equals(s) || "@completions".equals(s)) {
|
||||
s = commandCompletions.findDefaultCompletion(this, origArgs);
|
||||
}
|
||||
//noinspection unchecked
|
||||
List<String> check = commandCompletions.getCompletionValues(this, sender, s, origArgs, opContext.isAsync());
|
||||
if (!check.isEmpty()) {
|
||||
|
||||
@@ -63,7 +63,7 @@ public class SomeCommand extends BaseCommand {
|
||||
private String testString3;
|
||||
|
||||
@Subcommand("testDI")
|
||||
public void onTestDI(CommandSender sender){
|
||||
public void onTestDI(CommandSender sender) {
|
||||
sender.sendMessage("The value for the injected SomeHandler is " + someHandler.getSomeField());
|
||||
sender.sendMessage("Plugin is null? " + (plugin == null));
|
||||
sender.sendMessage("Test String 1: " + testString);
|
||||
@@ -117,11 +117,15 @@ public class SomeCommand extends BaseCommand {
|
||||
// /acf Aikar wo<tab> with a world named "world" would provide world as a completion.
|
||||
// @test was custom defined in ACFExample as foobar, so only that value would show up as a completion
|
||||
// then finally /acfc Aikar world foobar <tab> would show foo1, foo2, foo3 statically
|
||||
// * means 'default', which is registered to @players for OnlinePlayer. Same for worlds.
|
||||
// @test is a manually defined handler to a String input, can't default that one.
|
||||
// the foo1|foo2|foo3 defines a static list of completion values for the foo1 parameter
|
||||
// Then the enum will also pick up default of its values even though it was left off of the completion
|
||||
@Subcommand("completions")
|
||||
@CommandAlias("acfcompletions|acfc")
|
||||
@CommandCompletion("@players @worlds @test foo1|foo2|foo3")
|
||||
public void onTestCompletion(CommandSender sender, OnlinePlayer player, World world, String test, String misc) {
|
||||
sender.sendMessage("You got " + player.getPlayer().getName() + " - " + world.getName() + " - " + test + " - " + misc);
|
||||
@CommandCompletion("* * @test foo1|foo2|foo3")
|
||||
public void onTestCompletion(CommandSender sender, OnlinePlayer player, World world, String test, String foo1, TestEnum e) {
|
||||
sender.sendMessage("You got " + player.getPlayer().getName() + " - " + world.getName() + " - " + test + " - " + foo1 + " - " + e.name());
|
||||
}
|
||||
|
||||
|
||||
@@ -166,6 +170,7 @@ public class SomeCommand extends BaseCommand {
|
||||
public void onTest1(Player player, String testX) {
|
||||
player.sendMessage("You got test inner inner test3: " + testX);
|
||||
}
|
||||
|
||||
// Requires /acf test next test4 or simply /deepinner command alias
|
||||
@CommandAlias("deepinner")
|
||||
@Subcommand("test4|td4")
|
||||
@@ -175,4 +180,9 @@ public class SomeCommand extends BaseCommand {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum TestEnum {
|
||||
FOOTEST1,
|
||||
BARTEST2
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user