New suggestion API

This commit is contained in:
Nathan Adams
2017-11-20 13:45:05 +01:00
parent 557352f289
commit 095498c94e
21 changed files with 494 additions and 150 deletions
+1 -1
View File
@@ -3,7 +3,7 @@ import groovy.io.FileType
apply plugin: 'java-library' apply plugin: 'java-library'
apply plugin: 'maven' apply plugin: 'maven'
version = '0.1.11' version = '0.1.13'
group = 'com.mojang' group = 'com.mojang'
task wrapper(type: Wrapper) { task wrapper(type: Wrapper) {
@@ -11,6 +11,8 @@ import com.mojang.brigadier.context.CommandContextBuilder;
import com.mojang.brigadier.context.StringRange; import com.mojang.brigadier.context.StringRange;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import com.mojang.brigadier.tree.CommandNode; import com.mojang.brigadier.tree.CommandNode;
import com.mojang.brigadier.tree.LiteralCommandNode; import com.mojang.brigadier.tree.LiteralCommandNode;
import com.mojang.brigadier.tree.RootCommandNode; import com.mojang.brigadier.tree.RootCommandNode;
@@ -310,7 +312,7 @@ public class CommandDispatcher<S> {
return self; return self;
} }
public CompletableFuture<CommandSuggestions> getCompletionSuggestions(final ParseResults<S> parse) { public CompletableFuture<Suggestions> getCompletionSuggestions(final ParseResults<S> parse) {
final CommandContextBuilder<S> rootContext = parse.getContext(); final CommandContextBuilder<S> rootContext = parse.getContext();
final CommandContextBuilder<S> context = rootContext.getLastChild(); final CommandContextBuilder<S> context = rootContext.getLastChild();
final CommandNode<S> parent; final CommandNode<S> parent;
@@ -336,25 +338,23 @@ public class CommandDispatcher<S> {
start = 0; start = 0;
} }
@SuppressWarnings("unchecked") final CompletableFuture<Collection<String>>[] futures = new CompletableFuture[parent.getChildren().size()]; @SuppressWarnings("unchecked") final CompletableFuture<Suggestions>[] futures = new CompletableFuture[parent.getChildren().size()];
int i = 0; int i = 0;
for (final CommandNode<S> node : parent.getChildren()) { for (final CommandNode<S> node : parent.getChildren()) {
try { try {
futures[i++] = node.listSuggestions(context.build(parse.getReader().getString()), parse.getReader().getString().substring(start)); futures[i++] = node.listSuggestions(context.build(parse.getReader().getString()), new SuggestionsBuilder(parse.getReader().getString(), start));
} catch (final CommandSyntaxException e) { } catch (final CommandSyntaxException e) {
futures[i++] = CompletableFuture.completedFuture(Collections.emptyList()); futures[i++] = Suggestions.empty();
} }
} }
final CompletableFuture<CommandSuggestions> result = new CompletableFuture<>(); final CompletableFuture<Suggestions> result = new CompletableFuture<>();
CompletableFuture.allOf(futures).thenRun(() -> { CompletableFuture.allOf(futures).thenRun(() -> {
final Set<String> suggestions = Sets.newHashSet(); final List<Suggestions> suggestions = Lists.newArrayList();
for (final CompletableFuture<Collection<String>> future : futures) { for (final CompletableFuture<Suggestions> future : futures) {
suggestions.addAll(future.join()); suggestions.add(future.join());
} }
final List<String> sorted = new ArrayList<>(suggestions); result.complete(Suggestions.merge(suggestions));
Collections.sort(sorted);
result.complete(new CommandSuggestions(new StringRange(start, parse.getReader().getTotalLength()), sorted));
}); });
return result; return result;
@@ -1,55 +0,0 @@
package com.mojang.brigadier;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.context.StringRange;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
public class CommandSuggestions {
private final StringRange range;
private final List<String> suggestions;
public CommandSuggestions(final StringRange range, final List<String> suggestions) {
this.range = range;
this.suggestions = suggestions;
}
public StringRange getRange() {
return range;
}
public List<String> getSuggestions() {
return suggestions;
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (!(o instanceof CommandSuggestions)) {
return false;
}
final CommandSuggestions that = (CommandSuggestions) o;
return Objects.equals(range, that.range) && Objects.equals(suggestions, that.suggestions);
}
@Override
public int hashCode() {
return Objects.hash(range, suggestions);
}
public boolean isEmpty() {
return suggestions.isEmpty();
}
@FunctionalInterface
public interface Provider<S> {
CompletableFuture<Collection<String>> getSuggestions(final CommandContext<S> context, final String prefix) throws CommandSyntaxException;
}
}
@@ -4,15 +4,15 @@ import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.context.CommandContextBuilder; import com.mojang.brigadier.context.CommandContextBuilder;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
public interface ArgumentType<T> { public interface ArgumentType<T> {
<S> T parse(StringReader reader, CommandContextBuilder<S> contextBuilder) throws CommandSyntaxException; <S> T parse(StringReader reader, CommandContextBuilder<S> contextBuilder) throws CommandSyntaxException;
default <S> CompletableFuture<Collection<String>> listSuggestions(final CommandContext<S> context, final String command) { default <S> CompletableFuture<Suggestions> listSuggestions(final CommandContext<S> context, final SuggestionsBuilder builder) {
return CompletableFuture.completedFuture(Collections.emptyList()); return Suggestions.empty();
} }
} }
@@ -1,13 +1,12 @@
package com.mojang.brigadier.arguments; package com.mojang.brigadier.arguments;
import com.google.common.collect.Lists;
import com.mojang.brigadier.StringReader; import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.context.CommandContextBuilder; import com.mojang.brigadier.context.CommandContextBuilder;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
public class BoolArgumentType implements ArgumentType<Boolean> { public class BoolArgumentType implements ArgumentType<Boolean> {
@@ -28,16 +27,13 @@ public class BoolArgumentType implements ArgumentType<Boolean> {
} }
@Override @Override
public <S> CompletableFuture<Collection<String>> listSuggestions(final CommandContext<S> context, final String command) { public <S> CompletableFuture<Suggestions> listSuggestions(final CommandContext<S> context, final SuggestionsBuilder builder) {
final List<String> result = Lists.newArrayList(); if ("true".startsWith(builder.getRemaining().toLowerCase())) {
builder.suggest("true");
if ("true".startsWith(command)) {
result.add("true");
} }
if ("false".startsWith(command)) { if ("false".startsWith(builder.getRemaining().toLowerCase())) {
result.add("false"); builder.suggest("false");
} }
return builder.buildFuture();
return CompletableFuture.completedFuture(result);
} }
} }
@@ -1,14 +1,14 @@
package com.mojang.brigadier.builder; package com.mojang.brigadier.builder;
import com.mojang.brigadier.CommandSuggestions;
import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.suggestion.SuggestionProvider;
import com.mojang.brigadier.tree.ArgumentCommandNode; import com.mojang.brigadier.tree.ArgumentCommandNode;
import com.mojang.brigadier.tree.CommandNode; import com.mojang.brigadier.tree.CommandNode;
public class RequiredArgumentBuilder<S, T> extends ArgumentBuilder<S, RequiredArgumentBuilder<S, T>> { public class RequiredArgumentBuilder<S, T> extends ArgumentBuilder<S, RequiredArgumentBuilder<S, T>> {
private final String name; private final String name;
private final ArgumentType<T> type; private final ArgumentType<T> type;
private CommandSuggestions.Provider<S> suggestionsProvider = null; private SuggestionProvider<S> suggestionsProvider = null;
private RequiredArgumentBuilder(final String name, final ArgumentType<T> type) { private RequiredArgumentBuilder(final String name, final ArgumentType<T> type) {
this.name = name; this.name = name;
@@ -19,12 +19,12 @@ public class RequiredArgumentBuilder<S, T> extends ArgumentBuilder<S, RequiredAr
return new RequiredArgumentBuilder<>(name, type); return new RequiredArgumentBuilder<>(name, type);
} }
public RequiredArgumentBuilder<S, T> suggests(final CommandSuggestions.Provider<S> provider) { public RequiredArgumentBuilder<S, T> suggests(final SuggestionProvider<S> provider) {
this.suggestionsProvider = provider; this.suggestionsProvider = provider;
return getThis(); return getThis();
} }
public CommandSuggestions.Provider<S> getSuggestionsProvider() { public SuggestionProvider<S> getSuggestionsProvider() {
return suggestionsProvider; return suggestionsProvider;
} }
@@ -0,0 +1,68 @@
package com.mojang.brigadier.suggestion;
import com.mojang.brigadier.context.StringRange;
import java.util.Objects;
public class Suggestion implements Comparable<Suggestion> {
private final StringRange range;
private final String text;
public Suggestion(final StringRange range, final String text) {
this.range = range;
this.text = text;
}
public StringRange getRange() {
return range;
}
public String getText() {
return text;
}
public String apply(final String input) {
if (range.getStart() == 0 && range.getEnd() == input.length()) {
return text;
}
final StringBuilder result = new StringBuilder();
if (range.getStart() > 0) {
result.append(input.substring(0, range.getStart()));
}
result.append(text);
if (range.getEnd() < input.length()) {
result.append(input.substring(range.getEnd()));
}
return result.toString();
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Suggestion)) {
return false;
}
final Suggestion that = (Suggestion) o;
return Objects.equals(range, that.range) && Objects.equals(text, that.text);
}
@Override
public int hashCode() {
return Objects.hash(range, text);
}
@Override
public String toString() {
return "Suggestion{" +
"range=" + range +
", text='" + text + '\'' +
'}';
}
@Override
public int compareTo(final Suggestion o) {
return text.compareTo(o.text);
}
}
@@ -0,0 +1,11 @@
package com.mojang.brigadier.suggestion;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import java.util.concurrent.CompletableFuture;
@FunctionalInterface
public interface SuggestionProvider<S> {
CompletableFuture<Suggestions> getSuggestions(final CommandContext<S> context, final SuggestionsBuilder builder) throws CommandSyntaxException;
}
@@ -0,0 +1,92 @@
package com.mojang.brigadier.suggestion;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.mojang.brigadier.context.StringRange;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
public class Suggestions {
private static final Suggestions EMPTY = new Suggestions("", Lists.newArrayList());
private final String input;
private final StringRange range;
private final List<Suggestion> suggestions;
public Suggestions(final String input, final List<Suggestion> suggestions) {
this.input = input;
this.suggestions = suggestions;
if (suggestions.isEmpty()) {
range = new StringRange(input.length(), input.length());
} else {
int start = Integer.MAX_VALUE;
int end = Integer.MIN_VALUE;
for (final Suggestion suggestion : suggestions) {
start = Math.min(start, suggestion.getRange().getStart());
end = Math.max(end, suggestion.getRange().getEnd());
}
range = new StringRange(start, end);
}
}
public String getInput() {
return input;
}
public StringRange getRange() {
return range;
}
public List<Suggestion> getList() {
return suggestions;
}
public boolean isEmpty() {
return suggestions.isEmpty();
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Suggestions)) {
return false;
}
final Suggestions that = (Suggestions) o;
return Objects.equals(input, that.input) &&
Objects.equals(range, that.range) &&
Objects.equals(suggestions, that.suggestions);
}
@Override
public int hashCode() {
return Objects.hash(input, range, suggestions);
}
public static CompletableFuture<Suggestions> empty() {
return CompletableFuture.completedFuture(EMPTY);
}
public static Suggestions merge(final Collection<Suggestions> inputs) {
if (inputs.isEmpty()) {
return EMPTY;
} else if (inputs.size() == 1) {
return inputs.iterator().next();
}
final Set<Suggestion> suggestions = Sets.newHashSet();
for (final Suggestions input : inputs) {
suggestions.addAll(input.getList());
}
final List<Suggestion> sorted = Lists.newArrayList(suggestions);
Collections.sort(sorted);
return new Suggestions(inputs.iterator().next().getInput(), sorted);
}
}
@@ -0,0 +1,54 @@
package com.mojang.brigadier.suggestion;
import com.google.common.base.Strings;
import com.mojang.brigadier.context.StringRange;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
public class SuggestionsBuilder {
private final String input;
private final int start;
private final String remaining;
private final List<Suggestion> result = new ArrayList<>();
public SuggestionsBuilder(final String input, final int start) {
this.input = input;
this.start = start;
this.remaining = input.substring(start);
}
public String getInput() {
return input;
}
public int getStart() {
return start;
}
public String getRemaining() {
return remaining;
}
public Suggestions build() {
return new Suggestions(input, result);
}
public CompletableFuture<Suggestions> buildFuture() {
return CompletableFuture.completedFuture(new Suggestions(input, result));
}
public SuggestionsBuilder suggest(final String text) {
if (text.equals(remaining)) {
return this;
}
final String prefix = Strings.commonPrefix(text, remaining);
result.add(new Suggestion(new StringRange(start + prefix.length(), input.length()), text.substring(prefix.length())));
return this;
}
public SuggestionsBuilder restart() {
return new SuggestionsBuilder(input, start);
}
}
@@ -1,7 +1,6 @@
package com.mojang.brigadier.tree; package com.mojang.brigadier.tree;
import com.mojang.brigadier.Command; import com.mojang.brigadier.Command;
import com.mojang.brigadier.CommandSuggestions;
import com.mojang.brigadier.RedirectModifier; import com.mojang.brigadier.RedirectModifier;
import com.mojang.brigadier.StringReader; import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.brigadier.arguments.ArgumentType;
@@ -10,8 +9,10 @@ import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.context.CommandContextBuilder; import com.mojang.brigadier.context.CommandContextBuilder;
import com.mojang.brigadier.context.ParsedArgument; import com.mojang.brigadier.context.ParsedArgument;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.suggestion.SuggestionProvider;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import java.util.Collection;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.function.Predicate; import java.util.function.Predicate;
@@ -21,9 +22,9 @@ public class ArgumentCommandNode<S, T> extends CommandNode<S> {
private final String name; private final String name;
private final ArgumentType<T> type; private final ArgumentType<T> type;
private final CommandSuggestions.Provider<S> customSuggestions; private final SuggestionProvider<S> customSuggestions;
public ArgumentCommandNode(final String name, final ArgumentType<T> type, final Command<S> command, final Predicate<S> requirement, final CommandNode<S> redirect, final RedirectModifier<S> modifier, final CommandSuggestions.Provider<S> customSuggestions) { public ArgumentCommandNode(final String name, final ArgumentType<T> type, final Command<S> command, final Predicate<S> requirement, final CommandNode<S> redirect, final RedirectModifier<S> modifier, final SuggestionProvider<S> customSuggestions) {
super(command, requirement, redirect, modifier); super(command, requirement, redirect, modifier);
this.name = name; this.name = name;
this.type = type; this.type = type;
@@ -44,7 +45,7 @@ public class ArgumentCommandNode<S, T> extends CommandNode<S> {
return USAGE_ARGUMENT_OPEN + name + USAGE_ARGUMENT_CLOSE; return USAGE_ARGUMENT_OPEN + name + USAGE_ARGUMENT_CLOSE;
} }
public CommandSuggestions.Provider<S> getCustomSuggestions() { public SuggestionProvider<S> getCustomSuggestions() {
return customSuggestions; return customSuggestions;
} }
@@ -59,11 +60,11 @@ public class ArgumentCommandNode<S, T> extends CommandNode<S> {
} }
@Override @Override
public CompletableFuture<Collection<String>> listSuggestions(final CommandContext<S> context, final String command) throws CommandSyntaxException { public CompletableFuture<Suggestions> listSuggestions(final CommandContext<S> context, final SuggestionsBuilder builder) throws CommandSyntaxException {
if (customSuggestions == null) { if (customSuggestions == null) {
return type.listSuggestions(context, command); return type.listSuggestions(context, builder);
} else { } else {
return customSuggestions.getSuggestions(context, command); return customSuggestions.getSuggestions(context, builder);
} }
} }
@@ -9,6 +9,8 @@ import com.mojang.brigadier.builder.ArgumentBuilder;
import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.context.CommandContextBuilder; import com.mojang.brigadier.context.CommandContextBuilder;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import java.util.Collection; import java.util.Collection;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
@@ -104,7 +106,7 @@ public abstract class CommandNode<S> implements Comparable<CommandNode<S>> {
public abstract void parse(StringReader reader, CommandContextBuilder<S> contextBuilder) throws CommandSyntaxException; public abstract void parse(StringReader reader, CommandContextBuilder<S> contextBuilder) throws CommandSyntaxException;
public abstract CompletableFuture<Collection<String>> listSuggestions(CommandContext<S> context, String command) throws CommandSyntaxException; public abstract CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) throws CommandSyntaxException;
public abstract ArgumentBuilder<S, ?> createBuilder(); public abstract ArgumentBuilder<S, ?> createBuilder();
@@ -9,9 +9,9 @@ import com.mojang.brigadier.context.CommandContextBuilder;
import com.mojang.brigadier.context.StringRange; import com.mojang.brigadier.context.StringRange;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.ParameterizedCommandExceptionType; import com.mojang.brigadier.exceptions.ParameterizedCommandExceptionType;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.function.Predicate; import java.util.function.Predicate;
@@ -50,11 +50,11 @@ public class LiteralCommandNode<S> extends CommandNode<S> {
} }
@Override @Override
public CompletableFuture<Collection<String>> listSuggestions(final CommandContext<S> context, final String command) { public CompletableFuture<Suggestions> listSuggestions(final CommandContext<S> context, final SuggestionsBuilder builder) {
if (literal.toLowerCase().startsWith(command.toLowerCase())) { if (literal.toLowerCase().startsWith(builder.getRemaining().toLowerCase())) {
return CompletableFuture.completedFuture(Collections.singleton(literal)); return builder.suggest(literal).buildFuture();
} else { } else {
return CompletableFuture.completedFuture(Collections.emptyList()); return Suggestions.empty();
} }
} }
@@ -5,8 +5,9 @@ import com.mojang.brigadier.builder.ArgumentBuilder;
import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.context.CommandContextBuilder; import com.mojang.brigadier.context.CommandContextBuilder;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
@@ -30,8 +31,8 @@ public class RootCommandNode<S> extends CommandNode<S> {
} }
@Override @Override
public CompletableFuture<Collection<String>> listSuggestions(CommandContext<S> context, final String command) { public CompletableFuture<Suggestions> listSuggestions(final CommandContext<S> context, final SuggestionsBuilder builder) {
return CompletableFuture.completedFuture(Collections.emptyList()); return Suggestions.empty();
} }
@Override @Override
@@ -2,6 +2,8 @@ package com.mojang.brigadier;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.mojang.brigadier.context.StringRange; import com.mojang.brigadier.context.StringRange;
import com.mojang.brigadier.suggestion.Suggestion;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.tree.LiteralCommandNode; import com.mojang.brigadier.tree.LiteralCommandNode;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -14,6 +16,7 @@ import static com.mojang.brigadier.arguments.StringArgumentType.word;
import static com.mojang.brigadier.builder.LiteralArgumentBuilder.literal; import static com.mojang.brigadier.builder.LiteralArgumentBuilder.literal;
import static com.mojang.brigadier.builder.RequiredArgumentBuilder.argument; import static com.mojang.brigadier.builder.RequiredArgumentBuilder.argument;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
@@ -33,10 +36,14 @@ public class CommandSuggestionsTest {
subject.register(literal("bar")); subject.register(literal("bar"));
subject.register(literal("baz")); subject.register(literal("baz"));
final CommandSuggestions result = subject.getCompletionSuggestions(subject.parse("", source)).join(); final Suggestions result = subject.getCompletionSuggestions(subject.parse("", source)).join();
assertThat(result.getRange(), equalTo(new StringRange(0, 0))); assertThat(result.getRange(), equalTo(new StringRange(0, 0)));
assertThat(result.getSuggestions(), equalTo(Lists.newArrayList("bar", "baz", "foo"))); assertThat(result.getList(), equalTo(Lists.newArrayList(
new Suggestion(new StringRange(0, 0), "bar"),
new Suggestion(new StringRange(0, 0), "baz"),
new Suggestion(new StringRange(0, 0), "foo")
)));
} }
@Test @Test
@@ -45,10 +52,13 @@ public class CommandSuggestionsTest {
subject.register(literal("bar")); subject.register(literal("bar"));
subject.register(literal("baz")); subject.register(literal("baz"));
final CommandSuggestions result = subject.getCompletionSuggestions(subject.parse("b", source)).join(); final Suggestions result = subject.getCompletionSuggestions(subject.parse("b", source)).join();
assertThat(result.getRange(), equalTo(new StringRange(0, 1))); assertThat(result.getRange(), equalTo(new StringRange(1, 1)));
assertThat(result.getSuggestions(), equalTo(Lists.newArrayList("bar", "baz"))); assertThat(result.getList(), equalTo(Lists.newArrayList(
new Suggestion(new StringRange(1, 1), "ar"),
new Suggestion(new StringRange(1, 1), "az")
)));
} }
@Test @Test
@@ -60,10 +70,14 @@ public class CommandSuggestionsTest {
.then(literal("baz")) .then(literal("baz"))
); );
final CommandSuggestions result = subject.getCompletionSuggestions(subject.parse("parent ", source)).join(); final Suggestions result = subject.getCompletionSuggestions(subject.parse("parent ", source)).join();
assertThat(result.getRange(), equalTo(new StringRange(7, 7))); assertThat(result.getRange(), equalTo(new StringRange(7, 7)));
assertThat(result.getSuggestions(), equalTo(Lists.newArrayList("bar", "baz", "foo"))); assertThat(result.getList(), equalTo(Lists.newArrayList(
new Suggestion(new StringRange(7, 7), "bar"),
new Suggestion(new StringRange(7, 7), "baz"),
new Suggestion(new StringRange(7, 7), "foo")
)));
} }
@Test @Test
@@ -76,10 +90,13 @@ public class CommandSuggestionsTest {
); );
final ParseResults<Object> parse = subject.parse("parent b", source); final ParseResults<Object> parse = subject.parse("parent b", source);
final CommandSuggestions result = subject.getCompletionSuggestions(parse).join(); final Suggestions result = subject.getCompletionSuggestions(parse).join();
assertThat(result.getRange(), equalTo(new StringRange(7, 8))); assertThat(result.getRange(), equalTo(new StringRange(8, 8)));
assertThat(result.getSuggestions(), equalTo(Lists.newArrayList("bar", "baz"))); assertThat(result.getList(), equalTo(Lists.newArrayList(
new Suggestion(new StringRange(8, 8), "ar"),
new Suggestion(new StringRange(8, 8), "az")
)));
} }
@Test @Test
@@ -88,10 +105,12 @@ public class CommandSuggestionsTest {
subject.register(literal("redirect").redirect(actual)); subject.register(literal("redirect").redirect(actual));
final ParseResults<Object> parse = subject.parse("redirect ", source); final ParseResults<Object> parse = subject.parse("redirect ", source);
final CommandSuggestions result = subject.getCompletionSuggestions(parse).join(); final Suggestions result = subject.getCompletionSuggestions(parse).join();
assertThat(result.getRange(), equalTo(new StringRange(9, 9))); assertThat(result.getRange(), equalTo(new StringRange(9, 9)));
assertThat(result.getSuggestions(), equalTo(Lists.newArrayList("sub"))); assertThat(result.getList(), equalTo(Lists.newArrayList(
new Suggestion(new StringRange(9, 9), "sub")
)));
} }
@Test @Test
@@ -100,10 +119,12 @@ public class CommandSuggestionsTest {
subject.register(literal("redirect").redirect(actual)); subject.register(literal("redirect").redirect(actual));
final ParseResults<Object> parse = subject.parse("redirect s", source); final ParseResults<Object> parse = subject.parse("redirect s", source);
final CommandSuggestions result = subject.getCompletionSuggestions(parse).join(); final Suggestions result = subject.getCompletionSuggestions(parse).join();
assertThat(result.getRange(), equalTo(new StringRange(9, 10))); assertThat(result.getRange(), equalTo(new StringRange(10, 10)));
assertThat(result.getSuggestions(), equalTo(Lists.newArrayList("sub"))); assertThat(result.getList(), equalTo(Lists.newArrayList(
new Suggestion(new StringRange(10, 10), "ub")
)));
} }
@Test @Test
@@ -120,10 +141,12 @@ public class CommandSuggestionsTest {
) )
); );
final CommandSuggestions result = subject.getCompletionSuggestions(subject.parse("redirect loop 1 loop 02 loop 003 ", source)).join(); final Suggestions result = subject.getCompletionSuggestions(subject.parse("redirect loop 1 loop 02 loop 003 ", source)).join();
assertThat(result.getRange(), equalTo(new StringRange(33, 33))); assertThat(result.getRange(), equalTo(new StringRange(33, 33)));
assertThat(result.getSuggestions(), equalTo(Lists.newArrayList("loop"))); assertThat(result.getList(), equalTo(Lists.newArrayList(
new Suggestion(new StringRange(33, 33), "loop")
)));
} }
@Test @Test
@@ -152,10 +175,9 @@ public class CommandSuggestionsTest {
); );
final ParseResults<Object> parse = subject.parse("execute as Dinnerbone as", source); final ParseResults<Object> parse = subject.parse("execute as Dinnerbone as", source);
final CommandSuggestions result = subject.getCompletionSuggestions(parse).join(); final Suggestions result = subject.getCompletionSuggestions(parse).join();
assertThat(result.getRange(), equalTo(new StringRange(22, 24))); assertThat(result.isEmpty(), is(true));
assertThat(result.getSuggestions(), equalTo(Lists.newArrayList("as")));
} }
@Test @Test
@@ -182,9 +204,12 @@ public class CommandSuggestionsTest {
); );
final ParseResults<Object> parse = subject.parse("execute as bar as ", source); final ParseResults<Object> parse = subject.parse("execute as bar as ", source);
final CommandSuggestions result = subject.getCompletionSuggestions(parse).join(); final Suggestions result = subject.getCompletionSuggestions(parse).join();
assertThat(result.getRange(), equalTo(new StringRange(18, 18))); assertThat(result.getRange(), equalTo(new StringRange(18, 18)));
assertThat(result.getSuggestions(), equalTo(Lists.newArrayList("bar", "baz"))); assertThat(result.getList(), equalTo(Lists.newArrayList(
new Suggestion(new StringRange(18, 18), "bar"),
new Suggestion(new StringRange(18, 18), "baz")
)));
} }
} }
@@ -0,0 +1,51 @@
package com.mojang.brigadier.suggestion;
import com.mojang.brigadier.context.StringRange;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;
public class SuggestionTest {
@Test
public void apply_insertation_start() {
final Suggestion suggestion = new Suggestion(new StringRange(0, 0), "And so I said: ");
assertThat(suggestion.apply("Hello world!"), equalTo("And so I said: Hello world!"));
}
@Test
public void apply_insertation_middle() {
final Suggestion suggestion = new Suggestion(new StringRange(6, 6), "small ");
assertThat(suggestion.apply("Hello world!"), equalTo("Hello small world!"));
}
@Test
public void apply_insertation_end() {
final Suggestion suggestion = new Suggestion(new StringRange(5, 5), " world!");
assertThat(suggestion.apply("Hello"), equalTo("Hello world!"));
}
@Test
public void apply_replacement_start() {
final Suggestion suggestion = new Suggestion(new StringRange(0, 5), "Goodbye");
assertThat(suggestion.apply("Hello world!"), equalTo("Goodbye world!"));
}
@Test
public void apply_replacement_middle() {
final Suggestion suggestion = new Suggestion(new StringRange(6, 11), "Alex");
assertThat(suggestion.apply("Hello world!"), equalTo("Hello Alex!"));
}
@Test
public void apply_replacement_end() {
final Suggestion suggestion = new Suggestion(new StringRange(6, 12), "Creeper!");
assertThat(suggestion.apply("Hello world!"), equalTo("Hello Creeper!"));
}
@Test
public void apply_replacement_everything() {
final Suggestion suggestion = new Suggestion(new StringRange(0, 12), "Oh dear.");
assertThat(suggestion.apply("Hello world!"), equalTo("Oh dear."));
}
}
@@ -0,0 +1,62 @@
package com.mojang.brigadier.suggestion;
import com.google.common.collect.Lists;
import com.mojang.brigadier.context.StringRange;
import org.junit.Before;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertThat;
public class SuggestionsBuilderTest {
private SuggestionsBuilder builder;
@Before
public void setUp() throws Exception {
builder = new SuggestionsBuilder("Hello w", 6);
}
@Test
public void suggest_appends() {
final Suggestions result = builder.suggest("world!").build();
assertThat(result.getList(), equalTo(Lists.newArrayList(new Suggestion(new StringRange(7, 7), "orld!"))));
assertThat(result.getRange(), equalTo(new StringRange(7, 7)));
assertThat(result.isEmpty(), is(false));
}
@Test
public void suggest_replaces() {
final Suggestions result = builder.suggest("everybody").build();
assertThat(result.getList(), equalTo(Lists.newArrayList(new Suggestion(new StringRange(6, 7), "everybody"))));
assertThat(result.getRange(), equalTo(new StringRange(6, 7)));
assertThat(result.isEmpty(), is(false));
}
@Test
public void suggest_noop() {
final Suggestions result = builder.suggest("w").build();
assertThat(result.getList(), equalTo(Lists.newArrayList()));
assertThat(result.getRange(), equalTo(new StringRange(7, 7)));
assertThat(result.isEmpty(), is(true));
}
@Test
public void suggest_multiple() {
final Suggestions result = builder.suggest("world!").suggest("everybody").suggest("weekend").build();
assertThat(result.getList(), equalTo(Lists.newArrayList(new Suggestion(new StringRange(7, 7), "orld!"), new Suggestion(new StringRange(6, 7), "everybody"), new Suggestion(new StringRange(7, 7), "eekend"))));
assertThat(result.getRange(), equalTo(new StringRange(6, 7)));
assertThat(result.isEmpty(), is(false));
}
@Test
public void restart() {
builder.suggest("won't be included in restart");
final SuggestionsBuilder other = builder.restart();
assertThat(other, is(not(builder)));
assertThat(other.getInput(), equalTo(builder.getInput()));
assertThat(other.getStart(), is(builder.getStart()));
assertThat(other.getRemaining(), equalTo(builder.getRemaining()));
}
}
@@ -0,0 +1,38 @@
package com.mojang.brigadier.suggestion;
import com.google.common.collect.Lists;
import com.mojang.brigadier.context.StringRange;
import org.junit.Test;
import java.util.Collections;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
public class SuggestionsTest {
@Test
public void merge_empty() {
final Suggestions merged = Suggestions.merge(Collections.emptyList());
assertThat(merged.isEmpty(), is(true));
}
@Test
public void merge_single() {
final Suggestions suggestions = new Suggestions("", Lists.newArrayList(new Suggestion(new StringRange(0, 0), "foo")));
final Suggestions merged = Suggestions.merge(Collections.singleton(suggestions));
assertThat(merged, equalTo(suggestions));
}
@Test
public void merge_multiple() {
final Suggestion foo = new Suggestion(new StringRange(0, 0), "foo");
final Suggestion bar = new Suggestion(new StringRange(0, 0), "bar");
final Suggestion baz = new Suggestion(new StringRange(0, 0), "baz");
final Suggestion qux = new Suggestion(new StringRange(0, 0), "qux");
final Suggestions a = new Suggestions("", Lists.newArrayList(foo, bar));
final Suggestions b = new Suggestions("", Lists.newArrayList(baz, qux));
final Suggestions merged = Suggestions.merge(Lists.newArrayList(a, b));
assertThat(merged.getList(), equalTo(Lists.newArrayList(bar, baz, foo, qux)));
}
}
@@ -5,16 +5,14 @@ import com.mojang.brigadier.Command;
import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.StringReader; import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.builder.RequiredArgumentBuilder; import com.mojang.brigadier.builder.RequiredArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.context.CommandContextBuilder; import com.mojang.brigadier.context.CommandContextBuilder;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import java.util.Collection;
import static com.mojang.brigadier.arguments.IntegerArgumentType.integer; import static com.mojang.brigadier.arguments.IntegerArgumentType.integer;
import static com.mojang.brigadier.builder.RequiredArgumentBuilder.argument; import static com.mojang.brigadier.builder.RequiredArgumentBuilder.argument;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
@@ -50,8 +48,8 @@ public class ArgumentCommandNodeTest extends AbstractCommandNodeTest {
@Test @Test
public void testSuggestions() throws Exception { public void testSuggestions() throws Exception {
final Collection<String> result = node.listSuggestions(contextBuilder.build(""), "").join(); final Suggestions result = node.listSuggestions(contextBuilder.build(""), new SuggestionsBuilder("", 0)).join();
assertThat(result, is(empty())); assertThat(result.isEmpty(), is(true));
} }
@Test @Test
@@ -1,21 +1,22 @@
package com.mojang.brigadier.tree; package com.mojang.brigadier.tree;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets; import com.google.common.collect.Lists;
import com.google.common.testing.EqualsTester; import com.google.common.testing.EqualsTester;
import com.mojang.brigadier.Command; import com.mojang.brigadier.Command;
import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.StringReader; import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContextBuilder; import com.mojang.brigadier.context.CommandContextBuilder;
import com.mojang.brigadier.context.StringRange;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.suggestion.Suggestion;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import java.util.Collection;
import static com.mojang.brigadier.builder.LiteralArgumentBuilder.literal; import static com.mojang.brigadier.builder.LiteralArgumentBuilder.literal;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
@@ -79,17 +80,17 @@ public class LiteralCommandNodeTest extends AbstractCommandNodeTest {
@Test @Test
public void testSuggestions() throws Exception { public void testSuggestions() throws Exception {
final Collection<String> empty = node.listSuggestions(contextBuilder.build(""), "").join(); final Suggestions empty = node.listSuggestions(contextBuilder.build(""), new SuggestionsBuilder("", 0)).join();
assertThat(empty, equalTo(Sets.newHashSet("foo"))); assertThat(empty.getList(), equalTo(Lists.newArrayList(new Suggestion(new StringRange(0, 0), "foo"))));
final Collection<String> foo = node.listSuggestions(contextBuilder.build("foo"), "foo").join(); final Suggestions foo = node.listSuggestions(contextBuilder.build("foo"), new SuggestionsBuilder("foo", 0)).join();
assertThat(foo, equalTo(Sets.newHashSet("foo"))); assertThat(foo.isEmpty(), is(true));
final Collection<String> food = node.listSuggestions(contextBuilder.build("food"), "food").join(); final Suggestions food = node.listSuggestions(contextBuilder.build("food"), new SuggestionsBuilder("food", 0)).join();
assertThat(food, is(empty())); assertThat(food.isEmpty(), is(true));
final Collection<String> b = node.listSuggestions(contextBuilder.build("b"), "b").join(); final Suggestions b = node.listSuggestions(contextBuilder.build("b"), new SuggestionsBuilder("b", 0)).join();
assertThat(b, is(empty())); assertThat(food.isEmpty(), is(true));
} }
@Test @Test
@@ -5,13 +5,12 @@ import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.StringReader; import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.context.CommandContextBuilder; import com.mojang.brigadier.context.CommandContextBuilder;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import java.util.Collection;
import static com.mojang.brigadier.builder.LiteralArgumentBuilder.literal; import static com.mojang.brigadier.builder.LiteralArgumentBuilder.literal;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
@@ -49,8 +48,8 @@ public class RootCommandNodeTest extends AbstractCommandNodeTest {
@Test @Test
public void testSuggestions() throws Exception { public void testSuggestions() throws Exception {
final CommandContext<Object> context = mock(CommandContext.class); final CommandContext<Object> context = mock(CommandContext.class);
final Collection<String> result = node.listSuggestions(context, "").join(); final Suggestions result = node.listSuggestions(context, new SuggestionsBuilder("", 0)).join();
assertThat(result, is(empty())); assertThat(result.isEmpty(), is(true));
} }
@Test(expected = IllegalStateException.class) @Test(expected = IllegalStateException.class)