Construct more reasonable syntax messages

This commit is contained in:
Alexander Söderberg 2020-09-18 22:42:18 +02:00
parent 3f852d068e
commit c208204fa3
No known key found for this signature in database
GPG key ID: C0207FF7EA146678
6 changed files with 135 additions and 43 deletions

View file

@ -39,6 +39,7 @@ import com.intellectualsites.commands.meta.CommandMeta;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
@ -101,7 +102,10 @@ public final class CommandTree<C, M extends CommandMeta> {
public Optional<Command<C, M>> parse(@Nonnull final CommandContext<C> commandContext,
@Nonnull final Queue<String> args) throws
NoSuchCommandException, NoPermissionException, InvalidSyntaxException {
final Optional<Command<C, M>> commandOptional = parseCommand(commandContext, args, this.internalTree);
final Optional<Command<C, M>> commandOptional = parseCommand(new ArrayList<>(),
commandContext,
args,
this.internalTree);
commandOptional.flatMap(Command::getSenderType).ifPresent(requiredType -> {
if (!requiredType.isAssignableFrom(commandContext.getSender().getClass())) {
throw new InvalidCommandSenderException(commandContext.getSender(), requiredType, Collections.emptyList());
@ -110,7 +114,8 @@ public final class CommandTree<C, M extends CommandMeta> {
return commandOptional;
}
private Optional<Command<C, M>> parseCommand(@Nonnull final CommandContext<C> commandContext,
private Optional<Command<C, M>> parseCommand(@Nonnull final List<CommandArgument<C, ?>> parsedArguments,
@Nonnull final CommandContext<C> commandContext,
@Nonnull final Queue<String> commandQueue,
@Nonnull final Node<CommandArgument<C, ?>> root) {
String permission = this.isPermitted(commandContext.getSender(), root);
@ -121,7 +126,10 @@ public final class CommandTree<C, M extends CommandMeta> {
.collect(Collectors.toList()));
}
final Optional<Command<C, M>> parsedChild = this.attemptParseUnambiguousChild(commandContext, root, commandQueue);
final Optional<Command<C, M>> parsedChild = this.attemptParseUnambiguousChild(parsedArguments,
commandContext,
root,
commandQueue);
// noinspection all
if (parsedChild != null) {
return parsedChild;
@ -136,9 +144,7 @@ public final class CommandTree<C, M extends CommandMeta> {
} else {
/* Too many arguments. We have a unique path, so we can send the entire context */
throw new InvalidSyntaxException(this.commandManager.getCommandSyntaxFormatter()
.apply(root.getValue()
.getOwningCommand()
.getArguments()),
.apply(parsedArguments, root),
commandContext.getSender(), this.getChain(root)
.stream()
.map(Node::getValue)
@ -147,10 +153,7 @@ public final class CommandTree<C, M extends CommandMeta> {
} else {
/* Too many arguments. We have a unique path, so we can send the entire context */
throw new InvalidSyntaxException(this.commandManager.getCommandSyntaxFormatter()
.apply(Objects.requireNonNull(
Objects.requireNonNull(root.getValue())
.getOwningCommand())
.getArguments()),
.apply(parsedArguments, root),
commandContext.getSender(), this.getChain(root)
.stream()
.map(Node::getValue)
@ -164,21 +167,32 @@ public final class CommandTree<C, M extends CommandMeta> {
if (child.getValue() != null) {
final ArgumentParseResult<?> result = child.getValue().getParser().parse(commandContext, commandQueue);
if (result.getParsedValue().isPresent()) {
return this.parseCommand(commandContext, commandQueue, child);
parsedArguments.add(child.getValue());
return this.parseCommand(parsedArguments, commandContext, commandQueue, child);
} /*else if (result.getFailure().isPresent() && root.children.size() == 1) {
}*/
}
}
}
/* We could not find a match */
throw new NoSuchCommandException(commandContext.getSender(),
getChain(root).stream().map(Node::getValue).collect(Collectors.toList()),
stringOrEmpty(commandQueue.peek()));
if (root.equals(this.internalTree)) {
throw new NoSuchCommandException(commandContext.getSender(),
getChain(root).stream().map(Node::getValue).collect(Collectors.toList()),
stringOrEmpty(commandQueue.peek()));
}
/* We have already traversed the tree */
throw new InvalidSyntaxException(this.commandManager.getCommandSyntaxFormatter()
.apply(parsedArguments, root),
commandContext.getSender(), this.getChain(root)
.stream()
.map(Node::getValue)
.collect(Collectors.toList()));
}
}
@Nullable
private Optional<Command<C, M>> attemptParseUnambiguousChild(@Nonnull final CommandContext<C> commandContext,
private Optional<Command<C, M>> attemptParseUnambiguousChild(@Nonnull final List<CommandArgument<C, ?>> parsedArguments,
@Nonnull final CommandContext<C> commandContext,
@Nonnull final Node<CommandArgument<C, ?>> root,
@Nonnull final Queue<String> commandQueue) {
String permission;
@ -204,18 +218,25 @@ public final class CommandTree<C, M extends CommandMeta> {
throw new InvalidSyntaxException(this.commandManager.getCommandSyntaxFormatter()
.apply(Objects.requireNonNull(
child.getValue().getOwningCommand())
.getArguments()),
.getArguments(), child),
commandContext.getSender(), this.getChain(root)
.stream()
.map(Node::getValue)
.collect(Collectors.toList()));
} else {
/*
throw new NoSuchCommandException(commandContext.getSender(),
this.getChain(root)
.stream()
.map(Node::getValue)
.collect(Collectors.toList()),
"");
"");*/
throw new InvalidSyntaxException(this.commandManager.getCommandSyntaxFormatter()
.apply(parsedArguments, root),
commandContext.getSender(), this.getChain(root)
.stream()
.map(Node::getValue)
.collect(Collectors.toList()));
}
}
final ArgumentParseResult<?> result = child.getValue().getParser().parse(commandContext, commandQueue);
@ -227,9 +248,7 @@ public final class CommandTree<C, M extends CommandMeta> {
} else {
/* Too many arguments. We have a unique path, so we can send the entire context */
throw new InvalidSyntaxException(this.commandManager.getCommandSyntaxFormatter()
.apply(Objects.requireNonNull(child.getValue()
.getOwningCommand())
.getArguments()),
.apply(parsedArguments, child),
commandContext.getSender(), this.getChain(root)
.stream()
.map(Node::getValue)
@ -237,7 +256,8 @@ public final class CommandTree<C, M extends CommandMeta> {
Collectors.toList()));
}
} else {
return this.parseCommand(commandContext, commandQueue, child);
parsedArguments.add(child.getValue());
return this.parseCommand(parsedArguments, commandContext, commandQueue, child);
}
} else if (result.getFailure().isPresent()) {
throw new ArgumentParseException(result.getFailure().get(), commandContext.getSender(),

View file

@ -23,9 +23,10 @@
//
package com.intellectualsites.commands.arguments;
import com.intellectualsites.commands.CommandTree;
import javax.annotation.Nonnull;
import java.util.List;
import java.util.function.Function;
/**
* Utility that formats chains of {@link CommandArgument command arguments} into syntax strings
@ -33,10 +34,17 @@ import java.util.function.Function;
* @param <C> Command sender type
*/
@FunctionalInterface
public interface CommandSyntaxFormatter<C> extends Function<List<CommandArgument<C, ?>>, String> {
public interface CommandSyntaxFormatter<C> {
@Override
/**
* Format the command arguments into a syntax string
*
* @param commandArguments Command arguments
* @param node Trailing node
* @return Syntax string
*/
@Nonnull
String apply(@Nonnull List<CommandArgument<C, ?>> commandArguments);
String apply(@Nonnull List<CommandArgument<C, ?>> commandArguments,
@Nonnull CommandTree.Node<CommandArgument<C, ?>> node);
}

View file

@ -23,6 +23,8 @@
//
package com.intellectualsites.commands.arguments;
import com.intellectualsites.commands.CommandTree;
import javax.annotation.Nonnull;
import java.util.Iterator;
import java.util.List;
@ -41,7 +43,8 @@ public class StandardCommandSyntaxFormatter<C> implements CommandSyntaxFormatter
@Nonnull
@Override
public final String apply(@Nonnull final List<CommandArgument<C, ?>> commandArguments) {
public final String apply(@Nonnull final List<CommandArgument<C, ?>> commandArguments,
@Nonnull final CommandTree.Node<CommandArgument<C, ?>> node) {
final StringBuilder stringBuilder = new StringBuilder();
final Iterator<CommandArgument<C, ?>> iterator = commandArguments.iterator();
while (iterator.hasNext()) {
@ -59,6 +62,27 @@ public class StandardCommandSyntaxFormatter<C> implements CommandSyntaxFormatter
stringBuilder.append(" ");
}
}
CommandTree.Node<CommandArgument<C, ?>> tail = node;
while (tail != null && !tail.isLeaf()) {
if (tail.getChildren().size() > 1) {
stringBuilder.append(" ");
final Iterator<CommandTree.Node<CommandArgument<C, ?>>> childIterator = tail.getChildren().iterator();
while (childIterator.hasNext()) {
final CommandTree.Node<CommandArgument<C, ?>> child = childIterator.next();
stringBuilder.append(child.getValue().getName());
if (childIterator.hasNext()) {
stringBuilder.append("|");
}
}
break;
}
final CommandArgument<C, ?> argument = tail.getChildren().get(0).getValue();
stringBuilder.append(" ")
.append(argument.isRequired() ? '<' : '[')
.append(argument.getName())
.append(argument.isRequired() ? '>' : ']');
tail = tail.getChildren().get(0);
}
return stringBuilder.toString();
}

View file

@ -66,14 +66,4 @@ public class InvalidSyntaxException extends CommandParseException {
return String.format("Invalid command syntax. Correct syntax is: %s", this.correctSyntax);
}
@Override
public final synchronized Throwable fillInStackTrace() {
return this;
}
@Override
public final synchronized Throwable initCause(final Throwable cause) {
return this;
}
}

View file

@ -114,6 +114,12 @@ class CommandTreeTest {
manager.executeCommand(new TestCommandSender(), "default 5").join();
}
@Test
void invalidCommand() {
Assertions.assertThrows(CompletionException.class, () -> manager
.executeCommand(new TestCommandSender(), "invalid test").join());
}
public static final class SpecificCommandSender extends TestCommandSender {
}