From 8f586762131ac2efe9632506b7639818e6311b04 Mon Sep 17 00:00:00 2001 From: Aikar Date: Sun, 24 Mar 2019 10:25:14 -0400 Subject: [PATCH] (DEPLOYED ACF) Updated JavaDocs --- .../co/aikar/commands/BaseCommand.html | 36 +- ...letions.AsyncCommandCompletionHandler.html | 2 +- ...dCompletions.CommandCompletionHandler.html | 4 +- ...andCompletions.SyncCompletionRequired.html | 4 +- .../co/aikar/commands/CommandCompletions.html | 18 +- .../co/aikar/commands/BaseCommand.html | 244 ++++---- ...letions.AsyncCommandCompletionHandler.html | 540 +++++++++--------- ...dCompletions.CommandCompletionHandler.html | 540 +++++++++--------- ...andCompletions.SyncCompletionRequired.html | 540 +++++++++--------- .../co/aikar/commands/CommandCompletions.html | 540 +++++++++--------- 10 files changed, 1276 insertions(+), 1192 deletions(-) diff --git a/docs/acf-core/co/aikar/commands/BaseCommand.html b/docs/acf-core/co/aikar/commands/BaseCommand.html index 6e88bd26..b22b81b8 100644 --- a/docs/acf-core/co/aikar/commands/BaseCommand.html +++ b/docs/acf-core/co/aikar/commands/BaseCommand.html @@ -524,7 +524,7 @@ public boolean 

getCommandHelp

@Deprecated
-public CommandHelp getCommandHelp()
+public CommandHelp getCommandHelp()
Deprecated. Unstable API
@@ -535,7 +535,7 @@ public 

showCommandHelp

