Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement calculation of suggestions for any text position #7

Merged
merged 2 commits into from
Sep 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 28 additions & 52 deletions src/main/java/com/mojang/brigadier/CommandDispatcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@

package com.mojang.brigadier;

import com.google.common.collect.Iterables;
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.context.SuggestionContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
Expand Down Expand Up @@ -57,6 +56,7 @@ public class CommandDispatcher<S> {
private static final String USAGE_OR = "|";

private final RootCommandNode<S> root;

private final Predicate<CommandNode<S>> hasCommand = new Predicate<CommandNode<S>>() {
@Override
public boolean test(final CommandNode<S> input) {
Expand Down Expand Up @@ -231,7 +231,7 @@ public int execute(final ParseResults<S> parse) throws CommandSyntaxException {
final CommandContext<S> child = context.getChild();
if (child != null) {
forked |= context.isForked();
if (!child.getNodes().isEmpty()) {
if (child.hasNodes()) {
foundCommand = true;
final RedirectModifier<S> modifier = context.getRedirectModifier();
if (modifier == null) {
Expand Down Expand Up @@ -345,24 +345,14 @@ public ParseResults<S> parse(final String command, final S source) {
* @see #execute(String, Object)
*/
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, root, command.getCursor());
return parseNodes(root, command, context);
}

private static class PartialParse<S> {
public final CommandContextBuilder<S> context;
public final ParseResults<S> parse;

private PartialParse(final CommandContextBuilder<S> context, final ParseResults<S> parse) {
this.context = context;
this.parse = parse;
}
}

private ParseResults<S> parseNodes(final CommandNode<S> node, final StringReader originalReader, final CommandContextBuilder<S> contextSoFar) {
final S source = contextSoFar.getSource();
Map<CommandNode<S>, CommandSyntaxException> errors = null;
List<PartialParse<S>> potentials = null;
List<ParseResults<S>> potentials = null;
final int cursor = originalReader.getCursor();

for (final CommandNode<S> child : node.getRelevantNodes(originalReader)) {
Expand Down Expand Up @@ -395,48 +385,47 @@ private ParseResults<S> parseNodes(final CommandNode<S> node, final StringReader
if (reader.canRead(child.getRedirect() == null ? 2 : 1)) {
reader.skip();
if (child.getRedirect() != null) {
final CommandContextBuilder<S> childContext = new CommandContextBuilder<>(this, source, reader.getCursor());
childContext.withNode(child.getRedirect(), StringRange.between(cursor, reader.getCursor() - 1));
final CommandContextBuilder<S> childContext = new CommandContextBuilder<>(this, source, child.getRedirect(), reader.getCursor());
final ParseResults<S> parse = parseNodes(child.getRedirect(), reader, childContext);
context.withChild(parse.getContext());
return new ParseResults<>(context, originalReader.getCursor(), parse.getReader(), parse.getExceptions());
return new ParseResults<>(context, parse.getReader(), parse.getExceptions());
} else {
final ParseResults<S> parse = parseNodes(child, reader, context);
if (potentials == null) {
potentials = new ArrayList<>(1);
}
potentials.add(new PartialParse<>(context, parse));
potentials.add(parse);
}
} else {
if (potentials == null) {
potentials = new ArrayList<>(1);
}
potentials.add(new PartialParse<>(context, new ParseResults<>(context, originalReader.getCursor(), reader, Collections.emptyMap())));
potentials.add(new ParseResults<>(context, reader, Collections.emptyMap()));
}
}

if (potentials != null) {
if (potentials.size() > 1) {
potentials.sort((a, b) -> {
if (!a.parse.getReader().canRead() && b.parse.getReader().canRead()) {
if (!a.getReader().canRead() && b.getReader().canRead()) {
return -1;
}
if (a.parse.getReader().canRead() && !b.parse.getReader().canRead()) {
if (a.getReader().canRead() && !b.getReader().canRead()) {
return 1;
}
if (a.parse.getExceptions().isEmpty() && !b.parse.getExceptions().isEmpty()) {
if (a.getExceptions().isEmpty() && !b.getExceptions().isEmpty()) {
return -1;
}
if (!a.parse.getExceptions().isEmpty() && b.parse.getExceptions().isEmpty()) {
if (!a.getExceptions().isEmpty() && b.getExceptions().isEmpty()) {
return 1;
}
return 0;
});
}
return potentials.get(0).parse;
return potentials.get(0);
}

return new ParseResults<>(contextSoFar, originalReader.getCursor(), originalReader, errors == null ? Collections.emptyMap() : errors);
return new ParseResults<>(contextSoFar, originalReader, errors == null ? Collections.emptyMap() : errors);
}

/**
Expand Down Expand Up @@ -589,37 +578,24 @@ private String getSmartUsage(final CommandNode<S> node, final S source, final bo
* @return a future that will eventually resolve into a {@link Suggestions} object
*/
public CompletableFuture<Suggestions> getCompletionSuggestions(final ParseResults<S> parse) {
final CommandContextBuilder<S> rootContext = parse.getContext();
final CommandContextBuilder<S> context = rootContext.getLastChild();
final CommandNode<S> parent;
final int start;

if (context.getNodes().isEmpty()) {
parent = root;
start = parse.getStartIndex();
} 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 if (rootContext != context && context.getNodes().size() > 0) {
final Map.Entry<CommandNode<S>, StringRange> entry = Iterables.getLast(context.getNodes().entrySet());
parent = entry.getKey();
start = entry.getValue().getEnd() + 1;
} else {
parent = root;
start = parse.getStartIndex();
}
return getCompletionSuggestions(parse, parse.getReader().getTotalLength());
}

public CompletableFuture<Suggestions> getCompletionSuggestions(final ParseResults<S> parse, int cursor) {
final CommandContextBuilder<S> context = parse.getContext();

final SuggestionContext<S> nodeBeforeCursor = context.findSuggestionContext(cursor);
final CommandNode<S> parent = nodeBeforeCursor.parent;
final int start = Math.min(nodeBeforeCursor.startPos, cursor);

final String fullInput = parse.getReader().getString();
final String truncatedInput = fullInput.substring(0, cursor);
@SuppressWarnings("unchecked") final CompletableFuture<Suggestions>[] futures = new CompletableFuture[parent.getChildren().size()];
int i = 0;
for (final CommandNode<S> node : parent.getChildren()) {
CompletableFuture<Suggestions> future = Suggestions.empty();
try {
future = node.listSuggestions(context.build(parse.getReader().getString()), new SuggestionsBuilder(parse.getReader().getString(), start));
future = node.listSuggestions(context.build(truncatedInput), new SuggestionsBuilder(truncatedInput, start));
} catch (final CommandSyntaxException ignored) {
}
futures[i++] = future;
Expand All @@ -631,7 +607,7 @@ public CompletableFuture<Suggestions> getCompletionSuggestions(final ParseResult
for (final CompletableFuture<Suggestions> future : futures) {
suggestions.add(future.join());
}
result.complete(Suggestions.merge(parse.getReader().getString(), suggestions));
result.complete(Suggestions.merge(fullInput, suggestions));
});

return result;
Expand Down
10 changes: 2 additions & 8 deletions src/main/java/com/mojang/brigadier/ParseResults.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,16 @@
public class ParseResults<S> {
private final CommandContextBuilder<S> context;
private final Map<CommandNode<S>, CommandSyntaxException> exceptions;
private final int startIndex;
private final ImmutableStringReader reader;

public ParseResults(final CommandContextBuilder<S> context, final int startIndex, final ImmutableStringReader reader, final Map<CommandNode<S>, CommandSyntaxException> exceptions) {
public ParseResults(final CommandContextBuilder<S> context, final ImmutableStringReader reader, final Map<CommandNode<S>, CommandSyntaxException> exceptions) {
this.context = context;
this.startIndex = startIndex;
this.reader = reader;
this.exceptions = exceptions;
}

public ParseResults(final CommandContextBuilder<S> context) {
this(context, 0, new StringReader(""), Collections.emptyMap());
}

public int getStartIndex() {
return startIndex;
this(context, new StringReader(""), Collections.emptyMap());
}

public CommandContextBuilder<S> getContext() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

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;
Expand All @@ -15,7 +14,7 @@
import java.util.concurrent.CompletableFuture;

public interface ArgumentType<T> {
<S> T parse(StringReader reader) throws CommandSyntaxException;
T parse(StringReader reader) throws CommandSyntaxException;

default <S> CompletableFuture<Suggestions> listSuggestions(final CommandContext<S> context, final SuggestionsBuilder builder) {
return Suggestions.empty();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

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;
Expand All @@ -29,7 +28,7 @@ public static boolean getBool(final CommandContext<?> context, final String name
}

@Override
public <S> Boolean parse(final StringReader reader) throws CommandSyntaxException {
public Boolean parse(final StringReader reader) throws CommandSyntaxException {
return reader.readBoolean();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public double getMaximum() {
}

@Override
public <S> Double parse(final StringReader reader) throws CommandSyntaxException {
public Double parse(final StringReader reader) throws CommandSyntaxException {
final int start = reader.getCursor();
final double result = reader.readDouble();
if (result < minimum) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public float getMaximum() {
}

@Override
public <S> Float parse(final StringReader reader) throws CommandSyntaxException {
public Float parse(final StringReader reader) throws CommandSyntaxException {
final int start = reader.getCursor();
final float result = reader.readFloat();
if (result < minimum) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public int getMaximum() {
}

@Override
public <S> Integer parse(final StringReader reader) throws CommandSyntaxException {
public Integer parse(final StringReader reader) throws CommandSyntaxException {
final int start = reader.getCursor();
final int result = reader.readInt();
if (result < minimum) {
Expand Down Expand Up @@ -84,4 +84,9 @@ public String toString() {
return "integer(" + minimum + ", " + maximum + ")";
}
}

@Override
public Collection<String> getExamples() {
return EXAMPLES;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.context.CommandContextBuilder;
import com.mojang.brigadier.exceptions.CommandSyntaxException;

import java.util.Arrays;
Expand Down Expand Up @@ -39,7 +38,7 @@ public StringType getType() {
}

@Override
public <S> String parse(final StringReader reader) throws CommandSyntaxException {
public String parse(final StringReader reader) throws CommandSyntaxException {
if (type == StringType.GREEDY_PHRASE) {
final String text = reader.getRemaining();
reader.setCursor(reader.getTotalLength());
Expand Down
23 changes: 18 additions & 5 deletions src/main/java/com/mojang/brigadier/context/CommandContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,27 @@
import com.mojang.brigadier.RedirectModifier;
import com.mojang.brigadier.tree.CommandNode;

import java.util.List;
import java.util.Map;

public class CommandContext<S> {
private final S source;
private final String input;
private final Command<S> command;
private final Map<String, ParsedArgument<S, ?>> arguments;
private final Map<CommandNode<S>, StringRange> nodes;
private final CommandNode<S> rootNode;
private final List<ParsedCommandNode<S>> nodes;
private final StringRange range;
private final CommandContext<S> child;
private final RedirectModifier<S> modifier;
private final boolean forks;

public CommandContext(final S source, final String input, final Map<String, ParsedArgument<S, ?>> arguments, final Command<S> command, final Map<CommandNode<S>, StringRange> nodes, final StringRange range, final CommandContext<S> child, final RedirectModifier<S> modifier, boolean forks) {
public CommandContext(final S source, final String input, final Map<String, ParsedArgument<S, ?>> arguments, final Command<S> command, final CommandNode<S> rootNode, final List<ParsedCommandNode<S>> nodes, final StringRange range, final CommandContext<S> child, final RedirectModifier<S> modifier, boolean forks) {
this.source = source;
this.input = input;
this.arguments = arguments;
this.command = command;
this.rootNode = rootNode;
this.nodes = nodes;
this.range = range;
this.child = child;
Expand All @@ -38,7 +41,7 @@ public CommandContext<S> copyFor(final S source) {
if (this.source == source) {
return this;
}
return new CommandContext<>(source, input, arguments, command, nodes, range, child, modifier, forks);
return new CommandContext<>(source, input, arguments, command, rootNode, nodes, range, child, modifier, forks);
}

public CommandContext<S> getChild() {
Expand Down Expand Up @@ -85,7 +88,8 @@ public boolean equals(final Object o) {
final CommandContext that = (CommandContext) o;

if (!arguments.equals(that.arguments)) return false;
if (!Iterables.elementsEqual(nodes.entrySet(), that.nodes.entrySet())) return false;
if (!rootNode.equals(that.rootNode)) return false;
if (!Iterables.elementsEqual(nodes, that.nodes)) return false;
if (command != null ? !command.equals(that.command) : that.command != null) return false;
if (!source.equals(that.source)) return false;
if (child != null ? !child.equals(that.child) : that.child != null) return false;
Expand All @@ -98,6 +102,7 @@ public int hashCode() {
int result = source.hashCode();
result = 31 * result + arguments.hashCode();
result = 31 * result + (command != null ? command.hashCode() : 0);
result = 31 * result + rootNode.hashCode();
result = 31 * result + nodes.hashCode();
result = 31 * result + (child != null ? child.hashCode() : 0);
return result;
Expand All @@ -115,10 +120,18 @@ public String getInput() {
return input;
}

public Map<CommandNode<S>, StringRange> getNodes() {
public CommandNode<S> getRootNode() {
return rootNode;
}

public List<ParsedCommandNode<S>> getNodes() {
return nodes;
}

public boolean hasNodes() {
return !nodes.isEmpty();
}

public boolean isForked() {
return forks;
}
Expand Down
Loading