From c703f099e2af36335dd9906d7e531a58da80fbd0 Mon Sep 17 00:00:00 2001 From: Wyvest <45589059+Wyvest@users.noreply.github.com> Date: Sat, 11 Jun 2022 12:04:07 +0700 Subject: implement tab completions --- .../oneconfig/utils/commands/CommandManager.java | 275 ++++++++++++++------- .../utils/commands/arguments/BooleanParser.java | 9 +- 2 files changed, 188 insertions(+), 96 deletions(-) (limited to 'src') diff --git a/src/main/java/cc/polyfrost/oneconfig/utils/commands/CommandManager.java b/src/main/java/cc/polyfrost/oneconfig/utils/commands/CommandManager.java index 338e9a3..e0f4779 100644 --- a/src/main/java/cc/polyfrost/oneconfig/utils/commands/CommandManager.java +++ b/src/main/java/cc/polyfrost/oneconfig/utils/commands/CommandManager.java @@ -9,7 +9,12 @@ import cc.polyfrost.oneconfig.libs.universal.ChatColor; import cc.polyfrost.oneconfig.libs.universal.UChat; import net.minecraft.command.CommandBase; import net.minecraft.command.ICommandSender; +import net.minecraft.util.BlockPos; import net.minecraftforge.client.ClientCommandHandler; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.ImmutableTriple; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.commons.lang3.tuple.Triple; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -25,19 +30,20 @@ import java.util.*; public class CommandManager { public static final CommandManager INSTANCE = new CommandManager(); private static final String NOT_FOUND_TEXT = "Command not found! Type /@ROOT_COMMAND@ help for help."; + private static final String TOO_MANY_PARAMETERS = "There were too many / little parameters for this command! Type /@ROOT_COMMAND@ help for help."; private static final String METHOD_RUN_ERROR = "Error while running @ROOT_COMMAND@ method! Please report this to the developer."; private final HashMap, ArgumentParser> parsers = new HashMap<>(); private CommandManager() { addParser(new StringParser()); addParser(new IntegerParser()); - addParser(new IntegerParser(), int.class); + addParser(new IntegerParser(), Integer.TYPE); addParser(new DoubleParser()); - addParser(new DoubleParser(), double.class); + addParser(new DoubleParser(), Double.TYPE); addParser(new FloatParser()); - addParser(new FloatParser(), float.class); + addParser(new FloatParser(), Float.TYPE); addParser(new BooleanParser()); - addParser(new BooleanParser(), boolean.class); + addParser(new BooleanParser(), Boolean.TYPE); } /** @@ -89,62 +95,7 @@ public class CommandManager { @Override public void processCommand(ICommandSender sender, String[] args) { - if (args.length == 0) { - if (!root.invokers.isEmpty()) { - try { - root.invokers.get(0).method.invoke(null); - } catch (IllegalAccessException | InvocationTargetException | IllegalArgumentException | - ExceptionInInitializerError e) { - e.printStackTrace(); - UChat.chat(ChatColor.RED.toString() + ChatColor.BOLD + METHOD_RUN_ERROR); - } - } - } else { - if (annotation.helpCommand() && args[0].equalsIgnoreCase("help")) { - //UChat.chat(sendHelpCommand(root)); - } else { - List commands = new ArrayList<>(); - int depth = 0; - for (InternalCommand command : root.children) { - int newDepth = loopThroughCommands(commands, 0, command, args, true); - if (newDepth != -1) { - depth = newDepth; - break; - } - } - System.out.println(depth); - System.out.println(commands); - if (commands.isEmpty()) { - UChat.chat(ChatColor.RED.toString() + ChatColor.BOLD + NOT_FOUND_TEXT.replace("@ROOT_COMMAND@", annotation.value())); - } else { - List errors = new ArrayList<>(); - for (InternalCommand.InternalCommandInvoker invoker : commands) { - try { - List params = getParametersForInvoker(invoker, depth, args); - if (params.size() == 1) { - Object first = params.get(0); - if (first instanceof CustomError) { - errors.add((CustomError) first); - continue; - } - } - invoker.method.invoke(null, params.toArray()); - return; - } catch (Exception e) { - e.printStackTrace(); - UChat.chat(ChatColor.RED.toString() + ChatColor.BOLD + METHOD_RUN_ERROR); - return; - } - } - if (!errors.isEmpty()) { - UChat.chat(ChatColor.RED.toString() + ChatColor.BOLD + "Multiple errors occurred:"); - for (CustomError error : errors) { - UChat.chat(" " + ChatColor.RED + ChatColor.BOLD + error.message); - } - } - } - } - } + handleCommand(root, annotation, args); } @Override @@ -152,36 +103,152 @@ public class CommandManager { return -1; } - /*/ @Override public List addTabCompletionOptions(ICommandSender sender, String[] args, BlockPos pos) { - List commands = new ArrayList<>(); - int depth = 0; - for (InternalCommand command : root.children) { - int newDepth = loopThroughCommands(commands, 0, command, args, false); - if (newDepth != -1) { - depth = newDepth; - break; + return handleTabCompletion(root, args); + } + }); + } + } + + private void handleCommand(InternalCommand root, Command annotation, String[] args) { + if (args.length == 0) { + if (!root.invokers.isEmpty()) { + try { + root.invokers.get(0).method.invoke(null); + } catch (IllegalAccessException | InvocationTargetException | IllegalArgumentException | + ExceptionInInitializerError e) { + e.printStackTrace(); + UChat.chat(ChatColor.RED.toString() + ChatColor.BOLD + METHOD_RUN_ERROR); + } + } + } else { + if (annotation.helpCommand() && args[0].equalsIgnoreCase("help")) { + //UChat.chat(sendHelpCommand(root)); + } else { + List commands = new ArrayList<>(); + int depth = 0; + for (InternalCommand command : root.children) { + int newDepth = loopThroughCommands(commands, 0, command, args); + if (newDepth != -1) { + depth = newDepth; + break; + } + } + if (commands.isEmpty()) { + if (depth == -2) { + UChat.chat(ChatColor.RED.toString() + ChatColor.BOLD + TOO_MANY_PARAMETERS.replace("@ROOT_COMMAND@", annotation.value())); + } else { + UChat.chat(ChatColor.RED.toString() + ChatColor.BOLD + NOT_FOUND_TEXT.replace("@ROOT_COMMAND@", annotation.value())); + } + } else { + List errors = new ArrayList<>(); + for (InternalCommand.InternalCommandInvoker invoker : commands) { + try { + List params = getParametersForInvoker(invoker, depth, args); + if (params.size() == 1) { + Object first = params.get(0); + if (first instanceof CustomError) { + errors.add((CustomError) first); + continue; + } + } + invoker.method.invoke(null, params.toArray()); + return; + } catch (Exception e) { + e.printStackTrace(); + UChat.chat(ChatColor.RED.toString() + ChatColor.BOLD + METHOD_RUN_ERROR); + return; } } - System.out.println(depth); - System.out.println(commands); - if (!commands.isEmpty()) { - for (InternalCommand.InternalCommandInvoker invoker : commands) { - try { - List params = getParametersForInvoker(invoker, depth, args); - invoker.method.invoke(null, params.toArray()); - return; - } catch (Exception ignored) { + //noinspection ConstantConditions + if (!errors.isEmpty()) { + UChat.chat(ChatColor.RED.toString() + ChatColor.BOLD + "Multiple errors occurred:"); + for (CustomError error : errors) { + UChat.chat(" " + ChatColor.RED + ChatColor.BOLD + error.message); + } + } + } + } + } + } + private List handleTabCompletion(InternalCommand root, String[] args) { + try { + Set> commands = new HashSet<>(); + for (InternalCommand command : root.children) { + loopThroughCommandsTab(commands, 0, command, args); + } + if (!commands.isEmpty()) { + List> validCommands = new ArrayList<>(); // command, depth, and all processed params + for (Pair pair : commands) { + InternalCommand.InternalCommandInvoker invoker = pair.getLeft(); + int depth = pair.getRight(); + int currentParam = 0; + boolean failed = false; + while (args.length - depth > 1) { + Parameter param = invoker.method.getParameters()[currentParam]; + if (param.isAnnotationPresent(Greedy.class) && currentParam + 1 != invoker.parameterTypes.length) { + failed = true; + break; + } + ArgumentParser parser = parsers.get(param.getType()); + if (parser == null) { + failed = true; + break; + } + try { + Arguments arguments = new Arguments(Arrays.copyOfRange(args, depth, args.length), param.isAnnotationPresent(Greedy.class)); + if (parser.parse(arguments) != null) { + depth += arguments.getPosition(); + currentParam++; + } else { + failed = true; + break; } + } catch (Exception e) { + failed = true; + break; } } + if (!failed) { + validCommands.add(new ImmutableTriple<>(pair.getLeft(), depth, currentParam)); + } } + if (!validCommands.isEmpty()) { + Set completions = new HashSet<>(); + for (Triple valid : validCommands) { + if (valid.getMiddle() == args.length) { + completions.add(valid.getLeft().name); + completions.addAll(Arrays.asList(valid.getLeft().aliases)); + continue; + } + if (valid.getRight() + 1 > valid.getLeft().parameterTypes.length) continue; + Parameter param = valid.getLeft().method.getParameters()[valid.getRight()]; + if (param.isAnnotationPresent(Greedy.class) && valid.getRight() + 1 != valid.getLeft().parameterTypes.length) { + continue; + } + ArgumentParser parser = parsers.get(param.getType()); + if (parser == null) { + continue; + } + try { + Arguments arguments = new Arguments(Arrays.copyOfRange(args, valid.getMiddle(), args.length), param.isAnnotationPresent(Greedy.class)); + List possibleCompletions = parser.complete(arguments, param); + if (possibleCompletions != null) { + completions.addAll(possibleCompletions); + } + } catch (Exception ignored) { + + } + } + return new ArrayList<>(completions); + } + } + } catch (Exception ignored) { - */ - }); } + return null; } private List getParametersForInvoker(InternalCommand.InternalCommandInvoker invoker, int depth, String[] args) { @@ -190,7 +257,7 @@ public class CommandManager { int currentParam = 0; while (processed < args.length) { Parameter param = invoker.method.getParameters()[currentParam]; - if (param.isAnnotationPresent(Greedy.class) && currentParam + 1 != invoker.method.getParameterCount()) { + if (param.isAnnotationPresent(Greedy.class) && currentParam + 1 != invoker.parameterTypes.length) { return Collections.singletonList(new CustomError("Parsing failed: Greedy parameter must be the last one.")); } ArgumentParser parser = parsers.get(param.getType()); @@ -218,36 +285,50 @@ public class CommandManager { return parameters; } - private int loopThroughCommands(List commands, int depth, InternalCommand command, String[] args, boolean checkParams) { + private int loopThroughCommands(List commands, int depth, InternalCommand command, String[] args) { int nextDepth = depth + 1; - if (command.isEqual(args[depth])) { + boolean thatOneSpecialError = false; + if (command.isValid(args[depth], false)) { for (InternalCommand child : command.children) { - if (args.length > nextDepth && child.isEqual(args[nextDepth])) { - int result = loopThroughCommands(commands, nextDepth, child, args, checkParams); - if (result != -1) { + if (args.length > nextDepth && child.isValid(args[nextDepth], false)) { + int result = loopThroughCommands(commands, nextDepth, child, args); + if (result > -1) { return result; + } else if (result == -2) { + thatOneSpecialError = true; } } } boolean added = false; for (InternalCommand.InternalCommandInvoker invoker : command.invokers) { - if (!checkParams || args.length - nextDepth == invoker.method.getParameterCount()) { + if (args.length - nextDepth == invoker.parameterTypes.length) { commands.add(invoker); added = true; + } else { + thatOneSpecialError = true; } } if (added) { return nextDepth; } - } else { - for (InternalCommand child : command.children) { - int childDepth = loopThroughCommands(commands, nextDepth, child, args, checkParams); - if (childDepth != -1) { - return childDepth; + } + return thatOneSpecialError ? -2 : -1; + } + + private void loopThroughCommandsTab(Set> commands, int depth, InternalCommand command, String[] args) { + int nextDepth = depth + 1; + if (command.isValid(args[depth], args.length == nextDepth)) { + if (args.length != nextDepth) { + for (InternalCommand child : command.children) { + if (child.isValid(args[nextDepth], args.length == nextDepth + 1)) { + loopThroughCommandsTab(commands, nextDepth, child, args); + } } } + for (InternalCommand.InternalCommandInvoker invoker : command.invokers) { + commands.add(new ImmutablePair<>(invoker, nextDepth)); + } } - return -1; } private void addToInvokers(Class[] classes, InternalCommand parent) { @@ -289,12 +370,15 @@ public class CommandManager { this.parent = parent; } - public boolean isEqual(String name) { - if (this.name.equals(name)) { + public boolean isValid(String name, boolean tabCompletion) { + String lowerCaseName = this.name.toLowerCase(Locale.ENGLISH); + String lowerCaseOtherName = name.toLowerCase(Locale.ENGLISH); + if (!tabCompletion ? lowerCaseName.equals(lowerCaseOtherName) : lowerCaseName.startsWith(lowerCaseOtherName)) { return true; } else { for (String alias : aliases) { - if (alias.equals(name)) { + String lowerCaseAlias = alias.toLowerCase(Locale.ENGLISH); + if (!tabCompletion ? lowerCaseAlias.equals(lowerCaseOtherName) : lowerCaseAlias.startsWith(lowerCaseOtherName)) { return true; } } @@ -345,3 +429,4 @@ public class CommandManager { } } } + diff --git a/src/main/java/cc/polyfrost/oneconfig/utils/commands/arguments/BooleanParser.java b/src/main/java/cc/polyfrost/oneconfig/utils/commands/arguments/BooleanParser.java index 7411cbe..ede83fb 100644 --- a/src/main/java/cc/polyfrost/oneconfig/utils/commands/arguments/BooleanParser.java +++ b/src/main/java/cc/polyfrost/oneconfig/utils/commands/arguments/BooleanParser.java @@ -13,7 +13,14 @@ public class BooleanParser extends ArgumentParser { @Override public @Nullable Boolean parse(Arguments arguments) { - return Boolean.parseBoolean(arguments.poll()); + String next = arguments.poll(); + if (next.equalsIgnoreCase("true")) { + return true; + } else if (next.equalsIgnoreCase("false")) { + return false; + } else { + return null; + } } @Override -- cgit