@Deprecated
-public void showCommandHelp()
+public void showCommandHelp()
Deprecated. Unstable API
@@ -545,7 +545,7 @@ public void 
  • help

    -
    public void help(Object issuer,
    +
    public void help(Object issuer,
                      String[] args)
  • @@ -555,7 +555,7 @@ public void 
  • help

    -
    public void help(CommandIssuer issuer,
    +
    public void help(CommandIssuer issuer,
                      String[] args)
  • @@ -565,7 +565,7 @@ public void 
  • doHelp

    -
    public void doHelp(Object issuer,
    +
    public void doHelp(Object issuer,
                        String... args)
  • @@ -575,7 +575,7 @@ public void 
  • doHelp

    -
    public void doHelp(CommandIssuer issuer,
    +
    public void doHelp(CommandIssuer issuer,
                        String... args)
  • @@ -585,7 +585,7 @@ public void 
  • showSyntax

    -
    public void showSyntax(CommandIssuer issuer,
    +
    public void showSyntax(CommandIssuer issuer,
                            RegisteredCommand<?> cmd)
  • @@ -595,7 +595,7 @@ public void 
  • hasPermission

    -
    public boolean hasPermission(Object issuer)
    +
    public boolean hasPermission(Object issuer)
  • @@ -604,7 +604,7 @@ public void 
  • hasPermission

    -
    public boolean hasPermission(CommandIssuer issuer)
    +
    public boolean hasPermission(CommandIssuer issuer)
  • @@ -613,7 +613,7 @@ public void 
  • getRequiredPermissions

    -
    public Set<StringgetRequiredPermissions()
    +
    public Set<StringgetRequiredPermissions()
  • @@ -622,7 +622,7 @@ public void 
  • requiresPermission

    -
    public boolean requiresPermission(String permission)
    +
    public boolean requiresPermission(String permission)
  • @@ -631,7 +631,7 @@ public void 
  • getName

    -
    public String getName()
    +
    public String getName()
  • @@ -640,7 +640,7 @@ public void 
  • getExceptionHandler

    -
    public ExceptionHandler getExceptionHandler()
    +
    public ExceptionHandler getExceptionHandler()
  • @@ -649,7 +649,7 @@ public void 
  • setExceptionHandler

    -
    public BaseCommand setExceptionHandler(ExceptionHandler exceptionHandler)
    +
    public BaseCommand setExceptionHandler(ExceptionHandler exceptionHandler)
  • @@ -658,7 +658,7 @@ public void 
  • getDefaultRegisteredCommand

    -
    public RegisteredCommand getDefaultRegisteredCommand()
    +
    public RegisteredCommand getDefaultRegisteredCommand()
  • @@ -667,7 +667,7 @@ public void 
  • setContextFlags

    -
    public String setContextFlags(Class<?> cls,
    +
    public String setContextFlags(Class<?> cls,
                                   String flags)
  • @@ -677,7 +677,7 @@ public void 
  • getContextFlags

    -
    public String getContextFlags(Class<?> cls)
    +
    public String getContextFlags(Class<?> cls)
  • @@ -686,7 +686,7 @@ public void 
  • getRegisteredCommands

    -
    public List<RegisteredCommandgetRegisteredCommands()
    +
    public List<RegisteredCommandgetRegisteredCommands()
  • diff --git a/docs/acf-core/co/aikar/commands/CommandCompletions.AsyncCommandCompletionHandler.html b/docs/acf-core/co/aikar/commands/CommandCompletions.AsyncCommandCompletionHandler.html index e47b6fa4..03ffbfab 100644 --- a/docs/acf-core/co/aikar/commands/CommandCompletions.AsyncCommandCompletionHandler.html +++ b/docs/acf-core/co/aikar/commands/CommandCompletions.AsyncCommandCompletionHandler.html @@ -102,7 +102,7 @@

    -
    public static interface CommandCompletions.AsyncCommandCompletionHandler<C extends CommandCompletionContext>
    +
    public static interface CommandCompletions.AsyncCommandCompletionHandler<C extends CommandCompletionContext>
     extends CommandCompletions.CommandCompletionHandler<C>
    diff --git a/docs/acf-core/co/aikar/commands/CommandCompletions.CommandCompletionHandler.html b/docs/acf-core/co/aikar/commands/CommandCompletions.CommandCompletionHandler.html index 885ec98b..23154375 100644 --- a/docs/acf-core/co/aikar/commands/CommandCompletions.CommandCompletionHandler.html +++ b/docs/acf-core/co/aikar/commands/CommandCompletions.CommandCompletionHandler.html @@ -108,7 +108,7 @@ var activeTableTab = "activeTableTab";

    -
    public static interface CommandCompletions.CommandCompletionHandler<C extends CommandCompletionContext>
    +
    public static interface CommandCompletions.CommandCompletionHandler<C extends CommandCompletionContext>
    @@ -154,7 +154,7 @@ var activeTableTab = "activeTableTab"; diff --git a/docs/acf-core/co/aikar/commands/CommandCompletions.html b/docs/acf-core/co/aikar/commands/CommandCompletions.html index ee4aa849..9bf7291d 100644 --- a/docs/acf-core/co/aikar/commands/CommandCompletions.html +++ b/docs/acf-core/co/aikar/commands/CommandCompletions.html @@ -108,7 +108,7 @@ var activeTableTab = "activeTableTab";


  • -
    public class CommandCompletions<C extends CommandCompletionContext>
    +
    public class CommandCompletions<C extends CommandCompletionContext>
     extends Object
  • @@ -249,7 +249,7 @@ extends
  • CommandCompletions

    -
    public CommandCompletions(CommandManager manager)
    +
    public CommandCompletions(CommandManager manager)
  • @@ -266,7 +266,7 @@ extends
  • registerCompletion

    -
    public CommandCompletions.CommandCompletionHandler registerCompletion(String id,
    +
    public CommandCompletions.CommandCompletionHandler registerCompletion(String id,
                                                                           CommandCompletions.CommandCompletionHandler<C> handler)
    Registr a completion handler to provide command completions based on the user input.
    @@ -283,7 +283,7 @@ extends
  • registerAsyncCompletion

    -
    public CommandCompletions.CommandCompletionHandler registerAsyncCompletion(String id,
    +
    public CommandCompletions.CommandCompletionHandler registerAsyncCompletion(String id,
                                                                                CommandCompletions.AsyncCommandCompletionHandler<C> handler)
    Registr a completion handler to provide command completions based on the user input. This handler is declared to be safe to be executed asynchronously. @@ -308,7 +308,7 @@ extends
  • registerStaticCompletion

    -
    public CommandCompletions.CommandCompletionHandler registerStaticCompletion(String id,
    +
    public CommandCompletions.CommandCompletionHandler registerStaticCompletion(String id,
                                                                                 String list)
    Register a static list of command completions that will never change. Like @CommandCompletion, values are | (PIPE) separated. @@ -328,7 +328,7 @@ extends
  • registerStaticCompletion

    -
    public CommandCompletions.CommandCompletionHandler registerStaticCompletion(String id,
    +
    public CommandCompletions.CommandCompletionHandler registerStaticCompletion(String id,
                                                                                 String[] completions)
    Register a static list of command completions that will never change
    @@ -345,7 +345,7 @@ extends
  • registerStaticCompletion

    -
    public CommandCompletions.CommandCompletionHandler registerStaticCompletion(String id,
    +
    public CommandCompletions.CommandCompletionHandler registerStaticCompletion(String id,
                                                                                 Supplier<Collection<String>> supplier)
    Register a static list of command completions that will never change. The list is obtained from the supplier immediately as part of this method call.
    @@ -363,7 +363,7 @@ extends
  • registerStaticCompletion

    -
    public CommandCompletions.CommandCompletionHandler registerStaticCompletion(String id,
    +
    public CommandCompletions.CommandCompletionHandler registerStaticCompletion(String id,
                                                                                 Collection<String> completions)
    Register a static list of command completions that will never change
    @@ -380,7 +380,7 @@ extends
  • setDefaultCompletion

    -
    public void setDefaultCompletion(String id,
    +
    public void setDefaultCompletion(String id,
                                      Class... classes)
    Registers a completion handler such as @players to default apply to all command parameters of the specified types

    diff --git a/docs/acf-core/src-html/co/aikar/commands/BaseCommand.html b/docs/acf-core/src-html/co/aikar/commands/BaseCommand.html index 2fb89386..7ef565fb 100644 --- a/docs/acf-core/src-html/co/aikar/commands/BaseCommand.html +++ b/docs/acf-core/src-html/co/aikar/commands/BaseCommand.html @@ -693,156 +693,160 @@ 685 * @return All results to complete the command. 686 */ 687 private List<String> completeCommand(CommandIssuer issuer, RegisteredCommand cmd, String[] args, String commandLabel, boolean isAsync) { -688 if (!cmd.hasPermission(issuer) || args.length > cmd.consumeInputResolvers || args.length == 0) { +688 if (!cmd.hasPermission(issuer) || args.length == 0) { 689 return Collections.emptyList(); 690 } 691 -692 List<String> cmds = manager.getCommandCompletions().of(cmd, issuer, args, isAsync); -693 return filterTabComplete(args[args.length - 1], cmds); -694 } +692 if (!cmd.parameters[cmd.parameters.length - 1].consumesRest && args.length > cmd.consumeInputResolvers) { +693 return Collections.emptyList(); +694 } 695 -696 /** -697 * Gets the actual args in string form the user typed -698 * This returns a list of all tab complete options which are possible with the given argument and commands. -699 * -700 * @param arg Argument which was pressed tab on. -701 * @param cmds The possibilities to return. -702 * @return All possible options. This may be empty. -703 */ -704 private static List<String> filterTabComplete(String arg, List<String> cmds) { -705 return cmds.stream() -706 .distinct() -707 .filter(cmd -> cmd != null && (arg.isEmpty() || ApacheCommonsLangUtil.startsWithIgnoreCase(cmd, arg))) -708 .collect(Collectors.toList()); -709 } -710 -711 /** -712 * Executes the precommand and sees whether something is wrong. Ideally, you get false from this. -713 * -714 * @param commandOperationContext The context to use. -715 * @param cmd The command executed. -716 * @param issuer The issuer who executed the command. -717 * @param args The arguments the issuer provided. -718 * @return Whether something went wrong. -719 */ -720 private boolean checkPrecommand(CommandOperationContext commandOperationContext, RegisteredCommand cmd, CommandIssuer issuer, String[] args) { -721 Method pre = this.preCommandHandler; -722 if (pre != null) { -723 try { -724 Class<?>[] types = pre.getParameterTypes(); -725 Object[] parameters = new Object[pre.getParameterCount()]; -726 for (int i = 0; i < parameters.length; i++) { -727 Class<?> type = types[i]; -728 Object issuerObject = issuer.getIssuer(); -729 if (manager.isCommandIssuer(type) && type.isAssignableFrom(issuerObject.getClass())) { -730 parameters[i] = issuerObject; -731 } else if (CommandIssuer.class.isAssignableFrom(type)) { -732 parameters[i] = issuer; -733 } else if (RegisteredCommand.class.isAssignableFrom(type)) { -734 parameters[i] = cmd; -735 } else if (String[].class.isAssignableFrom((type))) { -736 parameters[i] = args; -737 } else { -738 parameters[i] = null; -739 } -740 } -741 -742 return (boolean) pre.invoke(this, parameters); -743 } catch (IllegalAccessException | InvocationTargetException e) { -744 this.manager.log(LogLevel.ERROR, "Exception encountered while command pre-processing", e); -745 } -746 } -747 return false; -748 } -749 -750 /** -751 * @deprecated Unstable API -752 */ -753 @Deprecated -754 @UnstableAPI -755 public CommandHelp getCommandHelp() { -756 return manager.generateCommandHelp(); -757 } -758 -759 /** -760 * @deprecated Unstable API -761 */ -762 @Deprecated -763 @UnstableAPI -764 public void showCommandHelp() { -765 getCommandHelp().showHelp(); -766 } -767 -768 public void help(Object issuer, String[] args) { -769 help(manager.getCommandIssuer(issuer), args); +696 List<String> cmds = manager.getCommandCompletions().of(cmd, issuer, args, isAsync); +697 return filterTabComplete(args[args.length - 1], cmds); +698 } +699 +700 /** +701 * Gets the actual args in string form the user typed +702 * This returns a list of all tab complete options which are possible with the given argument and commands. +703 * +704 * @param arg Argument which was pressed tab on. +705 * @param cmds The possibilities to return. +706 * @return All possible options. This may be empty. +707 */ +708 private static List<String> filterTabComplete(String arg, List<String> cmds) { +709 return cmds.stream() +710 .distinct() +711 .filter(cmd -> cmd != null && (arg.isEmpty() || ApacheCommonsLangUtil.startsWithIgnoreCase(cmd, arg))) +712 .collect(Collectors.toList()); +713 } +714 +715 /** +716 * Executes the precommand and sees whether something is wrong. Ideally, you get false from this. +717 * +718 * @param commandOperationContext The context to use. +719 * @param cmd The command executed. +720 * @param issuer The issuer who executed the command. +721 * @param args The arguments the issuer provided. +722 * @return Whether something went wrong. +723 */ +724 private boolean checkPrecommand(CommandOperationContext commandOperationContext, RegisteredCommand cmd, CommandIssuer issuer, String[] args) { +725 Method pre = this.preCommandHandler; +726 if (pre != null) { +727 try { +728 Class<?>[] types = pre.getParameterTypes(); +729 Object[] parameters = new Object[pre.getParameterCount()]; +730 for (int i = 0; i < parameters.length; i++) { +731 Class<?> type = types[i]; +732 Object issuerObject = issuer.getIssuer(); +733 if (manager.isCommandIssuer(type) && type.isAssignableFrom(issuerObject.getClass())) { +734 parameters[i] = issuerObject; +735 } else if (CommandIssuer.class.isAssignableFrom(type)) { +736 parameters[i] = issuer; +737 } else if (RegisteredCommand.class.isAssignableFrom(type)) { +738 parameters[i] = cmd; +739 } else if (String[].class.isAssignableFrom((type))) { +740 parameters[i] = args; +741 } else { +742 parameters[i] = null; +743 } +744 } +745 +746 return (boolean) pre.invoke(this, parameters); +747 } catch (IllegalAccessException | InvocationTargetException e) { +748 this.manager.log(LogLevel.ERROR, "Exception encountered while command pre-processing", e); +749 } +750 } +751 return false; +752 } +753 +754 /** +755 * @deprecated Unstable API +756 */ +757 @Deprecated +758 @UnstableAPI +759 public CommandHelp getCommandHelp() { +760 return manager.generateCommandHelp(); +761 } +762 +763 /** +764 * @deprecated Unstable API +765 */ +766 @Deprecated +767 @UnstableAPI +768 public void showCommandHelp() { +769 getCommandHelp().showHelp(); 770 } 771 -772 public void help(CommandIssuer issuer, String[] args) { -773 issuer.sendMessage(MessageType.ERROR, MessageKeys.UNKNOWN_COMMAND); +772 public void help(Object issuer, String[] args) { +773 help(manager.getCommandIssuer(issuer), args); 774 } 775 -776 public void doHelp(Object issuer, String... args) { -777 doHelp(manager.getCommandIssuer(issuer), args); +776 public void help(CommandIssuer issuer, String[] args) { +777 issuer.sendMessage(MessageType.ERROR, MessageKeys.UNKNOWN_COMMAND); 778 } 779 -780 public void doHelp(CommandIssuer issuer, String... args) { -781 help(issuer, args); +780 public void doHelp(Object issuer, String... args) { +781 doHelp(manager.getCommandIssuer(issuer), args); 782 } 783 -784 public void showSyntax(CommandIssuer issuer, RegisteredCommand<?> cmd) { -785 issuer.sendMessage(MessageType.SYNTAX, MessageKeys.INVALID_SYNTAX, -786 "{command}", manager.getCommandPrefix(issuer) + cmd.command, -787 "{syntax}", cmd.syntaxText -788 ); -789 } -790 -791 public boolean hasPermission(Object issuer) { -792 return hasPermission(manager.getCommandIssuer(issuer)); +784 public void doHelp(CommandIssuer issuer, String... args) { +785 help(issuer, args); +786 } +787 +788 public void showSyntax(CommandIssuer issuer, RegisteredCommand<?> cmd) { +789 issuer.sendMessage(MessageType.SYNTAX, MessageKeys.INVALID_SYNTAX, +790 "{command}", manager.getCommandPrefix(issuer) + cmd.command, +791 "{syntax}", cmd.syntaxText +792 ); 793 } 794 -795 public boolean hasPermission(CommandIssuer issuer) { -796 return this.manager.hasPermission(issuer, getRequiredPermissions()); +795 public boolean hasPermission(Object issuer) { +796 return hasPermission(manager.getCommandIssuer(issuer)); 797 } 798 -799 public Set<String> getRequiredPermissions() { -800 return this.permissions; +799 public boolean hasPermission(CommandIssuer issuer) { +800 return this.manager.hasPermission(issuer, getRequiredPermissions()); 801 } 802 -803 public boolean requiresPermission(String permission) { -804 return getRequiredPermissions().contains(permission); +803 public Set<String> getRequiredPermissions() { +804 return this.permissions; 805 } 806 -807 public String getName() { -808 return commandName; +807 public boolean requiresPermission(String permission) { +808 return getRequiredPermissions().contains(permission); 809 } 810 -811 public ExceptionHandler getExceptionHandler() { -812 return exceptionHandler; +811 public String getName() { +812 return commandName; 813 } 814 -815 public BaseCommand setExceptionHandler(ExceptionHandler exceptionHandler) { -816 this.exceptionHandler = exceptionHandler; -817 return this; -818 } -819 -820 public RegisteredCommand getDefaultRegisteredCommand() { -821 return ACFUtil.getFirstElement(this.subCommands.get(DEFAULT)); +815 public ExceptionHandler getExceptionHandler() { +816 return exceptionHandler; +817 } +818 +819 public BaseCommand setExceptionHandler(ExceptionHandler exceptionHandler) { +820 this.exceptionHandler = exceptionHandler; +821 return this; 822 } 823 -824 public String setContextFlags(Class<?> cls, String flags) { -825 return this.contextFlags.put(cls, flags); +824 public RegisteredCommand getDefaultRegisteredCommand() { +825 return ACFUtil.getFirstElement(this.subCommands.get(DEFAULT)); 826 } 827 -828 public String getContextFlags(Class<?> cls) { -829 return this.contextFlags.get(cls); +828 public String setContextFlags(Class<?> cls, String flags) { +829 return this.contextFlags.put(cls, flags); 830 } 831 -832 public List<RegisteredCommand> getRegisteredCommands() { -833 List<RegisteredCommand> registeredCommands = new ArrayList<>(); -834 registeredCommands.addAll(this.subCommands.values()); -835 return registeredCommands; -836 } -837} +832 public String getContextFlags(Class<?> cls) { +833 return this.contextFlags.get(cls); +834 } +835 +836 public List<RegisteredCommand> getRegisteredCommands() { +837 List<RegisteredCommand> registeredCommands = new ArrayList<>(); +838 registeredCommands.addAll(this.subCommands.values()); +839 return registeredCommands; +840 } +841} diff --git a/docs/acf-core/src-html/co/aikar/commands/CommandCompletions.AsyncCommandCompletionHandler.html b/docs/acf-core/src-html/co/aikar/commands/CommandCompletions.AsyncCommandCompletionHandler.html index 124cf0d9..cfa029d2 100644 --- a/docs/acf-core/src-html/co/aikar/commands/CommandCompletions.AsyncCommandCompletionHandler.html +++ b/docs/acf-core/src-html/co/aikar/commands/CommandCompletions.AsyncCommandCompletionHandler.html @@ -31,269 +31,289 @@ 023 024package co.aikar.commands; 025 -026import org.jetbrains.annotations.NotNull; -027 -028import java.util.ArrayList; -029import java.util.Arrays; -030import java.util.Collection; -031import java.util.Collections; -032import java.util.HashMap; -033import java.util.List; -034import java.util.Map; -035import java.util.function.Supplier; -036import java.util.stream.Collectors; -037import java.util.stream.IntStream; -038 +026import co.aikar.commands.apachecommonslang.ApacheCommonsLangUtil; +027import org.jetbrains.annotations.NotNull; +028 +029import java.util.ArrayList; +030import java.util.Arrays; +031import java.util.Collection; +032import java.util.Collections; +033import java.util.HashMap; +034import java.util.List; +035import java.util.Map; +036import java.util.function.Supplier; +037import java.util.stream.Collectors; +038import java.util.stream.IntStream; 039 -040@SuppressWarnings({"WeakerAccess", "UnusedReturnValue"}) -041public class CommandCompletions<C extends CommandCompletionContext> { -042 private static final String DEFAULT_ENUM_ID = "@__defaultenum__"; -043 private final CommandManager manager; -044 // TODO: use a CompletionProvider that can return a delegated Id or provide values such as enum support -045 private Map<String, CommandCompletionHandler> completionMap = new HashMap<>(); -046 private Map<Class, String> defaultCompletions = new HashMap<>(); -047 -048 public CommandCompletions(CommandManager manager) { -049 this.manager = manager; -050 registerStaticCompletion("empty", Collections.emptyList()); -051 registerStaticCompletion("nothing", Collections.emptyList()); -052 registerStaticCompletion("timeunits", Arrays.asList("minutes", "hours", "days", "weeks", "months", "years")); -053 registerAsyncCompletion("range", (c) -> { -054 String config = c.getConfig(); -055 if (config == null) { -056 return Collections.emptyList(); -057 } -058 final String[] ranges = ACFPatterns.DASH.split(config); -059 int start; -060 int end; -061 if (ranges.length != 2) { -062 start = 0; -063 end = ACFUtil.parseInt(ranges[0], 0); -064 } else { -065 start = ACFUtil.parseInt(ranges[0], 0); -066 end = ACFUtil.parseInt(ranges[1], 0); -067 } -068 return IntStream.rangeClosed(start, end).mapToObj(Integer::toString).collect(Collectors.toList()); -069 }); -070 } -071 -072 /** -073 * Registr a completion handler to provide command completions based on the user input. -074 * -075 * @param id -076 * @param handler -077 * @return -078 */ -079 public CommandCompletionHandler registerCompletion(String id, CommandCompletionHandler<C> handler) { -080 return this.completionMap.put(prepareCompletionId(id), handler); -081 } -082 -083 /** -084 * Registr a completion handler to provide command completions based on the user input. -085 * This handler is declared to be safe to be executed asynchronously. -086 * <p> -087 * Not all platforms support this, so if the platform does not support asynchronous execution, -088 * your handler will be executed on the main thread. -089 * <p> -090 * Use this anytime your handler does not need to access state that is not considered thread safe. -091 * <p> -092 * Use context.isAsync() to determine if you are async or not. -093 * -094 * @param id -095 * @param handler -096 * @return -097 */ -098 public CommandCompletionHandler registerAsyncCompletion(String id, AsyncCommandCompletionHandler<C> handler) { -099 return this.completionMap.put(prepareCompletionId(id), handler); -100 } -101 -102 /** -103 * Register a static list of command completions that will never change. -104 * Like @CommandCompletion, values are | (PIPE) separated. -105 * <p> -106 * Example: foo|bar|baz -107 * -108 * @param id -109 * @param list -110 * @return -111 */ -112 public CommandCompletionHandler registerStaticCompletion(String id, String list) { -113 return registerStaticCompletion(id, ACFPatterns.PIPE.split(list)); -114 } -115 -116 /** -117 * Register a static list of command completions that will never change -118 * -119 * @param id -120 * @param completions -121 * @return -122 */ -123 public CommandCompletionHandler registerStaticCompletion(String id, String[] completions) { -124 return registerStaticCompletion(id, Arrays.asList(completions)); -125 } -126 -127 /** -128 * Register a static list of command completions that will never change. The list is obtained from the supplier -129 * immediately as part of this method call. -130 * -131 * @param id -132 * @param supplier -133 * @return -134 */ -135 public CommandCompletionHandler registerStaticCompletion(String id, Supplier<Collection<String>> supplier) { -136 return registerStaticCompletion(id, supplier.get()); -137 } -138 -139 /** -140 * Register a static list of command completions that will never change -141 * -142 * @param id -143 * @param completions -144 * @return -145 */ -146 public CommandCompletionHandler registerStaticCompletion(String id, Collection<String> completions) { -147 return registerAsyncCompletion(id, x -> completions); -148 } -149 -150 /** -151 * Registers a completion handler such as @players to default apply to all command parameters of the specified types -152 * <p> -153 * This enables automatic completion support for parameters without manually defining it for custom objects -154 * -155 * @param id -156 * @param classes -157 */ -158 public void setDefaultCompletion(String id, Class... classes) { -159 // get completion with specified id -160 id = prepareCompletionId(id); -161 CommandCompletionHandler completion = completionMap.get(id); -162 -163 if (completion == null) { -164 // Throw something because no completion with specified id -165 throw new IllegalStateException("Completion not registered for " + id); -166 } -167 -168 for (Class clazz : classes) { -169 defaultCompletions.put(clazz, id); -170 } -171 } -172 -173 @NotNull -174 private static String prepareCompletionId(String id) { -175 return (id.startsWith("@") ? "" : "@") + id.toLowerCase(); -176 } -177 -178 @NotNull -179 List<String> of(RegisteredCommand cmd, CommandIssuer sender, String[] args, boolean isAsync) { -180 String[] completions = ACFPatterns.SPACE.split(cmd.complete); -181 final int argIndex = args.length - 1; -182 -183 String input = args[argIndex]; -184 -185 String completion = argIndex < completions.length ? completions[argIndex] : null; -186 if (completion == null || "*".equals(completion)) { -187 completion = findDefaultCompletion(cmd, args); -188 } -189 -190 if (completion == null && completions.length > 0) { -191 String last = completions[completions.length - 1]; -192 if (last.startsWith("repeat@")) { -193 completion = last; -194 } -195 } -196 -197 if (completion == null) { -198 return Collections.singletonList(input); -199 } -200 -201 return getCompletionValues(cmd, sender, completion, args, isAsync); -202 } +040 +041@SuppressWarnings({"WeakerAccess", "UnusedReturnValue"}) +042public class CommandCompletions<C extends CommandCompletionContext> { +043 private static final String DEFAULT_ENUM_ID = "@__defaultenum__"; +044 private final CommandManager manager; +045 // TODO: use a CompletionProvider that can return a delegated Id or provide values such as enum support +046 private Map<String, CommandCompletionHandler> completionMap = new HashMap<>(); +047 private Map<Class, String> defaultCompletions = new HashMap<>(); +048 +049 public CommandCompletions(CommandManager manager) { +050 this.manager = manager; +051 registerStaticCompletion("empty", Collections.emptyList()); +052 registerStaticCompletion("nothing", Collections.emptyList()); +053 registerStaticCompletion("timeunits", Arrays.asList("minutes", "hours", "days", "weeks", "months", "years")); +054 registerAsyncCompletion("range", (c) -> { +055 String config = c.getConfig(); +056 if (config == null) { +057 return Collections.emptyList(); +058 } +059 final String[] ranges = ACFPatterns.DASH.split(config); +060 int start; +061 int end; +062 if (ranges.length != 2) { +063 start = 0; +064 end = ACFUtil.parseInt(ranges[0], 0); +065 } else { +066 start = ACFUtil.parseInt(ranges[0], 0); +067 end = ACFUtil.parseInt(ranges[1], 0); +068 } +069 return IntStream.rangeClosed(start, end).mapToObj(Integer::toString).collect(Collectors.toList()); +070 }); +071 } +072 +073 /** +074 * Registr a completion handler to provide command completions based on the user input. +075 * +076 * @param id +077 * @param handler +078 * @return +079 */ +080 public CommandCompletionHandler registerCompletion(String id, CommandCompletionHandler<C> handler) { +081 return this.completionMap.put(prepareCompletionId(id), handler); +082 } +083 +084 /** +085 * Registr a completion handler to provide command completions based on the user input. +086 * This handler is declared to be safe to be executed asynchronously. +087 * <p> +088 * Not all platforms support this, so if the platform does not support asynchronous execution, +089 * your handler will be executed on the main thread. +090 * <p> +091 * Use this anytime your handler does not need to access state that is not considered thread safe. +092 * <p> +093 * Use context.isAsync() to determine if you are async or not. +094 * +095 * @param id +096 * @param handler +097 * @return +098 */ +099 public CommandCompletionHandler registerAsyncCompletion(String id, AsyncCommandCompletionHandler<C> handler) { +100 return this.completionMap.put(prepareCompletionId(id), handler); +101 } +102 +103 /** +104 * Register a static list of command completions that will never change. +105 * Like @CommandCompletion, values are | (PIPE) separated. +106 * <p> +107 * Example: foo|bar|baz +108 * +109 * @param id +110 * @param list +111 * @return +112 */ +113 public CommandCompletionHandler registerStaticCompletion(String id, String list) { +114 return registerStaticCompletion(id, ACFPatterns.PIPE.split(list)); +115 } +116 +117 /** +118 * Register a static list of command completions that will never change +119 * +120 * @param id +121 * @param completions +122 * @return +123 */ +124 public CommandCompletionHandler registerStaticCompletion(String id, String[] completions) { +125 return registerStaticCompletion(id, Arrays.asList(completions)); +126 } +127 +128 /** +129 * Register a static list of command completions that will never change. The list is obtained from the supplier +130 * immediately as part of this method call. +131 * +132 * @param id +133 * @param supplier +134 * @return +135 */ +136 public CommandCompletionHandler registerStaticCompletion(String id, Supplier<Collection<String>> supplier) { +137 return registerStaticCompletion(id, supplier.get()); +138 } +139 +140 /** +141 * Register a static list of command completions that will never change +142 * +143 * @param id +144 * @param completions +145 * @return +146 */ +147 public CommandCompletionHandler registerStaticCompletion(String id, Collection<String> completions) { +148 return registerAsyncCompletion(id, x -> completions); +149 } +150 +151 /** +152 * Registers a completion handler such as @players to default apply to all command parameters of the specified types +153 * <p> +154 * This enables automatic completion support for parameters without manually defining it for custom objects +155 * +156 * @param id +157 * @param classes +158 */ +159 public void setDefaultCompletion(String id, Class... classes) { +160 // get completion with specified id +161 id = prepareCompletionId(id); +162 CommandCompletionHandler completion = completionMap.get(id); +163 +164 if (completion == null) { +165 // Throw something because no completion with specified id +166 throw new IllegalStateException("Completion not registered for " + id); +167 } +168 +169 for (Class clazz : classes) { +170 defaultCompletions.put(clazz, id); +171 } +172 } +173 +174 @NotNull +175 private static String prepareCompletionId(String id) { +176 return (id.startsWith("@") ? "" : "@") + id.toLowerCase(); +177 } +178 +179 @NotNull +180 List<String> of(RegisteredCommand cmd, CommandIssuer sender, String[] args, boolean isAsync) { +181 String[] completions = ACFPatterns.SPACE.split(cmd.complete); +182 final int argIndex = args.length - 1; +183 +184 String input = args[argIndex]; +185 +186 String completion = argIndex < completions.length ? completions[argIndex] : null; +187 if (completion == null || "*".equals(completion)) { +188 completion = findDefaultCompletion(cmd, args); +189 } +190 +191 if (completion == null && completions.length > 0) { +192 String last = completions[completions.length - 1]; +193 if (last.startsWith("repeat@")) { +194 completion = last; +195 } else if (argIndex >= completions.length && cmd.parameters[cmd.parameters.length - 1].consumesRest) { +196 completion = last; +197 } +198 } +199 +200 if (completion == null) { +201 return Collections.singletonList(input); +202 } 203 -204 String findDefaultCompletion(RegisteredCommand cmd, String[] args) { -205 int i = 0; -206 for (CommandParameter param : cmd.parameters) { -207 if (param.canConsumeInput() && ++i == args.length) { -208 Class type = param.getType(); -209 while (type != null) { -210 String completion = this.defaultCompletions.get(type); -211 if (completion != null) { -212 return completion; -213 } -214 type = type.getSuperclass(); -215 } -216 if (param.getType().isEnum()) { -217 CommandOperationContext ctx = CommandManager.getCurrentCommandOperationContext(); -218 //noinspection unchecked -219 ctx.enumCompletionValues = ACFUtil.enumNames((Class<? extends Enum<?>>) param.getType()); -220 return DEFAULT_ENUM_ID; -221 } -222 break; -223 } -224 } -225 return null; -226 } -227 -228 List<String> getCompletionValues(RegisteredCommand command, CommandIssuer sender, String completion, String[] args, boolean isAsync) { -229 if (DEFAULT_ENUM_ID.equals(completion)) { -230 CommandOperationContext<?> ctx = CommandManager.getCurrentCommandOperationContext(); -231 return ctx.enumCompletionValues; -232 } -233 if (completion.startsWith("repeat@")) { -234 completion = completion.substring(6); +204 return getCompletionValues(cmd, sender, completion, args, isAsync); +205 } +206 +207 String findDefaultCompletion(RegisteredCommand cmd, String[] args) { +208 int i = 0; +209 for (CommandParameter param : cmd.parameters) { +210 if (param.canConsumeInput() && ++i == args.length) { +211 Class type = param.getType(); +212 while (type != null) { +213 String completion = this.defaultCompletions.get(type); +214 if (completion != null) { +215 return completion; +216 } +217 type = type.getSuperclass(); +218 } +219 if (param.getType().isEnum()) { +220 CommandOperationContext ctx = CommandManager.getCurrentCommandOperationContext(); +221 //noinspection unchecked +222 ctx.enumCompletionValues = ACFUtil.enumNames((Class<? extends Enum<?>>) param.getType()); +223 return DEFAULT_ENUM_ID; +224 } +225 break; +226 } +227 } +228 return null; +229 } +230 +231 List<String> getCompletionValues(RegisteredCommand command, CommandIssuer sender, String completion, String[] args, boolean isAsync) { +232 if (DEFAULT_ENUM_ID.equals(completion)) { +233 CommandOperationContext<?> ctx = CommandManager.getCurrentCommandOperationContext(); +234 return ctx.enumCompletionValues; 235 } -236 completion = manager.getCommandReplacements().replace(completion); -237 -238 List<String> allCompletions = new ArrayList<>(); -239 String input = args.length > 0 ? args[args.length - 1] : ""; -240 -241 for (String value : ACFPatterns.PIPE.split(completion)) { -242 String[] complete = ACFPatterns.COLONEQUALS.split(value, 2); -243 CommandCompletionHandler handler = this.completionMap.get(complete[0].toLowerCase()); -244 if (handler != null) { -245 if (isAsync && !(handler instanceof AsyncCommandCompletionHandler)) { -246 ACFUtil.sneaky(new SyncCompletionRequired()); -247 return null; -248 } -249 String config = complete.length == 1 ? null : complete[1]; -250 CommandCompletionContext context = manager.createCompletionContext(command, sender, input, config, args); -251 -252 try { -253 //noinspection unchecked -254 Collection<String> completions = handler.getCompletions(context); -255 if (completions != null) { -256 allCompletions.addAll(completions); -257 continue; -258 } -259 //noinspection ConstantIfStatement,ConstantConditions -260 if (false) { // Hack to fool compiler. since its sneakily thrown. -261 throw new CommandCompletionTextLookupException(); -262 } -263 } catch (CommandCompletionTextLookupException ignored) { -264 // This should only happen if some other feedback error occured. -265 } catch (Exception e) { -266 command.handleException(sender, Arrays.asList(args), e); -267 } -268 // Something went wrong in lookup, fall back to input -269 return Collections.singletonList(input); -270 } else { -271 // Plaintext value -272 allCompletions.add(value); -273 } -274 } -275 return allCompletions; -276 } -277 -278 public interface CommandCompletionHandler<C extends CommandCompletionContext> { -279 Collection<String> getCompletions(C context) throws InvalidCommandArgument; -280 } -281 -282 public interface AsyncCommandCompletionHandler<C extends CommandCompletionContext> extends CommandCompletionHandler<C> { -283 } -284 -285 public static class SyncCompletionRequired extends RuntimeException { -286 } -287 -288} +236 boolean repeat = completion.startsWith("repeat@"); +237 if (repeat) { +238 completion = completion.substring(6); +239 } +240 completion = manager.getCommandReplacements().replace(completion); +241 +242 List<String> allCompletions = new ArrayList<>(); +243 String input = args.length > 0 ? args[args.length - 1] : ""; +244 +245 for (String value : ACFPatterns.PIPE.split(completion)) { +246 String[] complete = ACFPatterns.COLONEQUALS.split(value, 2); +247 CommandCompletionHandler handler = this.completionMap.get(complete[0].toLowerCase()); +248 if (handler != null) { +249 if (isAsync && !(handler instanceof AsyncCommandCompletionHandler)) { +250 ACFUtil.sneaky(new SyncCompletionRequired()); +251 return null; +252 } +253 String config = complete.length == 1 ? null : complete[1]; +254 CommandCompletionContext context = manager.createCompletionContext(command, sender, input, config, args); +255 +256 try { +257 //noinspection unchecked +258 Collection<String> completions = handler.getCompletions(context); +259 +260 //Handle completions with more than one word: +261 if (!repeat && +262 command.parameters[command.parameters.length - 1].consumesRest +263 && args.length > ACFPatterns.SPACE.split(command.complete).length) { +264 String start = String.join(" ", args); +265 completions = completions.stream() +266 .filter(s -> s.split(" ").length >= args.length) +267 .filter(s -> ApacheCommonsLangUtil.startsWithIgnoreCase(s, start)) +268 .map(s -> { +269 String[] completionArgs = s.split(" "); +270 return String.join(" ", +271 Arrays.copyOfRange(completionArgs, args.length - 1, completionArgs.length)); +272 }).collect(Collectors.toList()); +273 } +274 +275 if (completions != null) { +276 allCompletions.addAll(completions); +277 continue; +278 } +279 //noinspection ConstantIfStatement,ConstantConditions +280 if (false) { // Hack to fool compiler. since its sneakily thrown. +281 throw new CommandCompletionTextLookupException(); +282 } +283 } catch (CommandCompletionTextLookupException ignored) { +284 // This should only happen if some other feedback error occured. +285 } catch (Exception e) { +286 command.handleException(sender, Arrays.asList(args), e); +287 } +288 // Something went wrong in lookup, fall back to input +289 return Collections.singletonList(input); +290 } else { +291 // Plaintext value +292 allCompletions.add(value); +293 } +294 } +295 return allCompletions; +296 } +297 +298 public interface CommandCompletionHandler<C extends CommandCompletionContext> { +299 Collection<String> getCompletions(C context) throws InvalidCommandArgument; +300 } +301 +302 public interface AsyncCommandCompletionHandler<C extends CommandCompletionContext> extends CommandCompletionHandler<C> { +303 } +304 +305 public static class SyncCompletionRequired extends RuntimeException { +306 } +307 +308} diff --git a/docs/acf-core/src-html/co/aikar/commands/CommandCompletions.CommandCompletionHandler.html b/docs/acf-core/src-html/co/aikar/commands/CommandCompletions.CommandCompletionHandler.html index 124cf0d9..cfa029d2 100644 --- a/docs/acf-core/src-html/co/aikar/commands/CommandCompletions.CommandCompletionHandler.html +++ b/docs/acf-core/src-html/co/aikar/commands/CommandCompletions.CommandCompletionHandler.html @@ -31,269 +31,289 @@ 023 024package co.aikar.commands; 025 -026import org.jetbrains.annotations.NotNull; -027 -028import java.util.ArrayList; -029import java.util.Arrays; -030import java.util.Collection; -031import java.util.Collections; -032import java.util.HashMap; -033import java.util.List; -034import java.util.Map; -035import java.util.function.Supplier; -036import java.util.stream.Collectors; -037import java.util.stream.IntStream; -038 +026import co.aikar.commands.apachecommonslang.ApacheCommonsLangUtil; +027import org.jetbrains.annotations.NotNull; +028 +029import java.util.ArrayList; +030import java.util.Arrays; +031import java.util.Collection; +032import java.util.Collections; +033import java.util.HashMap; +034import java.util.List; +035import java.util.Map; +036import java.util.function.Supplier; +037import java.util.stream.Collectors; +038import java.util.stream.IntStream; 039 -040@SuppressWarnings({"WeakerAccess", "UnusedReturnValue"}) -041public class CommandCompletions<C extends CommandCompletionContext> { -042 private static final String DEFAULT_ENUM_ID = "@__defaultenum__"; -043 private final CommandManager manager; -044 // TODO: use a CompletionProvider that can return a delegated Id or provide values such as enum support -045 private Map<String, CommandCompletionHandler> completionMap = new HashMap<>(); -046 private Map<Class, String> defaultCompletions = new HashMap<>(); -047 -048 public CommandCompletions(CommandManager manager) { -049 this.manager = manager; -050 registerStaticCompletion("empty", Collections.emptyList()); -051 registerStaticCompletion("nothing", Collections.emptyList()); -052 registerStaticCompletion("timeunits", Arrays.asList("minutes", "hours", "days", "weeks", "months", "years")); -053 registerAsyncCompletion("range", (c) -> { -054 String config = c.getConfig(); -055 if (config == null) { -056 return Collections.emptyList(); -057 } -058 final String[] ranges = ACFPatterns.DASH.split(config); -059 int start; -060 int end; -061 if (ranges.length != 2) { -062 start = 0; -063 end = ACFUtil.parseInt(ranges[0], 0); -064 } else { -065 start = ACFUtil.parseInt(ranges[0], 0); -066 end = ACFUtil.parseInt(ranges[1], 0); -067 } -068 return IntStream.rangeClosed(start, end).mapToObj(Integer::toString).collect(Collectors.toList()); -069 }); -070 } -071 -072 /** -073 * Registr a completion handler to provide command completions based on the user input. -074 * -075 * @param id -076 * @param handler -077 * @return -078 */ -079 public CommandCompletionHandler registerCompletion(String id, CommandCompletionHandler<C> handler) { -080 return this.completionMap.put(prepareCompletionId(id), handler); -081 } -082 -083 /** -084 * Registr a completion handler to provide command completions based on the user input. -085 * This handler is declared to be safe to be executed asynchronously. -086 * <p> -087 * Not all platforms support this, so if the platform does not support asynchronous execution, -088 * your handler will be executed on the main thread. -089 * <p> -090 * Use this anytime your handler does not need to access state that is not considered thread safe. -091 * <p> -092 * Use context.isAsync() to determine if you are async or not. -093 * -094 * @param id -095 * @param handler -096 * @return -097 */ -098 public CommandCompletionHandler registerAsyncCompletion(String id, AsyncCommandCompletionHandler<C> handler) { -099 return this.completionMap.put(prepareCompletionId(id), handler); -100 } -101 -102 /** -103 * Register a static list of command completions that will never change. -104 * Like @CommandCompletion, values are | (PIPE) separated. -105 * <p> -106 * Example: foo|bar|baz -107 * -108 * @param id -109 * @param list -110 * @return -111 */ -112 public CommandCompletionHandler registerStaticCompletion(String id, String list) { -113 return registerStaticCompletion(id, ACFPatterns.PIPE.split(list)); -114 } -115 -116 /** -117 * Register a static list of command completions that will never change -118 * -119 * @param id -120 * @param completions -121 * @return -122 */ -123 public CommandCompletionHandler registerStaticCompletion(String id, String[] completions) { -124 return registerStaticCompletion(id, Arrays.asList(completions)); -125 } -126 -127 /** -128 * Register a static list of command completions that will never change. The list is obtained from the supplier -129 * immediately as part of this method call. -130 * -131 * @param id -132 * @param supplier -133 * @return -134 */ -135 public CommandCompletionHandler registerStaticCompletion(String id, Supplier<Collection<String>> supplier) { -136 return registerStaticCompletion(id, supplier.get()); -137 } -138 -139 /** -140 * Register a static list of command completions that will never change -141 * -142 * @param id -143 * @param completions -144 * @return -145 */ -146 public CommandCompletionHandler registerStaticCompletion(String id, Collection<String> completions) { -147 return registerAsyncCompletion(id, x -> completions); -148 } -149 -150 /** -151 * Registers a completion handler such as @players to default apply to all command parameters of the specified types -152 * <p> -153 * This enables automatic completion support for parameters without manually defining it for custom objects -154 * -155 * @param id -156 * @param classes -157 */ -158 public void setDefaultCompletion(String id, Class... classes) { -159 // get completion with specified id -160 id = prepareCompletionId(id); -161 CommandCompletionHandler completion = completionMap.get(id); -162 -163 if (completion == null) { -164 // Throw something because no completion with specified id -165 throw new IllegalStateException("Completion not registered for " + id); -166 } -167 -168 for (Class clazz : classes) { -169 defaultCompletions.put(clazz, id); -170 } -171 } -172 -173 @NotNull -174 private static String prepareCompletionId(String id) { -175 return (id.startsWith("@") ? "" : "@") + id.toLowerCase(); -176 } -177 -178 @NotNull -179 List<String> of(RegisteredCommand cmd, CommandIssuer sender, String[] args, boolean isAsync) { -180 String[] completions = ACFPatterns.SPACE.split(cmd.complete); -181 final int argIndex = args.length - 1; -182 -183 String input = args[argIndex]; -184 -185 String completion = argIndex < completions.length ? completions[argIndex] : null; -186 if (completion == null || "*".equals(completion)) { -187 completion = findDefaultCompletion(cmd, args); -188 } -189 -190 if (completion == null && completions.length > 0) { -191 String last = completions[completions.length - 1]; -192 if (last.startsWith("repeat@")) { -193 completion = last; -194 } -195 } -196 -197 if (completion == null) { -198 return Collections.singletonList(input); -199 } -200 -201 return getCompletionValues(cmd, sender, completion, args, isAsync); -202 } +040 +041@SuppressWarnings({"WeakerAccess", "UnusedReturnValue"}) +042public class CommandCompletions<C extends CommandCompletionContext> { +043 private static final String DEFAULT_ENUM_ID = "@__defaultenum__"; +044 private final CommandManager manager; +045 // TODO: use a CompletionProvider that can return a delegated Id or provide values such as enum support +046 private Map<String, CommandCompletionHandler> completionMap = new HashMap<>(); +047 private Map<Class, String> defaultCompletions = new HashMap<>(); +048 +049 public CommandCompletions(CommandManager manager) { +050 this.manager = manager; +051 registerStaticCompletion("empty", Collections.emptyList()); +052 registerStaticCompletion("nothing", Collections.emptyList()); +053 registerStaticCompletion("timeunits", Arrays.asList("minutes", "hours", "days", "weeks", "months", "years")); +054 registerAsyncCompletion("range", (c) -> { +055 String config = c.getConfig(); +056 if (config == null) { +057 return Collections.emptyList(); +058 } +059 final String[] ranges = ACFPatterns.DASH.split(config); +060 int start; +061 int end; +062 if (ranges.length != 2) { +063 start = 0; +064 end = ACFUtil.parseInt(ranges[0], 0); +065 } else { +066 start = ACFUtil.parseInt(ranges[0], 0); +067 end = ACFUtil.parseInt(ranges[1], 0); +068 } +069 return IntStream.rangeClosed(start, end).mapToObj(Integer::toString).collect(Collectors.toList()); +070 }); +071 } +072 +073 /** +074 * Registr a completion handler to provide command completions based on the user input. +075 * +076 * @param id +077 * @param handler +078 * @return +079 */ +080 public CommandCompletionHandler registerCompletion(String id, CommandCompletionHandler<C> handler) { +081 return this.completionMap.put(prepareCompletionId(id), handler); +082 } +083 +084 /** +085 * Registr a completion handler to provide command completions based on the user input. +086 * This handler is declared to be safe to be executed asynchronously. +087 * <p> +088 * Not all platforms support this, so if the platform does not support asynchronous execution, +089 * your handler will be executed on the main thread. +090 * <p> +091 * Use this anytime your handler does not need to access state that is not considered thread safe. +092 * <p> +093 * Use context.isAsync() to determine if you are async or not. +094 * +095 * @param id +096 * @param handler +097 * @return +098 */ +099 public CommandCompletionHandler registerAsyncCompletion(String id, AsyncCommandCompletionHandler<C> handler) { +100 return this.completionMap.put(prepareCompletionId(id), handler); +101 } +102 +103 /** +104 * Register a static list of command completions that will never change. +105 * Like @CommandCompletion, values are | (PIPE) separated. +106 * <p> +107 * Example: foo|bar|baz +108 * +109 * @param id +110 * @param list +111 * @return +112 */ +113 public CommandCompletionHandler registerStaticCompletion(String id, String list) { +114 return registerStaticCompletion(id, ACFPatterns.PIPE.split(list)); +115 } +116 +117 /** +118 * Register a static list of command completions that will never change +119 * +120 * @param id +121 * @param completions +122 * @return +123 */ +124 public CommandCompletionHandler registerStaticCompletion(String id, String[] completions) { +125 return registerStaticCompletion(id, Arrays.asList(completions)); +126 } +127 +128 /** +129 * Register a static list of command completions that will never change. The list is obtained from the supplier +130 * immediately as part of this method call. +131 * +132 * @param id +133 * @param supplier +134 * @return +135 */ +136 public CommandCompletionHandler registerStaticCompletion(String id, Supplier<Collection<String>> supplier) { +137 return registerStaticCompletion(id, supplier.get()); +138 } +139 +140 /** +141 * Register a static list of command completions that will never change +142 * +143 * @param id +144 * @param completions +145 * @return +146 */ +147 public CommandCompletionHandler registerStaticCompletion(String id, Collection<String> completions) { +148 return registerAsyncCompletion(id, x -> completions); +149 } +150 +151 /** +152 * Registers a completion handler such as @players to default apply to all command parameters of the specified types +153 * <p> +154 * This enables automatic completion support for parameters without manually defining it for custom objects +155 * +156 * @param id +157 * @param classes +158 */ +159 public void setDefaultCompletion(String id, Class... classes) { +160 // get completion with specified id +161 id = prepareCompletionId(id); +162 CommandCompletionHandler completion = completionMap.get(id); +163 +164 if (completion == null) { +165 // Throw something because no completion with specified id +166 throw new IllegalStateException("Completion not registered for " + id); +167 } +168 +169 for (Class clazz : classes) { +170 defaultCompletions.put(clazz, id); +171 } +172 } +173 +174 @NotNull +175 private static String prepareCompletionId(String id) { +176 return (id.startsWith("@") ? "" : "@") + id.toLowerCase(); +177 } +178 +179 @NotNull +180 List<String> of(RegisteredCommand cmd, CommandIssuer sender, String[] args, boolean isAsync) { +181 String[] completions = ACFPatterns.SPACE.split(cmd.complete); +182 final int argIndex = args.length - 1; +183 +184 String input = args[argIndex]; +185 +186 String completion = argIndex < completions.length ? completions[argIndex] : null; +187 if (completion == null || "*".equals(completion)) { +188 completion = findDefaultCompletion(cmd, args); +189 } +190 +191 if (completion == null && completions.length > 0) { +192 String last = completions[completions.length - 1]; +193 if (last.startsWith("repeat@")) { +194 completion = last; +195 } else if (argIndex >= completions.length && cmd.parameters[cmd.parameters.length - 1].consumesRest) { +196 completion = last; +197 } +198 } +199 +200 if (completion == null) { +201 return Collections.singletonList(input); +202 } 203 -204 String findDefaultCompletion(RegisteredCommand cmd, String[] args) { -205 int i = 0; -206 for (CommandParameter param : cmd.parameters) { -207 if (param.canConsumeInput() && ++i == args.length) { -208 Class type = param.getType(); -209 while (type != null) { -210 String completion = this.defaultCompletions.get(type); -211 if (completion != null) { -212 return completion; -213 } -214 type = type.getSuperclass(); -215 } -216 if (param.getType().isEnum()) { -217 CommandOperationContext ctx = CommandManager.getCurrentCommandOperationContext(); -218 //noinspection unchecked -219 ctx.enumCompletionValues = ACFUtil.enumNames((Class<? extends Enum<?>>) param.getType()); -220 return DEFAULT_ENUM_ID; -221 } -222 break; -223 } -224 } -225 return null; -226 } -227 -228 List<String> getCompletionValues(RegisteredCommand command, CommandIssuer sender, String completion, String[] args, boolean isAsync) { -229 if (DEFAULT_ENUM_ID.equals(completion)) { -230 CommandOperationContext<?> ctx = CommandManager.getCurrentCommandOperationContext(); -231 return ctx.enumCompletionValues; -232 } -233 if (completion.startsWith("repeat@")) { -234 completion = completion.substring(6); +204 return getCompletionValues(cmd, sender, completion, args, isAsync); +205 } +206 +207 String findDefaultCompletion(RegisteredCommand cmd, String[] args) { +208 int i = 0; +209 for (CommandParameter param : cmd.parameters) { +210 if (param.canConsumeInput() && ++i == args.length) { +211 Class type = param.getType(); +212 while (type != null) { +213 String completion = this.defaultCompletions.get(type); +214 if (completion != null) { +215 return completion; +216 } +217 type = type.getSuperclass(); +218 } +219 if (param.getType().isEnum()) { +220 CommandOperationContext ctx = CommandManager.getCurrentCommandOperationContext(); +221 //noinspection unchecked +222 ctx.enumCompletionValues = ACFUtil.enumNames((Class<? extends Enum<?>>) param.getType()); +223 return DEFAULT_ENUM_ID; +224 } +225 break; +226 } +227 } +228 return null; +229 } +230 +231 List<String> getCompletionValues(RegisteredCommand command, CommandIssuer sender, String completion, String[] args, boolean isAsync) { +232 if (DEFAULT_ENUM_ID.equals(completion)) { +233 CommandOperationContext<?> ctx = CommandManager.getCurrentCommandOperationContext(); +234 return ctx.enumCompletionValues; 235 } -236 completion = manager.getCommandReplacements().replace(completion); -237 -238 List<String> allCompletions = new ArrayList<>(); -239 String input = args.length > 0 ? args[args.length - 1] : ""; -240 -241 for (String value : ACFPatterns.PIPE.split(completion)) { -242 String[] complete = ACFPatterns.COLONEQUALS.split(value, 2); -243 CommandCompletionHandler handler = this.completionMap.get(complete[0].toLowerCase()); -244 if (handler != null) { -245 if (isAsync && !(handler instanceof AsyncCommandCompletionHandler)) { -246 ACFUtil.sneaky(new SyncCompletionRequired()); -247 return null; -248 } -249 String config = complete.length == 1 ? null : complete[1]; -250 CommandCompletionContext context = manager.createCompletionContext(command, sender, input, config, args); -251 -252 try { -253 //noinspection unchecked -254 Collection<String> completions = handler.getCompletions(context); -255 if (completions != null) { -256 allCompletions.addAll(completions); -257 continue; -258 } -259 //noinspection ConstantIfStatement,ConstantConditions -260 if (false) { // Hack to fool compiler. since its sneakily thrown. -261 throw new CommandCompletionTextLookupException(); -262 } -263 } catch (CommandCompletionTextLookupException ignored) { -264 // This should only happen if some other feedback error occured. -265 } catch (Exception e) { -266 command.handleException(sender, Arrays.asList(args), e); -267 } -268 // Something went wrong in lookup, fall back to input -269 return Collections.singletonList(input); -270 } else { -271 // Plaintext value -272 allCompletions.add(value); -273 } -274 } -275 return allCompletions; -276 } -277 -278 public interface CommandCompletionHandler<C extends CommandCompletionContext> { -279 Collection<String> getCompletions(C context) throws InvalidCommandArgument; -280 } -281 -282 public interface AsyncCommandCompletionHandler<C extends CommandCompletionContext> extends CommandCompletionHandler<C> { -283 } -284 -285 public static class SyncCompletionRequired extends RuntimeException { -286 } -287 -288} +236 boolean repeat = completion.startsWith("repeat@"); +237 if (repeat) { +238 completion = completion.substring(6); +239 } +240 completion = manager.getCommandReplacements().replace(completion); +241 +242 List<String> allCompletions = new ArrayList<>(); +243 String input = args.length > 0 ? args[args.length - 1] : ""; +244 +245 for (String value : ACFPatterns.PIPE.split(completion)) { +246 String[] complete = ACFPatterns.COLONEQUALS.split(value, 2); +247 CommandCompletionHandler handler = this.completionMap.get(complete[0].toLowerCase()); +248 if (handler != null) { +249 if (isAsync && !(handler instanceof AsyncCommandCompletionHandler)) { +250 ACFUtil.sneaky(new SyncCompletionRequired()); +251 return null; +252 } +253 String config = complete.length == 1 ? null : complete[1]; +254 CommandCompletionContext context = manager.createCompletionContext(command, sender, input, config, args); +255 +256 try { +257 //noinspection unchecked +258 Collection<String> completions = handler.getCompletions(context); +259 +260 //Handle completions with more than one word: +261 if (!repeat && +262 command.parameters[command.parameters.length - 1].consumesRest +263 && args.length > ACFPatterns.SPACE.split(command.complete).length) { +264 String start = String.join(" ", args); +265 completions = completions.stream() +266 .filter(s -> s.split(" ").length >= args.length) +267 .filter(s -> ApacheCommonsLangUtil.startsWithIgnoreCase(s, start)) +268 .map(s -> { +269 String[] completionArgs = s.split(" "); +270 return String.join(" ", +271 Arrays.copyOfRange(completionArgs, args.length - 1, completionArgs.length)); +272 }).collect(Collectors.toList()); +273 } +274 +275 if (completions != null) { +276 allCompletions.addAll(completions); +277 continue; +278 } +279 //noinspection ConstantIfStatement,ConstantConditions +280 if (false) { // Hack to fool compiler. since its sneakily thrown. +281 throw new CommandCompletionTextLookupException(); +282 } +283 } catch (CommandCompletionTextLookupException ignored) { +284 // This should only happen if some other feedback error occured. +285 } catch (Exception e) { +286 command.handleException(sender, Arrays.asList(args), e); +287 } +288 // Something went wrong in lookup, fall back to input +289 return Collections.singletonList(input); +290 } else { +291 // Plaintext value +292 allCompletions.add(value); +293 } +294 } +295 return allCompletions; +296 } +297 +298 public interface CommandCompletionHandler<C extends CommandCompletionContext> { +299 Collection<String> getCompletions(C context) throws InvalidCommandArgument; +300 } +301 +302 public interface AsyncCommandCompletionHandler<C extends CommandCompletionContext> extends CommandCompletionHandler<C> { +303 } +304 +305 public static class SyncCompletionRequired extends RuntimeException { +306 } +307 +308} diff --git a/docs/acf-core/src-html/co/aikar/commands/CommandCompletions.SyncCompletionRequired.html b/docs/acf-core/src-html/co/aikar/commands/CommandCompletions.SyncCompletionRequired.html index 124cf0d9..cfa029d2 100644 --- a/docs/acf-core/src-html/co/aikar/commands/CommandCompletions.SyncCompletionRequired.html +++ b/docs/acf-core/src-html/co/aikar/commands/CommandCompletions.SyncCompletionRequired.html @@ -31,269 +31,289 @@ 023 024package co.aikar.commands; 025 -026import org.jetbrains.annotations.NotNull; -027 -028import java.util.ArrayList; -029import java.util.Arrays; -030import java.util.Collection; -031import java.util.Collections; -032import java.util.HashMap; -033import java.util.List; -034import java.util.Map; -035import java.util.function.Supplier; -036import java.util.stream.Collectors; -037import java.util.stream.IntStream; -038 +026import co.aikar.commands.apachecommonslang.ApacheCommonsLangUtil; +027import org.jetbrains.annotations.NotNull; +028 +029import java.util.ArrayList; +030import java.util.Arrays; +031import java.util.Collection; +032import java.util.Collections; +033import java.util.HashMap; +034import java.util.List; +035import java.util.Map; +036import java.util.function.Supplier; +037import java.util.stream.Collectors; +038import java.util.stream.IntStream; 039 -040@SuppressWarnings({"WeakerAccess", "UnusedReturnValue"}) -041public class CommandCompletions<C extends CommandCompletionContext> { -042 private static final String DEFAULT_ENUM_ID = "@__defaultenum__"; -043 private final CommandManager manager; -044 // TODO: use a CompletionProvider that can return a delegated Id or provide values such as enum support -045 private Map<String, CommandCompletionHandler> completionMap = new HashMap<>(); -046 private Map<Class, String> defaultCompletions = new HashMap<>(); -047 -048 public CommandCompletions(CommandManager manager) { -049 this.manager = manager; -050 registerStaticCompletion("empty", Collections.emptyList()); -051 registerStaticCompletion("nothing", Collections.emptyList()); -052 registerStaticCompletion("timeunits", Arrays.asList("minutes", "hours", "days", "weeks", "months", "years")); -053 registerAsyncCompletion("range", (c) -> { -054 String config = c.getConfig(); -055 if (config == null) { -056 return Collections.emptyList(); -057 } -058 final String[] ranges = ACFPatterns.DASH.split(config); -059 int start; -060 int end; -061 if (ranges.length != 2) { -062 start = 0; -063 end = ACFUtil.parseInt(ranges[0], 0); -064 } else { -065 start = ACFUtil.parseInt(ranges[0], 0); -066 end = ACFUtil.parseInt(ranges[1], 0); -067 } -068 return IntStream.rangeClosed(start, end).mapToObj(Integer::toString).collect(Collectors.toList()); -069 }); -070 } -071 -072 /** -073 * Registr a completion handler to provide command completions based on the user input. -074 * -075 * @param id -076 * @param handler -077 * @return -078 */ -079 public CommandCompletionHandler registerCompletion(String id, CommandCompletionHandler<C> handler) { -080 return this.completionMap.put(prepareCompletionId(id), handler); -081 } -082 -083 /** -084 * Registr a completion handler to provide command completions based on the user input. -085 * This handler is declared to be safe to be executed asynchronously. -086 * <p> -087 * Not all platforms support this, so if the platform does not support asynchronous execution, -088 * your handler will be executed on the main thread. -089 * <p> -090 * Use this anytime your handler does not need to access state that is not considered thread safe. -091 * <p> -092 * Use context.isAsync() to determine if you are async or not. -093 * -094 * @param id -095 * @param handler -096 * @return -097 */ -098 public CommandCompletionHandler registerAsyncCompletion(String id, AsyncCommandCompletionHandler<C> handler) { -099 return this.completionMap.put(prepareCompletionId(id), handler); -100 } -101 -102 /** -103 * Register a static list of command completions that will never change. -104 * Like @CommandCompletion, values are | (PIPE) separated. -105 * <p> -106 * Example: foo|bar|baz -107 * -108 * @param id -109 * @param list -110 * @return -111 */ -112 public CommandCompletionHandler registerStaticCompletion(String id, String list) { -113 return registerStaticCompletion(id, ACFPatterns.PIPE.split(list)); -114 } -115 -116 /** -117 * Register a static list of command completions that will never change -118 * -119 * @param id -120 * @param completions -121 * @return -122 */ -123 public CommandCompletionHandler registerStaticCompletion(String id, String[] completions) { -124 return registerStaticCompletion(id, Arrays.asList(completions)); -125 } -126 -127 /** -128 * Register a static list of command completions that will never change. The list is obtained from the supplier -129 * immediately as part of this method call. -130 * -131 * @param id -132 * @param supplier -133 * @return -134 */ -135 public CommandCompletionHandler registerStaticCompletion(String id, Supplier<Collection<String>> supplier) { -136 return registerStaticCompletion(id, supplier.get()); -137 } -138 -139 /** -140 * Register a static list of command completions that will never change -141 * -142 * @param id -143 * @param completions -144 * @return -145 */ -146 public CommandCompletionHandler registerStaticCompletion(String id, Collection<String> completions) { -147 return registerAsyncCompletion(id, x -> completions); -148 } -149 -150 /** -151 * Registers a completion handler such as @players to default apply to all command parameters of the specified types -152 * <p> -153 * This enables automatic completion support for parameters without manually defining it for custom objects -154 * -155 * @param id -156 * @param classes -157 */ -158 public void setDefaultCompletion(String id, Class... classes) { -159 // get completion with specified id -160 id = prepareCompletionId(id); -161 CommandCompletionHandler completion = completionMap.get(id); -162 -163 if (completion == null) { -164 // Throw something because no completion with specified id -165 throw new IllegalStateException("Completion not registered for " + id); -166 } -167 -168 for (Class clazz : classes) { -169 defaultCompletions.put(clazz, id); -170 } -171 } -172 -173 @NotNull -174 private static String prepareCompletionId(String id) { -175 return (id.startsWith("@") ? "" : "@") + id.toLowerCase(); -176 } -177 -178 @NotNull -179 List<String> of(RegisteredCommand cmd, CommandIssuer sender, String[] args, boolean isAsync) { -180 String[] completions = ACFPatterns.SPACE.split(cmd.complete); -181 final int argIndex = args.length - 1; -182 -183 String input = args[argIndex]; -184 -185 String completion = argIndex < completions.length ? completions[argIndex] : null; -186 if (completion == null || "*".equals(completion)) { -187 completion = findDefaultCompletion(cmd, args); -188 } -189 -190 if (completion == null && completions.length > 0) { -191 String last = completions[completions.length - 1]; -192 if (last.startsWith("repeat@")) { -193 completion = last; -194 } -195 } -196 -197 if (completion == null) { -198 return Collections.singletonList(input); -199 } -200 -201 return getCompletionValues(cmd, sender, completion, args, isAsync); -202 } +040 +041@SuppressWarnings({"WeakerAccess", "UnusedReturnValue"}) +042public class CommandCompletions<C extends CommandCompletionContext> { +043 private static final String DEFAULT_ENUM_ID = "@__defaultenum__"; +044 private final CommandManager manager; +045 // TODO: use a CompletionProvider that can return a delegated Id or provide values such as enum support +046 private Map<String, CommandCompletionHandler> completionMap = new HashMap<>(); +047 private Map<Class, String> defaultCompletions = new HashMap<>(); +048 +049 public CommandCompletions(CommandManager manager) { +050 this.manager = manager; +051 registerStaticCompletion("empty", Collections.emptyList()); +052 registerStaticCompletion("nothing", Collections.emptyList()); +053 registerStaticCompletion("timeunits", Arrays.asList("minutes", "hours", "days", "weeks", "months", "years")); +054 registerAsyncCompletion("range", (c) -> { +055 String config = c.getConfig(); +056 if (config == null) { +057 return Collections.emptyList(); +058 } +059 final String[] ranges = ACFPatterns.DASH.split(config); +060 int start; +061 int end; +062 if (ranges.length != 2) { +063 start = 0; +064 end = ACFUtil.parseInt(ranges[0], 0); +065 } else { +066 start = ACFUtil.parseInt(ranges[0], 0); +067 end = ACFUtil.parseInt(ranges[1], 0); +068 } +069 return IntStream.rangeClosed(start, end).mapToObj(Integer::toString).collect(Collectors.toList()); +070 }); +071 } +072 +073 /** +074 * Registr a completion handler to provide command completions based on the user input. +075 * +076 * @param id +077 * @param handler +078 * @return +079 */ +080 public CommandCompletionHandler registerCompletion(String id, CommandCompletionHandler<C> handler) { +081 return this.completionMap.put(prepareCompletionId(id), handler); +082 } +083 +084 /** +085 * Registr a completion handler to provide command completions based on the user input. +086 * This handler is declared to be safe to be executed asynchronously. +087 * <p> +088 * Not all platforms support this, so if the platform does not support asynchronous execution, +089 * your handler will be executed on the main thread. +090 * <p> +091 * Use this anytime your handler does not need to access state that is not considered thread safe. +092 * <p> +093 * Use context.isAsync() to determine if you are async or not. +094 * +095 * @param id +096 * @param handler +097 * @return +098 */ +099 public CommandCompletionHandler registerAsyncCompletion(String id, AsyncCommandCompletionHandler<C> handler) { +100 return this.completionMap.put(prepareCompletionId(id), handler); +101 } +102 +103 /** +104 * Register a static list of command completions that will never change. +105 * Like @CommandCompletion, values are | (PIPE) separated. +106 * <p> +107 * Example: foo|bar|baz +108 * +109 * @param id +110 * @param list +111 * @return +112 */ +113 public CommandCompletionHandler registerStaticCompletion(String id, String list) { +114 return registerStaticCompletion(id, ACFPatterns.PIPE.split(list)); +115 } +116 +117 /** +118 * Register a static list of command completions that will never change +119 * +120 * @param id +121 * @param completions +122 * @return +123 */ +124 public CommandCompletionHandler registerStaticCompletion(String id, String[] completions) { +125 return registerStaticCompletion(id, Arrays.asList(completions)); +126 } +127 +128 /** +129 * Register a static list of command completions that will never change. The list is obtained from the supplier +130 * immediately as part of this method call. +131 * +132 * @param id +133 * @param supplier +134 * @return +135 */ +136 public CommandCompletionHandler registerStaticCompletion(String id, Supplier<Collection<String>> supplier) { +137 return registerStaticCompletion(id, supplier.get()); +138 } +139 +140 /** +141 * Register a static list of command completions that will never change +142 * +143 * @param id +144 * @param completions +145 * @return +146 */ +147 public CommandCompletionHandler registerStaticCompletion(String id, Collection<String> completions) { +148 return registerAsyncCompletion(id, x -> completions); +149 } +150 +151 /** +152 * Registers a completion handler such as @players to default apply to all command parameters of the specified types +153 * <p> +154 * This enables automatic completion support for parameters without manually defining it for custom objects +155 * +156 * @param id +157 * @param classes +158 */ +159 public void setDefaultCompletion(String id, Class... classes) { +160 // get completion with specified id +161 id = prepareCompletionId(id); +162 CommandCompletionHandler completion = completionMap.get(id); +163 +164 if (completion == null) { +165 // Throw something because no completion with specified id +166 throw new IllegalStateException("Completion not registered for " + id); +167 } +168 +169 for (Class clazz : classes) { +170 defaultCompletions.put(clazz, id); +171 } +172 } +173 +174 @NotNull +175 private static String prepareCompletionId(String id) { +176 return (id.startsWith("@") ? "" : "@") + id.toLowerCase(); +177 } +178 +179 @NotNull +180 List<String> of(RegisteredCommand cmd, CommandIssuer sender, String[] args, boolean isAsync) { +181 String[] completions = ACFPatterns.SPACE.split(cmd.complete); +182 final int argIndex = args.length - 1; +183 +184 String input = args[argIndex]; +185 +186 String completion = argIndex < completions.length ? completions[argIndex] : null; +187 if (completion == null || "*".equals(completion)) { +188 completion = findDefaultCompletion(cmd, args); +189 } +190 +191 if (completion == null && completions.length > 0) { +192 String last = completions[completions.length - 1]; +193 if (last.startsWith("repeat@")) { +194 completion = last; +195 } else if (argIndex >= completions.length && cmd.parameters[cmd.parameters.length - 1].consumesRest) { +196 completion = last; +197 } +198 } +199 +200 if (completion == null) { +201 return Collections.singletonList(input); +202 } 203 -204 String findDefaultCompletion(RegisteredCommand cmd, String[] args) { -205 int i = 0; -206 for (CommandParameter param : cmd.parameters) { -207 if (param.canConsumeInput() && ++i == args.length) { -208 Class type = param.getType(); -209 while (type != null) { -210 String completion = this.defaultCompletions.get(type); -211 if (completion != null) { -212 return completion; -213 } -214 type = type.getSuperclass(); -215 } -216 if (param.getType().isEnum()) { -217 CommandOperationContext ctx = CommandManager.getCurrentCommandOperationContext(); -218 //noinspection unchecked -219 ctx.enumCompletionValues = ACFUtil.enumNames((Class<? extends Enum<?>>) param.getType()); -220 return DEFAULT_ENUM_ID; -221 } -222 break; -223 } -224 } -225 return null; -226 } -227 -228 List<String> getCompletionValues(RegisteredCommand command, CommandIssuer sender, String completion, String[] args, boolean isAsync) { -229 if (DEFAULT_ENUM_ID.equals(completion)) { -230 CommandOperationContext<?> ctx = CommandManager.getCurrentCommandOperationContext(); -231 return ctx.enumCompletionValues; -232 } -233 if (completion.startsWith("repeat@")) { -234 completion = completion.substring(6); +204 return getCompletionValues(cmd, sender, completion, args, isAsync); +205 } +206 +207 String findDefaultCompletion(RegisteredCommand cmd, String[] args) { +208 int i = 0; +209 for (CommandParameter param : cmd.parameters) { +210 if (param.canConsumeInput() && ++i == args.length) { +211 Class type = param.getType(); +212 while (type != null) { +213 String completion = this.defaultCompletions.get(type); +214 if (completion != null) { +215 return completion; +216 } +217 type = type.getSuperclass(); +218 } +219 if (param.getType().isEnum()) { +220 CommandOperationContext ctx = CommandManager.getCurrentCommandOperationContext(); +221 //noinspection unchecked +222 ctx.enumCompletionValues = ACFUtil.enumNames((Class<? extends Enum<?>>) param.getType()); +223 return DEFAULT_ENUM_ID; +224 } +225 break; +226 } +227 } +228 return null; +229 } +230 +231 List<String> getCompletionValues(RegisteredCommand command, CommandIssuer sender, String completion, String[] args, boolean isAsync) { +232 if (DEFAULT_ENUM_ID.equals(completion)) { +233 CommandOperationContext<?> ctx = CommandManager.getCurrentCommandOperationContext(); +234 return ctx.enumCompletionValues; 235 } -236 completion = manager.getCommandReplacements().replace(completion); -237 -238 List<String> allCompletions = new ArrayList<>(); -239 String input = args.length > 0 ? args[args.length - 1] : ""; -240 -241 for (String value : ACFPatterns.PIPE.split(completion)) { -242 String[] complete = ACFPatterns.COLONEQUALS.split(value, 2); -243 CommandCompletionHandler handler = this.completionMap.get(complete[0].toLowerCase()); -244 if (handler != null) { -245 if (isAsync && !(handler instanceof AsyncCommandCompletionHandler)) { -246 ACFUtil.sneaky(new SyncCompletionRequired()); -247 return null; -248 } -249 String config = complete.length == 1 ? null : complete[1]; -250 CommandCompletionContext context = manager.createCompletionContext(command, sender, input, config, args); -251 -252 try { -253 //noinspection unchecked -254 Collection<String> completions = handler.getCompletions(context); -255 if (completions != null) { -256 allCompletions.addAll(completions); -257 continue; -258 } -259 //noinspection ConstantIfStatement,ConstantConditions -260 if (false) { // Hack to fool compiler. since its sneakily thrown. -261 throw new CommandCompletionTextLookupException(); -262 } -263 } catch (CommandCompletionTextLookupException ignored) { -264 // This should only happen if some other feedback error occured. -265 } catch (Exception e) { -266 command.handleException(sender, Arrays.asList(args), e); -267 } -268 // Something went wrong in lookup, fall back to input -269 return Collections.singletonList(input); -270 } else { -271 // Plaintext value -272 allCompletions.add(value); -273 } -274 } -275 return allCompletions; -276 } -277 -278 public interface CommandCompletionHandler<C extends CommandCompletionContext> { -279 Collection<String> getCompletions(C context) throws InvalidCommandArgument; -280 } -281 -282 public interface AsyncCommandCompletionHandler<C extends CommandCompletionContext> extends CommandCompletionHandler<C> { -283 } -284 -285 public static class SyncCompletionRequired extends RuntimeException { -286 } -287 -288} +236 boolean repeat = completion.startsWith("repeat@"); +237 if (repeat) { +238 completion = completion.substring(6); +239 } +240 completion = manager.getCommandReplacements().replace(completion); +241 +242 List<String> allCompletions = new ArrayList<>(); +243 String input = args.length > 0 ? args[args.length - 1] : ""; +244 +245 for (String value : ACFPatterns.PIPE.split(completion)) { +246 String[] complete = ACFPatterns.COLONEQUALS.split(value, 2); +247 CommandCompletionHandler handler = this.completionMap.get(complete[0].toLowerCase()); +248 if (handler != null) { +249 if (isAsync && !(handler instanceof AsyncCommandCompletionHandler)) { +250 ACFUtil.sneaky(new SyncCompletionRequired()); +251 return null; +252 } +253 String config = complete.length == 1 ? null : complete[1]; +254 CommandCompletionContext context = manager.createCompletionContext(command, sender, input, config, args); +255 +256 try { +257 //noinspection unchecked +258 Collection<String> completions = handler.getCompletions(context); +259 +260 //Handle completions with more than one word: +261 if (!repeat && +262 command.parameters[command.parameters.length - 1].consumesRest +263 && args.length > ACFPatterns.SPACE.split(command.complete).length) { +264 String start = String.join(" ", args); +265 completions = completions.stream() +266 .filter(s -> s.split(" ").length >= args.length) +267 .filter(s -> ApacheCommonsLangUtil.startsWithIgnoreCase(s, start)) +268 .map(s -> { +269 String[] completionArgs = s.split(" "); +270 return String.join(" ", +271 Arrays.copyOfRange(completionArgs, args.length - 1, completionArgs.length)); +272 }).collect(Collectors.toList()); +273 } +274 +275 if (completions != null) { +276 allCompletions.addAll(completions); +277 continue; +278 } +279 //noinspection ConstantIfStatement,ConstantConditions +280 if (false) { // Hack to fool compiler. since its sneakily thrown. +281 throw new CommandCompletionTextLookupException(); +282 } +283 } catch (CommandCompletionTextLookupException ignored) { +284 // This should only happen if some other feedback error occured. +285 } catch (Exception e) { +286 command.handleException(sender, Arrays.asList(args), e); +287 } +288 // Something went wrong in lookup, fall back to input +289 return Collections.singletonList(input); +290 } else { +291 // Plaintext value +292 allCompletions.add(value); +293 } +294 } +295 return allCompletions; +296 } +297 +298 public interface CommandCompletionHandler<C extends CommandCompletionContext> { +299 Collection<String> getCompletions(C context) throws InvalidCommandArgument; +300 } +301 +302 public interface AsyncCommandCompletionHandler<C extends CommandCompletionContext> extends CommandCompletionHandler<C> { +303 } +304 +305 public static class SyncCompletionRequired extends RuntimeException { +306 } +307 +308} diff --git a/docs/acf-core/src-html/co/aikar/commands/CommandCompletions.html b/docs/acf-core/src-html/co/aikar/commands/CommandCompletions.html index 124cf0d9..cfa029d2 100644 --- a/docs/acf-core/src-html/co/aikar/commands/CommandCompletions.html +++ b/docs/acf-core/src-html/co/aikar/commands/CommandCompletions.html @@ -31,269 +31,289 @@ 023 024package co.aikar.commands; 025 -026import org.jetbrains.annotations.NotNull; -027 -028import java.util.ArrayList; -029import java.util.Arrays; -030import java.util.Collection; -031import java.util.Collections; -032import java.util.HashMap; -033import java.util.List; -034import java.util.Map; -035import java.util.function.Supplier; -036import java.util.stream.Collectors; -037import java.util.stream.IntStream; -038 +026import co.aikar.commands.apachecommonslang.ApacheCommonsLangUtil; +027import org.jetbrains.annotations.NotNull; +028 +029import java.util.ArrayList; +030import java.util.Arrays; +031import java.util.Collection; +032import java.util.Collections; +033import java.util.HashMap; +034import java.util.List; +035import java.util.Map; +036import java.util.function.Supplier; +037import java.util.stream.Collectors; +038import java.util.stream.IntStream; 039 -040@SuppressWarnings({"WeakerAccess", "UnusedReturnValue"}) -041public class CommandCompletions<C extends CommandCompletionContext> { -042 private static final String DEFAULT_ENUM_ID = "@__defaultenum__"; -043 private final CommandManager manager; -044 // TODO: use a CompletionProvider that can return a delegated Id or provide values such as enum support -045 private Map<String, CommandCompletionHandler> completionMap = new HashMap<>(); -046 private Map<Class, String> defaultCompletions = new HashMap<>(); -047 -048 public CommandCompletions(CommandManager manager) { -049 this.manager = manager; -050 registerStaticCompletion("empty", Collections.emptyList()); -051 registerStaticCompletion("nothing", Collections.emptyList()); -052 registerStaticCompletion("timeunits", Arrays.asList("minutes", "hours", "days", "weeks", "months", "years")); -053 registerAsyncCompletion("range", (c) -> { -054 String config = c.getConfig(); -055 if (config == null) { -056 return Collections.emptyList(); -057 } -058 final String[] ranges = ACFPatterns.DASH.split(config); -059 int start; -060 int end; -061 if (ranges.length != 2) { -062 start = 0; -063 end = ACFUtil.parseInt(ranges[0], 0); -064 } else { -065 start = ACFUtil.parseInt(ranges[0], 0); -066 end = ACFUtil.parseInt(ranges[1], 0); -067 } -068 return IntStream.rangeClosed(start, end).mapToObj(Integer::toString).collect(Collectors.toList()); -069 }); -070 } -071 -072 /** -073 * Registr a completion handler to provide command completions based on the user input. -074 * -075 * @param id -076 * @param handler -077 * @return -078 */ -079 public CommandCompletionHandler registerCompletion(String id, CommandCompletionHandler<C> handler) { -080 return this.completionMap.put(prepareCompletionId(id), handler); -081 } -082 -083 /** -084 * Registr a completion handler to provide command completions based on the user input. -085 * This handler is declared to be safe to be executed asynchronously. -086 * <p> -087 * Not all platforms support this, so if the platform does not support asynchronous execution, -088 * your handler will be executed on the main thread. -089 * <p> -090 * Use this anytime your handler does not need to access state that is not considered thread safe. -091 * <p> -092 * Use context.isAsync() to determine if you are async or not. -093 * -094 * @param id -095 * @param handler -096 * @return -097 */ -098 public CommandCompletionHandler registerAsyncCompletion(String id, AsyncCommandCompletionHandler<C> handler) { -099 return this.completionMap.put(prepareCompletionId(id), handler); -100 } -101 -102 /** -103 * Register a static list of command completions that will never change. -104 * Like @CommandCompletion, values are | (PIPE) separated. -105 * <p> -106 * Example: foo|bar|baz -107 * -108 * @param id -109 * @param list -110 * @return -111 */ -112 public CommandCompletionHandler registerStaticCompletion(String id, String list) { -113 return registerStaticCompletion(id, ACFPatterns.PIPE.split(list)); -114 } -115 -116 /** -117 * Register a static list of command completions that will never change -118 * -119 * @param id -120 * @param completions -121 * @return -122 */ -123 public CommandCompletionHandler registerStaticCompletion(String id, String[] completions) { -124 return registerStaticCompletion(id, Arrays.asList(completions)); -125 } -126 -127 /** -128 * Register a static list of command completions that will never change. The list is obtained from the supplier -129 * immediately as part of this method call. -130 * -131 * @param id -132 * @param supplier -133 * @return -134 */ -135 public CommandCompletionHandler registerStaticCompletion(String id, Supplier<Collection<String>> supplier) { -136 return registerStaticCompletion(id, supplier.get()); -137 } -138 -139 /** -140 * Register a static list of command completions that will never change -141 * -142 * @param id -143 * @param completions -144 * @return -145 */ -146 public CommandCompletionHandler registerStaticCompletion(String id, Collection<String> completions) { -147 return registerAsyncCompletion(id, x -> completions); -148 } -149 -150 /** -151 * Registers a completion handler such as @players to default apply to all command parameters of the specified types -152 * <p> -153 * This enables automatic completion support for parameters without manually defining it for custom objects -154 * -155 * @param id -156 * @param classes -157 */ -158 public void setDefaultCompletion(String id, Class... classes) { -159 // get completion with specified id -160 id = prepareCompletionId(id); -161 CommandCompletionHandler completion = completionMap.get(id); -162 -163 if (completion == null) { -164 // Throw something because no completion with specified id -165 throw new IllegalStateException("Completion not registered for " + id); -166 } -167 -168 for (Class clazz : classes) { -169 defaultCompletions.put(clazz, id); -170 } -171 } -172 -173 @NotNull -174 private static String prepareCompletionId(String id) { -175 return (id.startsWith("@") ? "" : "@") + id.toLowerCase(); -176 } -177 -178 @NotNull -179 List<String> of(RegisteredCommand cmd, CommandIssuer sender, String[] args, boolean isAsync) { -180 String[] completions = ACFPatterns.SPACE.split(cmd.complete); -181 final int argIndex = args.length - 1; -182 -183 String input = args[argIndex]; -184 -185 String completion = argIndex < completions.length ? completions[argIndex] : null; -186 if (completion == null || "*".equals(completion)) { -187 completion = findDefaultCompletion(cmd, args); -188 } -189 -190 if (completion == null && completions.length > 0) { -191 String last = completions[completions.length - 1]; -192 if (last.startsWith("repeat@")) { -193 completion = last; -194 } -195 } -196 -197 if (completion == null) { -198 return Collections.singletonList(input); -199 } -200 -201 return getCompletionValues(cmd, sender, completion, args, isAsync); -202 } +040 +041@SuppressWarnings({"WeakerAccess", "UnusedReturnValue"}) +042public class CommandCompletions<C extends CommandCompletionContext> { +043 private static final String DEFAULT_ENUM_ID = "@__defaultenum__"; +044 private final CommandManager manager; +045 // TODO: use a CompletionProvider that can return a delegated Id or provide values such as enum support +046 private Map<String, CommandCompletionHandler> completionMap = new HashMap<>(); +047 private Map<Class, String> defaultCompletions = new HashMap<>(); +048 +049 public CommandCompletions(CommandManager manager) { +050 this.manager = manager; +051 registerStaticCompletion("empty", Collections.emptyList()); +052 registerStaticCompletion("nothing", Collections.emptyList()); +053 registerStaticCompletion("timeunits", Arrays.asList("minutes", "hours", "days", "weeks", "months", "years")); +054 registerAsyncCompletion("range", (c) -> { +055 String config = c.getConfig(); +056 if (config == null) { +057 return Collections.emptyList(); +058 } +059 final String[] ranges = ACFPatterns.DASH.split(config); +060 int start; +061 int end; +062 if (ranges.length != 2) { +063 start = 0; +064 end = ACFUtil.parseInt(ranges[0], 0); +065 } else { +066 start = ACFUtil.parseInt(ranges[0], 0); +067 end = ACFUtil.parseInt(ranges[1], 0); +068 } +069 return IntStream.rangeClosed(start, end).mapToObj(Integer::toString).collect(Collectors.toList()); +070 }); +071 } +072 +073 /** +074 * Registr a completion handler to provide command completions based on the user input. +075 * +076 * @param id +077 * @param handler +078 * @return +079 */ +080 public CommandCompletionHandler registerCompletion(String id, CommandCompletionHandler<C> handler) { +081 return this.completionMap.put(prepareCompletionId(id), handler); +082 } +083 +084 /** +085 * Registr a completion handler to provide command completions based on the user input. +086 * This handler is declared to be safe to be executed asynchronously. +087 * <p> +088 * Not all platforms support this, so if the platform does not support asynchronous execution, +089 * your handler will be executed on the main thread. +090 * <p> +091 * Use this anytime your handler does not need to access state that is not considered thread safe. +092 * <p> +093 * Use context.isAsync() to determine if you are async or not. +094 * +095 * @param id +096 * @param handler +097 * @return +098 */ +099 public CommandCompletionHandler registerAsyncCompletion(String id, AsyncCommandCompletionHandler<C> handler) { +100 return this.completionMap.put(prepareCompletionId(id), handler); +101 } +102 +103 /** +104 * Register a static list of command completions that will never change. +105 * Like @CommandCompletion, values are | (PIPE) separated. +106 * <p> +107 * Example: foo|bar|baz +108 * +109 * @param id +110 * @param list +111 * @return +112 */ +113 public CommandCompletionHandler registerStaticCompletion(String id, String list) { +114 return registerStaticCompletion(id, ACFPatterns.PIPE.split(list)); +115 } +116 +117 /** +118 * Register a static list of command completions that will never change +119 * +120 * @param id +121 * @param completions +122 * @return +123 */ +124 public CommandCompletionHandler registerStaticCompletion(String id, String[] completions) { +125 return registerStaticCompletion(id, Arrays.asList(completions)); +126 } +127 +128 /** +129 * Register a static list of command completions that will never change. The list is obtained from the supplier +130 * immediately as part of this method call. +131 * +132 * @param id +133 * @param supplier +134 * @return +135 */ +136 public CommandCompletionHandler registerStaticCompletion(String id, Supplier<Collection<String>> supplier) { +137 return registerStaticCompletion(id, supplier.get()); +138 } +139 +140 /** +141 * Register a static list of command completions that will never change +142 * +143 * @param id +144 * @param completions +145 * @return +146 */ +147 public CommandCompletionHandler registerStaticCompletion(String id, Collection<String> completions) { +148 return registerAsyncCompletion(id, x -> completions); +149 } +150 +151 /** +152 * Registers a completion handler such as @players to default apply to all command parameters of the specified types +153 * <p> +154 * This enables automatic completion support for parameters without manually defining it for custom objects +155 * +156 * @param id +157 * @param classes +158 */ +159 public void setDefaultCompletion(String id, Class... classes) { +160 // get completion with specified id +161 id = prepareCompletionId(id); +162 CommandCompletionHandler completion = completionMap.get(id); +163 +164 if (completion == null) { +165 // Throw something because no completion with specified id +166 throw new IllegalStateException("Completion not registered for " + id); +167 } +168 +169 for (Class clazz : classes) { +170 defaultCompletions.put(clazz, id); +171 } +172 } +173 +174 @NotNull +175 private static String prepareCompletionId(String id) { +176 return (id.startsWith("@") ? "" : "@") + id.toLowerCase(); +177 } +178 +179 @NotNull +180 List<String> of(RegisteredCommand cmd, CommandIssuer sender, String[] args, boolean isAsync) { +181 String[] completions = ACFPatterns.SPACE.split(cmd.complete); +182 final int argIndex = args.length - 1; +183 +184 String input = args[argIndex]; +185 +186 String completion = argIndex < completions.length ? completions[argIndex] : null; +187 if (completion == null || "*".equals(completion)) { +188 completion = findDefaultCompletion(cmd, args); +189 } +190 +191 if (completion == null && completions.length > 0) { +192 String last = completions[completions.length - 1]; +193 if (last.startsWith("repeat@")) { +194 completion = last; +195 } else if (argIndex >= completions.length && cmd.parameters[cmd.parameters.length - 1].consumesRest) { +196 completion = last; +197 } +198 } +199 +200 if (completion == null) { +201 return Collections.singletonList(input); +202 } 203 -204 String findDefaultCompletion(RegisteredCommand cmd, String[] args) { -205 int i = 0; -206 for (CommandParameter param : cmd.parameters) { -207 if (param.canConsumeInput() && ++i == args.length) { -208 Class type = param.getType(); -209 while (type != null) { -210 String completion = this.defaultCompletions.get(type); -211 if (completion != null) { -212 return completion; -213 } -214 type = type.getSuperclass(); -215 } -216 if (param.getType().isEnum()) { -217 CommandOperationContext ctx = CommandManager.getCurrentCommandOperationContext(); -218 //noinspection unchecked -219 ctx.enumCompletionValues = ACFUtil.enumNames((Class<? extends Enum<?>>) param.getType()); -220 return DEFAULT_ENUM_ID; -221 } -222 break; -223 } -224 } -225 return null; -226 } -227 -228 List<String> getCompletionValues(RegisteredCommand command, CommandIssuer sender, String completion, String[] args, boolean isAsync) { -229 if (DEFAULT_ENUM_ID.equals(completion)) { -230 CommandOperationContext<?> ctx = CommandManager.getCurrentCommandOperationContext(); -231 return ctx.enumCompletionValues; -232 } -233 if (completion.startsWith("repeat@")) { -234 completion = completion.substring(6); +204 return getCompletionValues(cmd, sender, completion, args, isAsync); +205 } +206 +207 String findDefaultCompletion(RegisteredCommand cmd, String[] args) { +208 int i = 0; +209 for (CommandParameter param : cmd.parameters) { +210 if (param.canConsumeInput() && ++i == args.length) { +211 Class type = param.getType(); +212 while (type != null) { +213 String completion = this.defaultCompletions.get(type); +214 if (completion != null) { +215 return completion; +216 } +217 type = type.getSuperclass(); +218 } +219 if (param.getType().isEnum()) { +220 CommandOperationContext ctx = CommandManager.getCurrentCommandOperationContext(); +221 //noinspection unchecked +222 ctx.enumCompletionValues = ACFUtil.enumNames((Class<? extends Enum<?>>) param.getType()); +223 return DEFAULT_ENUM_ID; +224 } +225 break; +226 } +227 } +228 return null; +229 } +230 +231 List<String> getCompletionValues(RegisteredCommand command, CommandIssuer sender, String completion, String[] args, boolean isAsync) { +232 if (DEFAULT_ENUM_ID.equals(completion)) { +233 CommandOperationContext<?> ctx = CommandManager.getCurrentCommandOperationContext(); +234 return ctx.enumCompletionValues; 235 } -236 completion = manager.getCommandReplacements().replace(completion); -237 -238 List<String> allCompletions = new ArrayList<>(); -239 String input = args.length > 0 ? args[args.length - 1] : ""; -240 -241 for (String value : ACFPatterns.PIPE.split(completion)) { -242 String[] complete = ACFPatterns.COLONEQUALS.split(value, 2); -243 CommandCompletionHandler handler = this.completionMap.get(complete[0].toLowerCase()); -244 if (handler != null) { -245 if (isAsync && !(handler instanceof AsyncCommandCompletionHandler)) { -246 ACFUtil.sneaky(new SyncCompletionRequired()); -247 return null; -248 } -249 String config = complete.length == 1 ? null : complete[1]; -250 CommandCompletionContext context = manager.createCompletionContext(command, sender, input, config, args); -251 -252 try { -253 //noinspection unchecked -254 Collection<String> completions = handler.getCompletions(context); -255 if (completions != null) { -256 allCompletions.addAll(completions); -257 continue; -258 } -259 //noinspection ConstantIfStatement,ConstantConditions -260 if (false) { // Hack to fool compiler. since its sneakily thrown. -261 throw new CommandCompletionTextLookupException(); -262 } -263 } catch (CommandCompletionTextLookupException ignored) { -264 // This should only happen if some other feedback error occured. -265 } catch (Exception e) { -266 command.handleException(sender, Arrays.asList(args), e); -267 } -268 // Something went wrong in lookup, fall back to input -269 return Collections.singletonList(input); -270 } else { -271 // Plaintext value -272 allCompletions.add(value); -273 } -274 } -275 return allCompletions; -276 } -277 -278 public interface CommandCompletionHandler<C extends CommandCompletionContext> { -279 Collection<String> getCompletions(C context) throws InvalidCommandArgument; -280 } -281 -282 public interface AsyncCommandCompletionHandler<C extends CommandCompletionContext> extends CommandCompletionHandler<C> { -283 } -284 -285 public static class SyncCompletionRequired extends RuntimeException { -286 } -287 -288} +236 boolean repeat = completion.startsWith("repeat@"); +237 if (repeat) { +238 completion = completion.substring(6); +239 } +240 completion = manager.getCommandReplacements().replace(completion); +241 +242 List<String> allCompletions = new ArrayList<>(); +243 String input = args.length > 0 ? args[args.length - 1] : ""; +244 +245 for (String value : ACFPatterns.PIPE.split(completion)) { +246 String[] complete = ACFPatterns.COLONEQUALS.split(value, 2); +247 CommandCompletionHandler handler = this.completionMap.get(complete[0].toLowerCase()); +248 if (handler != null) { +249 if (isAsync && !(handler instanceof AsyncCommandCompletionHandler)) { +250 ACFUtil.sneaky(new SyncCompletionRequired()); +251 return null; +252 } +253 String config = complete.length == 1 ? null : complete[1]; +254 CommandCompletionContext context = manager.createCompletionContext(command, sender, input, config, args); +255 +256 try { +257 //noinspection unchecked +258 Collection<String> completions = handler.getCompletions(context); +259 +260 //Handle completions with more than one word: +261 if (!repeat && +262 command.parameters[command.parameters.length - 1].consumesRest +263 && args.length > ACFPatterns.SPACE.split(command.complete).length) { +264 String start = String.join(" ", args); +265 completions = completions.stream() +266 .filter(s -> s.split(" ").length >= args.length) +267 .filter(s -> ApacheCommonsLangUtil.startsWithIgnoreCase(s, start)) +268 .map(s -> { +269 String[] completionArgs = s.split(" "); +270 return String.join(" ", +271 Arrays.copyOfRange(completionArgs, args.length - 1, completionArgs.length)); +272 }).collect(Collectors.toList()); +273 } +274 +275 if (completions != null) { +276 allCompletions.addAll(completions); +277 continue; +278 } +279 //noinspection ConstantIfStatement,ConstantConditions +280 if (false) { // Hack to fool compiler. since its sneakily thrown. +281 throw new CommandCompletionTextLookupException(); +282 } +283 } catch (CommandCompletionTextLookupException ignored) { +284 // This should only happen if some other feedback error occured. +285 } catch (Exception e) { +286 command.handleException(sender, Arrays.asList(args), e); +287 } +288 // Something went wrong in lookup, fall back to input +289 return Collections.singletonList(input); +290 } else { +291 // Plaintext value +292 allCompletions.add(value); +293 } +294 } +295 return allCompletions; +296 } +297 +298 public interface CommandCompletionHandler<C extends CommandCompletionContext> { +299 Collection<String> getCompletions(C context) throws InvalidCommandArgument; +300 } +301 +302 public interface AsyncCommandCompletionHandler<C extends CommandCompletionContext> extends CommandCompletionHandler<C> { +303 } +304 +305 public static class SyncCompletionRequired extends RuntimeException { +306 } +307 +308}