From 96e2b6c5af6861f0d57c2dc0bee799bb7a981c17 Mon Sep 17 00:00:00 2001 From: wizjany Date: Thu, 4 Jul 2019 01:48:55 -0400 Subject: [PATCH 1/5] First attempt at fixing quoted string oddities. --- .../extension/factory/MaskFactory.java | 11 +++++++++ .../internal/command/CommandArgParser.java | 24 ++++++++++++++----- 2 files changed, 29 insertions(+), 6 deletions(-) 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/internal/command/CommandArgParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/CommandArgParser.java index c7ce23311..156563bbb 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 @@ -67,16 +67,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(), part.getEnd() + )); + finishArg(); + } else { + state = State.QUOTE; + currentArg.add(Substring.wrap( + strPart.substring(1), + part.getStart(), part.getEnd() + )); + } } else { currentArg.add(part); finishArg(); From 3a5170a0e80736bf7d966b3c142509195dabe3b4 Mon Sep 17 00:00:00 2001 From: Kenzie Togami Date: Sat, 27 Jul 2019 18:12:18 -0700 Subject: [PATCH 2/5] Potenially fix quoted string completion --- .../platform/PlatformCommandManager.java | 2 +- .../internal/command/CommandArgParser.java | 10 +++++--- .../internal/command/CommandUtil.java | 5 +++- .../command/CommandArgParserTest.java | 23 ++++++++++++------- 4 files changed, 27 insertions(+), 13 deletions(-) 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 156563bbb..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; @@ -79,14 +83,14 @@ public class CommandArgParser { if (strPart.endsWith("\"") && strPart.length() > 1) { currentArg.add(Substring.wrap( strPart.substring(1, strPart.length() - 1), - part.getStart(), part.getEnd() + part.getStart() + 1, part.getEnd() - 1 )); finishArg(); } else { state = State.QUOTE; currentArg.add(Substring.wrap( strPart.substring(1), - part.getStart(), part.getEnd() + part.getStart() + 1, part.getEnd() )); } } else { @@ -100,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..6dbb89648 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 @@ -21,6 +21,7 @@ package com.sk89q.worldedit.internal.command; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; +import com.google.common.collect.Iterators; import com.sk89q.worldedit.extension.platform.PlatformCommandManager; import com.sk89q.worldedit.internal.util.Substring; import com.sk89q.worldedit.util.formatting.text.Component; @@ -65,7 +66,9 @@ 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() .map(suggestion -> CommandUtil.suggestLast(lastArg, suggestion)) .filter(Optional::isPresent) 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")); } } From 76b608f90b2afd4e4cf6c0497ef86731f6bfc0e4 Mon Sep 17 00:00:00 2001 From: Kenzie Togami Date: Sat, 27 Jul 2019 23:31:38 -0700 Subject: [PATCH 3/5] Fix suggestions on Bukkit for good --- .../sk89q/worldedit/bukkit/WorldEditPlugin.java | 4 ++-- .../worldedit/internal/command/CommandUtil.java | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) 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/internal/command/CommandUtil.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/CommandUtil.java index 6dbb89648..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 @@ -21,7 +21,6 @@ package com.sk89q.worldedit.internal.command; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; -import com.google.common.collect.Iterators; import com.sk89q.worldedit.extension.platform.PlatformCommandManager; import com.sk89q.worldedit.internal.util.Substring; import com.sk89q.worldedit.util.formatting.text.Component; @@ -70,18 +69,29 @@ public class CommandUtil { 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()); From 50cea37439ed048f0a28e14535fb855987aef027 Mon Sep 17 00:00:00 2001 From: Kenzie Togami Date: Sat, 27 Jul 2019 23:49:11 -0700 Subject: [PATCH 4/5] Fix other minor suggestion bugs --- .../com/sk89q/worldedit/command/util/SuggestionHelper.java | 2 +- .../main/java/com/sk89q/worldedit/fabric/CommandWrapper.java | 3 ++- .../main/java/com/sk89q/worldedit/forge/CommandWrapper.java | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) 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..3a41711b6 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 @@ -51,7 +51,7 @@ public final class SuggestionHelper { if (tag.isEmpty() || tag.equals("#")) { return 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)); } else if (tag.equals("##*") && allowRandom) { 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( From c93e5ccfdfd100c20836c66ce7f280beaf5d0e6f Mon Sep 17 00:00:00 2001 From: wizjany Date: Sun, 28 Jul 2019 10:02:47 -0400 Subject: [PATCH 5/5] Don't suggest ##* for masks. --- .../com/sk89q/worldedit/command/util/SuggestionHelper.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 3a41711b6..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.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 {