Pass StringReader instead of strings to CommandDispatcher
This commit is contained in:
parent
235f0c9b28
commit
75dcc36066
8 changed files with 129 additions and 9 deletions
|
@ -64,6 +64,10 @@ public class CommandDispatcher<S> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public int execute(final String input, final S source) throws CommandSyntaxException {
|
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<S> parse = parse(input, source);
|
final ParseResults<S> parse = parse(input, source);
|
||||||
return execute(parse);
|
return execute(parse);
|
||||||
}
|
}
|
||||||
|
@ -151,9 +155,12 @@ public class CommandDispatcher<S> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public ParseResults<S> parse(final String command, final S source) {
|
public ParseResults<S> parse(final String command, final S source) {
|
||||||
final StringReader reader = new StringReader(command);
|
return parse(new StringReader(command), source);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ParseResults<S> parse(final StringReader command, final S source) {
|
||||||
final CommandContextBuilder<S> context = new CommandContextBuilder<>(this, source, 0);
|
final CommandContextBuilder<S> context = new CommandContextBuilder<>(this, source, 0);
|
||||||
return parseNodes(root, reader, context);
|
return parseNodes(root, command, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class PartialParse<S> {
|
private static class PartialParse<S> {
|
||||||
|
@ -206,7 +213,7 @@ public class CommandDispatcher<S> {
|
||||||
childContext.withNode(child.getRedirect(), StringRange.between(cursor, reader.getCursor() - 1));
|
childContext.withNode(child.getRedirect(), StringRange.between(cursor, reader.getCursor() - 1));
|
||||||
final ParseResults<S> parse = parseNodes(child.getRedirect(), reader, childContext);
|
final ParseResults<S> parse = parseNodes(child.getRedirect(), reader, childContext);
|
||||||
context.withChild(parse.getContext());
|
context.withChild(parse.getContext());
|
||||||
return new ParseResults<>(context, parse.getReader(), parse.getExceptions());
|
return new ParseResults<>(context, originalReader.getCursor(), parse.getReader(), parse.getExceptions());
|
||||||
} else {
|
} else {
|
||||||
final ParseResults<S> parse = parseNodes(child, reader, context);
|
final ParseResults<S> parse = parseNodes(child, reader, context);
|
||||||
if (potentials == null) {
|
if (potentials == null) {
|
||||||
|
@ -218,7 +225,7 @@ public class CommandDispatcher<S> {
|
||||||
if (potentials == null) {
|
if (potentials == null) {
|
||||||
potentials = new ArrayList<>(1);
|
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<S> {
|
||||||
return potentials.get(0).parse;
|
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<S> node, final S source, final boolean restricted) {
|
public String[] getAllUsage(final CommandNode<S> node, final S source, final boolean restricted) {
|
||||||
|
@ -346,7 +353,7 @@ public class CommandDispatcher<S> {
|
||||||
|
|
||||||
if (context.getNodes().isEmpty()) {
|
if (context.getNodes().isEmpty()) {
|
||||||
parent = root;
|
parent = root;
|
||||||
start = 0;
|
start = parse.getStartIndex();
|
||||||
} else if (parse.getReader().canRead()) {
|
} else if (parse.getReader().canRead()) {
|
||||||
final Map.Entry<CommandNode<S>, StringRange> entry = Iterables.getLast(context.getNodes().entrySet());
|
final Map.Entry<CommandNode<S>, StringRange> entry = Iterables.getLast(context.getNodes().entrySet());
|
||||||
parent = entry.getKey();
|
parent = entry.getKey();
|
||||||
|
@ -361,7 +368,7 @@ public class CommandDispatcher<S> {
|
||||||
start = entry.getValue().getEnd() + 1;
|
start = entry.getValue().getEnd() + 1;
|
||||||
} else {
|
} else {
|
||||||
parent = root;
|
parent = root;
|
||||||
start = 0;
|
start = parse.getStartIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked") final CompletableFuture<Suggestions>[] futures = new CompletableFuture[parent.getChildren().size()];
|
@SuppressWarnings("unchecked") final CompletableFuture<Suggestions>[] futures = new CompletableFuture[parent.getChildren().size()];
|
||||||
|
|
|
@ -10,16 +10,22 @@ import java.util.Map;
|
||||||
public class ParseResults<S> {
|
public class ParseResults<S> {
|
||||||
private final CommandContextBuilder<S> context;
|
private final CommandContextBuilder<S> context;
|
||||||
private final Map<CommandNode<S>, CommandSyntaxException> exceptions;
|
private final Map<CommandNode<S>, CommandSyntaxException> exceptions;
|
||||||
|
private final int startIndex;
|
||||||
private final ImmutableStringReader reader;
|
private final ImmutableStringReader reader;
|
||||||
|
|
||||||
public ParseResults(final CommandContextBuilder<S> context, final ImmutableStringReader reader, final Map<CommandNode<S>, CommandSyntaxException> exceptions) {
|
public ParseResults(final CommandContextBuilder<S> context, final int startIndex, final ImmutableStringReader reader, final Map<CommandNode<S>, CommandSyntaxException> exceptions) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
|
this.startIndex = startIndex;
|
||||||
this.reader = reader;
|
this.reader = reader;
|
||||||
this.exceptions = exceptions;
|
this.exceptions = exceptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ParseResults(final CommandContextBuilder<S> context) {
|
public ParseResults(final CommandContextBuilder<S> context) {
|
||||||
this(context, new StringReader(""), Collections.emptyMap());
|
this(context, 0, new StringReader(""), Collections.emptyMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getStartIndex() {
|
||||||
|
return startIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CommandContextBuilder<S> getContext() {
|
public CommandContextBuilder<S> getContext() {
|
||||||
|
|
|
@ -120,4 +120,9 @@ public class ArgumentCommandNode<S, T> extends CommandNode<S> {
|
||||||
public Collection<String> getExamples() {
|
public Collection<String> getExamples() {
|
||||||
return type.getExamples();
|
return type.getExamples();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "<argument " + name + ":" + type +">";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,4 +118,9 @@ public class LiteralCommandNode<S> extends CommandNode<S> {
|
||||||
public Collection<String> getExamples() {
|
public Collection<String> getExamples() {
|
||||||
return Collections.singleton(literal);
|
return Collections.singleton(literal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "<literal " + literal + ">";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,4 +62,9 @@ public class RootCommandNode<S> extends CommandNode<S> {
|
||||||
public Collection<String> getExamples() {
|
public Collection<String> getExamples() {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "<root>";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,12 @@ public class CommandDispatcherTest {
|
||||||
when(command.run(any())).thenReturn(42);
|
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")
|
@SuppressWarnings("unchecked")
|
||||||
@Test
|
@Test
|
||||||
public void testCreateAndExecuteCommand() throws Exception {
|
public void testCreateAndExecuteCommand() throws Exception {
|
||||||
|
@ -52,6 +58,15 @@ public class CommandDispatcherTest {
|
||||||
verify(command).run(any(CommandContext.class));
|
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")
|
@SuppressWarnings("unchecked")
|
||||||
@Test
|
@Test
|
||||||
public void testCreateAndMergeCommands() throws Exception {
|
public void testCreateAndMergeCommands() throws Exception {
|
||||||
|
|
|
@ -102,6 +102,10 @@ public class CommandDispatcherUsagesTest {
|
||||||
return Iterators.getLast(subject.parse(command, source).getContext().getNodes().keySet().iterator());
|
return Iterators.getLast(subject.parse(command, source).getContext().getNodes().keySet().iterator());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private CommandNode<Object> get(final StringReader command) {
|
||||||
|
return Iterators.getLast(subject.parse(command, source).getContext().getNodes().keySet().iterator());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAllUsage_noCommands() throws Exception {
|
public void testAllUsage_noCommands() throws Exception {
|
||||||
subject = new CommandDispatcher<>();
|
subject = new CommandDispatcher<>();
|
||||||
|
@ -174,4 +178,18 @@ public class CommandDispatcherUsagesTest {
|
||||||
.build()
|
.build()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSmartUsage_offsetH() throws Exception {
|
||||||
|
final StringReader offsetH = new StringReader("/|/|/h");
|
||||||
|
offsetH.setCursor(5);
|
||||||
|
|
||||||
|
final Map<CommandNode<Object>, String> results = subject.getSmartUsage(get(offsetH), source);
|
||||||
|
assertThat(results, equalTo(ImmutableMap.builder()
|
||||||
|
.put(get("h 1"), "[1] i")
|
||||||
|
.put(get("h 2"), "[2] i ii")
|
||||||
|
.put(get("h 3"), "[3]")
|
||||||
|
.build()
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -30,6 +30,12 @@ public class CommandSuggestionsTest {
|
||||||
subject = new CommandDispatcher<>();
|
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
|
@Test
|
||||||
public void getCompletionSuggestions_rootCommands() throws Exception {
|
public void getCompletionSuggestions_rootCommands() throws Exception {
|
||||||
subject.register(literal("foo"));
|
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"))));
|
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
|
@Test
|
||||||
public void getCompletionSuggestions_rootCommands_partial() throws Exception {
|
public void getCompletionSuggestions_rootCommands_partial() throws Exception {
|
||||||
subject.register(literal("foo"));
|
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"))));
|
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
|
@Test
|
||||||
public void getCompletionSuggestions_subCommands() throws Exception {
|
public void getCompletionSuggestions_subCommands() throws Exception {
|
||||||
subject.register(
|
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"))));
|
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<Object> 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
|
@Test
|
||||||
public void getCompletionSuggestions_redirect() throws Exception {
|
public void getCompletionSuggestions_redirect() throws Exception {
|
||||||
final LiteralCommandNode<Object> actual = subject.register(literal("actual").then(literal("sub")));
|
final LiteralCommandNode<Object> 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"))));
|
assertThat(result.getList(), equalTo(Lists.newArrayList(new Suggestion(StringRange.between(9, 10), "sub"))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getCompletionSuggestions_redirectPartial_withInputOffset() throws Exception {
|
||||||
|
final LiteralCommandNode<Object> actual = subject.register(literal("actual").then(literal("sub")));
|
||||||
|
subject.register(literal("redirect").redirect(actual));
|
||||||
|
|
||||||
|
final ParseResults<Object> 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
|
@Test
|
||||||
public void getCompletionSuggestions_redirect_lots() throws Exception {
|
public void getCompletionSuggestions_redirect_lots() throws Exception {
|
||||||
final LiteralCommandNode<Object> loop = subject.register(literal("redirect"));
|
final LiteralCommandNode<Object> loop = subject.register(literal("redirect"));
|
||||||
|
|
Loading…
Reference in a new issue