Compare commits

...

2 commits

Author SHA1 Message Date
boq
e5de081185 Remove unused generic 2018-09-26 12:49:39 +02:00
boq
2bffe75ac9 Implement calculation of suggestions for any text position 2018-09-26 12:49:31 +02:00
19 changed files with 288 additions and 112 deletions

View file

@ -3,14 +3,13 @@
package com.mojang.brigadier;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.context.CommandContextBuilder;
import com.mojang.brigadier.context.StringRange;
import com.mojang.brigadier.context.SuggestionContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
@ -57,6 +56,7 @@ public class CommandDispatcher<S> {
private static final String USAGE_OR = "|";
private final RootCommandNode<S> root;
private final Predicate<CommandNode<S>> hasCommand = new Predicate<CommandNode<S>>() {
@Override
public boolean test(final CommandNode<S> input) {
@ -231,7 +231,7 @@ public class CommandDispatcher<S> {
final CommandContext<S> child = context.getChild();
if (child != null) {
forked |= context.isForked();
if (!child.getNodes().isEmpty()) {
if (child.hasNodes()) {
foundCommand = true;
final RedirectModifier<S> modifier = context.getRedirectModifier();
if (modifier == null) {
@ -345,24 +345,14 @@ public class CommandDispatcher<S> {
* @see #execute(String, Object)
*/
public ParseResults<S> parse(final StringReader command, final S source) {
final CommandContextBuilder<S> context = new CommandContextBuilder<>(this, source, 0);
final CommandContextBuilder<S> context = new CommandContextBuilder<>(this, source, root, command.getCursor());
return parseNodes(root, command, context);
}
private static class PartialParse<S> {
public final CommandContextBuilder<S> context;
public final ParseResults<S> parse;
private PartialParse(final CommandContextBuilder<S> context, final ParseResults<S> parse) {
this.context = context;
this.parse = parse;
}
}
private ParseResults<S> parseNodes(final CommandNode<S> node, final StringReader originalReader, final CommandContextBuilder<S> contextSoFar) {
final S source = contextSoFar.getSource();
Map<CommandNode<S>, CommandSyntaxException> errors = null;
List<PartialParse<S>> potentials = null;
List<ParseResults<S>> potentials = null;
final int cursor = originalReader.getCursor();
for (final CommandNode<S> child : node.getRelevantNodes(originalReader)) {
@ -395,48 +385,47 @@ public class CommandDispatcher<S> {
if (reader.canRead(child.getRedirect() == null ? 2 : 1)) {
reader.skip();
if (child.getRedirect() != null) {
final CommandContextBuilder<S> childContext = new CommandContextBuilder<>(this, source, reader.getCursor());
childContext.withNode(child.getRedirect(), StringRange.between(cursor, reader.getCursor() - 1));
final CommandContextBuilder<S> childContext = new CommandContextBuilder<>(this, source, child.getRedirect(), reader.getCursor());
final ParseResults<S> parse = parseNodes(child.getRedirect(), reader, childContext);
context.withChild(parse.getContext());
return new ParseResults<>(context, originalReader.getCursor(), parse.getReader(), parse.getExceptions());
return new ParseResults<>(context, parse.getReader(), parse.getExceptions());
} else {
final ParseResults<S> parse = parseNodes(child, reader, context);
if (potentials == null) {
potentials = new ArrayList<>(1);
}
potentials.add(new PartialParse<>(context, parse));
potentials.add(parse);
}
} else {
if (potentials == null) {
potentials = new ArrayList<>(1);
}
potentials.add(new PartialParse<>(context, new ParseResults<>(context, originalReader.getCursor(), reader, Collections.emptyMap())));
potentials.add(new ParseResults<>(context, reader, Collections.emptyMap()));
}
}
if (potentials != null) {
if (potentials.size() > 1) {
potentials.sort((a, b) -> {
if (!a.parse.getReader().canRead() && b.parse.getReader().canRead()) {
if (!a.getReader().canRead() && b.getReader().canRead()) {
return -1;
}
if (a.parse.getReader().canRead() && !b.parse.getReader().canRead()) {
if (a.getReader().canRead() && !b.getReader().canRead()) {
return 1;
}
if (a.parse.getExceptions().isEmpty() && !b.parse.getExceptions().isEmpty()) {
if (a.getExceptions().isEmpty() && !b.getExceptions().isEmpty()) {
return -1;
}
if (!a.parse.getExceptions().isEmpty() && b.parse.getExceptions().isEmpty()) {
if (!a.getExceptions().isEmpty() && b.getExceptions().isEmpty()) {
return 1;
}
return 0;
});
}
return potentials.get(0).parse;
return potentials.get(0);
}
return new ParseResults<>(contextSoFar, originalReader.getCursor(), originalReader, errors == null ? Collections.emptyMap() : errors);
return new ParseResults<>(contextSoFar, originalReader, errors == null ? Collections.emptyMap() : errors);
}
/**
@ -589,37 +578,24 @@ public class CommandDispatcher<S> {
* @return a future that will eventually resolve into a {@link Suggestions} object
*/
public CompletableFuture<Suggestions> getCompletionSuggestions(final ParseResults<S> parse) {
final CommandContextBuilder<S> rootContext = parse.getContext();
final CommandContextBuilder<S> context = rootContext.getLastChild();
final CommandNode<S> parent;
final int start;
return getCompletionSuggestions(parse, parse.getReader().getTotalLength());
}
if (context.getNodes().isEmpty()) {
parent = root;
start = parse.getStartIndex();
} else if (parse.getReader().canRead()) {
final Map.Entry<CommandNode<S>, StringRange> entry = Iterables.getLast(context.getNodes().entrySet());
parent = entry.getKey();
start = entry.getValue().getEnd() + 1;
} else if (context.getNodes().size() > 1) {
final Map.Entry<CommandNode<S>, StringRange> entry = Iterables.get(context.getNodes().entrySet(), context.getNodes().size() - 2);
parent = entry.getKey();
start = entry.getValue().getEnd() + 1;
} else if (rootContext != context && context.getNodes().size() > 0) {
final Map.Entry<CommandNode<S>, StringRange> entry = Iterables.getLast(context.getNodes().entrySet());
parent = entry.getKey();
start = entry.getValue().getEnd() + 1;
} else {
parent = root;
start = parse.getStartIndex();
}
public CompletableFuture<Suggestions> getCompletionSuggestions(final ParseResults<S> parse, int cursor) {
final CommandContextBuilder<S> context = parse.getContext();
final SuggestionContext<S> nodeBeforeCursor = context.findSuggestionContext(cursor);
final CommandNode<S> parent = nodeBeforeCursor.parent;
final int start = Math.min(nodeBeforeCursor.startPos, cursor);
final String fullInput = parse.getReader().getString();
final String truncatedInput = fullInput.substring(0, cursor);
@SuppressWarnings("unchecked") final CompletableFuture<Suggestions>[] futures = new CompletableFuture[parent.getChildren().size()];
int i = 0;
for (final CommandNode<S> node : parent.getChildren()) {
CompletableFuture<Suggestions> future = Suggestions.empty();
try {
future = node.listSuggestions(context.build(parse.getReader().getString()), new SuggestionsBuilder(parse.getReader().getString(), start));
future = node.listSuggestions(context.build(truncatedInput), new SuggestionsBuilder(truncatedInput, start));
} catch (final CommandSyntaxException ignored) {
}
futures[i++] = future;
@ -631,7 +607,7 @@ public class CommandDispatcher<S> {
for (final CompletableFuture<Suggestions> future : futures) {
suggestions.add(future.join());
}
result.complete(Suggestions.merge(parse.getReader().getString(), suggestions));
result.complete(Suggestions.merge(fullInput, suggestions));
});
return result;

View file

@ -13,22 +13,16 @@ import java.util.Map;
public class ParseResults<S> {
private final CommandContextBuilder<S> context;
private final Map<CommandNode<S>, CommandSyntaxException> exceptions;
private final int startIndex;
private final ImmutableStringReader reader;
public ParseResults(final CommandContextBuilder<S> context, final int startIndex, final ImmutableStringReader reader, final Map<CommandNode<S>, CommandSyntaxException> exceptions) {
public ParseResults(final CommandContextBuilder<S> context, final ImmutableStringReader reader, final Map<CommandNode<S>, CommandSyntaxException> exceptions) {
this.context = context;
this.startIndex = startIndex;
this.reader = reader;
this.exceptions = exceptions;
}
public ParseResults(final CommandContextBuilder<S> context) {
this(context, 0, new StringReader(""), Collections.emptyMap());
}
public int getStartIndex() {
return startIndex;
this(context, new StringReader(""), Collections.emptyMap());
}
public CommandContextBuilder<S> getContext() {

View file

@ -5,7 +5,6 @@ package com.mojang.brigadier.arguments;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.context.CommandContextBuilder;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
@ -15,7 +14,7 @@ import java.util.Collections;
import java.util.concurrent.CompletableFuture;
public interface ArgumentType<T> {
<S> T parse(StringReader reader) throws CommandSyntaxException;
T parse(StringReader reader) throws CommandSyntaxException;
default <S> CompletableFuture<Suggestions> listSuggestions(final CommandContext<S> context, final SuggestionsBuilder builder) {
return Suggestions.empty();

View file

@ -5,7 +5,6 @@ package com.mojang.brigadier.arguments;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.context.CommandContextBuilder;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
@ -29,7 +28,7 @@ public class BoolArgumentType implements ArgumentType<Boolean> {
}
@Override
public <S> Boolean parse(final StringReader reader) throws CommandSyntaxException {
public Boolean parse(final StringReader reader) throws CommandSyntaxException {
return reader.readBoolean();
}

View file

@ -46,7 +46,7 @@ public class DoubleArgumentType implements ArgumentType<Double> {
}
@Override
public <S> Double parse(final StringReader reader) throws CommandSyntaxException {
public Double parse(final StringReader reader) throws CommandSyntaxException {
final int start = reader.getCursor();
final double result = reader.readDouble();
if (result < minimum) {

View file

@ -46,7 +46,7 @@ public class FloatArgumentType implements ArgumentType<Float> {
}
@Override
public <S> Float parse(final StringReader reader) throws CommandSyntaxException {
public Float parse(final StringReader reader) throws CommandSyntaxException {
final int start = reader.getCursor();
final float result = reader.readFloat();
if (result < minimum) {

View file

@ -46,7 +46,7 @@ public class IntegerArgumentType implements ArgumentType<Integer> {
}
@Override
public <S> Integer parse(final StringReader reader) throws CommandSyntaxException {
public Integer parse(final StringReader reader) throws CommandSyntaxException {
final int start = reader.getCursor();
final int result = reader.readInt();
if (result < minimum) {
@ -84,4 +84,9 @@ public class IntegerArgumentType implements ArgumentType<Integer> {
return "integer(" + minimum + ", " + maximum + ")";
}
}
@Override
public Collection<String> getExamples() {
return EXAMPLES;
}
}

View file

@ -5,7 +5,6 @@ package com.mojang.brigadier.arguments;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.context.CommandContextBuilder;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import java.util.Arrays;
@ -39,7 +38,7 @@ public class StringArgumentType implements ArgumentType<String> {
}
@Override
public <S> String parse(final StringReader reader) throws CommandSyntaxException {
public String parse(final StringReader reader) throws CommandSyntaxException {
if (type == StringType.GREEDY_PHRASE) {
final String text = reader.getRemaining();
reader.setCursor(reader.getTotalLength());

View file

@ -9,6 +9,7 @@ import com.mojang.brigadier.Command;
import com.mojang.brigadier.RedirectModifier;
import com.mojang.brigadier.tree.CommandNode;
import java.util.List;
import java.util.Map;
public class CommandContext<S> {
@ -16,17 +17,19 @@ public class CommandContext<S> {
private final String input;
private final Command<S> command;
private final Map<String, ParsedArgument<S, ?>> arguments;
private final Map<CommandNode<S>, StringRange> nodes;
private final CommandNode<S> rootNode;
private final List<ParsedCommandNode<S>> nodes;
private final StringRange range;
private final CommandContext<S> child;
private final RedirectModifier<S> modifier;
private final boolean forks;
public CommandContext(final S source, final String input, final Map<String, ParsedArgument<S, ?>> arguments, final Command<S> command, final Map<CommandNode<S>, StringRange> nodes, final StringRange range, final CommandContext<S> child, final RedirectModifier<S> modifier, boolean forks) {
public CommandContext(final S source, final String input, final Map<String, ParsedArgument<S, ?>> arguments, final Command<S> command, final CommandNode<S> rootNode, final List<ParsedCommandNode<S>> nodes, final StringRange range, final CommandContext<S> child, final RedirectModifier<S> modifier, boolean forks) {
this.source = source;
this.input = input;
this.arguments = arguments;
this.command = command;
this.rootNode = rootNode;
this.nodes = nodes;
this.range = range;
this.child = child;
@ -38,7 +41,7 @@ public class CommandContext<S> {
if (this.source == source) {
return this;
}
return new CommandContext<>(source, input, arguments, command, nodes, range, child, modifier, forks);
return new CommandContext<>(source, input, arguments, command, rootNode, nodes, range, child, modifier, forks);
}
public CommandContext<S> getChild() {
@ -85,7 +88,8 @@ public class CommandContext<S> {
final CommandContext that = (CommandContext) o;
if (!arguments.equals(that.arguments)) return false;
if (!Iterables.elementsEqual(nodes.entrySet(), that.nodes.entrySet())) return false;
if (!rootNode.equals(that.rootNode)) return false;
if (!Iterables.elementsEqual(nodes, that.nodes)) return false;
if (command != null ? !command.equals(that.command) : that.command != null) return false;
if (!source.equals(that.source)) return false;
if (child != null ? !child.equals(that.child) : that.child != null) return false;
@ -98,6 +102,7 @@ public class CommandContext<S> {
int result = source.hashCode();
result = 31 * result + arguments.hashCode();
result = 31 * result + (command != null ? command.hashCode() : 0);
result = 31 * result + rootNode.hashCode();
result = 31 * result + nodes.hashCode();
result = 31 * result + (child != null ? child.hashCode() : 0);
return result;
@ -115,10 +120,18 @@ public class CommandContext<S> {
return input;
}
public Map<CommandNode<S>, StringRange> getNodes() {
public CommandNode<S> getRootNode() {
return rootNode;
}
public List<ParsedCommandNode<S>> getNodes() {
return nodes;
}
public boolean hasNodes() {
return !nodes.isEmpty();
}
public boolean isForked() {
return forks;
}

View file

@ -3,17 +3,21 @@
package com.mojang.brigadier.context;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.mojang.brigadier.Command;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.RedirectModifier;
import com.mojang.brigadier.tree.CommandNode;
import java.util.List;
import java.util.Map;
public class CommandContextBuilder<S> {
private final Map<String, ParsedArgument<S, ?>> arguments = Maps.newLinkedHashMap();
private final Map<CommandNode<S>, StringRange> nodes = Maps.newLinkedHashMap();
private final CommandNode<S> rootNode;
private final List<ParsedCommandNode<S>> nodes = Lists.newArrayList();
private final CommandDispatcher<S> dispatcher;
private S source;
private Command<S> command;
@ -22,7 +26,8 @@ public class CommandContextBuilder<S> {
private RedirectModifier<S> modifier = null;
private boolean forks;
public CommandContextBuilder(final CommandDispatcher<S> dispatcher, final S source, final int start) {
public CommandContextBuilder(final CommandDispatcher<S> dispatcher, final S source, final CommandNode<S> rootNode, final int start) {
this.rootNode = rootNode;
this.dispatcher = dispatcher;
this.source = source;
this.range = StringRange.at(start);
@ -37,6 +42,10 @@ public class CommandContextBuilder<S> {
return source;
}
public CommandNode<S> getRootNode() {
return rootNode;
}
public CommandContextBuilder<S> withArgument(final String name, final ParsedArgument<S, ?> argument) {
this.arguments.put(name, argument);
return this;
@ -52,7 +61,7 @@ public class CommandContextBuilder<S> {
}
public CommandContextBuilder<S> withNode(final CommandNode<S> node, final StringRange range) {
nodes.put(node, range);
nodes.add(new ParsedCommandNode<>(node, range));
this.range = StringRange.encompassing(this.range, range);
this.modifier = node.getRedirectModifier();
this.forks = node.isFork();
@ -60,10 +69,10 @@ public class CommandContextBuilder<S> {
}
public CommandContextBuilder<S> copy() {
final CommandContextBuilder<S> copy = new CommandContextBuilder<>(dispatcher, source, range.getStart());
final CommandContextBuilder<S> copy = new CommandContextBuilder<>(dispatcher, source, rootNode, range.getStart());
copy.command = command;
copy.arguments.putAll(arguments);
copy.nodes.putAll(nodes);
copy.nodes.addAll(nodes);
copy.child = child;
copy.range = range;
copy.forks = forks;
@ -91,12 +100,12 @@ public class CommandContextBuilder<S> {
return command;
}
public Map<CommandNode<S>, StringRange> getNodes() {
public List<ParsedCommandNode<S>> getNodes() {
return nodes;
}
public CommandContext<S> build(final String input) {
return new CommandContext<>(source, input, arguments, command, nodes, range, child == null ? null : child.build(input), modifier, forks);
return new CommandContext<>(source, input, arguments, command, rootNode, nodes, range, child == null ? null : child.build(input), modifier, forks);
}
public CommandDispatcher<S> getDispatcher() {
@ -106,4 +115,33 @@ public class CommandContextBuilder<S> {
public StringRange getRange() {
return range;
}
public SuggestionContext<S> findSuggestionContext(final int cursor) {
if (range.getStart() <= cursor) {
if (range.getEnd() < cursor) {
if (child != null) {
return child.findSuggestionContext(cursor);
} else if (!nodes.isEmpty()) {
final ParsedCommandNode<S> last = Iterables.getLast(nodes);
return new SuggestionContext<>(last.getNode(), last.getRange().getEnd() + 1);
} else {
return new SuggestionContext<>(rootNode, range.getStart());
}
} else {
CommandNode<S> prev = rootNode;
for (final ParsedCommandNode<S> node : nodes) {
final StringRange nodeRange = node.getRange();
if (nodeRange.getStart() <= cursor && cursor <= nodeRange.getEnd()) {
return new SuggestionContext<>(prev, nodeRange.getStart());
}
prev = node.getNode();
}
if (prev == null) {
throw new IllegalStateException("Can't find node before cursor");
}
return new SuggestionContext<>(prev, range.getStart());
}
}
throw new IllegalStateException("Can't find node before cursor");
}
}

View file

@ -0,0 +1,47 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
package com.mojang.brigadier.context;
import com.mojang.brigadier.tree.CommandNode;
import java.util.Objects;
public class ParsedCommandNode<S> {
private final CommandNode<S> node;
private final StringRange range;
public ParsedCommandNode(CommandNode<S> node, StringRange range) {
this.node = node;
this.range = range;
}
public CommandNode<S> getNode() {
return node;
}
public StringRange getRange() {
return range;
}
@Override
public String toString() {
return node + "@" + range;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ParsedCommandNode<?> that = (ParsedCommandNode<?>) o;
return Objects.equals(node, that.node) &&
Objects.equals(range, that.range);
}
@Override
public int hashCode() {
return Objects.hash(node, range);
}
}

View file

@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
package com.mojang.brigadier.context;
import com.mojang.brigadier.tree.CommandNode;
public class SuggestionContext<S> {
public final CommandNode<S> parent;
public final int startPos;
public SuggestionContext(CommandNode<S> parent, int startPos) {
this.parent = parent;
this.startPos = startPos;
}
}

View file

@ -259,23 +259,33 @@ public class CommandDispatcherTest {
@SuppressWarnings("unchecked")
@Test
public void testExecuteRedirectedMultipleTimes() throws Exception {
subject.register(literal("actual").executes(command));
subject.register(literal("redirected").redirect(subject.getRoot()));
final LiteralCommandNode<Object> concreteNode = subject.register(literal("actual").executes(command));
final LiteralCommandNode<Object> redirectNode = subject.register(literal("redirected").redirect(subject.getRoot()));
final String input = "redirected redirected actual";
final ParseResults<Object> parse = subject.parse(input, source);
assertThat(parse.getContext().getRange().get(input), equalTo("redirected"));
assertThat(parse.getContext().getNodes().size(), is(1));
assertThat(parse.getContext().getRootNode(), is(subject.getRoot()));
assertThat(parse.getContext().getNodes().get(0).getRange(), equalTo(parse.getContext().getRange()));
assertThat(parse.getContext().getNodes().get(0).getNode(), is(redirectNode));
final CommandContextBuilder<Object> child1 = parse.getContext().getChild();
assertThat(child1, is(notNullValue()));
assertThat(child1.getRange().get(input), equalTo("redirected redirected"));
assertThat(child1.getNodes().size(), is(2));
assertThat(child1.getRange().get(input), equalTo("redirected"));
assertThat(child1.getNodes().size(), is(1));
assertThat(child1.getRootNode(), is(subject.getRoot()));
assertThat(child1.getNodes().get(0).getRange(), equalTo(child1.getRange()));
assertThat(child1.getNodes().get(0).getNode(), is(redirectNode));
final CommandContextBuilder<Object> child2 = child1.getChild();
assertThat(child2, is(notNullValue()));
assertThat(child2.getRange().get(input), equalTo("redirected actual"));
assertThat(child2.getNodes().size(), is(2));
assertThat(child2.getRange().get(input), equalTo("actual"));
assertThat(child2.getNodes().size(), is(1));
assertThat(child2.getRootNode(), is(subject.getRoot()));
assertThat(child2.getNodes().get(0).getRange(), equalTo(child2.getRange()));
assertThat(child2.getNodes().get(0).getNode(), is(concreteNode));
assertThat(subject.execute(parse), is(42));
verify(command).run(any(CommandContext.class));
@ -290,19 +300,25 @@ public class CommandDispatcherTest {
when(modifier.apply(argThat(hasProperty("source", is(source))))).thenReturn(Lists.newArrayList(source1, source2));
subject.register(literal("actual").executes(command));
subject.register(literal("redirected").fork(subject.getRoot(), modifier));
final LiteralCommandNode<Object> concreteNode = subject.register(literal("actual").executes(command));
final LiteralCommandNode<Object> redirectNode = subject.register(literal("redirected").fork(subject.getRoot(), modifier));
final String input = "redirected actual";
final ParseResults<Object> parse = subject.parse(input, source);
assertThat(parse.getContext().getRange().get(input), equalTo("redirected"));
assertThat(parse.getContext().getNodes().size(), is(1));
assertThat(parse.getContext().getRootNode(), equalTo(subject.getRoot()));
assertThat(parse.getContext().getNodes().get(0).getRange(), equalTo(parse.getContext().getRange()));
assertThat(parse.getContext().getNodes().get(0).getNode(), is(redirectNode));
assertThat(parse.getContext().getSource(), is(source));
final CommandContextBuilder<Object> parent = parse.getContext().getChild();
assertThat(parent, is(notNullValue()));
assertThat(parent.getRange().get(input), equalTo("redirected actual"));
assertThat(parent.getNodes().size(), is(2));
assertThat(parent.getRange().get(input), equalTo("actual"));
assertThat(parent.getNodes().size(), is(1));
assertThat(parse.getContext().getRootNode(), equalTo(subject.getRoot()));
assertThat(parent.getNodes().get(0).getRange(), equalTo(parent.getRange()));
assertThat(parent.getNodes().get(0).getNode(), is(concreteNode));
assertThat(parent.getSource(), is(source));
assertThat(subject.execute(parse), is(2));

View file

@ -4,6 +4,7 @@
package com.mojang.brigadier;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.tree.CommandNode;
@ -102,11 +103,11 @@ public class CommandDispatcherUsagesTest {
}
private CommandNode<Object> get(final String command) {
return Iterators.getLast(subject.parse(command, source).getContext().getNodes().keySet().iterator());
return Iterables.getLast(subject.parse(command, source).getContext().getNodes()).getNode();
}
private CommandNode<Object> get(final StringReader command) {
return Iterators.getLast(subject.parse(command, source).getContext().getNodes().keySet().iterator());
return Iterables.getLast(subject.parse(command, source).getContext().getNodes()).getNode();
}
@Test
@ -189,10 +190,10 @@ public class CommandDispatcherUsagesTest {
final Map<CommandNode<Object>, String> results = subject.getSmartUsage(get(offsetH), source);
assertThat(results, equalTo(ImmutableMap.builder()
.put(get("h 1"), "[1] i")
.put(get("h 2"), "[2] i ii")
.put(get("h 3"), "[3]")
.build()
.put(get("h 1"), "[1] i")
.put(get("h 2"), "[2] i ii")
.put(get("h 3"), "[3]")
.build()
));
}
}

View file

@ -14,6 +14,8 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import java.util.List;
import static com.mojang.brigadier.arguments.IntegerArgumentType.integer;
import static com.mojang.brigadier.arguments.StringArgumentType.word;
import static com.mojang.brigadier.builder.LiteralArgumentBuilder.literal;
@ -33,6 +35,18 @@ public class CommandSuggestionsTest {
subject = new CommandDispatcher<>();
}
private void testSuggestions(final String contents, final int cursor, final StringRange range, final String... suggestions) {
final Suggestions result = subject.getCompletionSuggestions(subject.parse(contents, source), cursor).join();
assertThat(result.getRange(), equalTo(range));
final List<Suggestion> expected = Lists.newArrayList();
for (final String suggestion : suggestions) {
expected.add(new Suggestion(range, suggestion));
}
assertThat(result.getList(), equalTo(expected));
}
private static StringReader inputWithOffset(final String input, final int offset) {
final StringReader result = new StringReader(input);
result.setCursor(offset);
@ -102,6 +116,31 @@ public class CommandSuggestionsTest {
assertThat(result.getList(), equalTo(Lists.newArrayList(new Suggestion(StringRange.at(7), "bar"), new Suggestion(StringRange.at(7), "baz"), new Suggestion(StringRange.at(7), "foo"))));
}
@Test
public void getCompletionSuggestions_movingCursor_subCommands() throws Exception {
subject.register(
literal("parent_one")
.then(literal("faz"))
.then(literal("fbz"))
.then(literal("gaz"))
);
subject.register(
literal("parent_two")
);
testSuggestions("parent_one faz ", 0, StringRange.at(0), "parent_one", "parent_two");
testSuggestions("parent_one faz ", 1, StringRange.between(0, 1), "parent_one", "parent_two");
testSuggestions("parent_one faz ", 7, StringRange.between(0, 7), "parent_one", "parent_two");
testSuggestions("parent_one faz ", 8, StringRange.between(0, 8), "parent_one");
testSuggestions("parent_one faz ", 10, StringRange.at(0));
testSuggestions("parent_one faz ", 11, StringRange.at(11), "faz", "fbz", "gaz");
testSuggestions("parent_one faz ", 12, StringRange.between(11, 12), "faz", "fbz");
testSuggestions("parent_one faz ", 13, StringRange.between(11, 13), "faz");
testSuggestions("parent_one faz ", 14, StringRange.at(0));
testSuggestions("parent_one faz ", 15, StringRange.at(0));
}
@Test
public void getCompletionSuggestions_subCommands_partial() throws Exception {
subject.register(
@ -121,10 +160,10 @@ public class CommandSuggestionsTest {
@Test
public void getCompletionSuggestions_subCommands_partial_withInputOffset() throws Exception {
subject.register(
literal("parent")
.then(literal("foo"))
.then(literal("bar"))
.then(literal("baz"))
literal("parent")
.then(literal("foo"))
.then(literal("bar"))
.then(literal("baz"))
);
final ParseResults<Object> parse = subject.parse(inputWithOffset("junk parent b", 5), source);
@ -158,6 +197,29 @@ public class CommandSuggestionsTest {
assertThat(result.getList(), equalTo(Lists.newArrayList(new Suggestion(StringRange.between(9, 10), "sub"))));
}
@Test
public void getCompletionSuggestions_movingCursor_redirect() throws Exception {
final LiteralCommandNode<Object> actualOne = subject.register(literal("actual_one")
.then(literal("faz"))
.then(literal("fbz"))
.then(literal("gaz"))
);
final LiteralCommandNode<Object> actualTwo = subject.register(literal("actual_two"));
subject.register(literal("redirect_one").redirect(actualOne));
subject.register(literal("redirect_two").redirect(actualOne));
testSuggestions("redirect_one faz ", 0, StringRange.at(0), "actual_one", "actual_two", "redirect_one", "redirect_two");
testSuggestions("redirect_one faz ", 9, StringRange.between(0, 9), "redirect_one", "redirect_two");
testSuggestions("redirect_one faz ", 10, StringRange.between(0, 10), "redirect_one");
testSuggestions("redirect_one faz ", 12, StringRange.at(0));
testSuggestions("redirect_one faz ", 13, StringRange.at(13), "faz", "fbz", "gaz");
testSuggestions("redirect_one faz ", 14, StringRange.between(13, 14), "faz", "fbz");
testSuggestions("redirect_one faz ", 15, StringRange.between(13, 15), "faz");
testSuggestions("redirect_one faz ", 16, StringRange.at(0));
testSuggestions("redirect_one faz ", 17, StringRange.at(0));
}
@Test
public void getCompletionSuggestions_redirectPartial_withInputOffset() throws Exception {

View file

@ -7,13 +7,13 @@ import com.google.common.testing.EqualsTester;
import com.mojang.brigadier.Command;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.tree.CommandNode;
import com.mojang.brigadier.tree.RootCommandNode;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import static com.mojang.brigadier.arguments.IntegerArgumentType.integer;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
@ -26,9 +26,12 @@ public class CommandContextTest {
@Mock
private CommandDispatcher<Object> dispatcher;
@Mock
private CommandNode<Object> rootNode;
@Before
public void setUp() throws Exception {
builder = new CommandContextBuilder<>(dispatcher, source, 0);
builder = new CommandContextBuilder<>(dispatcher, source, rootNode, 0);
}
@Test(expected = IllegalArgumentException.class)
@ -53,22 +56,30 @@ public class CommandContextTest {
assertThat(builder.build("").getSource(), is(source));
}
@Test
public void testRootNode() throws Exception {
assertThat(builder.build("").getRootNode(), is(rootNode));
}
@SuppressWarnings("unchecked")
@Test
public void testEquals() throws Exception {
final Object otherSource = new Object();
final Command<Object> command = mock(Command.class);
final Command<Object> otherCommand = mock(Command.class);
final CommandNode<Object> rootNode = mock(CommandNode.class);
final CommandNode<Object> otherRootNode = mock(CommandNode.class);
final CommandNode<Object> node = mock(CommandNode.class);
final CommandNode<Object> otherNode = mock(CommandNode.class);
new EqualsTester()
.addEqualityGroup(new CommandContextBuilder<>(dispatcher, source, 0).build(""), new CommandContextBuilder<>(dispatcher, source, 0).build(""))
.addEqualityGroup(new CommandContextBuilder<>(dispatcher, otherSource, 0).build(""), new CommandContextBuilder<>(dispatcher, otherSource, 0).build(""))
.addEqualityGroup(new CommandContextBuilder<>(dispatcher, source, 0).withCommand(command).build(""), new CommandContextBuilder<>(dispatcher, source, 0).withCommand(command).build(""))
.addEqualityGroup(new CommandContextBuilder<>(dispatcher, source, 0).withCommand(otherCommand).build(""), new CommandContextBuilder<>(dispatcher, source, 0).withCommand(otherCommand).build(""))
.addEqualityGroup(new CommandContextBuilder<>(dispatcher, source, 0).withArgument("foo", new ParsedArgument<>(0, 1, 123)).build("123"), new CommandContextBuilder<>(dispatcher, source, 0).withArgument("foo", new ParsedArgument<>(0, 1, 123)).build("123"))
.addEqualityGroup(new CommandContextBuilder<>(dispatcher, source, 0).withNode(node, StringRange.between(0, 3)).withNode(otherNode, StringRange.between(4, 6)).build("123 456"), new CommandContextBuilder<>(dispatcher, source, 0).withNode(node, StringRange.between(0, 3)).withNode(otherNode, StringRange.between(4, 6)).build("123 456"))
.addEqualityGroup(new CommandContextBuilder<>(dispatcher, source, 0).withNode(otherNode, StringRange.between(0, 3)).withNode(node, StringRange.between(4, 6)).build("123 456"), new CommandContextBuilder<>(dispatcher, source, 0).withNode(otherNode, StringRange.between(0, 3)).withNode(node, StringRange.between(4, 6)).build("123 456"))
.addEqualityGroup(new CommandContextBuilder<>(dispatcher, source, rootNode, 0).build(""), new CommandContextBuilder<>(dispatcher, source, rootNode, 0).build(""))
.addEqualityGroup(new CommandContextBuilder<>(dispatcher, source, otherRootNode, 0).build(""), new CommandContextBuilder<>(dispatcher, source, otherRootNode, 0).build(""))
.addEqualityGroup(new CommandContextBuilder<>(dispatcher, otherSource, rootNode, 0).build(""), new CommandContextBuilder<>(dispatcher, otherSource, rootNode, 0).build(""))
.addEqualityGroup(new CommandContextBuilder<>(dispatcher, source, rootNode, 0).withCommand(command).build(""), new CommandContextBuilder<>(dispatcher, source, rootNode, 0).withCommand(command).build(""))
.addEqualityGroup(new CommandContextBuilder<>(dispatcher, source, rootNode, 0).withCommand(otherCommand).build(""), new CommandContextBuilder<>(dispatcher, source, rootNode, 0).withCommand(otherCommand).build(""))
.addEqualityGroup(new CommandContextBuilder<>(dispatcher, source, rootNode, 0).withArgument("foo", new ParsedArgument<>(0, 1, 123)).build("123"), new CommandContextBuilder<>(dispatcher, source, rootNode, 0).withArgument("foo", new ParsedArgument<>(0, 1, 123)).build("123"))
.addEqualityGroup(new CommandContextBuilder<>(dispatcher, source, rootNode, 0).withNode(node, StringRange.between(0, 3)).withNode(otherNode, StringRange.between(4, 6)).build("123 456"), new CommandContextBuilder<>(dispatcher, source, rootNode, 0).withNode(node, StringRange.between(0, 3)).withNode(otherNode, StringRange.between(4, 6)).build("123 456"))
.addEqualityGroup(new CommandContextBuilder<>(dispatcher, source, rootNode, 0).withNode(otherNode, StringRange.between(0, 3)).withNode(node, StringRange.between(4, 6)).build("123 456"), new CommandContextBuilder<>(dispatcher, source, rootNode, 0).withNode(otherNode, StringRange.between(0, 3)).withNode(node, StringRange.between(4, 6)).build("123 456"))
.testEquals();
}
}

View file

@ -32,7 +32,7 @@ public class ArgumentCommandNodeTest extends AbstractCommandNodeTest {
@Before
public void setUp() throws Exception {
node = argument("foo", integer()).build();
contextBuilder = new CommandContextBuilder<>(new CommandDispatcher<>(), new Object(), 0);
contextBuilder = new CommandContextBuilder<>(new CommandDispatcher<>(), new Object(), new RootCommandNode<>(), 0);
}
@Test

View file

@ -37,7 +37,7 @@ public class LiteralCommandNodeTest extends AbstractCommandNodeTest {
@Before
public void setUp() throws Exception {
node = literal("foo").build();
contextBuilder = new CommandContextBuilder<>(new CommandDispatcher<>(), new Object(), 0);
contextBuilder = new CommandContextBuilder<>(new CommandDispatcher<>(), new Object(), new RootCommandNode<>(), 0);
}
@Test

View file

@ -34,7 +34,7 @@ public class RootCommandNodeTest extends AbstractCommandNodeTest {
@Test
public void testParse() throws Exception {
final StringReader reader = new StringReader("hello world");
node.parse(reader, new CommandContextBuilder<>(new CommandDispatcher<>(), new Object(), 0));
node.parse(reader, new CommandContextBuilder<>(new CommandDispatcher<>(), new Object(), new RootCommandNode<>(), 0));
assertThat(reader.getCursor(), is(0));
}