use of org.spongepowered.common.command.brigadier.context.SpongeCommandContextBuilder in project SpongeCommon by SpongePowered.
the class SpongeCommandDispatcher method getCompletionSuggestions.
@Override
public CompletableFuture<Suggestions> getCompletionSuggestions(final ParseResults<CommandSourceStack> parse, final int cursor) {
final CommandContextBuilder<CommandSourceStack> context = parse.getContext();
// Sponge Start - redirect if this actually represents a non-Brig command
final CommandContextBuilder<CommandSourceStack> child = context.getLastChild();
if (child instanceof SpongeCommandContextBuilder) {
final SpongeCommandContextBuilder spongeChild = (SpongeCommandContextBuilder) child;
if (((SpongeCommandContextBuilder) child).representsNonBrigCommand()) {
// special handling!
final String rawCommand = parse.getReader().getString();
final String[] command = spongeChild.nonBrigCommand();
// we know this will exist.
final CommandMapping mapping = this.commandManager.commandMapping(command[0]).get();
return CommandUtil.createSuggestionsForRawCommand(rawCommand, spongeChild.nonBrigCommand(), spongeChild.cause(), mapping).buildFuture();
}
}
// Sponge End
final SuggestionContext<CommandSourceStack> nodeBeforeCursor = context.findSuggestionContext(cursor);
final CommandNode<CommandSourceStack> parent = nodeBeforeCursor.parent;
final int start = Math.min(nodeBeforeCursor.startPos, cursor);
final String fullInput = parse.getReader().getString();
final String truncatedInput = fullInput.substring(0, cursor);
// Sponge Start: the collection might be different.
final Collection<CommandNode<CommandSourceStack>> children;
if (parent instanceof SpongeNode) {
children = ((SpongeNode) parent).getChildrenForSuggestions();
} else {
children = parent.getChildren();
}
// @SuppressWarnings("unchecked") final CompletableFuture<Suggestions>[] futures = new CompletableFuture[parent.getChildren().size()];
@SuppressWarnings("unchecked") final CompletableFuture<Suggestions>[] futures = new CompletableFuture[children.size()];
// Sponge End
int i = 0;
for (final CommandNode<CommandSourceStack> node : children) {
// Sponge: parent.getChildren() -> children
CompletableFuture<Suggestions> future = Suggestions.empty();
try {
future = node.listSuggestions(context.build(truncatedInput), new SuggestionsBuilder(truncatedInput, start));
} catch (final CommandSyntaxException ignored) {
}
futures[i++] = future;
}
// See https://github.com/Mojang/brigadier/pull/81
return CompletableFuture.allOf(futures).handle((voidResult, exception) -> {
final List<Suggestions> suggestions = new ArrayList<>();
for (final CompletableFuture<Suggestions> future : futures) {
if (!future.isCompletedExceptionally()) {
suggestions.add(future.join());
}
}
return Suggestions.merge(fullInput, suggestions);
});
// Sponge End
}
use of org.spongepowered.common.command.brigadier.context.SpongeCommandContextBuilder in project SpongeCommon by SpongePowered.
the class SpongeCommandDispatcher method parseNodes.
private ParseResults<CommandSourceStack> parseNodes(// Sponge: used in permission checks
final boolean isRoot, // Sponge: needed for handling what we do with defaults.
final boolean isSuggestion, final CommandNode<CommandSourceStack> node, final SpongeStringReader originalReader, final SpongeCommandContextBuilder contextSoFar) {
final CommandSourceStack source = contextSoFar.getSource();
// Sponge Start
Map<CommandNode<CommandSourceStack>, CommandSyntaxException> errors = null;
List<ParseResults<CommandSourceStack>> potentials = null;
// Sponge End
final int cursor = originalReader.getCursor();
// Sponge Start: get relevant nodes if we're completing
final Collection<? extends CommandNode<CommandSourceStack>> nodes;
if (isSuggestion && node instanceof SpongeNode) {
nodes = ((SpongeNode) node).getRelevantNodesForSuggestions(originalReader);
} else if (originalReader.canRead()) {
nodes = node.getRelevantNodes(originalReader);
} else {
// Reader cannot read anymore so ONLY nodes with parsers that do not read can be processed
nodes = node.getChildren().stream().filter(n -> n instanceof SpongeArgumentCommandNode && (((SpongeArgumentCommandNode<?>) n).getParser().doesNotRead())).collect(Collectors.toList());
}
for (final CommandNode<CommandSourceStack> child : nodes) {
final boolean doesNotRead = child instanceof SpongeArgumentCommandNode && ((SpongeArgumentCommandNode<?>) child).getParser().doesNotRead();
// if (!child.canUse(source)) {
if (!SpongeNodePermissionCache.canUse(isRoot, this, child, source)) {
// Sponge End
continue;
}
// Sponge Start
final SpongeCommandContextBuilder context = contextSoFar.copy();
final SpongeStringReader reader = new SpongeStringReader(originalReader);
// Sponge End
try {
try {
child.parse(reader, context);
} catch (final RuntimeException ex) {
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherParseException().createWithContext(reader, ex.getMessage());
}
// Sponge Start: if we didn't consume anything we don't want Brig to complain at us.
if (reader.getCursor() == cursor) {
// of suggestions.
if (isSuggestion && !reader.canRead()) {
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherExpectedArgumentSeparator().createWithContext(reader);
}
reader.unskipWhitespace();
} else if (reader.canRead()) {
// Sponge End
if (reader.peek() != CommandDispatcher.ARGUMENT_SEPARATOR_CHAR) {
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherExpectedArgumentSeparator().createWithContext(reader);
}
}
} catch (final CommandSyntaxException ex) {
if (errors == null) {
errors = new LinkedHashMap<>();
}
errors.put(child, ex);
reader.setCursor(cursor);
continue;
}
context.withCommand(child.getCommand());
// must let it do so.
if (this.shouldContinueTraversing(reader, child)) {
// if (reader.canRead(child.getRedirect() == null ? 2 : 1)) {
// Sponge End
reader.skip();
// Sponge Start: redirect is now in a local variable as we use it a fair bit
final CommandNode<CommandSourceStack> redirect = child.getRedirect();
if (redirect != null) {
// If we've redirected to the root node, we may need to head back to our command
// manager - so we should check to see if that's going to actually be the case.
final boolean redirectingToRoot = redirect == this.getRoot();
final SpongeCommandContextBuilder childContext = new SpongeCommandContextBuilder(this, source, child.getRedirect(), reader.getCursor());
// For a redirect, we want to ensure all of our currently parsed information is available.
context.applySpongeElementsTo(childContext, false);
ParseResults<CommandSourceStack> parse = null;
if (redirectingToRoot) {
// check to see if we can get an element
final ArgumentReader.Immutable snapshot = reader.immutable();
try {
final String commandToAttempt = reader.peekString();
final int offset = reader.cursor();
if (redirect.getChild(commandToAttempt) == null) {
// The redirect fails, so we intercept here, and reroute to our command
// manager with the rest of the string and the new context.
reader.parseString();
final boolean hasMore = reader.canRead();
reader.skipWhitespace();
final Optional<CommandMapping> optionalMapping = this.commandManager.commandMapping(commandToAttempt);
if (optionalMapping.isPresent()) {
final CommandMapping mapping = optionalMapping.get();
final String remaining = reader.remaining();
childContext.withCommand(commandContext -> {
final SpongeCommandContext spongeContext = (SpongeCommandContext) commandContext;
try {
return mapping.registrar().process(spongeContext.cause(), mapping, commandToAttempt, remaining).result();
} catch (final CommandException e) {
throw new SpongeCommandSyntaxException(e, spongeContext);
}
});
childContext.withNode(new DummyCommandNode(childContext.getCommand()), StringRange.between(offset, reader.totalLength()));
childContext.setNonBrigCommand(hasMore ? new String[] { commandToAttempt, remaining } : new String[] { commandToAttempt });
reader.setCursor(reader.totalLength());
parse = new ParseResults<>(childContext, reader, Collections.emptyMap());
} else {
// We'll just let this parser fail as normal.
reader.setState(snapshot);
}
}
} catch (final ArgumentParseException ignored) {
// ignore it, it'll handle it later.
reader.setState(snapshot);
}
}
if (parse == null) {
parse = this.parseNodes(redirect instanceof RootCommandNode, isSuggestion, child.getRedirect(), reader, childContext);
}
// It worked out, so let's apply it back. We clear and reapply, it's simpler than comparing and conditional adding.
childContext.applySpongeElementsTo(context, true);
// Sponge End
context.withChild(parse.getContext());
final ParseResults<CommandSourceStack> parse2 = new ParseResults<>(context, parse.getReader(), parse.getExceptions());
if (doesNotRead && potentials != null) {
// If this is a optional or default parameter we only add the redirect as a potential option
potentials.add(parse2);
continue;
}
return parse2;
} else {
final ParseResults<CommandSourceStack> parse = this.parseNodes(false, isSuggestion, child, reader, context);
if (potentials == null) {
potentials = new ArrayList<>(1);
}
potentials.add(parse);
}
} else {
if (potentials == null) {
potentials = new ArrayList<>(1);
}
potentials.add(new ParseResults<>(context, reader, Collections.emptyMap()));
}
}
if (potentials != null) {
if (potentials.size() > 1) {
potentials.sort((a, b) -> {
if (!a.getReader().canRead() && b.getReader().canRead()) {
return -1;
}
if (a.getReader().canRead() && !b.getReader().canRead()) {
return 1;
}
if (a.getExceptions().isEmpty() && !b.getExceptions().isEmpty()) {
return -1;
}
if (!a.getExceptions().isEmpty() && b.getExceptions().isEmpty()) {
return 1;
}
// If we get here both potentials parsed everything and there was no exception
// BUT if parsing stopped at a non-terminal node this will cause an error later
// see at the end of #execute() where !foundCommand
// Instead we attempt to sort commands before that happens
final Command<CommandSourceStack> aCommand = SpongeCommandDispatcher.getCommand(a.getContext());
final Command<CommandSourceStack> bCommand = SpongeCommandDispatcher.getCommand(b.getContext());
if (aCommand == null && bCommand != null) {
return 1;
} else if (aCommand != null && bCommand == null) {
return -1;
}
return 0;
});
}
return potentials.get(0);
}
return new ParseResults<>(contextSoFar, originalReader, errors == null ? Collections.emptyMap() : errors);
}
use of org.spongepowered.common.command.brigadier.context.SpongeCommandContextBuilder in project SpongeCommon by SpongePowered.
the class SpongeCommandDispatcher method parse.
public ParseResults<CommandSourceStack> parse(final StringReader command, final CommandSourceStack source, final boolean isSuggestion) {
final SpongeCommandContextBuilder builder = new SpongeCommandContextBuilder(this, source, this.getRoot(), command.getCursor());
final SpongeStringReader reader = new SpongeStringReader(command);
return this.parseNodes(true, isSuggestion, this.getRoot(), reader, builder);
}
use of org.spongepowered.common.command.brigadier.context.SpongeCommandContextBuilder in project SpongeCommon by SpongePowered.
the class SpongeArgumentCommandNode method parse.
@Override
public final void parse(final StringReader reader, final CommandContextBuilder<CommandSourceStack> contextBuilder) throws CommandSyntaxException {
final int start = reader.getCursor();
final SpongeCommandContextBuilder builder = (SpongeCommandContextBuilder) contextBuilder;
final T result = this.parser.parse(this.key, builder, (SpongeStringReader) reader, this.modifier);
if (result != null) {
builder.putEntry(this.key, result);
final ParsedArgument<CommandSourceStack, T> parsed = new ParsedArgument<>(start, reader.getCursor(), result);
builder.withArgumentInternal(this.getName(), parsed, false);
builder.withNode(this, parsed.getRange());
} else if (this.parser.doesNotRead()) {
// Assume this is a null "optional" parser and add the node as read so that we dont end up with an empty context
builder.withNode(this, StringRange.at(start));
}
}
use of org.spongepowered.common.command.brigadier.context.SpongeCommandContextBuilder in project SpongeCommon by SpongePowered.
the class StandardArgumentParser method complete.
@Override
public List<CommandCompletion> complete(@NonNull final CommandCause context, @NonNull final String currentInput) {
final SuggestionsBuilder suggestionsBuilder = new SuggestionsBuilder(currentInput, 0);
this.listSuggestions(new SpongeCommandContextBuilder(null, (CommandSourceStack) context, new RootCommandNode<>(), 0).build(currentInput), suggestionsBuilder);
return suggestionsBuilder.build().getList().stream().map(SpongeCommandCompletion::from).collect(Collectors.toList());
}
Aggregations