Added support for suggestions tabcompletions of a command input

This commit is contained in:
Nathan Adams
2017-06-22 09:05:16 +02:00
parent eba88b2773
commit f372eb3b98
12 changed files with 186 additions and 10 deletions
@@ -2,6 +2,7 @@ package com.mojang.brigadier;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.context.CommandContextBuilder;
@@ -12,6 +13,7 @@ import com.mojang.brigadier.tree.LiteralCommandNode;
import com.mojang.brigadier.tree.RootCommandNode;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -23,7 +25,7 @@ public class CommandDispatcher<T> {
}
};
public static final SimpleCommandExceptionType ERROR_UNKNOWN_COMMAND = new SimpleCommandExceptionType("unknown_command", "Unknown command");
public static final SimpleCommandExceptionType ERROR_UNKNOWN_COMMAND = new SimpleCommandExceptionType("command.unknown", "Unknown command");
public static final String ARGUMENT_SEPARATOR = " ";
private static final String USAGE_OPTIONAL_OPEN = "[";
private static final String USAGE_OPTIONAL_CLOSE = "]";
@@ -52,7 +54,11 @@ public class CommandDispatcher<T> {
if (child.getCommand() != null) {
context.withCommand(child.getCommand());
}
return parseNodes(child, remaining, context);
if (remaining.isEmpty()) {
return context.build();
} else {
return parseNodes(child, remaining.substring(1), context);
}
} catch (CommandException ex) {
exception = ex;
}
@@ -106,4 +112,28 @@ public class CommandDispatcher<T> {
return result.toString();
}
private Set<String> findSuggestions(CommandNode node, String command, CommandContextBuilder<T> contextBuilder, Set<String> result) {
for (CommandNode child : node.getChildren()) {
try {
CommandContextBuilder<T> context = contextBuilder.copy();
String remaining = child.parse(command, context);
if (remaining.isEmpty()) {
child.listSuggestions(command, result);
} else {
return findSuggestions(child, remaining.substring(1), context, result);
}
} catch (CommandException e) {
child.listSuggestions(command, result);
}
}
return result;
}
public String[] getCompletionSuggestions(String command, T source) {
final Set<String> nodes = findSuggestions(root, command, new CommandContextBuilder<>(source), Sets.newLinkedHashSet());
return nodes.toArray(new String[nodes.size()]);
}
}
@@ -3,6 +3,10 @@ package com.mojang.brigadier.arguments;
import com.mojang.brigadier.context.ParsedArgument;
import com.mojang.brigadier.exceptions.CommandException;
import java.util.Set;
public interface CommandArgumentType<T> {
ParsedArgument<T> parse(String command) throws CommandException;
void listSuggestions(String command, Set<String> output);
}
@@ -7,10 +7,12 @@ import com.mojang.brigadier.context.ParsedArgument;
import com.mojang.brigadier.exceptions.CommandException;
import com.mojang.brigadier.exceptions.ParameterizedCommandExceptionType;
import java.util.Set;
public class IntegerArgumentType implements CommandArgumentType<Integer> {
public static final ParameterizedCommandExceptionType ERROR_NOT_A_NUMBER = new ParameterizedCommandExceptionType("argument-integer-invalid", "Expected an integer, found '${found}'", "found");
public static final ParameterizedCommandExceptionType ERROR_TOO_SMALL = new ParameterizedCommandExceptionType("argument-integer-low", "Integer must not be less than ${minimum}, found ${found}", "found", "minimum");
public static final ParameterizedCommandExceptionType ERROR_TOO_BIG = new ParameterizedCommandExceptionType("argument-integer-big", "Integer must not be more than ${maximum}, found ${found}", "found", "maximum");
public static final ParameterizedCommandExceptionType ERROR_NOT_A_NUMBER = new ParameterizedCommandExceptionType("argument.integer.invalid", "Expected an integer, found '${found}'", "found");
public static final ParameterizedCommandExceptionType ERROR_TOO_SMALL = new ParameterizedCommandExceptionType("argument.integer.low", "Integer must not be less than ${minimum}, found ${found}", "found", "minimum");
public static final ParameterizedCommandExceptionType ERROR_TOO_BIG = new ParameterizedCommandExceptionType("argument.integer.big", "Integer must not be more than ${maximum}, found ${found}", "found", "maximum");
private static final Splitter SPLITTER = Splitter.on(CommandDispatcher.ARGUMENT_SEPARATOR).limit(2);
@@ -58,6 +60,10 @@ public class IntegerArgumentType implements CommandArgumentType<Integer> {
}
}
@Override
public void listSuggestions(String command, Set<String> output) {
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -6,6 +6,8 @@ import com.mojang.brigadier.context.CommandContextBuilder;
import com.mojang.brigadier.context.ParsedArgument;
import com.mojang.brigadier.exceptions.CommandException;
import java.util.Set;
public class ArgumentCommandNode<T> extends CommandNode {
private static final String USAGE_ARGUMENT_OPEN = "<";
private static final String USAGE_ARGUMENT_CLOSE = ">";
@@ -46,12 +48,17 @@ public class ArgumentCommandNode<T> extends CommandNode {
contextBuilder.withNode(this, parsed.getRaw());
if (command.length() > start) {
return command.substring(start + 1);
return command.substring(start);
} else {
return "";
}
}
@Override
public void listSuggestions(String command, Set<String> output) {
type.listSuggestions(command, output);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -7,6 +7,7 @@ import com.mojang.brigadier.exceptions.CommandException;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
public abstract class CommandNode {
private final Map<Object, CommandNode> children = Maps.newLinkedHashMap();
@@ -62,4 +63,6 @@ public abstract class CommandNode {
public abstract String getUsageText();
public abstract String parse(String command, CommandContextBuilder<?> contextBuilder) throws CommandException;
public abstract void listSuggestions(String command, Set<String> output);
}
@@ -6,8 +6,10 @@ import com.mojang.brigadier.context.CommandContextBuilder;
import com.mojang.brigadier.exceptions.CommandException;
import com.mojang.brigadier.exceptions.ParameterizedCommandExceptionType;
import java.util.Set;
public class LiteralCommandNode extends CommandNode {
public static final ParameterizedCommandExceptionType ERROR_INCORRECT_LITERAL = new ParameterizedCommandExceptionType("incorrect_literal", "Expected literal ${expected}", "expected");
public static final ParameterizedCommandExceptionType ERROR_INCORRECT_LITERAL = new ParameterizedCommandExceptionType("argument.literal.incorrect", "Expected literal ${expected}", "expected");
private final String literal;
@@ -34,10 +36,17 @@ public class LiteralCommandNode extends CommandNode {
}
contextBuilder.withNode(this, literal);
int start = expected.length();
int start = literal.length();
return command.substring(start);
}
@Override
public void listSuggestions(String command, Set<String> output) {
if (literal.startsWith(command)) {
output.add(literal);
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -3,6 +3,8 @@ package com.mojang.brigadier.tree;
import com.mojang.brigadier.context.CommandContextBuilder;
import com.mojang.brigadier.exceptions.CommandException;
import java.util.Set;
public class RootCommandNode extends CommandNode {
public RootCommandNode() {
super(null);
@@ -23,6 +25,10 @@ public class RootCommandNode extends CommandNode {
return command;
}
@Override
public void listSuggestions(String command, Set<String> output) {
}
@Override
public boolean equals(Object o) {
if (this == o) return true;