Reimplemented command suggestions
This commit is contained in:
parent
4d91bc6e7e
commit
b48dbe7916
6 changed files with 163 additions and 100 deletions
|
@ -3,7 +3,7 @@ import groovy.io.FileType
|
|||
apply plugin: 'java-library'
|
||||
apply plugin: 'maven'
|
||||
|
||||
version = '0.1.3'
|
||||
version = '0.1.4'
|
||||
group = 'com.mojang'
|
||||
|
||||
task wrapper(type: Wrapper) {
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
package com.mojang.brigadier;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Iterators;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.context.CommandContextBuilder;
|
||||
import com.mojang.brigadier.context.StringRange;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
|
@ -19,10 +19,10 @@ import java.util.ArrayList;
|
|||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -285,42 +285,37 @@ public class CommandDispatcher<S> {
|
|||
return self;
|
||||
}
|
||||
|
||||
private Set<String> findSuggestions(final CommandNode<S> node, final StringReader reader, final CommandContextBuilder<S> contextBuilder, final Set<String> result) {
|
||||
if (node.getRedirect() != null) {
|
||||
return findSuggestions(node.getRedirect(), reader, contextBuilder, result);
|
||||
}
|
||||
final S source = contextBuilder.getSource();
|
||||
for (final CommandNode<S> child : node.getChildren()) {
|
||||
if (!child.canUse(source)) {
|
||||
continue;
|
||||
}
|
||||
final CommandContextBuilder<S> context = contextBuilder.copy();
|
||||
final int cursor = reader.getCursor();
|
||||
try {
|
||||
child.parse(reader, context);
|
||||
if (reader.canRead()) {
|
||||
if (reader.peek() == ARGUMENT_SEPARATOR_CHAR) {
|
||||
reader.skip();
|
||||
return findSuggestions(child, reader, context, result);
|
||||
}
|
||||
} else {
|
||||
reader.setCursor(cursor);
|
||||
child.listSuggestions(reader.getRemaining(), result, context);
|
||||
}
|
||||
} catch (final CommandSyntaxException e) {
|
||||
reader.setCursor(cursor);
|
||||
child.listSuggestions(reader.getRemaining(), result, context);
|
||||
}
|
||||
public CommandSuggestions getCompletionSuggestions(final ParseResults<S> parse) {
|
||||
final CommandContextBuilder<S> context = parse.getContext();
|
||||
|
||||
final Set<String> suggestions = new LinkedHashSet<>();
|
||||
|
||||
final CommandNode<S> parent;
|
||||
final int start;
|
||||
|
||||
if (context.getNodes().isEmpty()) {
|
||||
parent = root;
|
||||
start = 0;
|
||||
} else if (parse.getReader().canRead()) {
|
||||
final Map.Entry<CommandNode<S>, StringRange> entry = Iterables.getLast(context.getNodes().entrySet());
|
||||
parent = entry.getKey();
|
||||
start = entry.getValue().getEnd() + 1;
|
||||
} else if (context.getNodes().size() > 1) {
|
||||
final Map.Entry<CommandNode<S>, StringRange> entry = Iterables.get(context.getNodes().entrySet(), context.getNodes().size() - 2);
|
||||
parent = entry.getKey();
|
||||
start = entry.getValue().getEnd() + 1;
|
||||
} else {
|
||||
parent = root;
|
||||
start = 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
for (final CommandNode<S> node : parent.getChildren()) {
|
||||
node.listSuggestions(parse.getReader().getString().substring(start), suggestions, context);
|
||||
}
|
||||
|
||||
public String[] getCompletionSuggestions(final String command, final S source) {
|
||||
final StringReader reader = new StringReader(command);
|
||||
final Set<String> nodes = findSuggestions(root, reader, new CommandContextBuilder<>(this, source, 0), Sets.newLinkedHashSet());
|
||||
|
||||
return nodes.toArray(new String[nodes.size()]);
|
||||
final List<String> result = new ArrayList<>(suggestions);
|
||||
Collections.sort(result);
|
||||
return new CommandSuggestions(new StringRange(start, parse.getReader().getTotalLength()), result);
|
||||
}
|
||||
|
||||
public RootCommandNode<S> getRoot() {
|
||||
|
|
45
src/main/java/com/mojang/brigadier/CommandSuggestions.java
Normal file
45
src/main/java/com/mojang/brigadier/CommandSuggestions.java
Normal file
|
@ -0,0 +1,45 @@
|
|||
package com.mojang.brigadier;
|
||||
|
||||
import com.mojang.brigadier.context.StringRange;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
|
@ -53,4 +53,12 @@ public class StringRange {
|
|||
public int hashCode() {
|
||||
return Objects.hash(start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "StringRange{" +
|
||||
"start=" + start +
|
||||
", end=" + end +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
package com.mojang.brigadier;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import static com.mojang.brigadier.builder.LiteralArgumentBuilder.literal;
|
||||
import static org.hamcrest.Matchers.emptyArray;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class CommandDispatcherCompletionsTest {
|
||||
private CommandDispatcher<Object> subject;
|
||||
@Mock
|
||||
private Object source;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
subject = new CommandDispatcher<>();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoCommands() throws Exception {
|
||||
assertThat(subject.getCompletionSuggestions("", source), is(emptyArray()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCommand() throws Exception {
|
||||
subject.register(literal("foo"));
|
||||
subject.register(literal("bar"));
|
||||
subject.register(literal("baz").requires(s -> false));
|
||||
assertThat(subject.getCompletionSuggestions("", source), equalTo(new String[]{"bar", "foo"}));
|
||||
assertThat(subject.getCompletionSuggestions("f", source), equalTo(new String[]{"foo"}));
|
||||
assertThat(subject.getCompletionSuggestions("b", source), equalTo(new String[]{"bar"}));
|
||||
assertThat(subject.getCompletionSuggestions("q", source), is(emptyArray()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCommand_redirect() throws Exception {
|
||||
subject.register(literal("foo"));
|
||||
subject.register(literal("bar"));
|
||||
subject.register(literal("redirect").redirect(subject.getRoot(), Collections::singleton));
|
||||
assertThat(subject.getCompletionSuggestions("redirect ", source), equalTo(new String[]{"bar", "foo", "redirect"}));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubCommand() throws Exception {
|
||||
subject.register(literal("foo").then(literal("abc")).then(literal("def")).then(literal("ghi").requires(s -> false)));
|
||||
subject.register(literal("bar"));
|
||||
assertThat(subject.getCompletionSuggestions("", source), equalTo(new String[]{"bar", "foo"}));
|
||||
assertThat(subject.getCompletionSuggestions("f", source), equalTo(new String[]{"foo"}));
|
||||
assertThat(subject.getCompletionSuggestions("foo", source), equalTo(new String[]{"foo"}));
|
||||
assertThat(subject.getCompletionSuggestions("foo ", source), equalTo(new String[]{"abc", "def"}));
|
||||
assertThat(subject.getCompletionSuggestions("foo a", source), equalTo(new String[]{"abc"}));
|
||||
assertThat(subject.getCompletionSuggestions("foo d", source), equalTo(new String[]{"def"}));
|
||||
assertThat(subject.getCompletionSuggestions("foo g", source), is(emptyArray()));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package com.mojang.brigadier;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.mojang.brigadier.context.StringRange;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import static com.mojang.brigadier.builder.LiteralArgumentBuilder.literal;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class CommandSuggestionsTest {
|
||||
private CommandDispatcher<Object> subject;
|
||||
@Mock
|
||||
private Object source;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
subject = new CommandDispatcher<>();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCompletionSuggestions_rootCommands() throws Exception {
|
||||
subject.register(literal("foo"));
|
||||
subject.register(literal("bar"));
|
||||
subject.register(literal("baz"));
|
||||
|
||||
final CommandSuggestions result = subject.getCompletionSuggestions(subject.parse("", source));
|
||||
|
||||
assertThat(result.getRange(), equalTo(new StringRange(0, 0)));
|
||||
assertThat(result.getSuggestions(), equalTo(Lists.newArrayList("bar", "baz", "foo")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCompletionSuggestions_rootCommands_partial() throws Exception {
|
||||
subject.register(literal("foo"));
|
||||
subject.register(literal("bar"));
|
||||
subject.register(literal("baz"));
|
||||
|
||||
final CommandSuggestions result = subject.getCompletionSuggestions(subject.parse("b", source));
|
||||
|
||||
assertThat(result.getRange(), equalTo(new StringRange(0, 1)));
|
||||
assertThat(result.getSuggestions(), equalTo(Lists.newArrayList("bar", "baz")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCompletionSuggestions_subCommands() throws Exception {
|
||||
subject.register(
|
||||
literal("parent")
|
||||
.then(literal("foo"))
|
||||
.then(literal("bar"))
|
||||
.then(literal("baz"))
|
||||
);
|
||||
|
||||
final CommandSuggestions result = subject.getCompletionSuggestions(subject.parse("parent ", source));
|
||||
|
||||
assertThat(result.getRange(), equalTo(new StringRange(7, 7)));
|
||||
assertThat(result.getSuggestions(), equalTo(Lists.newArrayList("bar", "baz", "foo")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCompletionSuggestions_subCommands_partial() throws Exception {
|
||||
subject.register(
|
||||
literal("parent")
|
||||
.then(literal("foo"))
|
||||
.then(literal("bar"))
|
||||
.then(literal("baz"))
|
||||
);
|
||||
|
||||
final CommandSuggestions result = subject.getCompletionSuggestions(subject.parse("parent b", source));
|
||||
|
||||
assertThat(result.getRange(), equalTo(new StringRange(7, 8)));
|
||||
assertThat(result.getSuggestions(), equalTo(Lists.newArrayList("bar", "baz")));
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue