Construct more reasonable syntax messages
This commit is contained in:
parent
3f852d068e
commit
c208204fa3
6 changed files with 135 additions and 43 deletions
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue