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 {
}

View file

@ -25,16 +25,28 @@ package com.intellectualsites.commands;
import com.intellectualsites.commands.arguments.CommandArgument;
import com.intellectualsites.commands.arguments.StaticArgument;
import com.intellectualsites.commands.exceptions.ArgumentParseException;
import com.intellectualsites.commands.exceptions.InvalidCommandSenderException;
import com.intellectualsites.commands.exceptions.InvalidSyntaxException;
import com.intellectualsites.commands.exceptions.NoPermissionException;
import com.intellectualsites.commands.exceptions.NoSuchCommandException;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.PluginIdentifiableCommand;
import org.bukkit.plugin.Plugin;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;
import java.util.function.Consumer;
final class BukkitCommand<C> extends org.bukkit.command.Command implements PluginIdentifiableCommand {
private static final String MESSAGE_NO_PERMS = ChatColor.RED
+ "I'm sorry, but you do not have permission to perform this command. "
+ "Please contact the server administrators if you believe that this is in error.";
private static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command. Type \"/help\" for help.";
private final CommandArgument<C, ?> command;
private final BukkitCommandManager<C> bukkitCommandManager;
private final com.intellectualsites.commands.Command<C, BukkitCommandMeta> cloudCommand;
@ -63,13 +75,24 @@ final class BukkitCommand<C> extends org.bukkit.command.Command implements Plugi
builder.toString())
.whenComplete(((commandResult, throwable) -> {
if (throwable != null) {
commandSender.sendMessage(ChatColor.RED + throwable.getMessage());
commandSender.sendMessage(ChatColor.RED + throwable.getCause().getMessage());
throwable.printStackTrace();
throwable.getCause().printStackTrace();
} else {
// Do something...
commandSender.sendMessage("All good!");
if (throwable instanceof InvalidSyntaxException) {
commandSender.sendMessage(ChatColor.RED + "Invalid Command Syntax. "
+ "Correct command syntax is: "
+ ChatColor.GRAY + "/"
+ ((InvalidSyntaxException) throwable).getCorrectSyntax());
} else if (throwable instanceof InvalidCommandSenderException) {
commandSender.sendMessage(ChatColor.RED + throwable.getMessage());
} else if (throwable instanceof NoPermissionException) {
commandSender.sendMessage(MESSAGE_NO_PERMS);
} else if (throwable instanceof NoSuchCommandException) {
commandSender.sendMessage(MESSAGE_UNKNOWN_COMMAND);
} else if (throwable instanceof ArgumentParseException) {
commandSender.sendMessage(ChatColor.RED + "Invalid Command Argument: "
+ ChatColor.GRAY + throwable.getCause().getMessage());
} else {
commandSender.sendMessage(throwable.getMessage());
throwable.printStackTrace();
}
}
}));
return true;
@ -101,4 +124,25 @@ final class BukkitCommand<C> extends org.bukkit.command.Command implements Plugi
return this.cloudCommand.getCommandPermission();
}
@Nullable
private <E extends Throwable> E captureException(@Nonnull final Class<E> clazz, @Nullable final Throwable throwable) {
if (throwable == null) {
return null;
}
if (clazz.equals(throwable.getClass())) {
//noinspection unchecked
return (E) throwable;
}
return captureException(clazz, throwable.getCause());
}
private <E extends Throwable> boolean handleException(@Nullable final E throwable,
@Nonnull final Consumer<E> consumer) {
if (throwable == null) {
return false;
}
consumer.accept(throwable);
return true;
}
}