Allow default suggestion providers in @Parser

This commit is contained in:
Alexander Söderberg 2020-12-18 12:54:11 +01:00 committed by Alexander Söderberg
parent e5a35afb8a
commit 6cc3a21619
4 changed files with 55 additions and 7 deletions

View file

@ -58,6 +58,7 @@ import java.lang.reflect.Parameter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -317,7 +318,20 @@ public final class AnnotationParser<C> {
)); ));
} }
try { try {
final BiFunction<CommandContext<C>, String, List<String>> suggestionsProvider;
if (parser.suggestions().isEmpty()) {
suggestionsProvider = (context, input) -> Collections.emptyList();
} else {
suggestionsProvider = this.manager.getParserRegistry().getSuggestionProvider(parser.suggestions())
.orElseThrow(() -> new NullPointerException(
String.format(
"Cannot find the suggestions provider with name '%s'",
parser.suggestions()
)
));
}
final MethodArgumentParser<C, ?> methodArgumentParser = new MethodArgumentParser<>( final MethodArgumentParser<C, ?> methodArgumentParser = new MethodArgumentParser<>(
suggestionsProvider,
instance, instance,
method method
); );

View file

@ -31,7 +31,9 @@ import org.checkerframework.checker.nullness.qual.NonNull;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.List;
import java.util.Queue; import java.util.Queue;
import java.util.function.BiFunction;
/** /**
* Represents a method annotated with {@link Parser} * Represents a method annotated with {@link Parser}
@ -42,19 +44,23 @@ import java.util.Queue;
*/ */
public class MethodArgumentParser<C, T> implements ArgumentParser<C, T> { public class MethodArgumentParser<C, T> implements ArgumentParser<C, T> {
private final BiFunction<CommandContext<C>, String, List<String>> suggestionProvider;
private final MethodHandle methodHandle; private final MethodHandle methodHandle;
/** /**
* Create a new parser * Create a new parser
* *
* @param instance Instance that owns the method * @param suggestionProvider Suggestion provider
* @param method The annotated method * @param instance Instance that owns the method
* @param method The annotated method
* @throws Exception If the method lookup fails * @throws Exception If the method lookup fails
*/ */
public MethodArgumentParser( public MethodArgumentParser(
final @NonNull BiFunction<CommandContext<C>, String, List<String>> suggestionProvider,
final @NonNull Object instance, final @NonNull Object instance,
final @NonNull Method method final @NonNull Method method
) throws Exception { ) throws Exception {
this.suggestionProvider = suggestionProvider;
this.methodHandle = MethodHandles.lookup().unreflect(method).bindTo(instance); this.methodHandle = MethodHandles.lookup().unreflect(method).bindTo(instance);
} }
@ -73,4 +79,12 @@ public class MethodArgumentParser<C, T> implements ArgumentParser<C, T> {
} }
} }
@Override
public @NonNull List<@NonNull String> suggestions(
final @NonNull CommandContext<C> commandContext,
final @NonNull String input
) {
return this.suggestionProvider.apply(commandContext, input);
}
} }

View file

@ -27,6 +27,7 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import java.util.function.BiFunction;
/** /**
* This annotation allows you to create annotated methods that behave like argument parsers. * This annotation allows you to create annotated methods that behave like argument parsers.
@ -52,4 +53,18 @@ public @interface Parser {
*/ */
String name() default ""; String name() default "";
/**
* Name of the suggestions provider to use. If the string is left empty, the default
* provider for the {@link cloud.commandframework.annotations.Argument} will be used. Otherwise,
* the {@link cloud.commandframework.arguments.parser.ParserRegistry} instance in the
* {@link cloud.commandframework.CommandManager} will be queried for a matching suggestion provider.
* <p>
* For this to work, the suggestion needs to be registered in the parser registry. To do this, use
* {@link cloud.commandframework.arguments.parser.ParserRegistry#registerSuggestionProvider(String, BiFunction)}.
* The registry instance can be retrieved using {@link cloud.commandframework.CommandManager#getParserRegistry()}.
*
* @return The name of the suggestion provider, or {@code ""}
*/
String suggestions() default "";
} }

View file

@ -153,13 +153,18 @@ class AnnotationParserTest {
TypeToken.get(CustomType.class), TypeToken.get(CustomType.class),
ParserParameters.empty() ParserParameters.empty()
).orElseThrow(() -> new NullPointerException("Could not find CustomType parser")); ).orElseThrow(() -> new NullPointerException("Could not find CustomType parser"));
final CommandContext<TestCommandSender> context = new CommandContext<>(
new TestCommandSender(),
this.manager
);
Assertions.assertEquals("yay", parser.parse( Assertions.assertEquals("yay", parser.parse(
new CommandContext<>( context,
new TestCommandSender(),
this.manager
),
new LinkedList<>() new LinkedList<>()
).getParsedValue().orElse(new CustomType("")).toString()); ).getParsedValue().orElse(new CustomType("")).toString());
Assertions.assertTrue(parser.suggestions(
context,
""
).contains("Stella"));
} }
@Suggestions("cows") @Suggestions("cows")
@ -167,7 +172,7 @@ class AnnotationParserTest {
return Arrays.asList("Stella", "Bella", "Agda"); return Arrays.asList("Stella", "Bella", "Agda");
} }
@Parser @Parser(suggestions = "cows")
public CustomType customTypeParser(final CommandContext<TestCommandSender> context, final Queue<String> input) { public CustomType customTypeParser(final CommandContext<TestCommandSender> context, final Queue<String> input) {
return new CustomType("yay"); return new CustomType("yay");
} }