From 75dcc360669a5c799aeb13614ad8a349be3d5d6e Mon Sep 17 00:00:00 2001 From: boq Date: Mon, 16 Jul 2018 16:21:03 +0200 Subject: [PATCH] Pass StringReader instead of strings to CommandDispatcher --- .../mojang/brigadier/CommandDispatcher.java | 21 ++++--- .../com/mojang/brigadier/ParseResults.java | 10 +++- .../brigadier/tree/ArgumentCommandNode.java | 5 ++ .../brigadier/tree/LiteralCommandNode.java | 5 ++ .../brigadier/tree/RootCommandNode.java | 5 ++ .../brigadier/CommandDispatcherTest.java | 15 +++++ .../CommandDispatcherUsagesTest.java | 18 ++++++ .../brigadier/CommandSuggestionsTest.java | 59 +++++++++++++++++++ 8 files changed, 129 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/mojang/brigadier/CommandDispatcher.java b/src/main/java/com/mojang/brigadier/CommandDispatcher.java index 239e338..5584c29 100644 --- a/src/main/java/com/mojang/brigadier/CommandDispatcher.java +++ b/src/main/java/com/mojang/brigadier/CommandDispatcher.java @@ -64,6 +64,10 @@ public class CommandDispatcher { } public int execute(final String input, final S source) throws CommandSyntaxException { + return execute(new StringReader(input), source); + } + + public int execute(final StringReader input, final S source) throws CommandSyntaxException { final ParseResults parse = parse(input, source); return execute(parse); } @@ -151,9 +155,12 @@ public class CommandDispatcher { } public ParseResults parse(final String command, final S source) { - final StringReader reader = new StringReader(command); + return parse(new StringReader(command), source); + } + + public ParseResults parse(final StringReader command, final S source) { final CommandContextBuilder context = new CommandContextBuilder<>(this, source, 0); - return parseNodes(root, reader, context); + return parseNodes(root, command, context); } private static class PartialParse { @@ -206,7 +213,7 @@ public class CommandDispatcher { childContext.withNode(child.getRedirect(), StringRange.between(cursor, reader.getCursor() - 1)); final ParseResults parse = parseNodes(child.getRedirect(), reader, childContext); context.withChild(parse.getContext()); - return new ParseResults<>(context, parse.getReader(), parse.getExceptions()); + return new ParseResults<>(context, originalReader.getCursor(), parse.getReader(), parse.getExceptions()); } else { final ParseResults parse = parseNodes(child, reader, context); if (potentials == null) { @@ -218,7 +225,7 @@ public class CommandDispatcher { if (potentials == null) { potentials = new ArrayList<>(1); } - potentials.add(new PartialParse<>(context, new ParseResults<>(context, reader, Collections.emptyMap()))); + potentials.add(new PartialParse<>(context, new ParseResults<>(context, originalReader.getCursor(), reader, Collections.emptyMap()))); } } @@ -243,7 +250,7 @@ public class CommandDispatcher { return potentials.get(0).parse; } - return new ParseResults<>(contextSoFar, originalReader, errors == null ? Collections.emptyMap() : errors); + return new ParseResults<>(contextSoFar, originalReader.getCursor(), originalReader, errors == null ? Collections.emptyMap() : errors); } public String[] getAllUsage(final CommandNode node, final S source, final boolean restricted) { @@ -346,7 +353,7 @@ public class CommandDispatcher { if (context.getNodes().isEmpty()) { parent = root; - start = 0; + start = parse.getStartIndex(); } else if (parse.getReader().canRead()) { final Map.Entry, StringRange> entry = Iterables.getLast(context.getNodes().entrySet()); parent = entry.getKey(); @@ -361,7 +368,7 @@ public class CommandDispatcher { start = entry.getValue().getEnd() + 1; } else { parent = root; - start = 0; + start = parse.getStartIndex(); } @SuppressWarnings("unchecked") final CompletableFuture[] futures = new CompletableFuture[parent.getChildren().size()]; diff --git a/src/main/java/com/mojang/brigadier/ParseResults.java b/src/main/java/com/mojang/brigadier/ParseResults.java index 5b56609..8146bde 100644 --- a/src/main/java/com/mojang/brigadier/ParseResults.java +++ b/src/main/java/com/mojang/brigadier/ParseResults.java @@ -10,16 +10,22 @@ import java.util.Map; public class ParseResults { private final CommandContextBuilder context; private final Map, CommandSyntaxException> exceptions; + private final int startIndex; private final ImmutableStringReader reader; - public ParseResults(final CommandContextBuilder context, final ImmutableStringReader reader, final Map, CommandSyntaxException> exceptions) { + public ParseResults(final CommandContextBuilder context, final int startIndex, final ImmutableStringReader reader, final Map, CommandSyntaxException> exceptions) { this.context = context; + this.startIndex = startIndex; this.reader = reader; this.exceptions = exceptions; } public ParseResults(final CommandContextBuilder context) { - this(context, new StringReader(""), Collections.emptyMap()); + this(context, 0, new StringReader(""), Collections.emptyMap()); + } + + public int getStartIndex() { + return startIndex; } public CommandContextBuilder getContext() { diff --git a/src/main/java/com/mojang/brigadier/tree/ArgumentCommandNode.java b/src/main/java/com/mojang/brigadier/tree/ArgumentCommandNode.java index db15407..3b938e4 100644 --- a/src/main/java/com/mojang/brigadier/tree/ArgumentCommandNode.java +++ b/src/main/java/com/mojang/brigadier/tree/ArgumentCommandNode.java @@ -120,4 +120,9 @@ public class ArgumentCommandNode extends CommandNode { public Collection getExamples() { return type.getExamples(); } + + @Override + public String toString() { + return ""; + } } diff --git a/src/main/java/com/mojang/brigadier/tree/LiteralCommandNode.java b/src/main/java/com/mojang/brigadier/tree/LiteralCommandNode.java index 4f3e8a6..4024254 100644 --- a/src/main/java/com/mojang/brigadier/tree/LiteralCommandNode.java +++ b/src/main/java/com/mojang/brigadier/tree/LiteralCommandNode.java @@ -118,4 +118,9 @@ public class LiteralCommandNode extends CommandNode { public Collection getExamples() { return Collections.singleton(literal); } + + @Override + public String toString() { + return ""; + } } diff --git a/src/main/java/com/mojang/brigadier/tree/RootCommandNode.java b/src/main/java/com/mojang/brigadier/tree/RootCommandNode.java index 12fcbf5..b728c13 100644 --- a/src/main/java/com/mojang/brigadier/tree/RootCommandNode.java +++ b/src/main/java/com/mojang/brigadier/tree/RootCommandNode.java @@ -62,4 +62,9 @@ public class RootCommandNode extends CommandNode { public Collection getExamples() { return Collections.emptyList(); } + + @Override + public String toString() { + return ""; + } } diff --git a/src/test/java/com/mojang/brigadier/CommandDispatcherTest.java b/src/test/java/com/mojang/brigadier/CommandDispatcherTest.java index 873c003..70bfe1c 100644 --- a/src/test/java/com/mojang/brigadier/CommandDispatcherTest.java +++ b/src/test/java/com/mojang/brigadier/CommandDispatcherTest.java @@ -43,6 +43,12 @@ public class CommandDispatcherTest { when(command.run(any())).thenReturn(42); } + private static StringReader inputWithOffset(final String input, final int offset) { + final StringReader result = new StringReader(input); + result.setCursor(offset); + return result; + } + @SuppressWarnings("unchecked") @Test public void testCreateAndExecuteCommand() throws Exception { @@ -52,6 +58,15 @@ public class CommandDispatcherTest { verify(command).run(any(CommandContext.class)); } + @SuppressWarnings("unchecked") + @Test + public void testCreateAndExecuteOffsetCommand() throws Exception { + subject.register(literal("foo").executes(command)); + + assertThat(subject.execute(inputWithOffset("/foo", 1), source), is(42)); + verify(command).run(any(CommandContext.class)); + } + @SuppressWarnings("unchecked") @Test public void testCreateAndMergeCommands() throws Exception { diff --git a/src/test/java/com/mojang/brigadier/CommandDispatcherUsagesTest.java b/src/test/java/com/mojang/brigadier/CommandDispatcherUsagesTest.java index a95ee5d..1d90038 100644 --- a/src/test/java/com/mojang/brigadier/CommandDispatcherUsagesTest.java +++ b/src/test/java/com/mojang/brigadier/CommandDispatcherUsagesTest.java @@ -102,6 +102,10 @@ public class CommandDispatcherUsagesTest { return Iterators.getLast(subject.parse(command, source).getContext().getNodes().keySet().iterator()); } + private CommandNode get(final StringReader command) { + return Iterators.getLast(subject.parse(command, source).getContext().getNodes().keySet().iterator()); + } + @Test public void testAllUsage_noCommands() throws Exception { subject = new CommandDispatcher<>(); @@ -174,4 +178,18 @@ public class CommandDispatcherUsagesTest { .build() )); } + + @Test + public void testSmartUsage_offsetH() throws Exception { + final StringReader offsetH = new StringReader("/|/|/h"); + offsetH.setCursor(5); + + final Map, 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() + )); + } } \ No newline at end of file diff --git a/src/test/java/com/mojang/brigadier/CommandSuggestionsTest.java b/src/test/java/com/mojang/brigadier/CommandSuggestionsTest.java index f131a3f..dc7ec79 100644 --- a/src/test/java/com/mojang/brigadier/CommandSuggestionsTest.java +++ b/src/test/java/com/mojang/brigadier/CommandSuggestionsTest.java @@ -30,6 +30,12 @@ public class CommandSuggestionsTest { subject = new CommandDispatcher<>(); } + private static StringReader inputWithOffset(final String input, final int offset) { + final StringReader result = new StringReader(input); + result.setCursor(offset); + return result; + } + @Test public void getCompletionSuggestions_rootCommands() throws Exception { subject.register(literal("foo")); @@ -42,6 +48,18 @@ public class CommandSuggestionsTest { assertThat(result.getList(), equalTo(Lists.newArrayList(new Suggestion(StringRange.at(0), "bar"), new Suggestion(StringRange.at(0), "baz"), new Suggestion(StringRange.at(0), "foo")))); } + @Test + public void getCompletionSuggestions_rootCommands_withInputOffset() throws Exception { + subject.register(literal("foo")); + subject.register(literal("bar")); + subject.register(literal("baz")); + + final Suggestions result = subject.getCompletionSuggestions(subject.parse(inputWithOffset("OOO", 3), source)).join(); + + assertThat(result.getRange(), equalTo(StringRange.at(3))); + assertThat(result.getList(), equalTo(Lists.newArrayList(new Suggestion(StringRange.at(3), "bar"), new Suggestion(StringRange.at(3), "baz"), new Suggestion(StringRange.at(3), "foo")))); + } + @Test public void getCompletionSuggestions_rootCommands_partial() throws Exception { subject.register(literal("foo")); @@ -54,6 +72,18 @@ public class CommandSuggestionsTest { assertThat(result.getList(), equalTo(Lists.newArrayList(new Suggestion(StringRange.between(0, 1), "bar"), new Suggestion(StringRange.between(0, 1), "baz")))); } + @Test + public void getCompletionSuggestions_rootCommands_partial_withInputOffset() throws Exception { + subject.register(literal("foo")); + subject.register(literal("bar")); + subject.register(literal("baz")); + + final Suggestions result = subject.getCompletionSuggestions(subject.parse(inputWithOffset("Zb", 1), source)).join(); + + assertThat(result.getRange(), equalTo(StringRange.between(1, 2))); + assertThat(result.getList(), equalTo(Lists.newArrayList(new Suggestion(StringRange.between(1, 2), "bar"), new Suggestion(StringRange.between(1, 2), "baz")))); + } + @Test public void getCompletionSuggestions_subCommands() throws Exception { subject.register( @@ -85,6 +115,22 @@ public class CommandSuggestionsTest { assertThat(result.getList(), equalTo(Lists.newArrayList(new Suggestion(StringRange.between(7, 8), "bar"), new Suggestion(StringRange.between(7, 8), "baz")))); } + @Test + public void getCompletionSuggestions_subCommands_partial_withInputOffset() throws Exception { + subject.register( + literal("parent") + .then(literal("foo")) + .then(literal("bar")) + .then(literal("baz")) + ); + + final ParseResults parse = subject.parse(inputWithOffset("junk parent b", 5), source); + final Suggestions result = subject.getCompletionSuggestions(parse).join(); + + assertThat(result.getRange(), equalTo(StringRange.between(12, 13))); + assertThat(result.getList(), equalTo(Lists.newArrayList(new Suggestion(StringRange.between(12, 13), "bar"), new Suggestion(StringRange.between(12, 13), "baz")))); + } + @Test public void getCompletionSuggestions_redirect() throws Exception { final LiteralCommandNode actual = subject.register(literal("actual").then(literal("sub"))); @@ -109,6 +155,19 @@ public class CommandSuggestionsTest { assertThat(result.getList(), equalTo(Lists.newArrayList(new Suggestion(StringRange.between(9, 10), "sub")))); } + + @Test + public void getCompletionSuggestions_redirectPartial_withInputOffset() throws Exception { + final LiteralCommandNode actual = subject.register(literal("actual").then(literal("sub"))); + subject.register(literal("redirect").redirect(actual)); + + final ParseResults parse = subject.parse(inputWithOffset("/redirect s", 1), source); + final Suggestions result = subject.getCompletionSuggestions(parse).join(); + + assertThat(result.getRange(), equalTo(StringRange.between(10, 11))); + assertThat(result.getList(), equalTo(Lists.newArrayList(new Suggestion(StringRange.between(10, 11), "sub")))); + } + @Test public void getCompletionSuggestions_redirect_lots() throws Exception { final LiteralCommandNode loop = subject.register(literal("redirect"));