From 095498c94e402bd4a432225fc9825813adf86e56 Mon Sep 17 00:00:00 2001 From: Nathan Adams Date: Mon, 20 Nov 2017 13:45:05 +0100 Subject: [PATCH] New suggestion API --- build.gradle | 2 +- .../mojang/brigadier/CommandDispatcher.java | 22 ++--- .../mojang/brigadier/CommandSuggestions.java | 55 ----------- .../brigadier/arguments/ArgumentType.java | 8 +- .../brigadier/arguments/BoolArgumentType.java | 20 ++-- .../builder/RequiredArgumentBuilder.java | 8 +- .../brigadier/suggestion/Suggestion.java | 68 ++++++++++++++ .../suggestion/SuggestionProvider.java | 11 +++ .../brigadier/suggestion/Suggestions.java | 92 +++++++++++++++++++ .../suggestion/SuggestionsBuilder.java | 54 +++++++++++ .../brigadier/tree/ArgumentCommandNode.java | 17 ++-- .../mojang/brigadier/tree/CommandNode.java | 4 +- .../brigadier/tree/LiteralCommandNode.java | 12 +-- .../brigadier/tree/RootCommandNode.java | 7 +- .../brigadier/CommandSuggestionsTest.java | 69 +++++++++----- .../brigadier/suggestion/SuggestionTest.java | 51 ++++++++++ .../suggestion/SuggestionsBuilderTest.java | 62 +++++++++++++ .../brigadier/suggestion/SuggestionsTest.java | 38 ++++++++ .../tree/ArgumentCommandNodeTest.java | 10 +- .../tree/LiteralCommandNodeTest.java | 25 ++--- .../brigadier/tree/RootCommandNodeTest.java | 9 +- 21 files changed, 494 insertions(+), 150 deletions(-) delete mode 100644 src/main/java/com/mojang/brigadier/CommandSuggestions.java create mode 100644 src/main/java/com/mojang/brigadier/suggestion/Suggestion.java create mode 100644 src/main/java/com/mojang/brigadier/suggestion/SuggestionProvider.java create mode 100644 src/main/java/com/mojang/brigadier/suggestion/Suggestions.java create mode 100644 src/main/java/com/mojang/brigadier/suggestion/SuggestionsBuilder.java create mode 100644 src/test/java/com/mojang/brigadier/suggestion/SuggestionTest.java create mode 100644 src/test/java/com/mojang/brigadier/suggestion/SuggestionsBuilderTest.java create mode 100644 src/test/java/com/mojang/brigadier/suggestion/SuggestionsTest.java diff --git a/build.gradle b/build.gradle index b6917b36..2a9aa2a2 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ import groovy.io.FileType apply plugin: 'java-library' apply plugin: 'maven' -version = '0.1.11' +version = '0.1.13' group = 'com.mojang' task wrapper(type: Wrapper) { diff --git a/src/main/java/com/mojang/brigadier/CommandDispatcher.java b/src/main/java/com/mojang/brigadier/CommandDispatcher.java index 1630525c..f43f9497 100644 --- a/src/main/java/com/mojang/brigadier/CommandDispatcher.java +++ b/src/main/java/com/mojang/brigadier/CommandDispatcher.java @@ -11,6 +11,8 @@ import com.mojang.brigadier.context.StringRange; import com.mojang.brigadier.exceptions.CommandSyntaxException; 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.LiteralCommandNode; import com.mojang.brigadier.tree.RootCommandNode; @@ -310,7 +312,7 @@ private String getSmartUsage(final CommandNode node, final S source, final bo return self; } - public CompletableFuture getCompletionSuggestions(final ParseResults parse) { + public CompletableFuture getCompletionSuggestions(final ParseResults parse) { final CommandContextBuilder rootContext = parse.getContext(); final CommandContextBuilder context = rootContext.getLastChild(); final CommandNode parent; @@ -336,25 +338,23 @@ public CompletableFuture getCompletionSuggestions(final Pars start = 0; } - @SuppressWarnings("unchecked") final CompletableFuture>[] futures = new CompletableFuture[parent.getChildren().size()]; + @SuppressWarnings("unchecked") final CompletableFuture[] futures = new CompletableFuture[parent.getChildren().size()]; int i = 0; for (final CommandNode node : parent.getChildren()) { 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) { - futures[i++] = CompletableFuture.completedFuture(Collections.emptyList()); + futures[i++] = Suggestions.empty(); } } - final CompletableFuture result = new CompletableFuture<>(); + final CompletableFuture result = new CompletableFuture<>(); CompletableFuture.allOf(futures).thenRun(() -> { - final Set suggestions = Sets.newHashSet(); - for (final CompletableFuture> future : futures) { - suggestions.addAll(future.join()); + final List suggestions = Lists.newArrayList(); + for (final CompletableFuture future : futures) { + suggestions.add(future.join()); } - final List sorted = new ArrayList<>(suggestions); - Collections.sort(sorted); - result.complete(new CommandSuggestions(new StringRange(start, parse.getReader().getTotalLength()), sorted)); + result.complete(Suggestions.merge(suggestions)); }); return result; diff --git a/src/main/java/com/mojang/brigadier/CommandSuggestions.java b/src/main/java/com/mojang/brigadier/CommandSuggestions.java deleted file mode 100644 index 6bde3ec7..00000000 --- a/src/main/java/com/mojang/brigadier/CommandSuggestions.java +++ /dev/null @@ -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 suggestions; - - public CommandSuggestions(final StringRange range, final List suggestions) { - this.range = range; - this.suggestions = suggestions; - } - - public StringRange getRange() { - return range; - } - - public List 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 { - CompletableFuture> getSuggestions(final CommandContext context, final String prefix) throws CommandSyntaxException; - } -} diff --git a/src/main/java/com/mojang/brigadier/arguments/ArgumentType.java b/src/main/java/com/mojang/brigadier/arguments/ArgumentType.java index 8780390d..7798d106 100644 --- a/src/main/java/com/mojang/brigadier/arguments/ArgumentType.java +++ b/src/main/java/com/mojang/brigadier/arguments/ArgumentType.java @@ -4,15 +4,15 @@ 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; -import java.util.Collection; -import java.util.Collections; import java.util.concurrent.CompletableFuture; public interface ArgumentType { T parse(StringReader reader, CommandContextBuilder contextBuilder) throws CommandSyntaxException; - default CompletableFuture> listSuggestions(final CommandContext context, final String command) { - return CompletableFuture.completedFuture(Collections.emptyList()); + default CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { + return Suggestions.empty(); } } diff --git a/src/main/java/com/mojang/brigadier/arguments/BoolArgumentType.java b/src/main/java/com/mojang/brigadier/arguments/BoolArgumentType.java index fbc59a06..2181c5b1 100644 --- a/src/main/java/com/mojang/brigadier/arguments/BoolArgumentType.java +++ b/src/main/java/com/mojang/brigadier/arguments/BoolArgumentType.java @@ -1,13 +1,12 @@ package com.mojang.brigadier.arguments; -import com.google.common.collect.Lists; 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; -import java.util.Collection; -import java.util.List; import java.util.concurrent.CompletableFuture; public class BoolArgumentType implements ArgumentType { @@ -28,16 +27,13 @@ public Boolean parse(final StringReader reader, final CommandContextBuilder< } @Override - public CompletableFuture> listSuggestions(final CommandContext context, final String command) { - final List result = Lists.newArrayList(); - - if ("true".startsWith(command)) { - result.add("true"); + public CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { + if ("true".startsWith(builder.getRemaining().toLowerCase())) { + builder.suggest("true"); } - if ("false".startsWith(command)) { - result.add("false"); + if ("false".startsWith(builder.getRemaining().toLowerCase())) { + builder.suggest("false"); } - - return CompletableFuture.completedFuture(result); + return builder.buildFuture(); } } diff --git a/src/main/java/com/mojang/brigadier/builder/RequiredArgumentBuilder.java b/src/main/java/com/mojang/brigadier/builder/RequiredArgumentBuilder.java index 6e619d8b..45125a17 100644 --- a/src/main/java/com/mojang/brigadier/builder/RequiredArgumentBuilder.java +++ b/src/main/java/com/mojang/brigadier/builder/RequiredArgumentBuilder.java @@ -1,14 +1,14 @@ package com.mojang.brigadier.builder; -import com.mojang.brigadier.CommandSuggestions; import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.suggestion.SuggestionProvider; import com.mojang.brigadier.tree.ArgumentCommandNode; import com.mojang.brigadier.tree.CommandNode; public class RequiredArgumentBuilder extends ArgumentBuilder> { private final String name; private final ArgumentType type; - private CommandSuggestions.Provider suggestionsProvider = null; + private SuggestionProvider suggestionsProvider = null; private RequiredArgumentBuilder(final String name, final ArgumentType type) { this.name = name; @@ -19,12 +19,12 @@ public static RequiredArgumentBuilder argument(final String name, f return new RequiredArgumentBuilder<>(name, type); } - public RequiredArgumentBuilder suggests(final CommandSuggestions.Provider provider) { + public RequiredArgumentBuilder suggests(final SuggestionProvider provider) { this.suggestionsProvider = provider; return getThis(); } - public CommandSuggestions.Provider getSuggestionsProvider() { + public SuggestionProvider getSuggestionsProvider() { return suggestionsProvider; } diff --git a/src/main/java/com/mojang/brigadier/suggestion/Suggestion.java b/src/main/java/com/mojang/brigadier/suggestion/Suggestion.java new file mode 100644 index 00000000..39887b77 --- /dev/null +++ b/src/main/java/com/mojang/brigadier/suggestion/Suggestion.java @@ -0,0 +1,68 @@ +package com.mojang.brigadier.suggestion; + +import com.mojang.brigadier.context.StringRange; + +import java.util.Objects; + +public class Suggestion implements Comparable { + 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); + } +} diff --git a/src/main/java/com/mojang/brigadier/suggestion/SuggestionProvider.java b/src/main/java/com/mojang/brigadier/suggestion/SuggestionProvider.java new file mode 100644 index 00000000..8f556103 --- /dev/null +++ b/src/main/java/com/mojang/brigadier/suggestion/SuggestionProvider.java @@ -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 { + CompletableFuture getSuggestions(final CommandContext context, final SuggestionsBuilder builder) throws CommandSyntaxException; +} diff --git a/src/main/java/com/mojang/brigadier/suggestion/Suggestions.java b/src/main/java/com/mojang/brigadier/suggestion/Suggestions.java new file mode 100644 index 00000000..4c9de7b8 --- /dev/null +++ b/src/main/java/com/mojang/brigadier/suggestion/Suggestions.java @@ -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 suggestions; + + public Suggestions(final String input, final List 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 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 empty() { + return CompletableFuture.completedFuture(EMPTY); + } + + public static Suggestions merge(final Collection inputs) { + if (inputs.isEmpty()) { + return EMPTY; + } else if (inputs.size() == 1) { + return inputs.iterator().next(); + } + + final Set suggestions = Sets.newHashSet(); + for (final Suggestions input : inputs) { + suggestions.addAll(input.getList()); + } + final List sorted = Lists.newArrayList(suggestions); + Collections.sort(sorted); + return new Suggestions(inputs.iterator().next().getInput(), sorted); + } +} diff --git a/src/main/java/com/mojang/brigadier/suggestion/SuggestionsBuilder.java b/src/main/java/com/mojang/brigadier/suggestion/SuggestionsBuilder.java new file mode 100644 index 00000000..becc7cb1 --- /dev/null +++ b/src/main/java/com/mojang/brigadier/suggestion/SuggestionsBuilder.java @@ -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 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 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); + } +} diff --git a/src/main/java/com/mojang/brigadier/tree/ArgumentCommandNode.java b/src/main/java/com/mojang/brigadier/tree/ArgumentCommandNode.java index 9399346b..f89c8ebf 100644 --- a/src/main/java/com/mojang/brigadier/tree/ArgumentCommandNode.java +++ b/src/main/java/com/mojang/brigadier/tree/ArgumentCommandNode.java @@ -1,7 +1,6 @@ package com.mojang.brigadier.tree; import com.mojang.brigadier.Command; -import com.mojang.brigadier.CommandSuggestions; import com.mojang.brigadier.RedirectModifier; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.arguments.ArgumentType; @@ -10,8 +9,10 @@ import com.mojang.brigadier.context.CommandContextBuilder; import com.mojang.brigadier.context.ParsedArgument; 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.function.Predicate; @@ -21,9 +22,9 @@ public class ArgumentCommandNode extends CommandNode { private final String name; private final ArgumentType type; - private final CommandSuggestions.Provider customSuggestions; + private final SuggestionProvider customSuggestions; - public ArgumentCommandNode(final String name, final ArgumentType type, final Command command, final Predicate requirement, final CommandNode redirect, final RedirectModifier modifier, final CommandSuggestions.Provider customSuggestions) { + public ArgumentCommandNode(final String name, final ArgumentType type, final Command command, final Predicate requirement, final CommandNode redirect, final RedirectModifier modifier, final SuggestionProvider customSuggestions) { super(command, requirement, redirect, modifier); this.name = name; this.type = type; @@ -44,7 +45,7 @@ public String getUsageText() { return USAGE_ARGUMENT_OPEN + name + USAGE_ARGUMENT_CLOSE; } - public CommandSuggestions.Provider getCustomSuggestions() { + public SuggestionProvider getCustomSuggestions() { return customSuggestions; } @@ -59,11 +60,11 @@ public void parse(final StringReader reader, final CommandContextBuilder cont } @Override - public CompletableFuture> listSuggestions(final CommandContext context, final String command) throws CommandSyntaxException { + public CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) throws CommandSyntaxException { if (customSuggestions == null) { - return type.listSuggestions(context, command); + return type.listSuggestions(context, builder); } else { - return customSuggestions.getSuggestions(context, command); + return customSuggestions.getSuggestions(context, builder); } } diff --git a/src/main/java/com/mojang/brigadier/tree/CommandNode.java b/src/main/java/com/mojang/brigadier/tree/CommandNode.java index 8c0b76e7..c4eef81e 100644 --- a/src/main/java/com/mojang/brigadier/tree/CommandNode.java +++ b/src/main/java/com/mojang/brigadier/tree/CommandNode.java @@ -9,6 +9,8 @@ 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; import java.util.Collection; import java.util.LinkedHashMap; @@ -104,7 +106,7 @@ public Predicate getRequirement() { public abstract void parse(StringReader reader, CommandContextBuilder contextBuilder) throws CommandSyntaxException; - public abstract CompletableFuture> listSuggestions(CommandContext context, String command) throws CommandSyntaxException; + public abstract CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) throws CommandSyntaxException; public abstract ArgumentBuilder createBuilder(); diff --git a/src/main/java/com/mojang/brigadier/tree/LiteralCommandNode.java b/src/main/java/com/mojang/brigadier/tree/LiteralCommandNode.java index 2a660c9b..2e641d79 100644 --- a/src/main/java/com/mojang/brigadier/tree/LiteralCommandNode.java +++ b/src/main/java/com/mojang/brigadier/tree/LiteralCommandNode.java @@ -9,9 +9,9 @@ import com.mojang.brigadier.context.StringRange; import com.mojang.brigadier.exceptions.CommandSyntaxException; 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.function.Predicate; @@ -50,11 +50,11 @@ public void parse(final StringReader reader, final CommandContextBuilder cont } @Override - public CompletableFuture> listSuggestions(final CommandContext context, final String command) { - if (literal.toLowerCase().startsWith(command.toLowerCase())) { - return CompletableFuture.completedFuture(Collections.singleton(literal)); + public CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { + if (literal.toLowerCase().startsWith(builder.getRemaining().toLowerCase())) { + return builder.suggest(literal).buildFuture(); } else { - return CompletableFuture.completedFuture(Collections.emptyList()); + return Suggestions.empty(); } } diff --git a/src/main/java/com/mojang/brigadier/tree/RootCommandNode.java b/src/main/java/com/mojang/brigadier/tree/RootCommandNode.java index a21c5ab6..848bf74a 100644 --- a/src/main/java/com/mojang/brigadier/tree/RootCommandNode.java +++ b/src/main/java/com/mojang/brigadier/tree/RootCommandNode.java @@ -5,8 +5,9 @@ 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; -import java.util.Collection; import java.util.Collections; import java.util.concurrent.CompletableFuture; @@ -30,8 +31,8 @@ public void parse(final StringReader reader, final CommandContextBuilder cont } @Override - public CompletableFuture> listSuggestions(CommandContext context, final String command) { - return CompletableFuture.completedFuture(Collections.emptyList()); + public CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { + return Suggestions.empty(); } @Override diff --git a/src/test/java/com/mojang/brigadier/CommandSuggestionsTest.java b/src/test/java/com/mojang/brigadier/CommandSuggestionsTest.java index 08238676..6730c790 100644 --- a/src/test/java/com/mojang/brigadier/CommandSuggestionsTest.java +++ b/src/test/java/com/mojang/brigadier/CommandSuggestionsTest.java @@ -2,6 +2,8 @@ import com.google.common.collect.Lists; 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 org.junit.Before; import org.junit.Test; @@ -14,6 +16,7 @@ import static com.mojang.brigadier.builder.LiteralArgumentBuilder.literal; import static com.mojang.brigadier.builder.RequiredArgumentBuilder.argument; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; @RunWith(MockitoJUnitRunner.class) @@ -33,10 +36,14 @@ public void getCompletionSuggestions_rootCommands() throws Exception { subject.register(literal("bar")); 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.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 @@ -45,10 +52,13 @@ public void getCompletionSuggestions_rootCommands_partial() throws Exception { subject.register(literal("bar")); 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.getSuggestions(), equalTo(Lists.newArrayList("bar", "baz"))); + assertThat(result.getRange(), equalTo(new StringRange(1, 1))); + assertThat(result.getList(), equalTo(Lists.newArrayList( + new Suggestion(new StringRange(1, 1), "ar"), + new Suggestion(new StringRange(1, 1), "az") + ))); } @Test @@ -60,10 +70,14 @@ public void getCompletionSuggestions_subCommands() throws Exception { .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.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 @@ -76,10 +90,13 @@ public void getCompletionSuggestions_subCommands_partial() throws Exception { ); final ParseResults 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.getSuggestions(), equalTo(Lists.newArrayList("bar", "baz"))); + assertThat(result.getRange(), equalTo(new StringRange(8, 8))); + assertThat(result.getList(), equalTo(Lists.newArrayList( + new Suggestion(new StringRange(8, 8), "ar"), + new Suggestion(new StringRange(8, 8), "az") + ))); } @Test @@ -88,10 +105,12 @@ public void getCompletionSuggestions_redirect() throws Exception { subject.register(literal("redirect").redirect(actual)); final ParseResults 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.getSuggestions(), equalTo(Lists.newArrayList("sub"))); + assertThat(result.getList(), equalTo(Lists.newArrayList( + new Suggestion(new StringRange(9, 9), "sub") + ))); } @Test @@ -100,10 +119,12 @@ public void getCompletionSuggestions_redirectPartial() throws Exception { subject.register(literal("redirect").redirect(actual)); final ParseResults 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.getSuggestions(), equalTo(Lists.newArrayList("sub"))); + assertThat(result.getRange(), equalTo(new StringRange(10, 10))); + assertThat(result.getList(), equalTo(Lists.newArrayList( + new Suggestion(new StringRange(10, 10), "ub") + ))); } @Test @@ -120,10 +141,12 @@ public void getCompletionSuggestions_redirect_lots() throws Exception { ) ); - 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.getSuggestions(), equalTo(Lists.newArrayList("loop"))); + assertThat(result.getList(), equalTo(Lists.newArrayList( + new Suggestion(new StringRange(33, 33), "loop") + ))); } @Test @@ -152,10 +175,9 @@ public void getCompletionSuggestions_execute_simulation() throws Exception { ); final ParseResults 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.getSuggestions(), equalTo(Lists.newArrayList("as"))); + assertThat(result.isEmpty(), is(true)); } @Test @@ -182,9 +204,12 @@ public void getCompletionSuggestions_execute_simulation_partial() throws Excepti ); final ParseResults 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.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") + ))); } } \ No newline at end of file diff --git a/src/test/java/com/mojang/brigadier/suggestion/SuggestionTest.java b/src/test/java/com/mojang/brigadier/suggestion/SuggestionTest.java new file mode 100644 index 00000000..fd2ba076 --- /dev/null +++ b/src/test/java/com/mojang/brigadier/suggestion/SuggestionTest.java @@ -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.")); + } +} \ No newline at end of file diff --git a/src/test/java/com/mojang/brigadier/suggestion/SuggestionsBuilderTest.java b/src/test/java/com/mojang/brigadier/suggestion/SuggestionsBuilderTest.java new file mode 100644 index 00000000..9b0c424b --- /dev/null +++ b/src/test/java/com/mojang/brigadier/suggestion/SuggestionsBuilderTest.java @@ -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())); + } +} \ No newline at end of file diff --git a/src/test/java/com/mojang/brigadier/suggestion/SuggestionsTest.java b/src/test/java/com/mojang/brigadier/suggestion/SuggestionsTest.java new file mode 100644 index 00000000..d376439f --- /dev/null +++ b/src/test/java/com/mojang/brigadier/suggestion/SuggestionsTest.java @@ -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))); + } +} \ No newline at end of file diff --git a/src/test/java/com/mojang/brigadier/tree/ArgumentCommandNodeTest.java b/src/test/java/com/mojang/brigadier/tree/ArgumentCommandNodeTest.java index 92ecf583..3d5d9a29 100644 --- a/src/test/java/com/mojang/brigadier/tree/ArgumentCommandNodeTest.java +++ b/src/test/java/com/mojang/brigadier/tree/ArgumentCommandNodeTest.java @@ -5,16 +5,14 @@ import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.builder.RequiredArgumentBuilder; -import com.mojang.brigadier.context.CommandContext; 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.Test; -import java.util.Collection; - import static com.mojang.brigadier.arguments.IntegerArgumentType.integer; import static com.mojang.brigadier.builder.RequiredArgumentBuilder.argument; -import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; @@ -50,8 +48,8 @@ public void testUsage() throws Exception { @Test public void testSuggestions() throws Exception { - final Collection result = node.listSuggestions(contextBuilder.build(""), "").join(); - assertThat(result, is(empty())); + final Suggestions result = node.listSuggestions(contextBuilder.build(""), new SuggestionsBuilder("", 0)).join(); + assertThat(result.isEmpty(), is(true)); } @Test diff --git a/src/test/java/com/mojang/brigadier/tree/LiteralCommandNodeTest.java b/src/test/java/com/mojang/brigadier/tree/LiteralCommandNodeTest.java index fe12b000..0e5c246c 100644 --- a/src/test/java/com/mojang/brigadier/tree/LiteralCommandNodeTest.java +++ b/src/test/java/com/mojang/brigadier/tree/LiteralCommandNodeTest.java @@ -1,21 +1,22 @@ package com.mojang.brigadier.tree; 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.mojang.brigadier.Command; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContextBuilder; +import com.mojang.brigadier.context.StringRange; 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.Test; -import java.util.Collection; - 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.is; import static org.junit.Assert.assertThat; @@ -79,17 +80,17 @@ public void testUsage() throws Exception { @Test public void testSuggestions() throws Exception { - final Collection empty = node.listSuggestions(contextBuilder.build(""), "").join(); - assertThat(empty, equalTo(Sets.newHashSet("foo"))); + final Suggestions empty = node.listSuggestions(contextBuilder.build(""), new SuggestionsBuilder("", 0)).join(); + assertThat(empty.getList(), equalTo(Lists.newArrayList(new Suggestion(new StringRange(0, 0), "foo")))); - final Collection foo = node.listSuggestions(contextBuilder.build("foo"), "foo").join(); - assertThat(foo, equalTo(Sets.newHashSet("foo"))); + final Suggestions foo = node.listSuggestions(contextBuilder.build("foo"), new SuggestionsBuilder("foo", 0)).join(); + assertThat(foo.isEmpty(), is(true)); - final Collection food = node.listSuggestions(contextBuilder.build("food"), "food").join(); - assertThat(food, is(empty())); + final Suggestions food = node.listSuggestions(contextBuilder.build("food"), new SuggestionsBuilder("food", 0)).join(); + assertThat(food.isEmpty(), is(true)); - final Collection b = node.listSuggestions(contextBuilder.build("b"), "b").join(); - assertThat(b, is(empty())); + final Suggestions b = node.listSuggestions(contextBuilder.build("b"), new SuggestionsBuilder("b", 0)).join(); + assertThat(food.isEmpty(), is(true)); } @Test diff --git a/src/test/java/com/mojang/brigadier/tree/RootCommandNodeTest.java b/src/test/java/com/mojang/brigadier/tree/RootCommandNodeTest.java index a49f00ba..0fde7e8f 100644 --- a/src/test/java/com/mojang/brigadier/tree/RootCommandNodeTest.java +++ b/src/test/java/com/mojang/brigadier/tree/RootCommandNodeTest.java @@ -5,13 +5,12 @@ import com.mojang.brigadier.StringReader; import com.mojang.brigadier.context.CommandContext; 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.Test; -import java.util.Collection; - import static com.mojang.brigadier.builder.LiteralArgumentBuilder.literal; -import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; @@ -49,8 +48,8 @@ public void testUsage() throws Exception { @Test public void testSuggestions() throws Exception { final CommandContext context = mock(CommandContext.class); - final Collection result = node.listSuggestions(context, "").join(); - assertThat(result, is(empty())); + final Suggestions result = node.listSuggestions(context, new SuggestionsBuilder("", 0)).join(); + assertThat(result.isEmpty(), is(true)); } @Test(expected = IllegalStateException.class)