Add predicate command filter option to the cloud help system

Signed-off-by: Irmo van den Berge <irmo.vandenberge@ziggo.nl>
This commit is contained in:
Irmo van den Berge 2020-12-20 20:30:30 +01:00 committed by Alexander Söderberg
parent 4556f12b6d
commit cabb7f426c
4 changed files with 168 additions and 5 deletions

View file

@ -38,13 +38,19 @@ import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import java.util.function.Predicate;
public final class CommandHelpHandler<C> {
private final CommandManager<C> commandManager;
private final Predicate<Command<C>> commandPredicate;
CommandHelpHandler(final @NonNull CommandManager<C> commandManager) {
CommandHelpHandler(
final @NonNull CommandManager<C> commandManager,
final @NonNull Predicate<Command<C>> commandPredicate
) {
this.commandManager = commandManager;
this.commandPredicate = commandPredicate;
}
/**
@ -55,6 +61,11 @@ public final class CommandHelpHandler<C> {
public @NonNull List<@NonNull VerboseHelpEntry<C>> getAllCommands() {
final List<VerboseHelpEntry<C>> syntaxHints = new ArrayList<>();
for (final Command<C> command : this.commandManager.getCommands()) {
/* Check command is not filtered */
if (!this.commandPredicate.test(command)) {
continue;
}
final List<CommandArgument<C, ?>> arguments = command.getArguments();
final String description = command.getCommandMeta().getOrDefault(CommandMeta.DESCRIPTION, "");
syntaxHints.add(new VerboseHelpEntry<>(
@ -188,7 +199,7 @@ public final class CommandHelpHandler<C> {
int index = 0;
outer:
while (head != null) {
while (head != null && this.isNodeVisible(head)) {
++index;
traversedNodes.add(head.getValue());
@ -233,6 +244,11 @@ public final class CommandHelpHandler<C> {
/* Attempt to parse the longest possible description for the children */
final List<String> childSuggestions = new LinkedList<>();
for (final CommandTree.Node<CommandArgument<C, ?>> child : head.getChildren()) {
/* Check filtered by predicate */
if (!this.isNodeVisible(child)) {
continue;
}
final List<CommandArgument<C, ?>> traversedNodesSub = new LinkedList<>(traversedNodes);
if (recipient == null
|| child.getValue() == null
@ -252,6 +268,29 @@ public final class CommandHelpHandler<C> {
return new IndexHelpTopic<>(Collections.emptyList());
}
/* Checks using the predicate whether a command node or one of its children is visible */
private boolean isNodeVisible(
final CommandTree.@NonNull Node<CommandArgument<C, ?>> node
) {
/* Check node is itself a command that is visible */
final CommandArgument<C, ?> argument = node.getValue();
if (argument != null) {
final Command<C> owningCommand = argument.getOwningCommand();
if (owningCommand != null && this.commandPredicate.test(owningCommand)) {
return true;
}
}
/* Query the children recursively */
for (CommandTree.Node<CommandArgument<C, ?>> childNode : node.getChildren()) {
if (this.isNodeVisible(childNode)) {
return true;
}
}
return false;
}
/**
* Something that can be returned as the result of a help query
* <p>

View file

@ -73,6 +73,7 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Predicate;
/**
* The manager is responsible for command registration, parsing delegation, etc.
@ -753,13 +754,30 @@ public abstract class CommandManager<C> {
/**
* Get a command help handler instance. This can be used to assist in the production
* of command help menus, etc.
* of command help menus, etc. This command help handler instance will display
* all commands registered in this command manager.
*
* @return Command help handler. A new instance will be created
* each time this method is called.
*/
public final @NonNull CommandHelpHandler<C> getCommandHelpHandler() {
return new CommandHelpHandler<>(this);
return new CommandHelpHandler<>(this, cmd -> true);
}
/**
* Get a command help handler instance. This can be used to assist in the production
* of command help menus, etc. A predicate can be specified to filter what commands
* registered in this command manager are visible in the help menu.
*
* @param commandPredicate Predicate that filters what commands are displayed in
* the help menu.
* @return Command help handler. A new instance will be created
* each time this method is called.
*/
public final @NonNull CommandHelpHandler<C> getCommandHelpHandler(
final @NonNull Predicate<Command<C>> commandPredicate
) {
return new CommandHelpHandler<>(this, commandPredicate);
}
/**

View file

@ -23,6 +23,8 @@
//
package cloud.commandframework;
import cloud.commandframework.arguments.CommandArgument;
import cloud.commandframework.arguments.StaticArgument;
import cloud.commandframework.arguments.standard.IntegerArgument;
import cloud.commandframework.arguments.standard.StringArgument;
import cloud.commandframework.meta.CommandMeta;
@ -36,6 +38,8 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
class CommandHelpHandlerTest {
@ -93,6 +97,94 @@ class CommandHelpHandlerTest {
this.printTopic("vec", query4);
}
@Test
void testPredicateFilter() {
/*
* This predicate only displays the commands starting with /test
* The one command ending in 'thing' is excluded as well, for complexity
*/
final Predicate<Command<TestCommandSender>> predicate = (command) -> {
return command.toString().startsWith("test ")
&& !command.toString().endsWith(" thing");
};
/*
* List all commands from root, which should show only:
* - /test <potato>
* - /test int <int>
*/
final CommandHelpHandler.HelpTopic<TestCommandSender> query1 = manager.getCommandHelpHandler(predicate).queryHelp("");
Assertions.assertTrue(query1 instanceof CommandHelpHandler.IndexHelpTopic);
Assertions.assertEquals(Arrays.asList("test <potato>", "test int <int>"), getSortedSyntaxStrings(query1));
/*
* List all commands from /test, which should show only:
* - /test <potato>
* - /test int <int>
*/
final CommandHelpHandler.HelpTopic<TestCommandSender> query2 = manager.getCommandHelpHandler(predicate).queryHelp("test");
Assertions.assertTrue(query2 instanceof CommandHelpHandler.MultiHelpTopic);
Assertions.assertEquals(Arrays.asList("test <potato>", "test int <int>"), getSortedSyntaxStrings(query2));
/*
* List all commands from /test int, which should show only:
* - /test int <int>
*/
final CommandHelpHandler.HelpTopic<TestCommandSender> query3 = manager.getCommandHelpHandler(predicate).queryHelp("test int");
Assertions.assertTrue(query3 instanceof CommandHelpHandler.VerboseHelpTopic);
Assertions.assertEquals(Arrays.asList("test int <int>"), getSortedSyntaxStrings(query3));
/*
* List all commands from /vec, which should show none
*/
final CommandHelpHandler.HelpTopic<TestCommandSender> query4 = manager.getCommandHelpHandler(predicate).queryHelp("vec");
Assertions.assertTrue(query4 instanceof CommandHelpHandler.IndexHelpTopic);
Assertions.assertEquals(Collections.emptyList(), getSortedSyntaxStrings(query4));
}
/* Lists all the syntax strings of the commands displayed in a help topic */
private List<String> getSortedSyntaxStrings(
final CommandHelpHandler.HelpTopic<TestCommandSender> helpTopic
) {
if (helpTopic instanceof CommandHelpHandler.IndexHelpTopic) {
CommandHelpHandler.IndexHelpTopic<TestCommandSender> index =
(CommandHelpHandler.IndexHelpTopic<TestCommandSender>) helpTopic;
return index.getEntries().stream()
.map(CommandHelpHandler.VerboseHelpEntry::getSyntaxString)
.sorted()
.collect(Collectors.toList());
} else if (helpTopic instanceof CommandHelpHandler.MultiHelpTopic) {
CommandHelpHandler.MultiHelpTopic<TestCommandSender> multi =
(CommandHelpHandler.MultiHelpTopic<TestCommandSender>) helpTopic;
return multi.getChildSuggestions().stream()
.sorted()
.collect(Collectors.toList());
} else if (helpTopic instanceof CommandHelpHandler.VerboseHelpTopic) {
CommandHelpHandler.VerboseHelpTopic<TestCommandSender> verbose =
(CommandHelpHandler.VerboseHelpTopic<TestCommandSender>) helpTopic;
//TODO: Use CommandManager syntax for this
StringBuilder syntax = new StringBuilder();
for (CommandArgument<TestCommandSender, ?> argument : verbose.getCommand().getArguments()) {
if (argument instanceof StaticArgument) {
syntax.append(argument.getName());
} else if (argument.isRequired()) {
syntax.append('<').append(argument.getName()).append('>');
} else {
syntax.append('[').append(argument.getName()).append(']');
}
syntax.append(' ');
}
syntax.setLength(syntax.length() - 1);
return Collections.singletonList(syntax.toString());
}
/* Dunno */
return Collections.emptyList();
}
private void printTopic(
final String query,
final CommandHelpHandler.HelpTopic<TestCommandSender> helpTopic