diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java index e4fb3ea8c..69e53ee5b 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/WorldEditPlugin.java @@ -335,7 +335,7 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter { // code of WorldEdit expects it String[] split = new String[args.length + 1]; System.arraycopy(args, 0, split, 1, args.length); - split[0] = "/" + cmd.getName(); + split[0] = "/" + commandLabel; CommandEvent event = new CommandEvent(wrapCommandSender(sender), Joiner.on(" ").join(split)); getWorldEdit().getEventBus().post(event); @@ -349,7 +349,7 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter { // code of WorldEdit expects it String[] split = new String[args.length + 1]; System.arraycopy(args, 0, split, 1, args.length); - split[0] = "/" + cmd.getName(); + split[0] = "/" + commandLabel; String arguments = Joiner.on(" ").join(split); CommandSuggestionEvent event = new CommandSuggestionEvent(wrapCommandSender(sender), arguments); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/SuggestionHelper.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/SuggestionHelper.java index e0014ee56..ed5e3aacb 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/SuggestionHelper.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/SuggestionHelper.java @@ -49,11 +49,12 @@ public final class SuggestionHelper { public static Stream getBlockCategorySuggestions(String tag, boolean allowRandom) { if (tag.isEmpty() || tag.equals("#")) { - return Stream.of("##", "##*"); + return allowRandom ? Stream.of("##", "##*") : Stream.of("##"); } - if (tag.startsWith("#")) { + if (tag.startsWith("##")) { if (tag.equals("##")) { - return Stream.concat(Stream.of("##*"), getNamespacedRegistrySuggestions(BlockCategory.REGISTRY, tag.substring(2)).map(s -> "##" + s)); + return Stream.concat(allowRandom ? Stream.of("##*") : Stream.empty(), + getNamespacedRegistrySuggestions(BlockCategory.REGISTRY, tag.substring(2)).map(s -> "##" + s)); } else if (tag.equals("##*") && allowRandom) { return getNamespacedRegistrySuggestions(BlockCategory.REGISTRY, tag.substring(3)).map(s -> "##*" + s); } else { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/MaskFactory.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/MaskFactory.java index 73df7a52d..b4845f35e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/MaskFactory.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/MaskFactory.java @@ -42,6 +42,7 @@ import com.sk89q.worldedit.internal.registry.InputParser; import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; /** * A registry of known {@link Mask}s. Provides methods to instantiate @@ -74,6 +75,16 @@ public final class MaskFactory extends AbstractFactory { register(new BiomeMaskParser(worldEdit)); } + @Override + public List getSuggestions(String input) { + final String[] split = input.split(" "); + if (split.length > 1) { + String prev = input.substring(0, input.lastIndexOf(" ")) + " "; + return super.getSuggestions(split[split.length -1]).stream().map(s -> prev + s).collect(Collectors.toList()); + } + return super.getSuggestions(input); + } + @Override public Mask parseFromInput(String input, ParserContext context) throws InputParseException { List masks = new ArrayList<>(); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java index 3086613ec..673bb2a17 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java @@ -431,7 +431,7 @@ public final class PlatformCommandManager { } private Stream parseArgs(String input) { - return new CommandArgParser(CommandArgParser.spaceSplit(input.substring(1))).parseArgs(); + return CommandArgParser.forArgString(input.substring(1)).parseArgs(); } @Subscribe diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/CommandArgParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/CommandArgParser.java index c7ce23311..07e878068 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/CommandArgParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/CommandArgParser.java @@ -31,6 +31,10 @@ import java.util.stream.Stream; public class CommandArgParser { + public static CommandArgParser forArgString(String argString) { + return new CommandArgParser(spaceSplit(argString)); + } + public static ImmutableList spaceSplit(String string) { ImmutableList.Builder result = ImmutableList.builder(); int index = 0; @@ -67,16 +71,28 @@ public class CommandArgParser { handleQuote(nextPart); } } + if (currentArg.size() > 0) { + finishArg(); // force finish "hanging" args + } return args.build(); } private void handleNormal(Substring part) { - if (part.getSubstring().startsWith("\"")) { - state = State.QUOTE; - currentArg.add(Substring.wrap( - part.getSubstring().substring(1), - part.getStart(), part.getEnd() - )); + final String strPart = part.getSubstring(); + if (strPart.startsWith("\"")) { + if (strPart.endsWith("\"") && strPart.length() > 1) { + currentArg.add(Substring.wrap( + strPart.substring(1, strPart.length() - 1), + part.getStart() + 1, part.getEnd() - 1 + )); + finishArg(); + } else { + state = State.QUOTE; + currentArg.add(Substring.wrap( + strPart.substring(1), + part.getStart() + 1, part.getEnd() + )); + } } else { currentArg.add(part); finishArg(); @@ -88,7 +104,7 @@ public class CommandArgParser { state = State.NORMAL; currentArg.add(Substring.wrap( part.getSubstring().substring(0, part.getSubstring().length() - 1), - part.getStart(), part.getEnd() + part.getStart(), part.getEnd() - 1 )); finishArg(); } else { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/CommandUtil.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/CommandUtil.java index b98236090..b259435a1 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/CommandUtil.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/CommandUtil.java @@ -65,20 +65,33 @@ public class CommandUtil { * Fix {@code suggestions} to replace the last space-separated word in {@code arguments}. */ public static List fixSuggestions(String arguments, List suggestions) { - Substring lastArg = Iterables.getLast(CommandArgParser.spaceSplit(arguments)); + Substring lastArg = Iterables.getLast( + CommandArgParser.spaceSplit(arguments) + ); return suggestions.stream() + // Re-map suggestions to only operate on the last non-quoted word + .map(CommandUtil::onlyOnLastQuotedWord) .map(suggestion -> CommandUtil.suggestLast(lastArg, suggestion)) .filter(Optional::isPresent) .map(Optional::get) .collect(toList()); } + private static Substring onlyOnLastQuotedWord(Substring suggestion) { + String substr = suggestion.getSubstring(); + int sp = substr.lastIndexOf(' '); + if (sp < 0) { + return suggestion; + } + return Substring.wrap(substr.substring(sp + 1), suggestion.getStart() + sp + 1, suggestion.getEnd()); + } + /** * Given the last word of a command, mutate the suggestion to replace the last word, if * possible. */ private static Optional suggestLast(Substring last, Substring suggestion) { - if (suggestion.getStart() == last.getEnd()) { + if (suggestion.getStart() == last.getEnd() && !last.getSubstring().equals("\"")) { // this suggestion is for the next argument. if (last.getSubstring().isEmpty()) { return Optional.of(suggestion.getSubstring()); diff --git a/worldedit-core/src/test/java/com/sk89q/worldedit/internal/command/CommandArgParserTest.java b/worldedit-core/src/test/java/com/sk89q/worldedit/internal/command/CommandArgParserTest.java index c696bdabb..632e1219b 100644 --- a/worldedit-core/src/test/java/com/sk89q/worldedit/internal/command/CommandArgParserTest.java +++ b/worldedit-core/src/test/java/com/sk89q/worldedit/internal/command/CommandArgParserTest.java @@ -23,29 +23,36 @@ import com.google.common.collect.ImmutableList; import com.sk89q.worldedit.internal.util.Substring; import org.junit.jupiter.api.Test; -import static com.sk89q.worldedit.internal.command.CommandArgParser.spaceSplit; +import java.util.List; +import java.util.stream.Collectors; + import static org.junit.jupiter.api.Assertions.assertEquals; -public class CommandArgParserTest { +class CommandArgParserTest { + + private static List argParse(String s) { + return CommandArgParser.forArgString(s).parseArgs().collect(Collectors.toList()); + } + @Test - public void testSpaceSplit() { + void testArgumentParsing() { assertEquals(ImmutableList.of( Substring.wrap("", 0, 0) - ), spaceSplit("")); + ), argParse("")); assertEquals(ImmutableList.of( Substring.wrap("ab", 0, 2) - ), spaceSplit("ab")); + ), argParse("ab")); assertEquals(ImmutableList.of( Substring.wrap("", 0, 0), Substring.wrap("", 1, 1) - ), spaceSplit(" ")); + ), argParse(" ")); assertEquals(ImmutableList.of( Substring.wrap("a", 0, 1), Substring.wrap("", 2, 2) - ), spaceSplit("a ")); + ), argParse("a ")); assertEquals(ImmutableList.of( Substring.wrap("a", 0, 1), Substring.wrap("b", 2, 3) - ), spaceSplit("a b")); + ), argParse("a b")); } } diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/CommandWrapper.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/CommandWrapper.java index 8efa5ce97..462975acb 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/CommandWrapper.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/CommandWrapper.java @@ -111,7 +111,8 @@ public final class CommandWrapper { // Ensure there is a space! if (suggestion.getStart() == suggestion.getEnd() && suggestion.getEnd() == builder.getInput().length() - && !builder.getInput().endsWith(" ")) { + && !builder.getInput().endsWith(" ") + && !builder.getInput().endsWith("\"")) { suggestionText = " " + suggestionText; } result.add(new Suggestion( diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/CommandWrapper.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/CommandWrapper.java index 05d4fe5ad..0cf62fb4b 100644 --- a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/CommandWrapper.java +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/CommandWrapper.java @@ -103,7 +103,8 @@ public final class CommandWrapper { // Ensure there is a space! if (suggestion.getStart() == suggestion.getEnd() && suggestion.getEnd() == builder.getInput().length() - && !builder.getInput().endsWith(" ")) { + && !builder.getInput().endsWith(" ") + && !builder.getInput().endsWith("\"")) { suggestionText = " " + suggestionText; } result.add(new Suggestion(