mirror of https://github.com/cabaletta/baritone
Merge pull request #4006 from ZacSharp/pr/blockOptionalMeta/tabCompleteProperties
Tab completion improvements
This commit is contained in:
commit
6dc953631b
|
@ -23,11 +23,17 @@ import net.minecraft.block.Block;
|
|||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public enum BlockById implements IDatatypeFor<Block> {
|
||||
INSTANCE;
|
||||
|
||||
/**
|
||||
* Matches (domain:)?name? where domain and name are [a-z0-9_.-]+ and [a-z0-9/_.-]+ respectively.
|
||||
*/
|
||||
private static Pattern PATTERN = Pattern.compile("(?:[a-z0-9_.-]+:)?[a-z0-9/_.-]*");
|
||||
|
||||
@Override
|
||||
public Block get(IDatatypeContext ctx) throws CommandException {
|
||||
ResourceLocation id = new ResourceLocation(ctx.getConsumer().getString());
|
||||
|
@ -40,13 +46,19 @@ public enum BlockById implements IDatatypeFor<Block> {
|
|||
|
||||
@Override
|
||||
public Stream<String> tabComplete(IDatatypeContext ctx) throws CommandException {
|
||||
String arg = ctx.getConsumer().getString();
|
||||
|
||||
if (!PATTERN.matcher(arg).matches()) {
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
return new TabCompleteHelper()
|
||||
.append(
|
||||
Block.REGISTRY.getKeys()
|
||||
.stream()
|
||||
.map(Object::toString)
|
||||
)
|
||||
.filterPrefixNamespaced(ctx.getConsumer().getString())
|
||||
.filterPrefixNamespaced(arg)
|
||||
.sortAlphabetically()
|
||||
.stream();
|
||||
}
|
||||
|
|
|
@ -18,20 +18,136 @@
|
|||
package baritone.api.command.datatypes;
|
||||
|
||||
import baritone.api.command.exception.CommandException;
|
||||
import baritone.api.command.helpers.TabCompleteHelper;
|
||||
import baritone.api.utils.BlockOptionalMeta;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.properties.IProperty;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public enum ForBlockOptionalMeta implements IDatatypeFor<BlockOptionalMeta> {
|
||||
INSTANCE;
|
||||
|
||||
/**
|
||||
* Matches (domain:)?name([(property=value)*])? but the input can be truncated at any position.
|
||||
* domain and name are [a-z0-9_.-]+ and [a-z0-9/_.-]+ because that's what mc 1.13+ accepts.
|
||||
* property and value use the same format as domain.
|
||||
*/
|
||||
// Good luck reading this.
|
||||
private static Pattern PATTERN = Pattern.compile("(?:[a-z0-9_.-]+:)?(?:[a-z0-9/_.-]+(?:\\[(?:(?:[a-z0-9_.-]+=[a-z0-9_.-]+,)*(?:[a-z0-9_.-]+(?:=(?:[a-z0-9_.-]+(?:\\])?)?)?)?|\\])?)?)?");
|
||||
|
||||
@Override
|
||||
public BlockOptionalMeta get(IDatatypeContext ctx) throws CommandException {
|
||||
return new BlockOptionalMeta(ctx.getConsumer().getString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<String> tabComplete(IDatatypeContext ctx) {
|
||||
return ctx.getConsumer().tabCompleteDatatype(BlockById.INSTANCE);
|
||||
public Stream<String> tabComplete(IDatatypeContext ctx) throws CommandException {
|
||||
String arg = ctx.getConsumer().peekString();
|
||||
|
||||
if (!PATTERN.matcher(arg).matches()) {
|
||||
// Invalid format; we can't complete this.
|
||||
ctx.getConsumer().getString();
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
if (arg.endsWith("]")) {
|
||||
// We are already done.
|
||||
ctx.getConsumer().getString();
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
if (!arg.contains("[")) {
|
||||
// no properties so we are completing the block id
|
||||
return ctx.getConsumer().tabCompleteDatatype(BlockById.INSTANCE);
|
||||
}
|
||||
|
||||
ctx.getConsumer().getString();
|
||||
|
||||
// destructuring assignment? Please?
|
||||
String blockId, properties;
|
||||
{
|
||||
String[] parts = splitLast(arg, '[');
|
||||
blockId = parts[0];
|
||||
properties = parts[1];
|
||||
}
|
||||
|
||||
Block block = Block.REGISTRY.getObject(new ResourceLocation(blockId));
|
||||
if (block == null) {
|
||||
// This block doesn't exist so there's no properties to complete.
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
String leadingProperties, lastProperty;
|
||||
{
|
||||
String[] parts = splitLast(properties, ',');
|
||||
leadingProperties = parts[0];
|
||||
lastProperty = parts[1];
|
||||
}
|
||||
|
||||
if (!lastProperty.contains("=")) {
|
||||
// The last property-value pair doesn't have a value yet so we are completing its name
|
||||
Set<String> usedProps = Stream.of(leadingProperties.split(","))
|
||||
.map(pair -> pair.split("=")[0])
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
String prefix = arg.substring(0, arg.length() - lastProperty.length());
|
||||
return new TabCompleteHelper()
|
||||
.append(
|
||||
block.getBlockState()
|
||||
.getProperties()
|
||||
.stream()
|
||||
.map(IProperty::getName)
|
||||
)
|
||||
.filter(prop -> !usedProps.contains(prop))
|
||||
.filterPrefix(lastProperty)
|
||||
.sortAlphabetically()
|
||||
.map(prop -> prefix + prop)
|
||||
.stream();
|
||||
}
|
||||
|
||||
String lastName, lastValue;
|
||||
{
|
||||
String[] parts = splitLast(lastProperty, '=');
|
||||
lastName = parts[0];
|
||||
lastValue = parts[1];
|
||||
}
|
||||
|
||||
// We are completing the value of a property
|
||||
String prefix = arg.substring(0, arg.length() - lastValue.length());
|
||||
|
||||
IProperty<?> property = block.getBlockState().getProperty(lastName);
|
||||
if (property == null) {
|
||||
// The property does not exist so there's no values to complete
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
return new TabCompleteHelper()
|
||||
.append(getValues(property))
|
||||
.filterPrefix(lastValue)
|
||||
.sortAlphabetically()
|
||||
.map(val -> prefix + val)
|
||||
.stream();
|
||||
}
|
||||
|
||||
/**
|
||||
* Always returns exactly two strings.
|
||||
* If the separator is not found the FIRST returned string is empty.
|
||||
*/
|
||||
private static String[] splitLast(String string, char chr) {
|
||||
int idx = string.lastIndexOf(chr);
|
||||
if (idx == -1) {
|
||||
return new String[]{"", string};
|
||||
}
|
||||
return new String[]{string.substring(0, idx), string.substring(idx + 1)};
|
||||
}
|
||||
|
||||
// this shouldn't need to be a separate method?
|
||||
private static <T extends Comparable<T>> Stream<String> getValues(IProperty<T> property) {
|
||||
return property.getAllowedValues().stream().map(property::getName);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -373,6 +373,8 @@ public class ArgConsumer implements IArgConsumer {
|
|||
public <T extends IDatatype> Stream<String> tabCompleteDatatype(T datatype) {
|
||||
try {
|
||||
return datatype.tabComplete(this.context);
|
||||
} catch (CommandException ignored) {
|
||||
// NOP
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ package baritone.command.defaults;
|
|||
import baritone.api.IBaritone;
|
||||
import baritone.api.command.Command;
|
||||
import baritone.api.command.argument.IArgConsumer;
|
||||
import baritone.api.command.datatypes.BlockById;
|
||||
import baritone.api.command.datatypes.ForBlockOptionalMeta;
|
||||
import baritone.api.command.datatypes.RelativeCoordinate;
|
||||
import baritone.api.command.datatypes.RelativeGoal;
|
||||
|
@ -61,7 +60,8 @@ public class GotoCommand extends Command {
|
|||
public Stream<String> tabComplete(String label, IArgConsumer args) throws CommandException {
|
||||
// since it's either a goal or a block, I don't think we can tab complete properly?
|
||||
// so just tab complete for the block variant
|
||||
return args.tabCompleteDatatype(BlockById.INSTANCE);
|
||||
args.requireMax(1);
|
||||
return args.tabCompleteDatatype(ForBlockOptionalMeta.INSTANCE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -21,7 +21,6 @@ import baritone.api.BaritoneAPI;
|
|||
import baritone.api.IBaritone;
|
||||
import baritone.api.command.Command;
|
||||
import baritone.api.command.argument.IArgConsumer;
|
||||
import baritone.api.command.datatypes.BlockById;
|
||||
import baritone.api.command.datatypes.ForBlockOptionalMeta;
|
||||
import baritone.api.command.exception.CommandException;
|
||||
import baritone.api.utils.BlockOptionalMeta;
|
||||
|
@ -52,8 +51,12 @@ public class MineCommand extends Command {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Stream<String> tabComplete(String label, IArgConsumer args) {
|
||||
return args.tabCompleteDatatype(BlockById.INSTANCE);
|
||||
public Stream<String> tabComplete(String label, IArgConsumer args) throws CommandException {
|
||||
args.getAsOrDefault(Integer.class, 0);
|
||||
while (args.has(2)) {
|
||||
args.getDatatypeFor(ForBlockOptionalMeta.INSTANCE);
|
||||
}
|
||||
return args.tabCompleteDatatype(ForBlockOptionalMeta.INSTANCE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -21,6 +21,7 @@ import baritone.Baritone;
|
|||
import baritone.api.IBaritone;
|
||||
import baritone.api.command.ICommand;
|
||||
import baritone.api.command.argument.ICommandArgument;
|
||||
import baritone.api.command.exception.CommandException;
|
||||
import baritone.api.command.exception.CommandUnhandledException;
|
||||
import baritone.api.command.exception.ICommandException;
|
||||
import baritone.api.command.helpers.TabCompleteHelper;
|
||||
|
@ -151,9 +152,12 @@ public class CommandManager implements ICommandManager {
|
|||
private Stream<String> tabComplete() {
|
||||
try {
|
||||
return this.command.tabComplete(this.label, this.args);
|
||||
} catch (CommandException ignored) {
|
||||
// NOP
|
||||
} catch (Throwable t) {
|
||||
return Stream.empty();
|
||||
t.printStackTrace();
|
||||
}
|
||||
return Stream.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue