🐛 Fix async completions (#38)
Co-authored-by: Alexander Söderberg <sauilitired@gmail.com>
This commit is contained in:
parent
aa572e3533
commit
882154a6a8
7 changed files with 160 additions and 54 deletions
|
|
@ -75,6 +75,8 @@ import java.util.function.Function;
|
|||
*/
|
||||
public abstract class CommandManager<C> {
|
||||
|
||||
private static final List<String> SINGLE_EMPTY_SUGGESTION = Collections.unmodifiableList(Collections.singletonList(""));
|
||||
|
||||
private final Map<Class<? extends Exception>, BiConsumer<C, ? extends Exception>> exceptionHandlers = new HashMap<>();
|
||||
private final EnumSet<ManagerSettings> managerSettings = EnumSet.of(
|
||||
ManagerSettings.ENFORCE_INTERMEDIARY_PERMISSIONS);
|
||||
|
|
@ -167,15 +169,23 @@ public abstract class CommandManager<C> {
|
|||
) {
|
||||
final CommandContext<C> context = this.commandContextFactory.create(true, commandSender);
|
||||
final LinkedList<String> inputQueue = tokenize(input);
|
||||
|
||||
final List<String> suggestions;
|
||||
if (this.preprocessContext(context, inputQueue) == State.ACCEPTED) {
|
||||
return this.commandSuggestionProcessor.apply(
|
||||
suggestions = this.commandSuggestionProcessor.apply(
|
||||
new CommandPreprocessingContext<>(context, inputQueue),
|
||||
this.commandTree.getSuggestions(
|
||||
context, inputQueue)
|
||||
);
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
suggestions = Collections.emptyList();
|
||||
}
|
||||
|
||||
if (this.getSetting(ManagerSettings.FORCE_SUGGESTION) && suggestions.isEmpty()) {
|
||||
return SINGLE_EMPTY_SUGGESTION;
|
||||
}
|
||||
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -635,7 +645,12 @@ public abstract class CommandManager<C> {
|
|||
* for child permission values, if a preceding command in the tree path
|
||||
* has a command handler attached
|
||||
*/
|
||||
ENFORCE_INTERMEDIARY_PERMISSIONS
|
||||
ENFORCE_INTERMEDIARY_PERMISSIONS,
|
||||
/**
|
||||
* Force sending of an empty suggestion (i.e. a singleton list containing an empty string)
|
||||
* when no suggestions are present
|
||||
*/
|
||||
FORCE_SUGGESTION
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ import cloud.commandframework.arguments.standard.IntegerArgument;
|
|||
import cloud.commandframework.arguments.standard.ShortArgument;
|
||||
import cloud.commandframework.arguments.standard.StringArgument;
|
||||
import cloud.commandframework.context.CommandContext;
|
||||
import cloud.commandframework.execution.preprocessor.CommandPreprocessingContext;
|
||||
import cloud.commandframework.permission.CommandPermission;
|
||||
import cloud.commandframework.permission.Permission;
|
||||
import cloud.commandframework.types.tuples.Pair;
|
||||
|
|
@ -62,9 +61,7 @@ import io.leangen.geantyref.TypeToken;
|
|||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
|
@ -438,28 +435,23 @@ public final class CloudBrigadierManager<C, S> {
|
|||
final @NonNull SuggestionsBuilder builder
|
||||
) {
|
||||
final CommandContext<C> commandContext = this.dummyContextProvider.get();
|
||||
final LinkedList<String> inputQueue = new LinkedList<>(Collections.singletonList(builder.getInput()));
|
||||
final CommandPreprocessingContext<C> commandPreprocessingContext =
|
||||
new CommandPreprocessingContext<>(commandContext, inputQueue);
|
||||
/*
|
||||
List<String> results = server.tabComplete(context.getSource().getBukkitSender(), builder.getInput(),
|
||||
context.getSource().getWorld(), context.getSource().getPosition(), true);
|
||||
*/
|
||||
|
||||
String command = builder.getInput();
|
||||
if (command.startsWith("/") /* Minecraft specific */) {
|
||||
command = command.substring(1);
|
||||
}
|
||||
final List<String> suggestions = this.commandManager.suggest(commandContext.getSender(), command);
|
||||
|
||||
/*
|
||||
System.out.println("Filtering out with: " + builder.getInput());
|
||||
final CommandSuggestionProcessor<C> processor = this.commandManager.getCommandSuggestionProcessor();
|
||||
final List<String> filteredSuggestions = processor.apply(commandPreprocessingContext, suggestions);
|
||||
System.out.println("Current suggestions: ");
|
||||
for (final String suggestion : filteredSuggestions) {
|
||||
System.out.printf("- %s\n", suggestion);
|
||||
}*/
|
||||
/* Remove namespace */
|
||||
String leading = command.split(" ")[0];
|
||||
if (leading.contains(":")) {
|
||||
command = command.substring(leading.split(":")[0].length() + 1);
|
||||
}
|
||||
|
||||
final List<String> suggestions = this.commandManager.suggest(
|
||||
commandContext.getSender(),
|
||||
command
|
||||
);
|
||||
|
||||
for (final String suggestion : suggestions) {
|
||||
String tooltip = argument.getName();
|
||||
if (!(argument instanceof StaticArgument)) {
|
||||
|
|
|
|||
|
|
@ -144,23 +144,6 @@ final class BukkitCommand<C> extends org.bukkit.command.Command implements Plugi
|
|||
return this.cloudCommand.getCommandMeta().getOrDefault("description", "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabComplete(
|
||||
final @NonNull CommandSender sender,
|
||||
final @NonNull String alias,
|
||||
final @NonNull String @NonNull [] args
|
||||
)
|
||||
throws IllegalArgumentException {
|
||||
final StringBuilder builder = new StringBuilder(this.command.getName());
|
||||
for (final String string : args) {
|
||||
builder.append(" ").append(string);
|
||||
}
|
||||
return this.manager.suggest(
|
||||
this.manager.getCommandSenderMapper().apply(sender),
|
||||
builder.toString()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plugin getPlugin() {
|
||||
return this.manager.getOwningPlugin();
|
||||
|
|
|
|||
|
|
@ -156,6 +156,12 @@ public class BukkitCommandManager<C> extends CommandManager<C> {
|
|||
new MultipleEntitySelectorArgument.MultipleEntitySelectorParser<>());
|
||||
this.getParserRegistry().registerParserSupplier(TypeToken.get(MultiplePlayerSelector.class), parserParameters ->
|
||||
new MultiplePlayerSelectorArgument.MultiplePlayerSelectorParser<>());
|
||||
|
||||
/* Register suggestion listener */
|
||||
this.owningPlugin.getServer().getPluginManager().registerEvents(
|
||||
new CommandSuggestionsListener<>(this),
|
||||
this.owningPlugin
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -282,6 +288,32 @@ public class BukkitCommandManager<C> extends CommandManager<C> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip the plugin namespace from a plugin namespaced command. This
|
||||
* will also strip the leading '/' if it's present
|
||||
*
|
||||
* @param command Command
|
||||
* @return Stripped command
|
||||
*/
|
||||
public final @NonNull String stripNamespace(final @NonNull String command) {
|
||||
@NonNull String input;
|
||||
|
||||
/* Remove leading '/' */
|
||||
if (command.charAt(0) == '/') {
|
||||
input = command.substring(1);
|
||||
} else {
|
||||
input = command;
|
||||
}
|
||||
|
||||
/* Remove leading plugin namespace */
|
||||
final String namespace = String.format("%s:", this.getOwningPlugin().getName().toLowerCase());
|
||||
if (input.startsWith(namespace)) {
|
||||
input = input.substring(namespace.length());
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the backwards command sender plugin
|
||||
*
|
||||
|
|
|
|||
|
|
@ -89,13 +89,16 @@ public class BukkitPluginRegistrationHandler<C> implements CommandRegistrationHa
|
|||
this.bukkitCommandManager
|
||||
);
|
||||
this.registeredCommands.put(commandArgument, bukkitCommand);
|
||||
if (!this.bukkitCommands.containsKey(label)) {
|
||||
this.recognizedAliases.add(label);
|
||||
}
|
||||
this.recognizedAliases.add(getNamespacedLabel(label));
|
||||
this.commandMap.register(
|
||||
label,
|
||||
this.bukkitCommandManager.getOwningPlugin().getName().toLowerCase(),
|
||||
bukkitCommand
|
||||
);
|
||||
this.registerExternal(label, command, bukkitCommand);
|
||||
this.recognizedAliases.add(label);
|
||||
|
||||
if (this.bukkitCommandManager.getSplitAliases()) {
|
||||
for (final String alias : aliases) {
|
||||
|
|
@ -107,12 +110,16 @@ public class BukkitPluginRegistrationHandler<C> implements CommandRegistrationHa
|
|||
(CommandArgument<C, ?>) commandArgument,
|
||||
this.bukkitCommandManager
|
||||
);
|
||||
this.commandMap.register(alias, this.bukkitCommandManager.getOwningPlugin()
|
||||
.getName().toLowerCase(),
|
||||
if (!this.bukkitCommands.containsKey(alias)) {
|
||||
this.recognizedAliases.add(alias);
|
||||
}
|
||||
this.recognizedAliases.add(getNamespacedLabel(alias));
|
||||
this.commandMap.register(
|
||||
alias,
|
||||
this.bukkitCommandManager.getOwningPlugin().getName().toLowerCase(),
|
||||
bukkitCommand
|
||||
);
|
||||
this.registerExternal(alias, command, aliasCommand);
|
||||
this.recognizedAliases.add(alias);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -120,6 +127,10 @@ public class BukkitPluginRegistrationHandler<C> implements CommandRegistrationHa
|
|||
return true;
|
||||
}
|
||||
|
||||
private @NonNull String getNamespacedLabel(final @NonNull String label) {
|
||||
return String.format("%s:%s", this.bukkitCommandManager.getOwningPlugin().getName(), label).toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given alias is recognizable by this registration handler
|
||||
*
|
||||
|
|
|
|||
|
|
@ -0,0 +1,70 @@
|
|||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2020 Alexander Söderberg & Contributors
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
package cloud.commandframework.bukkit;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.server.TabCompleteEvent;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
final class CommandSuggestionsListener<C> implements Listener {
|
||||
|
||||
private final BukkitCommandManager<C> bukkitCommandManager;
|
||||
|
||||
CommandSuggestionsListener(final @NonNull BukkitCommandManager<C> bukkitCommandManager) {
|
||||
this.bukkitCommandManager = bukkitCommandManager;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
void onTabCompletion(final @NonNull TabCompleteEvent event) {
|
||||
if (event.getBuffer().isEmpty() || !event.getBuffer().startsWith("/")) {
|
||||
return;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked") final BukkitPluginRegistrationHandler<C> bukkitPluginRegistrationHandler =
|
||||
(BukkitPluginRegistrationHandler<C>) this.bukkitCommandManager.getCommandRegistrationHandler();
|
||||
|
||||
/* Turn '/plugin:command arg1 arg2 ...' into 'plugin:command' */
|
||||
final String commandLabel = event.getBuffer().substring(1).split(" ")[0];
|
||||
if (!bukkitPluginRegistrationHandler.isRecognized(commandLabel)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final CommandSender sender = event.getSender();
|
||||
final C cloudSender = this.bukkitCommandManager.getCommandSenderMapper().apply(sender);
|
||||
final String inputBuffer = this.bukkitCommandManager.stripNamespace(event.getBuffer());
|
||||
|
||||
final List<String> suggestions = new ArrayList<>(this.bukkitCommandManager.suggest(
|
||||
cloudSender,
|
||||
inputBuffer
|
||||
));
|
||||
|
||||
event.setCompletions(suggestions);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -33,9 +33,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
class AsyncCommandSuggestionsListener<C> implements Listener {
|
||||
|
||||
private static final long CACHE_EXPIRATION_TIME = 30L;
|
||||
final class AsyncCommandSuggestionsListener<C> implements Listener {
|
||||
|
||||
private final PaperCommandManager<C> paperCommandManager;
|
||||
|
||||
|
|
@ -44,25 +42,30 @@ class AsyncCommandSuggestionsListener<C> implements Listener {
|
|||
}
|
||||
|
||||
@EventHandler
|
||||
void onTabCompletion(final @NonNull AsyncTabCompleteEvent event) throws Exception {
|
||||
void onTabCompletion(final @NonNull AsyncTabCompleteEvent event) {
|
||||
if (event.getBuffer().isEmpty() || !event.getBuffer().startsWith("/")) {
|
||||
return;
|
||||
}
|
||||
final String[] arguments = event.getBuffer().substring(1).split(" ");
|
||||
if (this.paperCommandManager.getCommandTree().getNamedNode(arguments[0]) == null) {
|
||||
return;
|
||||
}
|
||||
@SuppressWarnings("unchecked") final BukkitPluginRegistrationHandler<C> bukkitPluginRegistrationHandler =
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
final BukkitPluginRegistrationHandler<C> bukkitPluginRegistrationHandler =
|
||||
(BukkitPluginRegistrationHandler<C>) this.paperCommandManager.getCommandRegistrationHandler();
|
||||
if (!bukkitPluginRegistrationHandler.isRecognized(arguments[0])) {
|
||||
|
||||
/* Turn '/plugin:command arg1 arg2 ...' into 'plugin:command' */
|
||||
final String commandLabel = event.getBuffer().substring(1).split(" ")[0];
|
||||
if (!bukkitPluginRegistrationHandler.isRecognized(commandLabel)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final CommandSender sender = event.getSender();
|
||||
final C cloudSender = this.paperCommandManager.getCommandSenderMapper().apply(sender);
|
||||
final String inputBuffer = this.paperCommandManager.stripNamespace(event.getBuffer());
|
||||
|
||||
final List<String> suggestions = new ArrayList<>(this.paperCommandManager.suggest(
|
||||
cloudSender,
|
||||
event.getBuffer().substring(1)
|
||||
inputBuffer
|
||||
));
|
||||
|
||||
event.setCompletions(suggestions);
|
||||
event.setHandled(true);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue