Merge pull request #499 from EngineHub/fix/quoted

Fixes using suggestions with quoted args, as well as a few other minor issues with suggestions.
This commit is contained in:
wizjany 2019-07-28 10:07:32 -04:00 committed by GitHub
commit 6855f22152
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 75 additions and 25 deletions

View File

@ -335,7 +335,7 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter {
// code of WorldEdit expects it // code of WorldEdit expects it
String[] split = new String[args.length + 1]; String[] split = new String[args.length + 1];
System.arraycopy(args, 0, split, 1, args.length); 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)); CommandEvent event = new CommandEvent(wrapCommandSender(sender), Joiner.on(" ").join(split));
getWorldEdit().getEventBus().post(event); getWorldEdit().getEventBus().post(event);
@ -349,7 +349,7 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter {
// code of WorldEdit expects it // code of WorldEdit expects it
String[] split = new String[args.length + 1]; String[] split = new String[args.length + 1];
System.arraycopy(args, 0, split, 1, args.length); System.arraycopy(args, 0, split, 1, args.length);
split[0] = "/" + cmd.getName(); split[0] = "/" + commandLabel;
String arguments = Joiner.on(" ").join(split); String arguments = Joiner.on(" ").join(split);
CommandSuggestionEvent event = new CommandSuggestionEvent(wrapCommandSender(sender), arguments); CommandSuggestionEvent event = new CommandSuggestionEvent(wrapCommandSender(sender), arguments);

View File

@ -49,11 +49,12 @@ public final class SuggestionHelper {
public static Stream<String> getBlockCategorySuggestions(String tag, boolean allowRandom) { public static Stream<String> getBlockCategorySuggestions(String tag, boolean allowRandom) {
if (tag.isEmpty() || tag.equals("#")) { if (tag.isEmpty() || tag.equals("#")) {
return Stream.of("##", "##*"); return allowRandom ? Stream.of("##", "##*") : Stream.of("##");
} }
if (tag.startsWith("#")) { if (tag.startsWith("##")) {
if (tag.equals("##")) { 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) { } else if (tag.equals("##*") && allowRandom) {
return getNamespacedRegistrySuggestions(BlockCategory.REGISTRY, tag.substring(3)).map(s -> "##*" + s); return getNamespacedRegistrySuggestions(BlockCategory.REGISTRY, tag.substring(3)).map(s -> "##*" + s);
} else { } else {

View File

@ -42,6 +42,7 @@ import com.sk89q.worldedit.internal.registry.InputParser;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
/** /**
* A registry of known {@link Mask}s. Provides methods to instantiate * A registry of known {@link Mask}s. Provides methods to instantiate
@ -74,6 +75,16 @@ public final class MaskFactory extends AbstractFactory<Mask> {
register(new BiomeMaskParser(worldEdit)); register(new BiomeMaskParser(worldEdit));
} }
@Override
public List<String> 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 @Override
public Mask parseFromInput(String input, ParserContext context) throws InputParseException { public Mask parseFromInput(String input, ParserContext context) throws InputParseException {
List<Mask> masks = new ArrayList<>(); List<Mask> masks = new ArrayList<>();

View File

@ -431,7 +431,7 @@ public final class PlatformCommandManager {
} }
private Stream<Substring> parseArgs(String input) { private Stream<Substring> parseArgs(String input) {
return new CommandArgParser(CommandArgParser.spaceSplit(input.substring(1))).parseArgs(); return CommandArgParser.forArgString(input.substring(1)).parseArgs();
} }
@Subscribe @Subscribe

View File

@ -31,6 +31,10 @@ import java.util.stream.Stream;
public class CommandArgParser { public class CommandArgParser {
public static CommandArgParser forArgString(String argString) {
return new CommandArgParser(spaceSplit(argString));
}
public static ImmutableList<Substring> spaceSplit(String string) { public static ImmutableList<Substring> spaceSplit(String string) {
ImmutableList.Builder<Substring> result = ImmutableList.builder(); ImmutableList.Builder<Substring> result = ImmutableList.builder();
int index = 0; int index = 0;
@ -67,16 +71,28 @@ public class CommandArgParser {
handleQuote(nextPart); handleQuote(nextPart);
} }
} }
if (currentArg.size() > 0) {
finishArg(); // force finish "hanging" args
}
return args.build(); return args.build();
} }
private void handleNormal(Substring part) { private void handleNormal(Substring part) {
if (part.getSubstring().startsWith("\"")) { final String strPart = part.getSubstring();
state = State.QUOTE; if (strPart.startsWith("\"")) {
currentArg.add(Substring.wrap( if (strPart.endsWith("\"") && strPart.length() > 1) {
part.getSubstring().substring(1), currentArg.add(Substring.wrap(
part.getStart(), part.getEnd() 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 { } else {
currentArg.add(part); currentArg.add(part);
finishArg(); finishArg();
@ -88,7 +104,7 @@ public class CommandArgParser {
state = State.NORMAL; state = State.NORMAL;
currentArg.add(Substring.wrap( currentArg.add(Substring.wrap(
part.getSubstring().substring(0, part.getSubstring().length() - 1), part.getSubstring().substring(0, part.getSubstring().length() - 1),
part.getStart(), part.getEnd() part.getStart(), part.getEnd() - 1
)); ));
finishArg(); finishArg();
} else { } else {

View File

@ -65,20 +65,33 @@ public class CommandUtil {
* Fix {@code suggestions} to replace the last space-separated word in {@code arguments}. * Fix {@code suggestions} to replace the last space-separated word in {@code arguments}.
*/ */
public static List<String> fixSuggestions(String arguments, List<Substring> suggestions) { public static List<String> fixSuggestions(String arguments, List<Substring> suggestions) {
Substring lastArg = Iterables.getLast(CommandArgParser.spaceSplit(arguments)); Substring lastArg = Iterables.getLast(
CommandArgParser.spaceSplit(arguments)
);
return suggestions.stream() return suggestions.stream()
// Re-map suggestions to only operate on the last non-quoted word
.map(CommandUtil::onlyOnLastQuotedWord)
.map(suggestion -> CommandUtil.suggestLast(lastArg, suggestion)) .map(suggestion -> CommandUtil.suggestLast(lastArg, suggestion))
.filter(Optional::isPresent) .filter(Optional::isPresent)
.map(Optional::get) .map(Optional::get)
.collect(toList()); .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 * Given the last word of a command, mutate the suggestion to replace the last word, if
* possible. * possible.
*/ */
private static Optional<String> suggestLast(Substring last, Substring suggestion) { private static Optional<String> 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. // this suggestion is for the next argument.
if (last.getSubstring().isEmpty()) { if (last.getSubstring().isEmpty()) {
return Optional.of(suggestion.getSubstring()); return Optional.of(suggestion.getSubstring());

View File

@ -23,29 +23,36 @@ import com.google.common.collect.ImmutableList;
import com.sk89q.worldedit.internal.util.Substring; import com.sk89q.worldedit.internal.util.Substring;
import org.junit.jupiter.api.Test; 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; import static org.junit.jupiter.api.Assertions.assertEquals;
public class CommandArgParserTest { class CommandArgParserTest {
private static List<Substring> argParse(String s) {
return CommandArgParser.forArgString(s).parseArgs().collect(Collectors.toList());
}
@Test @Test
public void testSpaceSplit() { void testArgumentParsing() {
assertEquals(ImmutableList.of( assertEquals(ImmutableList.of(
Substring.wrap("", 0, 0) Substring.wrap("", 0, 0)
), spaceSplit("")); ), argParse(""));
assertEquals(ImmutableList.of( assertEquals(ImmutableList.of(
Substring.wrap("ab", 0, 2) Substring.wrap("ab", 0, 2)
), spaceSplit("ab")); ), argParse("ab"));
assertEquals(ImmutableList.of( assertEquals(ImmutableList.of(
Substring.wrap("", 0, 0), Substring.wrap("", 0, 0),
Substring.wrap("", 1, 1) Substring.wrap("", 1, 1)
), spaceSplit(" ")); ), argParse(" "));
assertEquals(ImmutableList.of( assertEquals(ImmutableList.of(
Substring.wrap("a", 0, 1), Substring.wrap("a", 0, 1),
Substring.wrap("", 2, 2) Substring.wrap("", 2, 2)
), spaceSplit("a ")); ), argParse("a "));
assertEquals(ImmutableList.of( assertEquals(ImmutableList.of(
Substring.wrap("a", 0, 1), Substring.wrap("a", 0, 1),
Substring.wrap("b", 2, 3) Substring.wrap("b", 2, 3)
), spaceSplit("a b")); ), argParse("a b"));
} }
} }

View File

@ -111,7 +111,8 @@ public final class CommandWrapper {
// Ensure there is a space! // Ensure there is a space!
if (suggestion.getStart() == suggestion.getEnd() if (suggestion.getStart() == suggestion.getEnd()
&& suggestion.getEnd() == builder.getInput().length() && suggestion.getEnd() == builder.getInput().length()
&& !builder.getInput().endsWith(" ")) { && !builder.getInput().endsWith(" ")
&& !builder.getInput().endsWith("\"")) {
suggestionText = " " + suggestionText; suggestionText = " " + suggestionText;
} }
result.add(new Suggestion( result.add(new Suggestion(

View File

@ -103,7 +103,8 @@ public final class CommandWrapper {
// Ensure there is a space! // Ensure there is a space!
if (suggestion.getStart() == suggestion.getEnd() if (suggestion.getStart() == suggestion.getEnd()
&& suggestion.getEnd() == builder.getInput().length() && suggestion.getEnd() == builder.getInput().length()
&& !builder.getInput().endsWith(" ")) { && !builder.getInput().endsWith(" ")
&& !builder.getInput().endsWith("\"")) {
suggestionText = " " + suggestionText; suggestionText = " " + suggestionText;
} }
result.add(new Suggestion( result.add(new Suggestion(