Initial progress towards on a more advanced help system
This commit is contained in:
parent
c38247b3ad
commit
a50b36e41f
3 changed files with 363 additions and 39 deletions
|
|
@ -30,9 +30,11 @@ import com.intellectualsites.commands.meta.CommandMeta;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
@ -45,7 +47,7 @@ import java.util.function.Consumer;
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public class Command<C> {
|
public class Command<C> {
|
||||||
|
|
||||||
@Nonnull private final List<CommandArgument<C, ?>> arguments;
|
@Nonnull private final Map<CommandArgument<C, ?>, String> arguments;
|
||||||
@Nonnull private final CommandExecutionHandler<C> commandExecutionHandler;
|
@Nonnull private final CommandExecutionHandler<C> commandExecutionHandler;
|
||||||
@Nullable private final Class<? extends C> senderType;
|
@Nullable private final Class<? extends C> senderType;
|
||||||
@Nonnull private final String commandPermission;
|
@Nonnull private final String commandPermission;
|
||||||
|
|
@ -54,13 +56,13 @@ public class Command<C> {
|
||||||
/**
|
/**
|
||||||
* Construct a new command
|
* Construct a new command
|
||||||
*
|
*
|
||||||
* @param commandArguments Command arguments
|
* @param commandArguments Command argument and description pairs
|
||||||
* @param commandExecutionHandler Execution handler
|
* @param commandExecutionHandler Execution handler
|
||||||
* @param senderType Required sender type. May be {@code null}
|
* @param senderType Required sender type. May be {@code null}
|
||||||
* @param commandPermission Command permission
|
* @param commandPermission Command permission
|
||||||
* @param commandMeta Command meta instance
|
* @param commandMeta Command meta instance
|
||||||
*/
|
*/
|
||||||
public Command(@Nonnull final List<CommandArgument<C, ?>> commandArguments,
|
public Command(@Nonnull final Map<CommandArgument<C, ?>, String> commandArguments,
|
||||||
@Nonnull final CommandExecutionHandler<C> commandExecutionHandler,
|
@Nonnull final CommandExecutionHandler<C> commandExecutionHandler,
|
||||||
@Nullable final Class<? extends C> senderType,
|
@Nullable final Class<? extends C> senderType,
|
||||||
@Nonnull final String commandPermission,
|
@Nonnull final String commandPermission,
|
||||||
|
|
@ -71,7 +73,7 @@ public class Command<C> {
|
||||||
}
|
}
|
||||||
// Enforce ordering of command arguments
|
// Enforce ordering of command arguments
|
||||||
boolean foundOptional = false;
|
boolean foundOptional = false;
|
||||||
for (final CommandArgument<C, ?> argument : this.arguments) {
|
for (final CommandArgument<C, ?> argument : this.arguments.keySet()) {
|
||||||
if (argument.getName().isEmpty()) {
|
if (argument.getName().isEmpty()) {
|
||||||
throw new IllegalArgumentException("Argument names may not be empty");
|
throw new IllegalArgumentException("Argument names may not be empty");
|
||||||
}
|
}
|
||||||
|
|
@ -97,7 +99,7 @@ public class Command<C> {
|
||||||
* @param senderType Required sender type. May be {@code null}
|
* @param senderType Required sender type. May be {@code null}
|
||||||
* @param commandMeta Command meta instance
|
* @param commandMeta Command meta instance
|
||||||
*/
|
*/
|
||||||
public Command(@Nonnull final List<CommandArgument<C, ?>> commandArguments,
|
public Command(@Nonnull final Map<CommandArgument<C, ?>, String> commandArguments,
|
||||||
@Nonnull final CommandExecutionHandler<C> commandExecutionHandler,
|
@Nonnull final CommandExecutionHandler<C> commandExecutionHandler,
|
||||||
@Nullable final Class<? extends C> senderType,
|
@Nullable final Class<? extends C> senderType,
|
||||||
@Nonnull final CommandMeta commandMeta) {
|
@Nonnull final CommandMeta commandMeta) {
|
||||||
|
|
@ -112,7 +114,7 @@ public class Command<C> {
|
||||||
* @param commandPermission Command permission
|
* @param commandPermission Command permission
|
||||||
* @param commandMeta Command meta instance
|
* @param commandMeta Command meta instance
|
||||||
*/
|
*/
|
||||||
public Command(@Nonnull final List<CommandArgument<C, ?>> commandArguments,
|
public Command(@Nonnull final Map<CommandArgument<C, ?>, String> commandArguments,
|
||||||
@Nonnull final CommandExecutionHandler<C> commandExecutionHandler,
|
@Nonnull final CommandExecutionHandler<C> commandExecutionHandler,
|
||||||
@Nonnull final String commandPermission,
|
@Nonnull final String commandPermission,
|
||||||
@Nonnull final CommandMeta commandMeta) {
|
@Nonnull final CommandMeta commandMeta) {
|
||||||
|
|
@ -133,8 +135,9 @@ public class Command<C> {
|
||||||
public static <C> Builder<C> newBuilder(@Nonnull final String commandName,
|
public static <C> Builder<C> newBuilder(@Nonnull final String commandName,
|
||||||
@Nonnull final CommandMeta commandMeta,
|
@Nonnull final CommandMeta commandMeta,
|
||||||
@Nonnull final String... aliases) {
|
@Nonnull final String... aliases) {
|
||||||
return new Builder<>(null, commandMeta, null,
|
final Map<CommandArgument<C, ?>, String> map = new LinkedHashMap<>();
|
||||||
Collections.singletonList(StaticArgument.required(commandName, aliases)),
|
map.put(StaticArgument.required(commandName, aliases), "");
|
||||||
|
return new Builder<>(null, commandMeta, null, map,
|
||||||
new CommandExecutionHandler.NullCommandExecutionHandler<>(), "");
|
new CommandExecutionHandler.NullCommandExecutionHandler<>(), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -145,7 +148,7 @@ public class Command<C> {
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public List<CommandArgument<C, ?>> getArguments() {
|
public List<CommandArgument<C, ?>> getArguments() {
|
||||||
return Collections.unmodifiableList(this.arguments);
|
return new ArrayList<>(this.arguments.keySet());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -189,22 +192,14 @@ public class Command<C> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the longest chain of similar arguments for
|
* Get the description for an argument
|
||||||
* two commands
|
|
||||||
*
|
*
|
||||||
* @param other Command to compare to
|
* @param argument Argument
|
||||||
* @return List containing the longest shared argument chain
|
* @return Argument description
|
||||||
*/
|
*/
|
||||||
public List<CommandArgument<C, ?>> getSharedArgumentChain(@Nonnull final Command<C> other) {
|
@Nonnull
|
||||||
final List<CommandArgument<C, ?>> commandArguments = new LinkedList<>();
|
public String getArgumentDescription(@Nonnull final CommandArgument<C, ?> argument) {
|
||||||
for (int i = 0; i < this.arguments.size() && i < other.arguments.size(); i++) {
|
return this.arguments.get(argument);
|
||||||
if (this.arguments.get(i).equals(other.arguments.get(i))) {
|
|
||||||
commandArguments.add(this.arguments.get(i));
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return commandArguments;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -217,7 +212,7 @@ public class Command<C> {
|
||||||
public static final class Builder<C> {
|
public static final class Builder<C> {
|
||||||
|
|
||||||
@Nonnull private final CommandMeta commandMeta;
|
@Nonnull private final CommandMeta commandMeta;
|
||||||
@Nonnull private final List<CommandArgument<C, ?>> commandArguments;
|
@Nonnull private final Map<CommandArgument<C, ?>, String> commandArguments;
|
||||||
@Nonnull private final CommandExecutionHandler<C> commandExecutionHandler;
|
@Nonnull private final CommandExecutionHandler<C> commandExecutionHandler;
|
||||||
@Nullable private final Class<? extends C> senderType;
|
@Nullable private final Class<? extends C> senderType;
|
||||||
@Nonnull private final String commandPermission;
|
@Nonnull private final String commandPermission;
|
||||||
|
|
@ -226,7 +221,7 @@ public class Command<C> {
|
||||||
private Builder(@Nullable final CommandManager<C> commandManager,
|
private Builder(@Nullable final CommandManager<C> commandManager,
|
||||||
@Nonnull final CommandMeta commandMeta,
|
@Nonnull final CommandMeta commandMeta,
|
||||||
@Nullable final Class<? extends C> senderType,
|
@Nullable final Class<? extends C> senderType,
|
||||||
@Nonnull final List<CommandArgument<C, ?>> commandArguments,
|
@Nonnull final Map<CommandArgument<C, ?>, String> commandArguments,
|
||||||
@Nonnull final CommandExecutionHandler<C> commandExecutionHandler,
|
@Nonnull final CommandExecutionHandler<C> commandExecutionHandler,
|
||||||
@Nonnull final String commandPermission) {
|
@Nonnull final String commandPermission) {
|
||||||
this.commandManager = commandManager;
|
this.commandManager = commandManager;
|
||||||
|
|
@ -264,7 +259,7 @@ public class Command<C> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a new command argument to the command
|
* Add a new command argument with an empty description to the command
|
||||||
*
|
*
|
||||||
* @param argument Argument to add
|
* @param argument Argument to add
|
||||||
* @param <T> Argument type
|
* @param <T> Argument type
|
||||||
|
|
@ -272,12 +267,26 @@ public class Command<C> {
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public <T> Builder<C> argument(@Nonnull final CommandArgument<C, T> argument) {
|
public <T> Builder<C> argument(@Nonnull final CommandArgument<C, T> argument) {
|
||||||
final List<CommandArgument<C, ?>> commandArguments = new LinkedList<>(this.commandArguments);
|
return this.argument(argument, "");
|
||||||
commandArguments.add(argument);
|
}
|
||||||
return new Builder<>(this.commandManager, this.commandMeta, this.senderType, commandArguments,
|
|
||||||
|
/**
|
||||||
|
* Add a new command argument to the command
|
||||||
|
*
|
||||||
|
* @param argument Argument to add
|
||||||
|
* @param description Argument description
|
||||||
|
* @param <T> Argument type
|
||||||
|
* @return New builder instance with the command argument inserted into the argument list
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public <T> Builder<C> argument(@Nonnull final CommandArgument<C, T> argument, @Nonnull final String description) {
|
||||||
|
final Map<CommandArgument<C, ?>, String> commandArgumentMap = new LinkedHashMap<>(this.commandArguments);
|
||||||
|
commandArgumentMap.put(argument, description);
|
||||||
|
return new Builder<>(this.commandManager, this.commandMeta, this.senderType, commandArgumentMap,
|
||||||
this.commandExecutionHandler, this.commandPermission);
|
this.commandExecutionHandler, this.commandPermission);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a new command argument by interacting with a constructed command argument builder
|
* Add a new command argument by interacting with a constructed command argument builder
|
||||||
*
|
*
|
||||||
|
|
@ -342,8 +351,11 @@ public class Command<C> {
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public Command<C> build() {
|
public Command<C> build() {
|
||||||
return new Command<>(Collections.unmodifiableList(this.commandArguments),
|
return new Command<>(Collections.unmodifiableMap(this.commandArguments),
|
||||||
this.commandExecutionHandler, this.senderType, this.commandPermission, this.commandMeta);
|
this.commandExecutionHandler,
|
||||||
|
this.senderType,
|
||||||
|
this.commandPermission,
|
||||||
|
this.commandMeta);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,11 +24,13 @@
|
||||||
package com.intellectualsites.commands;
|
package com.intellectualsites.commands;
|
||||||
|
|
||||||
import com.intellectualsites.commands.arguments.CommandArgument;
|
import com.intellectualsites.commands.arguments.CommandArgument;
|
||||||
|
import com.intellectualsites.commands.arguments.StaticArgument;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public final class CommandHelpHandler<C> {
|
public final class CommandHelpHandler<C> {
|
||||||
|
|
@ -49,8 +51,11 @@ public final class CommandHelpHandler<C> {
|
||||||
final List<VerboseHelpEntry<C>> syntaxHints = new ArrayList<>();
|
final List<VerboseHelpEntry<C>> syntaxHints = new ArrayList<>();
|
||||||
for (final Command<C> command : this.commandManager.getCommands()) {
|
for (final Command<C> command : this.commandManager.getCommands()) {
|
||||||
final List<CommandArgument<C, ?>> arguments = command.getArguments();
|
final List<CommandArgument<C, ?>> arguments = command.getArguments();
|
||||||
|
final String description = command.getCommandMeta().getOrDefault("description", "");
|
||||||
syntaxHints.add(new VerboseHelpEntry<>(command,
|
syntaxHints.add(new VerboseHelpEntry<>(command,
|
||||||
this.commandManager.getCommandSyntaxFormatter().apply(arguments, null)));
|
this.commandManager.getCommandSyntaxFormatter()
|
||||||
|
.apply(arguments, null),
|
||||||
|
description));
|
||||||
}
|
}
|
||||||
syntaxHints.sort(Comparator.comparing(VerboseHelpEntry::getSyntaxString));
|
syntaxHints.sort(Comparator.comparing(VerboseHelpEntry::getSyntaxString));
|
||||||
return syntaxHints;
|
return syntaxHints;
|
||||||
|
|
@ -67,8 +72,11 @@ public final class CommandHelpHandler<C> {
|
||||||
public List<String> getLongestSharedChains() {
|
public List<String> getLongestSharedChains() {
|
||||||
final List<String> chains = new ArrayList<>();
|
final List<String> chains = new ArrayList<>();
|
||||||
this.commandManager.getCommandTree().getRootNodes().forEach(node ->
|
this.commandManager.getCommandTree().getRootNodes().forEach(node ->
|
||||||
chains.add(node.getValue().getName() + this.commandManager.getCommandSyntaxFormatter()
|
chains.add(node.getValue()
|
||||||
.apply(Collections.emptyList(), node)));
|
.getName() + this.commandManager.getCommandSyntaxFormatter()
|
||||||
|
.apply(Collections
|
||||||
|
.emptyList(),
|
||||||
|
node)));
|
||||||
chains.sort(String::compareTo);
|
chains.sort(String::compareTo);
|
||||||
return chains;
|
return chains;
|
||||||
}
|
}
|
||||||
|
|
@ -78,10 +86,14 @@ public final class CommandHelpHandler<C> {
|
||||||
|
|
||||||
private final Command<C> command;
|
private final Command<C> command;
|
||||||
private final String syntaxString;
|
private final String syntaxString;
|
||||||
|
private final String description;
|
||||||
|
|
||||||
private VerboseHelpEntry(@Nonnull final Command<C> command, @Nonnull final String syntaxString) {
|
private VerboseHelpEntry(@Nonnull final Command<C> command,
|
||||||
|
@Nonnull final String syntaxString,
|
||||||
|
@Nonnull final String description) {
|
||||||
this.command = command;
|
this.command = command;
|
||||||
this.syntaxString = syntaxString;
|
this.syntaxString = syntaxString;
|
||||||
|
this.description = description;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -103,6 +115,222 @@ public final class CommandHelpHandler<C> {
|
||||||
public String getSyntaxString() {
|
public String getSyntaxString() {
|
||||||
return this.syntaxString;
|
return this.syntaxString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the command description
|
||||||
|
*
|
||||||
|
* @return Command description
|
||||||
|
*/
|
||||||
|
public String getDescription() {
|
||||||
|
return this.description;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query for help
|
||||||
|
*
|
||||||
|
* @param query Query string
|
||||||
|
* @return Help topic, will return an empty {@link IndexHelpTopic} if no results were found
|
||||||
|
*/
|
||||||
|
public HelpTopic<C> queryHelp(@Nonnull final String query) {
|
||||||
|
if (query.replace(" ", "").isEmpty()) {
|
||||||
|
return new IndexHelpTopic<>(this.getAllCommands());
|
||||||
|
}
|
||||||
|
|
||||||
|
final String[] queryFragments = query.split(" ");
|
||||||
|
final List<VerboseHelpEntry<C>> verboseEntries = this.getAllCommands();
|
||||||
|
final String rootFragment = queryFragments[0];
|
||||||
|
|
||||||
|
/* Determine which command we are querying for */
|
||||||
|
Command<C> queryCommand = null;
|
||||||
|
String queryCommandName = "";
|
||||||
|
|
||||||
|
outer:
|
||||||
|
for (final VerboseHelpEntry<C> entry : verboseEntries) {
|
||||||
|
final Command<C> command = entry.getCommand();
|
||||||
|
@SuppressWarnings("unchecked") final StaticArgument<C> staticArgument = (StaticArgument<C>) command.getArguments()
|
||||||
|
.get(0);
|
||||||
|
for (final String alias : staticArgument.getAliases()) {
|
||||||
|
if (alias.equalsIgnoreCase(rootFragment)) {
|
||||||
|
/* We found our command */
|
||||||
|
queryCommand = command;
|
||||||
|
queryCommandName = staticArgument.getName();
|
||||||
|
break outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No command found, return all possible commands */
|
||||||
|
if (queryCommand == null) {
|
||||||
|
return new IndexHelpTopic<>(verboseEntries);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Traverse command to find the most specific help topic */
|
||||||
|
final CommandTree.Node<CommandArgument<C, ?>> node = this.commandManager.getCommandTree().getNamedNode(queryCommandName);
|
||||||
|
|
||||||
|
final List<CommandArgument<C, ?>> traversedNodes = new LinkedList<>();
|
||||||
|
CommandTree.Node<CommandArgument<C, ?>> head = node;
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
outer: while (head != null) {
|
||||||
|
++index;
|
||||||
|
traversedNodes.add(head.getValue());
|
||||||
|
if (head.isLeaf()) {
|
||||||
|
return new VerboseHelpTopic<>(head.getValue().getOwningCommand());
|
||||||
|
} else if (head.getChildren().size() == 1) {
|
||||||
|
head = head.getChildren().get(0);
|
||||||
|
} else {
|
||||||
|
if (index < queryFragments.length) {
|
||||||
|
/* We might still be able to match an argument */
|
||||||
|
for (final CommandTree.Node<CommandArgument<C, ?>> child : head.getChildren()) {
|
||||||
|
final StaticArgument<C> childArgument = (StaticArgument<C>) child.getValue();
|
||||||
|
for (final String childAlias : childArgument.getAliases()) {
|
||||||
|
if (childAlias.equalsIgnoreCase(queryFragments[index])) {
|
||||||
|
head = child;
|
||||||
|
continue outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final String currentDescription = this.commandManager.getCommandSyntaxFormatter().apply(traversedNodes, null);
|
||||||
|
/* 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()) {
|
||||||
|
childSuggestions.add(this.commandManager.getCommandSyntaxFormatter().apply(traversedNodes, child));
|
||||||
|
}
|
||||||
|
return new MultiHelpTopic<>(currentDescription, childSuggestions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new IndexHelpTopic<>(Collections.emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Something that can be returned as the result of a help query
|
||||||
|
* <p>
|
||||||
|
* Implementations:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link IndexHelpTopic}</li>
|
||||||
|
* <li>{@link VerboseHelpTopic}</li>
|
||||||
|
* <li>{@link MultiHelpTopic}</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param <C> Command sender type
|
||||||
|
*/
|
||||||
|
public interface HelpTopic<C> {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Index of available commands
|
||||||
|
*
|
||||||
|
* @param <C> Command sender type
|
||||||
|
*/
|
||||||
|
public static final class IndexHelpTopic<C> implements HelpTopic<C> {
|
||||||
|
|
||||||
|
private final List<VerboseHelpEntry<C>> entries;
|
||||||
|
|
||||||
|
private IndexHelpTopic(@Nonnull final List<VerboseHelpEntry<C>> entries) {
|
||||||
|
this.entries = entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get help entries
|
||||||
|
*
|
||||||
|
* @return Entries
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public List<VerboseHelpEntry<C>> getEntries() {
|
||||||
|
return this.entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the help topic is entry
|
||||||
|
*
|
||||||
|
* @return {@code true} if the topic is entry, else {@code false}
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return this.getEntries().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verbose information about a specific {@link Command}
|
||||||
|
*
|
||||||
|
* @param <C> Command sender type
|
||||||
|
*/
|
||||||
|
public static final class VerboseHelpTopic<C> implements HelpTopic<C> {
|
||||||
|
|
||||||
|
private final Command<C> command;
|
||||||
|
private final String description;
|
||||||
|
|
||||||
|
private VerboseHelpTopic(@Nonnull final Command<C> command) {
|
||||||
|
this.command = command;
|
||||||
|
final String shortDescription = command.getCommandMeta().getOrDefault("description", "No description");
|
||||||
|
this.description = command.getCommandMeta().getOrDefault("long-description", shortDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the command
|
||||||
|
*
|
||||||
|
* @return Command
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public Command<C> getCommand() {
|
||||||
|
return this.command;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the command description
|
||||||
|
*
|
||||||
|
* @return Command description
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public String getDescription() {
|
||||||
|
return this.description;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Help topic with multiple semi-verbose command descriptions
|
||||||
|
*
|
||||||
|
* @param <C> Command sender type
|
||||||
|
*/
|
||||||
|
public static final class MultiHelpTopic<C> implements HelpTopic<C> {
|
||||||
|
|
||||||
|
private final String longestPath;
|
||||||
|
private final List<String> childSuggestions;
|
||||||
|
|
||||||
|
private MultiHelpTopic(@Nonnull final String longestPath, @Nonnull final List<String> childSuggestions) {
|
||||||
|
this.longestPath = longestPath;
|
||||||
|
this.childSuggestions = childSuggestions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the longest shared path
|
||||||
|
*
|
||||||
|
* @return Longest path
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public String getLongestPath() {
|
||||||
|
return this.longestPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get syntax hints for the node's children
|
||||||
|
*
|
||||||
|
* @return Child suggestions
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public List<String> getChildSuggestions() {
|
||||||
|
return this.childSuggestions;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,12 +23,17 @@
|
||||||
//
|
//
|
||||||
package com.intellectualsites.commands;
|
package com.intellectualsites.commands;
|
||||||
|
|
||||||
|
import com.intellectualsites.commands.arguments.CommandArgument;
|
||||||
import com.intellectualsites.commands.arguments.standard.IntegerArgument;
|
import com.intellectualsites.commands.arguments.standard.IntegerArgument;
|
||||||
|
import com.intellectualsites.commands.meta.SimpleCommandMeta;
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
class CommandHelpHandlerTest {
|
class CommandHelpHandlerTest {
|
||||||
|
|
@ -38,9 +43,11 @@ class CommandHelpHandlerTest {
|
||||||
@BeforeAll
|
@BeforeAll
|
||||||
static void setup() {
|
static void setup() {
|
||||||
manager = new TestCommandManager();
|
manager = new TestCommandManager();
|
||||||
manager.command(manager.commandBuilder("test").literal("this").literal("thing").build());
|
final SimpleCommandMeta meta1 = SimpleCommandMeta.builder().with("description", "Command with only literals").build();
|
||||||
manager.command(manager.commandBuilder("test").literal("int").
|
manager.command(manager.commandBuilder("test", meta1).literal("this").literal("thing").build());
|
||||||
argument(IntegerArgument.required("int")).build());
|
final SimpleCommandMeta meta2 = SimpleCommandMeta.builder().with("description", "Command with variables").build();
|
||||||
|
manager.command(manager.commandBuilder("test", meta2).literal("int").
|
||||||
|
argument(IntegerArgument.required("int"), "A number").build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -59,4 +66,81 @@ class CommandHelpHandlerTest {
|
||||||
Assertions.assertEquals(Arrays.asList("test int|this"), longestChains);
|
Assertions.assertEquals(Arrays.asList("test int|this"), longestChains);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testHelpQuery() {
|
||||||
|
final CommandHelpHandler.HelpTopic<TestCommandSender> query1 = manager.getCommandHelpHandler().queryHelp("");
|
||||||
|
Assertions.assertTrue(query1 instanceof CommandHelpHandler.IndexHelpTopic);
|
||||||
|
this.printTopic("", query1);
|
||||||
|
final CommandHelpHandler.HelpTopic<TestCommandSender> query2 = manager.getCommandHelpHandler().queryHelp("test");
|
||||||
|
Assertions.assertTrue(query2 instanceof CommandHelpHandler.MultiHelpTopic);
|
||||||
|
this.printTopic("test", query2);
|
||||||
|
final CommandHelpHandler.HelpTopic<TestCommandSender> query3 = manager.getCommandHelpHandler().queryHelp("test int");
|
||||||
|
Assertions.assertTrue(query3 instanceof CommandHelpHandler.VerboseHelpTopic);
|
||||||
|
this.printTopic("test int", query3);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printTopic(@Nonnull final String query,
|
||||||
|
@Nonnull final CommandHelpHandler.HelpTopic<TestCommandSender> helpTopic) {
|
||||||
|
System.out.printf("Showing results for query: \"/%s\"\n", query);
|
||||||
|
if (helpTopic instanceof CommandHelpHandler.IndexHelpTopic) {
|
||||||
|
this.printIndexHelpTopic((CommandHelpHandler.IndexHelpTopic<TestCommandSender>) helpTopic);
|
||||||
|
} else if (helpTopic instanceof CommandHelpHandler.MultiHelpTopic) {
|
||||||
|
this.printMultiHelpTopic((CommandHelpHandler.MultiHelpTopic<TestCommandSender>) helpTopic);
|
||||||
|
} else if (helpTopic instanceof CommandHelpHandler.VerboseHelpTopic) {
|
||||||
|
this.printVerboseHelpTopic((CommandHelpHandler.VerboseHelpTopic<TestCommandSender>) helpTopic);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Unknown help topic type");
|
||||||
|
}
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printIndexHelpTopic(@Nonnull final CommandHelpHandler.IndexHelpTopic<TestCommandSender> helpTopic) {
|
||||||
|
System.out.println("└── Available Commands: ");
|
||||||
|
final Iterator<CommandHelpHandler.VerboseHelpEntry<TestCommandSender>> iterator = helpTopic.getEntries().iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
final CommandHelpHandler.VerboseHelpEntry<TestCommandSender> entry = iterator.next();
|
||||||
|
final String prefix = iterator.hasNext() ? "├──" : "└──";
|
||||||
|
System.out.printf(" %s %s: %s\n", prefix, entry.getSyntaxString(), entry.getDescription());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printMultiHelpTopic(@Nonnull final CommandHelpHandler.MultiHelpTopic<TestCommandSender> helpTopic) {
|
||||||
|
System.out.printf("└── /%s\n", helpTopic.getLongestPath());
|
||||||
|
final int headerIndentation = helpTopic.getLongestPath().length();
|
||||||
|
final Iterator<String> iterator = helpTopic.getChildSuggestions().iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
final String suggestion = iterator.next();
|
||||||
|
final StringBuilder printBuilder = new StringBuilder();
|
||||||
|
for (int i = 0; i < headerIndentation; i++) {
|
||||||
|
printBuilder.append(' ');
|
||||||
|
}
|
||||||
|
if (iterator.hasNext()) {
|
||||||
|
printBuilder.append("├── ");
|
||||||
|
} else {
|
||||||
|
printBuilder.append("└── ");
|
||||||
|
}
|
||||||
|
printBuilder.append(suggestion);
|
||||||
|
System.out.println(printBuilder.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printVerboseHelpTopic(@Nonnull final CommandHelpHandler.VerboseHelpTopic<TestCommandSender> helpTopic) {
|
||||||
|
System.out.printf("└── Command: /%s\n", manager.getCommandSyntaxFormatter()
|
||||||
|
.apply(helpTopic.getCommand().getArguments(), null));
|
||||||
|
System.out.printf(" ├── Description: %s\n", helpTopic.getDescription());
|
||||||
|
System.out.println(" └── Args: ");
|
||||||
|
final Iterator<CommandArgument<TestCommandSender, ?>> iterator = helpTopic.getCommand().getArguments().iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
final CommandArgument<TestCommandSender, ?> argument = iterator.next();
|
||||||
|
|
||||||
|
String description = helpTopic.getCommand().getArgumentDescription(argument);
|
||||||
|
if (!description.isEmpty()) {
|
||||||
|
description = ": " + description;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.printf(" %s %s%s\n", iterator.hasNext() ? "├──" : "└──", manager.getCommandSyntaxFormatter().apply(
|
||||||
|
Collections.singletonList(argument), null), description);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue