diff --git a/bukkit/src/main/java/revxrsal/commands/bukkit/core/BukkitCommandExecutor.java b/bukkit/src/main/java/revxrsal/commands/bukkit/core/BukkitCommandExecutor.java index f6dca712..c9ed645c 100644 --- a/bukkit/src/main/java/revxrsal/commands/bukkit/core/BukkitCommandExecutor.java +++ b/bukkit/src/main/java/revxrsal/commands/bukkit/core/BukkitCommandExecutor.java @@ -35,7 +35,7 @@ public BukkitCommandExecutor(BukkitHandler handler) { @NotNull String alias, @NotNull String[] args) { BukkitCommandActor actor = new BukkitActor(sender); - ArgumentStack arguments = ArgumentStack.of(args); + ArgumentStack arguments = ArgumentStack.ofUnsafe(args); arguments.addFirst(command.getName()); return handler.getAutoCompleter().complete(actor, arguments); diff --git a/bungee/src/main/java/revxrsal/commands/bungee/core/BungeeCommand.java b/bungee/src/main/java/revxrsal/commands/bungee/core/BungeeCommand.java index a2b5a69f..c0225c0b 100644 --- a/bungee/src/main/java/revxrsal/commands/bungee/core/BungeeCommand.java +++ b/bungee/src/main/java/revxrsal/commands/bungee/core/BungeeCommand.java @@ -24,7 +24,7 @@ public BungeeCommand(String name, BungeeHandler handler) { } @Override public Iterable onTabComplete(CommandSender sender, String[] args) { - ArgumentStack arguments = ArgumentStack.of(args); + ArgumentStack arguments = ArgumentStack.ofUnsafe(args); arguments.addFirst(getName()); BungeeCommandActor actor = new BungeeActor(sender); diff --git a/common/src/main/java/revxrsal/commands/command/ArgumentStack.java b/common/src/main/java/revxrsal/commands/command/ArgumentStack.java index 0d3ab9da..45433340 100644 --- a/common/src/main/java/revxrsal/commands/command/ArgumentStack.java +++ b/common/src/main/java/revxrsal/commands/command/ArgumentStack.java @@ -3,6 +3,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Unmodifiable; import org.jetbrains.annotations.UnmodifiableView; +import revxrsal.commands.autocomplete.AutoCompleter; import revxrsal.commands.core.LinkedArgumentStack; import revxrsal.commands.util.tokenize.QuotedStringTokenizer; @@ -103,6 +104,20 @@ public interface ArgumentStack extends Deque, List, Cloneable { return new LinkedArgumentStack(QuotedStringTokenizer.tokenize(String.join(" ", arguments))); } + + /** + * Returns a new {@link ArgumentStack} with the specified arguments. This + * will not remove trailing space, and hence may give incorrect + * measures. This should only be used with {@link AutoCompleter#complete(CommandActor, ArgumentStack)}. + * + * @param arguments Arguments to clone from + * @return The newly created argument stack. + */ + static @NotNull ArgumentStack ofUnsafe(@NotNull String... arguments) { + if (arguments.length == 0) return ArgumentStack.empty(); + return new LinkedArgumentStack(QuotedStringTokenizer.tokenizeUnsafe(String.join(" ", arguments))); + } + /** * Returns a new {@link ArgumentStack} with the specified arguments, * from splitting the string by whitespace. diff --git a/common/src/main/java/revxrsal/commands/util/tokenize/QuotedStringTokenizer.java b/common/src/main/java/revxrsal/commands/util/tokenize/QuotedStringTokenizer.java index 10aa79a5..9a8fb135 100644 --- a/common/src/main/java/revxrsal/commands/util/tokenize/QuotedStringTokenizer.java +++ b/common/src/main/java/revxrsal/commands/util/tokenize/QuotedStringTokenizer.java @@ -46,6 +46,8 @@ */ public final class QuotedStringTokenizer { + public static final List EMPTY_TEXT = Collections.singletonList(""); + private QuotedStringTokenizer() {} private static final int CHAR_BACKSLASH = '\\'; @@ -74,6 +76,20 @@ public static List tokenize(String arguments) { return returnedArgs; } + /** + * Returns a list of tokens from parsing the given input, + * respecting quotes and breaks. + * + * @param arguments Argument string to parse + * @return A list of tokens. + */ + public static List tokenizeUnsafe(String arguments) { + if (arguments.length() == 0) { + return EMPTY_TEXT; + } + return tokenize(arguments); + } + // Parsing methods private static void skipWhiteSpace(TokenizerState state) throws ArgumentParseException { diff --git a/sponge/src/main/java/revxrsal/commands/sponge/core/SpongeCommandCallable.java b/sponge/src/main/java/revxrsal/commands/sponge/core/SpongeCommandCallable.java index 2ec2b93d..241dacb8 100644 --- a/sponge/src/main/java/revxrsal/commands/sponge/core/SpongeCommandCallable.java +++ b/sponge/src/main/java/revxrsal/commands/sponge/core/SpongeCommandCallable.java @@ -15,6 +15,8 @@ import java.util.List; import java.util.Optional; +import static revxrsal.commands.util.tokenize.QuotedStringTokenizer.EMPTY_TEXT; + // i'm not sure if we are supposed to be providing implementations // for testPermission(), getHelp() and getUsage(). final class SpongeCommandCallable implements CommandCallable { @@ -37,7 +39,7 @@ public SpongeCommandCallable(SpongeCommandHandler handler, String name) { @Override public @NotNull List getSuggestions(@NotNull CommandSource source, @NotNull String arguments, @Nullable Location targetPosition) { CommandActor actor = new SpongeActor(source); - ArgumentStack args = ArgumentStack.fromString(arguments); + ArgumentStack args = arguments.isEmpty() ? ArgumentStack.exactly(EMPTY_TEXT) : ArgumentStack.fromString(arguments); return handler.getAutoCompleter().complete(actor, args); } diff --git a/velocity/src/main/java/revxrsal/commands/velocity/core/VelocitySimpleCommand.java b/velocity/src/main/java/revxrsal/commands/velocity/core/VelocitySimpleCommand.java index 2621adb1..3b98ef68 100644 --- a/velocity/src/main/java/revxrsal/commands/velocity/core/VelocitySimpleCommand.java +++ b/velocity/src/main/java/revxrsal/commands/velocity/core/VelocitySimpleCommand.java @@ -25,7 +25,7 @@ public VelocitySimpleCommand(VelocityHandler handler) { } @Override public List suggest(Invocation invocation) { - ArgumentStack arguments = ArgumentStack.of(invocation.arguments()); + ArgumentStack arguments = ArgumentStack.ofUnsafe(invocation.arguments()); arguments.addFirst(invocation.alias()); VelocityCommandActor actor = new VelocityActor(invocation.source(), handler.getServer());