From bde084c14b2e9e5d71df7bd07a5c10273f713d80 Mon Sep 17 00:00:00 2001 From: Pablo Herrera Date: Tue, 13 Dec 2022 17:19:16 +0100 Subject: [PATCH] Allow greedy parsers to suggest after a space (#414) --- .../cloud/commandframework/CommandTree.java | 15 +++++---- .../CommandSuggestionsTest.java | 33 +++++++++++++++++++ .../examples/bukkit/ExamplePlugin.java | 21 +++++++++++- 3 files changed, 61 insertions(+), 8 deletions(-) diff --git a/cloud-core/src/main/java/cloud/commandframework/CommandTree.java b/cloud-core/src/main/java/cloud/commandframework/CommandTree.java index 906cacef..7486d3bc 100644 --- a/cloud-core/src/main/java/cloud/commandframework/CommandTree.java +++ b/cloud-core/src/main/java/cloud/commandframework/CommandTree.java @@ -629,15 +629,16 @@ public final class CommandTree { if (commandQueue.isEmpty()) { return Collections.emptyList(); - } else if (child.isLeaf() && commandQueue.size() < 2) { - return this.directSuggestions(commandContext, child, commandQueue.peek()); } else if (child.isLeaf()) { - if (child.getValue() instanceof CompoundArgument) { - final String last = ((LinkedList) commandQueue).getLast(); - commandContext.setCurrentArgument(child.getValue()); - return child.getValue().getSuggestionsProvider().apply(commandContext, last); + final String input; + if (commandQueue.size() == 1) { + input = commandQueue.peek(); + } else { + input = child.getValue() instanceof CompoundArgument + ? ((LinkedList) commandQueue).getLast() + : String.join(" ", commandQueue); } - return Collections.emptyList(); + return this.directSuggestions(commandContext, child, input); } else if (commandQueue.peek().isEmpty()) { return this.directSuggestions(commandContext, child, commandQueue.peek()); } diff --git a/cloud-core/src/test/java/cloud/commandframework/CommandSuggestionsTest.java b/cloud-core/src/test/java/cloud/commandframework/CommandSuggestionsTest.java index efcd45b5..ce679e11 100644 --- a/cloud-core/src/test/java/cloud/commandframework/CommandSuggestionsTest.java +++ b/cloud-core/src/test/java/cloud/commandframework/CommandSuggestionsTest.java @@ -30,6 +30,7 @@ import cloud.commandframework.arguments.standard.EnumArgument; import cloud.commandframework.arguments.standard.IntegerArgument; import cloud.commandframework.arguments.standard.StringArgument; import cloud.commandframework.arguments.standard.StringArrayArgument; +import cloud.commandframework.execution.FilteringCommandSuggestionProcessor; import cloud.commandframework.types.tuples.Pair; import cloud.commandframework.types.tuples.Triplet; import java.util.Arrays; @@ -511,6 +512,38 @@ public class CommandSuggestionsTest { assertThat(suggestions6).isEmpty(); } + @Test + void testGreedyArgumentSuggestsAfterSpace() { + // Arrange + final CommandManager manager = createManager(); + manager.command( + manager.commandBuilder("command") + .argument( + StringArgument.newBuilder("string") + .greedy() + .withSuggestionsProvider((context, input) -> Collections.singletonList("hello world")) + .build()) + ); + manager.commandSuggestionProcessor( + new FilteringCommandSuggestionProcessor<>( + FilteringCommandSuggestionProcessor.Filter.startsWith(true).andTrimBeforeLastSpace())); + + // Act + final List suggestions1 = suggest(manager, "command "); + final List suggestions2 = suggest(manager, "command hello"); + final List suggestions3 = suggest(manager, "command hello "); + final List suggestions4 = suggest(manager, "command hello wo"); + final List suggestions5 = suggest(manager, "command hello world"); + final List suggestions6 = suggest(manager, "command hello world "); + + // Assert + assertThat(suggestions1).containsExactly("hello world"); + assertThat(suggestions2).containsExactly("hello world"); + assertThat(suggestions3).containsExactly("world"); + assertThat(suggestions4).containsExactly("world"); + assertThat(suggestions5).containsExactly("world"); + assertThat(suggestions6).isEmpty(); + } @Test void testFlagYieldingGreedyStringWithLiberalFlagArgument() { diff --git a/examples/example-bukkit/src/main/java/cloud/commandframework/examples/bukkit/ExamplePlugin.java b/examples/example-bukkit/src/main/java/cloud/commandframework/examples/bukkit/ExamplePlugin.java index 37bf1a49..ee4aeadb 100644 --- a/examples/example-bukkit/src/main/java/cloud/commandframework/examples/bukkit/ExamplePlugin.java +++ b/examples/example-bukkit/src/main/java/cloud/commandframework/examples/bukkit/ExamplePlugin.java @@ -25,6 +25,7 @@ package cloud.commandframework.examples.bukkit; import cloud.commandframework.ArgumentDescription; import cloud.commandframework.Command; +import cloud.commandframework.CommandHelpHandler; import cloud.commandframework.CommandTree; import cloud.commandframework.annotations.AnnotationParser; import cloud.commandframework.annotations.Argument; @@ -56,6 +57,7 @@ import cloud.commandframework.captions.SimpleCaptionRegistry; import cloud.commandframework.context.CommandContext; import cloud.commandframework.execution.AsynchronousCommandExecutionCoordinator; import cloud.commandframework.execution.CommandExecutionCoordinator; +import cloud.commandframework.execution.FilteringCommandSuggestionProcessor; import cloud.commandframework.extra.confirmation.CommandConfirmationManager; import cloud.commandframework.keys.SimpleCloudKey; import cloud.commandframework.meta.CommandMeta; @@ -79,6 +81,7 @@ import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.function.Function; +import java.util.stream.Collectors; import net.kyori.adventure.identity.Identity; import net.kyori.adventure.platform.bukkit.BukkitAudiences; import net.kyori.adventure.text.format.NamedTextColor; @@ -149,6 +152,12 @@ public final class ExamplePlugin extends JavaPlugin { this.getServer().getPluginManager().disablePlugin(this); return; } + + // Use contains to filter suggestions instead of default startsWith + this.manager.commandSuggestionProcessor(new FilteringCommandSuggestionProcessor<>( + FilteringCommandSuggestionProcessor.Filter.contains(true).andTrimBeforeLastSpace() + )); + // // Create a BukkitAudiences instance (adventure) in order to use the minecraft-extras // help system @@ -483,11 +492,21 @@ public final class ExamplePlugin extends JavaPlugin { })); } + @Suggestions("help_queries") + public @NonNull List suggestHelpQueries( + final @NonNull CommandContext ctx, + final @NonNull String input + ) { + return this.manager.createCommandHelpHandler().queryRootIndex(ctx.getSender()).getEntries().stream() + .map(CommandHelpHandler.VerboseHelpEntry::getSyntaxString) + .collect(Collectors.toList()); + } + @CommandMethod("example|e|ex help [query]") @CommandDescription("Help menu") public void commandHelp( final @NonNull CommandSender sender, - final @Argument("query") @Greedy String query + final @Argument(value = "query", suggestions = "help_queries") @Greedy String query ) { this.minecraftHelp.queryCommands(query == null ? "" : query, sender); }