diff --git a/config/checkstyle/import-control.xml b/config/checkstyle/import-control.xml index c783552a9..a291b5ad5 100644 --- a/config/checkstyle/import-control.xml +++ b/config/checkstyle/import-control.xml @@ -44,7 +44,7 @@ - + diff --git a/worldedit-bukkit/build.gradle b/worldedit-bukkit/build.gradle index 21b5bdebb..8076e2ca1 100644 --- a/worldedit-bukkit/build.gradle +++ b/worldedit-bukkit/build.gradle @@ -19,7 +19,8 @@ dependencies { compile project(':worldedit-libs:bukkit') compile 'com.sk89q:dummypermscompat:1.10' compile 'org.bukkit:bukkit:1.13.2-R0.1-SNAPSHOT' // zzz - compile "io.papermc:paperlib:1.0.1" + compile 'com.destroystokyo.paper:paper-api:1.13.2-R0.1-SNAPSHOT' + compile "io.papermc:paperlib:1.0.2" compile 'org.apache.logging.log4j:log4j-slf4j-impl:2.8.1' compile 'org.bstats:bstats-bukkit:1.4' testCompile 'org.mockito:mockito-core:1.9.0-rc1' @@ -53,7 +54,7 @@ shadowJar { include(dependency("org.bstats:bstats-bukkit:1.4")) } relocate ("io.papermc.lib", "com.sk89q.worldedit.bukkit.paperlib") { - include(dependency("io.papermc:paperlib:1.0.1")) + include(dependency("io.papermc:paperlib:1.0.2")) } } } diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitAdapter.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitAdapter.java index d02bd1abc..b8418d03d 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitAdapter.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitAdapter.java @@ -151,7 +151,7 @@ public class BukkitAdapter { if (match != null) { return match; } else { - throw new IllegalArgumentException("Can't find a Bukkit world for " + world); + throw new IllegalArgumentException("Can't find a Bukkit world for " + world.getName()); } } } 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 1fb7633bf..570ef30be 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 @@ -49,6 +49,7 @@ import com.sk89q.worldedit.world.gamemode.GameModes; import com.sk89q.worldedit.world.item.ItemCategory; import com.sk89q.worldedit.world.item.ItemType; import com.sk89q.worldedit.world.weather.WeatherTypes; +import io.papermc.lib.PaperLib; import org.bstats.bukkit.Metrics; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -75,6 +76,7 @@ import java.io.InputStream; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Optional; import java.util.jar.JarFile; import java.util.logging.Level; import java.util.zip.ZipEntry; @@ -122,6 +124,10 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter { // Now we can register events getServer().getPluginManager().registerEvents(new WorldEditListener(this), this); + // register async tab complete, if available + if (PaperLib.isPaper()) { + getServer().getPluginManager().registerEvents(new AsyncTabCompleteListener(), this); + } // register this so we can load world-dependent data right as the first world is loading if (worldInitListener != null) { @@ -138,6 +144,7 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter { // Enable metrics new Metrics(this); + PaperLib.suggestPaper(this); } private void setupWorldData() { @@ -171,11 +178,12 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter { ).toImmutableState(); BlockState defaultState = blockState.getBlockType().getAllStates().get(0); for (Map.Entry, Object> propertyObjectEntry : state.getStates().entrySet()) { - defaultState = defaultState.with((Property) propertyObjectEntry.getKey(), propertyObjectEntry.getValue()); + //noinspection unchecked + defaultState = defaultState.with((Property) propertyObjectEntry.getKey(), propertyObjectEntry.getValue()); } return defaultState; } catch (InputParseException e) { - e.printStackTrace(); + getLogger().log(Level.WARNING, "Error loading block state for " + material.getKey(), e); return blockState; } })); @@ -206,7 +214,7 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter { for (Tag itemTag : Bukkit.getTags(Tag.REGISTRY_ITEMS, Material.class)) { ItemCategory.REGISTRY.register(itemTag.getKey().toString(), new ItemCategory(itemTag.getKey().toString())); } - } catch (NoSuchMethodError e) { + } catch (NoSuchMethodError ignored) { getLogger().warning("The version of Spigot/Paper you are using doesn't support Tags. The usage of tags with WorldEdit will not work until you update."); } } @@ -458,4 +466,29 @@ public class WorldEditPlugin extends JavaPlugin implements TabCompleter { setupWorldData(); } } + + private class AsyncTabCompleteListener implements Listener { + AsyncTabCompleteListener() { + } + + @SuppressWarnings("UnnecessaryFullyQualifiedName") + @EventHandler(ignoreCancelled = true) + public void onAsyncTabComplete(com.destroystokyo.paper.event.server.AsyncTabCompleteEvent event) { + if (!event.isCommand()) return; + + String buffer = event.getBuffer(); + final String[] parts = buffer.split(" "); + if (parts.length < 1) return; + final String label = parts[0]; + final Optional command + = WorldEdit.getInstance().getPlatformManager().getPlatformCommandManager().getCommandManager().getCommand(label); + if (!command.isPresent()) return; + + CommandSuggestionEvent suggestEvent = new CommandSuggestionEvent(wrapCommandSender(event.getSender()), event.getBuffer()); + getWorldEdit().getEventBus().post(suggestEvent); + + event.setCompletions(CommandUtil.fixSuggestions(event.getBuffer(), suggestEvent.getSuggestions())); + event.setHandled(true); + } + } } diff --git a/worldedit-core/src/main/java/com/sk89q/util/StringUtil.java b/worldedit-core/src/main/java/com/sk89q/util/StringUtil.java index 80792b846..dd4bf4b29 100644 --- a/worldedit-core/src/main/java/com/sk89q/util/StringUtil.java +++ b/worldedit-core/src/main/java/com/sk89q/util/StringUtil.java @@ -306,7 +306,11 @@ public final class StringUtil { } public static List parseListInQuotes(String[] input, char delimiter, char quoteOpen, char quoteClose) { - List parsableBlocks = new ArrayList<>(); + return parseListInQuotes(input, delimiter, quoteOpen, quoteClose, false); + } + + public static List parseListInQuotes(String[] input, char delimiter, char quoteOpen, char quoteClose, boolean appendLeftover) { + List parsableBlocks = new ArrayList<>(); StringBuilder buffer = new StringBuilder(); for (String split : input) { if (split.indexOf(quoteOpen) != -1 && split.indexOf(quoteClose) == -1) { @@ -321,6 +325,9 @@ public final class StringUtil { buffer.append(split).append(delimiter); } } + if (appendLeftover && buffer.length() != 0) { + parsableBlocks.add(buffer.delete(buffer.length() - 1, buffer.length()).toString()); + } return parsableBlocks; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java index ec3c09e4a..262cc57f6 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java @@ -89,7 +89,7 @@ public class RegionCommands { @Logging(REGION) public int set(Player player, EditSession editSession, @Selection Region region, - @Arg(desc = "The patter of blocks to set") + @Arg(desc = "The pattern of blocks to set") Pattern pattern) { RegionFunction set = new BlockReplace(editSession, pattern); RegionVisitor visitor = new RegionVisitor(region, set); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/EntityRemoverConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/EntityRemoverConverter.java index d2e4b6e35..cdbc63e53 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/EntityRemoverConverter.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/EntityRemoverConverter.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.command.argument; +import com.google.common.collect.ImmutableList; import com.sk89q.worldedit.command.util.EntityRemover; import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.util.formatting.text.TextComponent; @@ -30,12 +31,19 @@ import org.enginehub.piston.converter.SuccessfulConversion; import org.enginehub.piston.inject.InjectedValueAccess; import org.enginehub.piston.inject.Key; +import java.util.List; + +import static org.enginehub.piston.converter.SuggestionHelper.limitByPrefix; + public class EntityRemoverConverter implements ArgumentConverter { public static void register(CommandManager commandManager) { commandManager.registerConverter(Key.of(EntityRemover.class), new EntityRemoverConverter()); } + private final List suggestions + = ImmutableList.of("projectiles", "items", "paintings", "itemframes", "boats", "minecarts", "tnt", "xp", "all"); + private EntityRemoverConverter() { } @@ -46,6 +54,11 @@ public class EntityRemoverConverter implements ArgumentConverter ); } + @Override + public List getSuggestions(String input) { + return limitByPrefix(suggestions.stream(), input); + } + @Override public ConversionResult convert(String argument, InjectedValueAccess context) { try { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/FactoryConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/FactoryConverter.java index d28f15ec9..d6259abf3 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/FactoryConverter.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/FactoryConverter.java @@ -19,8 +19,6 @@ package com.sk89q.worldedit.command.argument; -import static org.enginehub.piston.converter.SuggestionHelper.limitByPrefix; - import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.blocks.BaseItem; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/RegistryConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/RegistryConverter.java index a37c5b174..264b1af0c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/RegistryConverter.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/RegistryConverter.java @@ -21,6 +21,7 @@ package com.sk89q.worldedit.command.argument; import com.google.common.collect.ImmutableList; import com.sk89q.worldedit.registry.Keyed; +import com.sk89q.worldedit.registry.NamespacedRegistry; import com.sk89q.worldedit.registry.Registry; import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.util.formatting.text.TextComponent; @@ -84,10 +85,12 @@ public final class RegistryConverter implements ArgumentConvert private final Registry registry; private final TextComponent choices; + private final boolean namespaced; private RegistryConverter(Registry registry) { this.registry = registry; this.choices = TextComponent.of("any " + registry.getName()); + this.namespaced = registry instanceof NamespacedRegistry; } @Override @@ -106,6 +109,9 @@ public final class RegistryConverter implements ArgumentConvert @Override public List getSuggestions(String input) { + if (namespaced && input.indexOf(':') < 0) { + input = "minecraft:" + input; + } return limitByPrefix(registry.keySet().stream(), input); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/factory/ItemUseFactory.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/factory/ItemUseFactory.java index 8b25780d4..a099a78a1 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/factory/ItemUseFactory.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/factory/ItemUseFactory.java @@ -42,6 +42,6 @@ public final class ItemUseFactory implements Contextual { @Override public String toString() { - return "application of the item " + item.getType() + ":" + item.getNbtData(); + return "application of the item " + item.getType(); } } 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 new file mode 100644 index 000000000..acf18f041 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/util/SuggestionHelper.java @@ -0,0 +1,143 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.command.util; + +import com.sk89q.worldedit.registry.state.Property; +import com.sk89q.worldedit.world.block.BlockCategory; +import com.sk89q.worldedit.world.block.BlockType; +import com.sk89q.worldedit.world.block.BlockTypes; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public final class SuggestionHelper { + private SuggestionHelper() { + } + + public static Stream getBlockCategorySuggestions(String tag, boolean allowRandom) { + final Stream allTags = BlockCategory.REGISTRY.keySet().stream().map(str -> "##" + str); + if (tag.isEmpty()) { + return allTags; + } + if (tag.startsWith("#")) { + String key; + if (tag.startsWith("##")) { + key = tag.substring(2); + if (key.isEmpty()) { + return allTags; + } + boolean anyState = false; + if (allowRandom && key.charAt(0) == '*') { + key = key.substring(1); + anyState = true; + } + if (key.indexOf(':') < 0) { + key = "minecraft:" + key; + } + String finalTag = key.toLowerCase(Locale.ROOT); + final Stream stream = BlockCategory.REGISTRY.keySet().stream().filter(s -> + s.startsWith(finalTag)); + return anyState ? stream.map(s -> "##*" + s) : stream.map(s -> "##" + s); + } else if (tag.length() == 1) { + return allTags; + } + } + return Stream.empty(); + } + + public static Stream getBlockPropertySuggestions(String blockType, String props) { + BlockType type = BlockTypes.get(blockType.toLowerCase(Locale.ROOT)); + if (type == null) { + return Stream.empty(); + } + final Map> propertyMap = type.getPropertyMap(); + Set matchedProperties = new HashSet<>(); + String[] propParts = props.split(",", -1); + for (int i = 0; i < propParts.length; i++) { + String[] propVal = propParts[i].split("="); + final String matchProp = propVal[0].toLowerCase(Locale.ROOT); + if (i == propParts.length - 1) { + // suggest for next property + String previous = Arrays.stream(propParts, 0, propParts.length - 1).collect(Collectors.joining(",")) + + (propParts.length == 1 ? "" : ","); + String lastValidInput = (blockType + "[" + previous).toLowerCase(Locale.ROOT); + if (propVal.length == 1) { + // only property, no value yet + final List> matchingProps = propertyMap.entrySet().stream() + .filter(p -> !matchedProperties.contains(p.getKey()) && p.getKey().startsWith(matchProp)) + .map(Map.Entry::getValue).collect(Collectors.toList()); + switch (matchingProps.size()) { + case 0: + return propertyMap.keySet().stream().filter(p -> !matchedProperties.contains(p)).map(prop -> + lastValidInput + prop + "="); + case 1: + return matchingProps.get(0).getValues().stream().map(val -> + lastValidInput + matchingProps.get(0).getName() + "=" + + val.toString().toLowerCase(Locale.ROOT)); + default: + return matchingProps.stream().map(p -> lastValidInput + p.getName() + "="); + } + } else { + Property prop = propertyMap.get(matchProp); + if (prop == null) { + return Stream.empty(); + } + final List values = prop.getValues().stream().map(v -> v.toString().toLowerCase(Locale.ROOT)).collect(Collectors.toList()); + String matchVal = propVal[1].toLowerCase(Locale.ROOT); + List matchingVals = values.stream().filter(val -> val.startsWith(matchVal)).collect(Collectors.toList()); + if (matchingVals.isEmpty()) { + return values.stream().map(val -> lastValidInput + prop.getName() + "=" + val); + } else { + if (matchingVals.size() == 1 && matchingVals.get(0).equals(matchVal)) { + String currProp = lastValidInput + prop.getName() + "=" + matchVal; + if (matchingVals.size() < values.size()) { + return Stream.of(currProp + "] ", currProp + ","); + } + return Stream.of(currProp + "] "); + } + return matchingVals.stream().map(val -> lastValidInput + prop.getName() + "=" + val); + } + } + } else { + // validate previous properties + if (propVal.length != 2) { + return Stream.empty(); + } + Property prop = propertyMap.get(matchProp); + if (prop == null) { + return Stream.empty(); + } + try { + prop.getValueFor(propVal[1]); + matchedProperties.add(prop.getName()); + } catch (IllegalArgumentException ignored) { + return Stream.empty(); + } + } + } + return Stream.empty(); + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/BlockFactory.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/BlockFactory.java index 235f0ae3e..c00583183 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/BlockFactory.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/BlockFactory.java @@ -61,7 +61,7 @@ public class BlockFactory extends AbstractFactory { public Set parseFromListInput(String input, ParserContext context) throws InputParseException { Set blocks = new HashSet<>(); String[] splits = input.split(","); - for (String token : StringUtil.parseListInQuotes(splits, ',', '[', ']')) { + for (String token : StringUtil.parseListInQuotes(splits, ',', '[', ']', true)) { blocks.add(parseFromInput(token, context)); } return blocks; 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 29bb9eb25..31b1ac2df 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 @@ -64,13 +64,15 @@ public final class MaskFactory extends AbstractFactory { register(new SolidMaskParser(worldEdit)); register(new LazyRegionMaskParser(worldEdit)); register(new RegionMaskParser(worldEdit)); - register(new BlockCategoryMaskParser(worldEdit)); register(new OffsetMaskParser(worldEdit)); - register(new BiomeMaskParser(worldEdit)); register(new NoiseMaskParser(worldEdit)); register(new BlockStateMaskParser(worldEdit)); register(new NegateMaskParser(worldEdit)); register(new ExpressionMaskParser(worldEdit)); + + register(new BlockCategoryMaskParser(worldEdit)); + register(new BiomeMaskParser(worldEdit)); + register(new BlocksMaskParser(worldEdit)); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/PatternFactory.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/PatternFactory.java index 8457f3fc3..0df1dc40c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/PatternFactory.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/PatternFactory.java @@ -50,10 +50,10 @@ public final class PatternFactory extends AbstractFactory { register(new RandomPatternParser(worldEdit)); // individual patterns - register(new BlockCategoryPatternParser(worldEdit)); register(new ClipboardPatternParser(worldEdit)); register(new TypeOrStateApplyingPatternParser(worldEdit)); register(new RandomStatePatternParser(worldEdit)); + register(new BlockCategoryPatternParser(worldEdit)); // inner-most pattern: just one block - must be last register(new SingleBlockPatternParser(worldEdit)); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultBlockParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultBlockParser.java index a2d069bd8..4a03a5471 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultBlockParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultBlockParser.java @@ -27,6 +27,7 @@ import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.MobSpawnerBlock; import com.sk89q.worldedit.blocks.SignBlock; import com.sk89q.worldedit.blocks.SkullBlock; +import com.sk89q.worldedit.command.util.SuggestionHelper; import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.input.DisallowedUsageException; import com.sk89q.worldedit.extension.input.InputParseException; @@ -38,7 +39,6 @@ import com.sk89q.worldedit.internal.registry.InputParser; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.util.HandSide; -import com.sk89q.worldedit.util.formatting.component.ErrorFormat; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; @@ -101,7 +101,7 @@ public class DefaultBlockParser extends InputParser { } } - private static String[] EMPTY_STRING_ARRAY = new String[]{}; + private static String[] EMPTY_STRING_ARRAY = {}; /** * Backwards compatibility for wool colours in block syntax. @@ -169,11 +169,9 @@ public class DefaultBlockParser extends InputParser { Property propertyKey = (Property) type.getPropertyMap().get(parts[0]); if (propertyKey == null) { if (context.getActor() != null) { - context.getActor().print(ErrorFormat.wrap("Unknown property ", parts[0], " for block ", type.getName(), - ". Defaulting to base.")); + throw new NoMatchException("Unknown property " + parts[0] + " for block " + type.getName()); } else { WorldEdit.logger.warn("Unknown property " + parts[0] + " for block " + type.getName()); -// throw new NoMatchException("Unknown property " + parts[0] + " for block " + type.getName()); } return Maps.newHashMap(); } @@ -202,8 +200,29 @@ public class DefaultBlockParser extends InputParser { @Override public Stream getSuggestions(String input) { - // TODO Include states - return BlockType.REGISTRY.keySet().stream(); + final int idx = input.lastIndexOf('['); + if (idx < 0) { + if (input.indexOf(':') == -1) { + String key = ("minecraft:" + input).toLowerCase(Locale.ROOT); + return BlockType.REGISTRY.keySet().stream().filter(s -> s.startsWith(key)); + } + if (input.contains(",")) { + return Stream.empty(); + } + return BlockType.REGISTRY.keySet().stream(); + } + String blockType = input.substring(0, idx); + BlockType type = BlockTypes.get(blockType.toLowerCase(Locale.ROOT)); + if (type == null) { + return Stream.empty(); + } + + String props = input.substring(idx + 1); + if (props.isEmpty()) { + return type.getProperties().stream().map(p -> input + p.getName() + "="); + } + + return SuggestionHelper.getBlockPropertySuggestions(blockType, props); } private BaseBlock parseLogic(String input, ParserContext context) throws InputParseException { @@ -238,6 +257,13 @@ public class DefaultBlockParser extends InputParser { typeString = blockAndExtraData[0]; } else { typeString = blockAndExtraData[0].substring(0, stateStart); + if (stateStart + 1 >= blockAndExtraData[0].length()) { + throw new InputParseException("Invalid format. Hanging bracket @ " + stateStart + "."); + } + int stateEnd = blockAndExtraData[0].lastIndexOf(']'); + if (stateEnd < 0) { + throw new InputParseException("Invalid format. Unclosed property."); + } stateString = blockAndExtraData[0].substring(stateStart + 1, blockAndExtraData[0].length() - 1); } if (typeString.isEmpty()) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultItemParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultItemParser.java index 702e54458..e325b24a1 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultItemParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultItemParser.java @@ -28,10 +28,7 @@ import com.sk89q.worldedit.world.item.ItemType; import com.sk89q.worldedit.world.item.ItemTypes; import com.sk89q.worldedit.world.registry.LegacyMapper; -import java.util.ArrayList; -import java.util.List; import java.util.Locale; -import java.util.stream.Collectors; import java.util.stream.Stream; public class DefaultItemParser extends InputParser { @@ -42,7 +39,11 @@ public class DefaultItemParser extends InputParser { @Override public Stream getSuggestions(String input) { - return ItemType.REGISTRY.keySet().stream(); + if (input.indexOf(':') == -1) { + input = "minecraft:" + input; + } + String key = input; + return ItemType.REGISTRY.keySet().stream().filter(s -> s.startsWith(key)); } @Override @@ -58,8 +59,10 @@ public class DefaultItemParser extends InputParser { } else { type = LegacyMapper.getInstance().getItemFromLegacy(Integer.parseInt(split[0]), Integer.parseInt(split[1])); } - item = new BaseItem(type); - } catch (NumberFormatException e) { + if (type != null) { + item = new BaseItem(type); + } + } catch (NumberFormatException ignored) { } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/BiomeMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/BiomeMaskParser.java index d484b4ff9..db0ab21e7 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/BiomeMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/BiomeMaskParser.java @@ -35,6 +35,7 @@ import com.sk89q.worldedit.world.registry.BiomeRegistry; import java.util.Collection; import java.util.HashSet; +import java.util.Locale; import java.util.Set; import java.util.stream.Stream; @@ -46,7 +47,22 @@ public class BiomeMaskParser extends InputParser { @Override public Stream getSuggestions(String input) { - return BiomeType.REGISTRY.keySet().stream().map(biomeType -> "$" + biomeType); + final Stream allBiomes = BiomeType.REGISTRY.keySet().stream().map(biomeType -> "$" + biomeType); + if (input.isEmpty()) { + return allBiomes; + } + if (input.charAt(0) == '$') { + String key = input.substring(1); + if (key.isEmpty()) { + return allBiomes; + } + if (key.indexOf(':') < 0) { + key = "minecraft:" + key; + } + String biomeId = key.toLowerCase(Locale.ROOT); + return BiomeType.REGISTRY.keySet().stream().filter(s -> s.startsWith(biomeId)).map(s -> "$" + s); + } + return Stream.empty(); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/BlockCategoryMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/BlockCategoryMaskParser.java index 630386b56..ee7bebf44 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/BlockCategoryMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/BlockCategoryMaskParser.java @@ -20,6 +20,7 @@ package com.sk89q.worldedit.extension.factory.parser.mask; import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.command.util.SuggestionHelper; import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.function.mask.BlockCategoryMask; @@ -39,7 +40,7 @@ public class BlockCategoryMaskParser extends InputParser { @Override public Stream getSuggestions(String input) { - return BlockCategory.REGISTRY.keySet().stream().map(str -> "##" + str); + return SuggestionHelper.getBlockCategorySuggestions(input, false); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/BlockStateMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/BlockStateMaskParser.java index 0a2bd6e56..95884b821 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/BlockStateMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/BlockStateMaskParser.java @@ -28,12 +28,22 @@ import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.internal.registry.InputParser; import com.sk89q.worldedit.session.request.RequestExtent; +import java.util.stream.Stream; + public class BlockStateMaskParser extends InputParser { public BlockStateMaskParser(WorldEdit worldEdit) { super(worldEdit); } + @Override + public Stream getSuggestions(String input) { + if (input.isEmpty()) { + return Stream.of("^[", "^=["); + } + return Stream.of("^[", "^=[").filter(s -> s.startsWith(input)); // no block type, can't suggest states + } + @Override public Mask parseFromInput(String input, ParserContext context) throws InputParseException { if (!(input.startsWith("^[") || input.startsWith("^=[")) || !input.endsWith("]")) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/BlocksMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/BlocksMaskParser.java index df7ed3a16..8580eb8a1 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/BlocksMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/BlocksMaskParser.java @@ -28,7 +28,6 @@ import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.internal.registry.InputParser; import com.sk89q.worldedit.session.request.RequestExtent; import com.sk89q.worldedit.world.block.BaseBlock; -import com.sk89q.worldedit.world.block.BlockCategory; import java.util.Set; import java.util.stream.Stream; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/ExistingMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/ExistingMaskParser.java index 5249d47ad..21feb6449 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/ExistingMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/ExistingMaskParser.java @@ -19,7 +19,7 @@ package com.sk89q.worldedit.extension.factory.parser.mask; -import com.google.common.collect.Lists; +import com.google.common.collect.ImmutableList; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.function.mask.ExistingBlockMask; @@ -31,13 +31,15 @@ import java.util.List; public class ExistingMaskParser extends SimpleInputParser { + private final List aliases = ImmutableList.of("#existing"); + public ExistingMaskParser(WorldEdit worldEdit) { super(worldEdit); } @Override public List getMatchedAliases() { - return Lists.newArrayList("#existing"); + return aliases; } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/ExpressionMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/ExpressionMaskParser.java index 3087610be..ac104e198 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/ExpressionMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/ExpressionMaskParser.java @@ -30,10 +30,10 @@ import com.sk89q.worldedit.internal.registry.InputParser; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.regions.shape.WorldEditExpressionEnvironment; import com.sk89q.worldedit.session.SessionOwner; -import com.sk89q.worldedit.session.request.Request; import com.sk89q.worldedit.session.request.RequestExtent; import java.util.function.IntSupplier; +import java.util.stream.Stream; public class ExpressionMaskParser extends InputParser { @@ -41,6 +41,14 @@ public class ExpressionMaskParser extends InputParser { super(worldEdit); } + @Override + public Stream getSuggestions(String input) { + if (input.isEmpty()) { + return Stream.of("="); + } + return Stream.empty(); + } + @Override public Mask parseFromInput(String input, ParserContext context) throws InputParseException { if (!input.startsWith("=")) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/LazyRegionMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/LazyRegionMaskParser.java index 82bc14d2b..29b7af7a9 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/LazyRegionMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/LazyRegionMaskParser.java @@ -19,9 +19,8 @@ package com.sk89q.worldedit.extension.factory.parser.mask; -import com.google.common.collect.Lists; +import com.google.common.collect.ImmutableList; import com.sk89q.worldedit.WorldEdit; -import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.mask.RegionMask; @@ -32,17 +31,19 @@ import java.util.List; public class LazyRegionMaskParser extends SimpleInputParser { + private final List aliases = ImmutableList.of("#dregion", "#dselection", "#dsel"); + public LazyRegionMaskParser(WorldEdit worldEdit) { super(worldEdit); } @Override public List getMatchedAliases() { - return Lists.newArrayList("#dregion", "#dselection", "#dsel"); + return aliases; } @Override - public Mask parseFromSimpleInput(String input, ParserContext context) throws InputParseException { + public Mask parseFromSimpleInput(String input, ParserContext context) { return new RegionMask(new RequestSelection()); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/NegateMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/NegateMaskParser.java index 9e1c2e9df..e8e4ddf57 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/NegateMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/NegateMaskParser.java @@ -26,12 +26,25 @@ import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.mask.Masks; import com.sk89q.worldedit.internal.registry.InputParser; +import java.util.stream.Stream; + public class NegateMaskParser extends InputParser { public NegateMaskParser(WorldEdit worldEdit) { super(worldEdit); } + @Override + public Stream getSuggestions(String input) { + if (input.isEmpty()) { + return Stream.of("!"); + } + if (input.charAt(0) != '!') { + return Stream.empty(); + } + return worldEdit.getMaskFactory().getSuggestions(input.substring(1)).stream().map(s -> "!" + s); + } + @Override public Mask parseFromInput(String input, ParserContext context) throws InputParseException { if (!input.startsWith("!")) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/NoiseMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/NoiseMaskParser.java index 0cb1a85e8..40f54ebfe 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/NoiseMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/NoiseMaskParser.java @@ -20,13 +20,14 @@ package com.sk89q.worldedit.extension.factory.parser.mask; import com.sk89q.worldedit.WorldEdit; -import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.function.mask.Mask; import com.sk89q.worldedit.function.mask.NoiseFilter; import com.sk89q.worldedit.internal.registry.InputParser; import com.sk89q.worldedit.math.noise.RandomNoise; +import java.util.stream.Stream; + public class NoiseMaskParser extends InputParser { public NoiseMaskParser(WorldEdit worldEdit) { @@ -34,7 +35,18 @@ public class NoiseMaskParser extends InputParser { } @Override - public Mask parseFromInput(String input, ParserContext context) throws InputParseException { + public Stream getSuggestions(String input) { + if (input.isEmpty()) { + return Stream.of("%"); + } + if (input.charAt(0) != '%') { + return Stream.empty(); + } + return Stream.of("%10", "%25", "%50", "%75").filter(s -> s.startsWith(input)); + } + + @Override + public Mask parseFromInput(String input, ParserContext context) { if (!input.startsWith("%")) { return null; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/OffsetMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/OffsetMaskParser.java index ab4882e00..ede9b6c79 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/OffsetMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/OffsetMaskParser.java @@ -31,12 +31,26 @@ import com.sk89q.worldedit.internal.registry.InputParser; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.session.request.RequestExtent; +import java.util.stream.Stream; + public class OffsetMaskParser extends InputParser { public OffsetMaskParser(WorldEdit worldEdit) { super(worldEdit); } + @Override + public Stream getSuggestions(String input) { + if (input.isEmpty()) { + return Stream.of(">", "<"); + } + final char firstChar = input.charAt(0); + if (firstChar != '>' && firstChar != '<') { + return Stream.empty(); + } + return worldEdit.getMaskFactory().getSuggestions(input.substring(1)).stream().map(s -> firstChar + s); + } + @Override public Mask parseFromInput(String input, ParserContext context) throws InputParseException { final char firstChar = input.charAt(0); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/RegionMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/RegionMaskParser.java index 21963835d..bd7ceaa3e 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/RegionMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/RegionMaskParser.java @@ -19,7 +19,7 @@ package com.sk89q.worldedit.extension.factory.parser.mask; -import com.google.common.collect.Lists; +import com.google.common.collect.ImmutableList; import com.sk89q.worldedit.IncompleteRegionException; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.input.InputParseException; @@ -32,13 +32,15 @@ import java.util.List; public class RegionMaskParser extends SimpleInputParser { + private final List aliases = ImmutableList.of("#region", "#selection", "#sel"); + public RegionMaskParser(WorldEdit worldEdit) { super(worldEdit); } @Override public List getMatchedAliases() { - return Lists.newArrayList("#region", "#selection", "#sel"); + return aliases; } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/SolidMaskParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/SolidMaskParser.java index 44e2f07a2..f5ae3244a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/SolidMaskParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/mask/SolidMaskParser.java @@ -19,7 +19,7 @@ package com.sk89q.worldedit.extension.factory.parser.mask; -import com.google.common.collect.Lists; +import com.google.common.collect.ImmutableList; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.function.mask.Mask; @@ -31,13 +31,15 @@ import java.util.List; public class SolidMaskParser extends SimpleInputParser { + private final List aliases = ImmutableList.of("#solid"); + public SolidMaskParser(WorldEdit worldEdit) { super(worldEdit); } @Override public List getMatchedAliases() { - return Lists.newArrayList("#solid"); + return aliases; } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/BlockCategoryPatternParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/BlockCategoryPatternParser.java index f9e0f8052..3260fa438 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/BlockCategoryPatternParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/BlockCategoryPatternParser.java @@ -20,6 +20,7 @@ package com.sk89q.worldedit.extension.factory.parser.pattern; import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.command.util.SuggestionHelper; import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.function.pattern.BlockPattern; @@ -41,7 +42,7 @@ public class BlockCategoryPatternParser extends InputParser { @Override public Stream getSuggestions(String input) { - return BlockCategory.REGISTRY.keySet().stream().map(str -> "##" + str); + return SuggestionHelper.getBlockCategorySuggestions(input, true); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/ClipboardPatternParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/ClipboardPatternParser.java index 4029c4359..532a981e6 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/ClipboardPatternParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/ClipboardPatternParser.java @@ -31,6 +31,7 @@ import com.sk89q.worldedit.internal.registry.InputParser; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.session.ClipboardHolder; +import java.util.Locale; import java.util.stream.Stream; public class ClipboardPatternParser extends InputParser { @@ -41,7 +42,27 @@ public class ClipboardPatternParser extends InputParser { @Override public Stream getSuggestions(String input) { - return Stream.of("#clipboard", "#copy"); + if (input.isEmpty()) { + return Stream.of("#clipoard"); + } + String[] offsetParts = input.split("@", 2); + String firstLower = offsetParts[0].toLowerCase(Locale.ROOT); + final boolean isClip = "#clipboard".startsWith(firstLower); + final boolean isCopy = "#copy".startsWith(firstLower); + if (isClip || isCopy) { + if (offsetParts.length == 2) { + String coords = offsetParts[1]; + if (coords.isEmpty()) { + return Stream.of(input + "[x,y,z]"); + } + } else { + if (isClip) { + return Stream.of("#clipboard", "#clipboard@[x,y,z]"); + } + return Stream.of("#copy", "#copy@[x,y,z]"); + } + } + return Stream.empty(); } @Override diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/RandomPatternParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/RandomPatternParser.java index 68c768954..81b1b11e3 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/RandomPatternParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/RandomPatternParser.java @@ -28,6 +28,7 @@ import com.sk89q.worldedit.function.pattern.RandomPattern; import com.sk89q.worldedit.internal.registry.InputParser; import java.util.List; +import java.util.stream.Stream; public class RandomPatternParser extends InputParser { @@ -35,12 +36,35 @@ public class RandomPatternParser extends InputParser { super(worldEdit); } + @Override + public Stream getSuggestions(String input) { + String[] splits = input.split(",", -1); + List patterns = StringUtil.parseListInQuotes(splits, ',', '[', ']', true); + if (patterns.size() == 1) { + return Stream.empty(); + } + // get suggestions for the last token only + String token = patterns.get(patterns.size() - 1); + String previous = String.join(",", patterns.subList(0, patterns.size() - 1)); + if (token.matches("[0-9]+(\\.[0-9]*)?%.*")) { + String[] p = token.split("%"); + + if (p.length < 2) { + return Stream.empty(); + } else { + token = p[1]; + } + } + final List innerSuggestions = worldEdit.getPatternFactory().getSuggestions(token); + return innerSuggestions.stream().map(s -> previous + "," + s); + } + @Override public Pattern parseFromInput(String input, ParserContext context) throws InputParseException { RandomPattern randomPattern = new RandomPattern(); - String[] splits = input.split(","); - List patterns = StringUtil.parseListInQuotes(splits, ',', '[', ']'); + String[] splits = input.split(",", -1); + List patterns = StringUtil.parseListInQuotes(splits, ',', '[', ']', true); if (patterns.size() == 1) { return null; // let a 'single'-pattern parser handle it } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/RandomStatePatternParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/RandomStatePatternParser.java index 27423f59e..3b6dc5c1f 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/RandomStatePatternParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/RandomStatePatternParser.java @@ -29,11 +29,25 @@ import com.sk89q.worldedit.internal.registry.InputParser; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.FuzzyBlockState; +import java.util.stream.Stream; + public class RandomStatePatternParser extends InputParser { public RandomStatePatternParser(WorldEdit worldEdit) { super(worldEdit); } + @Override + public Stream getSuggestions(String input) { + if (input.isEmpty()) { + return Stream.of("*"); + } + if (!input.startsWith("*")) { + return Stream.empty(); + } + + return worldEdit.getBlockFactory().getSuggestions(input.substring(1)).stream().map(s -> "*" + s); + } + @Override public Pattern parseFromInput(String input, ParserContext context) throws InputParseException { if (!input.startsWith("*")) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/SingleBlockPatternParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/SingleBlockPatternParser.java index 3bd852513..30f844611 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/SingleBlockPatternParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/SingleBlockPatternParser.java @@ -25,7 +25,6 @@ import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.function.pattern.BlockPattern; import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.internal.registry.InputParser; -import com.sk89q.worldedit.world.block.BlockType; import java.util.stream.Stream; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/TypeOrStateApplyingPatternParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/TypeOrStateApplyingPatternParser.java index db0ee8c38..8897b115a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/TypeOrStateApplyingPatternParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/pattern/TypeOrStateApplyingPatternParser.java @@ -20,9 +20,8 @@ package com.sk89q.worldedit.extension.factory.parser.pattern; import com.google.common.base.Splitter; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.command.util.SuggestionHelper; import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.extent.Extent; @@ -32,10 +31,9 @@ import com.sk89q.worldedit.function.pattern.Pattern; import com.sk89q.worldedit.function.pattern.StateApplyingPattern; import com.sk89q.worldedit.function.pattern.TypeApplyingPattern; import com.sk89q.worldedit.internal.registry.InputParser; -import com.sk89q.worldedit.world.block.BlockState; import java.util.Map; -import java.util.Set; +import java.util.stream.Stream; public class TypeOrStateApplyingPatternParser extends InputParser { @@ -44,6 +42,30 @@ public class TypeOrStateApplyingPatternParser extends InputParser { super(worldEdit); } + @Override + public Stream getSuggestions(String input) { + if (input.isEmpty()) { + return Stream.of("^"); + } + if (!input.startsWith("^")) { + return Stream.empty(); + } + input = input.substring(1); + + String[] parts = input.split("\\[", 2); + String type = parts[0]; + + if (parts.length == 1) { + return worldEdit.getBlockFactory().getSuggestions(input).stream().map(s -> "^" + s); + } else { + if (type.isEmpty()) { + return Stream.empty(); // without knowing a type, we can't really suggest states + } else { + return SuggestionHelper.getBlockPropertySuggestions(type, parts[1]).map(s -> "^" + s); + } + } + } + @Override public Pattern parseFromInput(String input, ParserContext context) throws InputParseException { if (!input.startsWith("^")) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/AbstractFactory.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/AbstractFactory.java index d4ec8bef8..f1b3e05aa 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/AbstractFactory.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/AbstractFactory.java @@ -20,7 +20,6 @@ package com.sk89q.worldedit.internal.registry; import static com.google.common.base.Preconditions.checkNotNull; -import static org.enginehub.piston.converter.SuggestionHelper.limitByPrefix; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.input.InputParseException; @@ -28,11 +27,9 @@ import com.sk89q.worldedit.extension.input.NoMatchException; import com.sk89q.worldedit.extension.input.ParserContext; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; -import java.util.stream.Stream; /** * An abstract implementation of a factory for internal usage. @@ -81,7 +78,9 @@ public abstract class AbstractFactory { } public List getSuggestions(String input) { - return limitByPrefix(parsers.stream().flatMap(parser -> parser.getSuggestions(input)), input); + return parsers.stream().flatMap( + p -> p.getSuggestions(input) + ).collect(Collectors.toList()); } /** diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/InputParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/InputParser.java index 5862d9cf9..febafacc8 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/InputParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/InputParser.java @@ -23,8 +23,6 @@ import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.ParserContext; -import java.util.Collections; -import java.util.List; import java.util.stream.Stream; /** @@ -37,7 +35,7 @@ public abstract class InputParser { protected final WorldEdit worldEdit; - public InputParser(WorldEdit worldEdit) { + protected InputParser(WorldEdit worldEdit) { this.worldEdit = worldEdit; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/SimpleInputParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/SimpleInputParser.java index 013e45958..aace1ee89 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/SimpleInputParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/registry/SimpleInputParser.java @@ -19,12 +19,12 @@ package com.sk89q.worldedit.internal.registry; -import com.google.common.collect.Lists; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.input.InputParseException; import com.sk89q.worldedit.extension.input.ParserContext; import java.util.List; +import java.util.Locale; import java.util.stream.Stream; /** @@ -34,7 +34,7 @@ import java.util.stream.Stream; */ public abstract class SimpleInputParser extends InputParser { - public SimpleInputParser(WorldEdit worldEdit) { + protected SimpleInputParser(WorldEdit worldEdit) { super(worldEdit); } @@ -67,6 +67,15 @@ public abstract class SimpleInputParser extends InputParser { @Override public Stream getSuggestions(String input) { - return Stream.of(getPrimaryMatcher()); + if (input.isEmpty()) { + return Stream.of(getPrimaryMatcher()); + } + final String prefix = input.toLowerCase(Locale.ROOT); + for (String alias : getMatchedAliases()) { + if (alias.startsWith(prefix)) { + return Stream.of(alias); + } + } + return Stream.empty(); } }