Fix last argument always being treated as greedy in suggestions (#428)

This commit is contained in:
Pablo Herrera 2023-02-22 04:25:05 +01:00 committed by Jason
parent 6fe744f33c
commit 1d3f1d3a56
2 changed files with 43 additions and 11 deletions

View file

@ -630,16 +630,13 @@ public final class CommandTree<C> {
if (commandQueue.isEmpty()) {
return Collections.emptyList();
} else if (child.isLeaf()) {
final String input;
// Handles only simple cases, others will attempt to parse and then decide based on what gets consumed.
if (commandQueue.size() == 1) {
input = commandQueue.peek();
} else {
input = child.getValue() instanceof CompoundArgument
? ((LinkedList<String>) commandQueue).getLast()
: String.join(" ", commandQueue);
return this.directSuggestions(commandContext, child, commandQueue.peek());
} else if (child.getValue() instanceof CompoundArgument) {
return this.directSuggestions(commandContext, child, ((LinkedList<String>) commandQueue).getLast());
}
return this.directSuggestions(commandContext, child, input);
} else if (commandQueue.peek().isEmpty()) {
} else if (commandQueue.size() == 1 && commandQueue.peek().isEmpty()) {
return this.directSuggestions(commandContext, child, commandQueue.peek());
}
@ -662,6 +659,18 @@ public final class CommandTree<C> {
final Optional<?> parsedValue = result.getParsedValue();
final boolean parseSuccess = parsedValue.isPresent();
// It's the last node, we don't care for success or not as we don't need to delegate to a child
if (child.isLeaf()) {
if (commandQueue.isEmpty()) {
// Greedy parser took all the input, we can restore and just ask for suggestions
commandQueue.addAll(commandQueueOriginal);
return this.directSuggestions(commandContext, child, String.join(" ", commandQueue));
} else {
// It's a leaf and there's leftover stuff, no possible suggestions!
return Collections.emptyList();
}
}
if (parseSuccess && !commandQueue.isEmpty()) {
// the current argument at the position is parsable and there are more arguments following
commandContext.store(child.getValue().getName(), parsedValue.get());
@ -688,11 +697,11 @@ public final class CommandTree<C> {
// Therefore we shouldn't list the suggestions of the current argument, as clearly the suggestions of
// one of the following arguments is requested
return Collections.emptyList();
}
} else {
// Fallback: use suggestion provider of argument
return this.directSuggestions(commandContext, child, commandQueue.peek());
}
}
private @NonNull String stringOrEmpty(final @Nullable String string) {
if (string == null) {

View file

@ -26,6 +26,7 @@ package cloud.commandframework;
import cloud.commandframework.arguments.compound.ArgumentTriplet;
import cloud.commandframework.arguments.parser.ArgumentParseResult;
import cloud.commandframework.arguments.standard.BooleanArgument;
import cloud.commandframework.arguments.standard.DurationArgument;
import cloud.commandframework.arguments.standard.EnumArgument;
import cloud.commandframework.arguments.standard.IntegerArgument;
import cloud.commandframework.arguments.standard.StringArgument;
@ -113,6 +114,8 @@ public class CommandSuggestionsTest {
manager.command(manager.commandBuilder("numberswithmin")
.argument(IntegerArgument.<TestCommandSender>builder("num").withMin(5).withMax(100)));
manager.command(manager.commandBuilder("duration").argument(DurationArgument.of("duration")));
manager.command(manager.commandBuilder("partial")
.argument(
StringArgument.<TestCommandSender>builder("arg")
@ -316,6 +319,10 @@ public class CommandSuggestionsTest {
final String input5 = "numberswithmin ";
final List<String> suggestions5 = manager.suggest(new TestCommandSender(), input5);
Assertions.assertEquals(Arrays.asList("5", "6", "7", "8", "9"), suggestions5);
final String input6 = "numbers 1 ";
final List<String> suggestions6 = manager.suggest(new TestCommandSender(), input6);
Assertions.assertEquals(Collections.emptyList(), suggestions6);
}
@Test
@ -337,6 +344,22 @@ public class CommandSuggestionsTest {
);
}
@Test
void testDurations() {
final String input = "duration ";
final List<String> suggestions = manager.suggest(new TestCommandSender(), input);
Assertions.assertEquals(Arrays.asList("1", "2", "3", "4", "5", "6", "7", "8", "9"), suggestions);
final String input2 = "duration 5";
final List<String> suggestions2 = manager.suggest(new TestCommandSender(), input2);
Assertions.assertEquals(Arrays.asList("5d", "5h", "5m", "5s"), suggestions2);
final String input3 = "duration 5s";
final List<String> suggestions3 = manager.suggest(new TestCommandSender(), input3);
Assertions.assertEquals(Collections.emptyList(), suggestions3);
final String input4 = "duration 5s ";
final List<String> suggestions4 = manager.suggest(new TestCommandSender(), input4);
Assertions.assertEquals(Collections.emptyList(), suggestions4);
}
@Test
void testInvalidLiteralThenSpace() {
final String input = "test o";