diff --git a/src/main/java/net/minestom/server/command/CommandManager.java b/src/main/java/net/minestom/server/command/CommandManager.java index 03b84a319..7bc5fa69b 100644 --- a/src/main/java/net/minestom/server/command/CommandManager.java +++ b/src/main/java/net/minestom/server/command/CommandManager.java @@ -214,7 +214,7 @@ public final class CommandManager { } final ArgumentQueryResult queryResult = CommandParser.findEligibleArgument(commandQueryResult.command, - commandQueryResult.args, input, false, true, syntax -> true, argument -> true); + commandQueryResult.content(), input, false, true, syntax -> true, argument -> true); if (queryResult == null) { // Invalid argument, return command node (default to root) final int commandNode = commandIdentityMap.getOrDefault(commandQueryResult.command, 0); diff --git a/src/main/java/net/minestom/server/command/builder/CommandData.java b/src/main/java/net/minestom/server/command/builder/CommandData.java index 70f412963..812c8f06f 100644 --- a/src/main/java/net/minestom/server/command/builder/CommandData.java +++ b/src/main/java/net/minestom/server/command/builder/CommandData.java @@ -7,7 +7,6 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public class CommandData { - private final Map dataMap = new ConcurrentHashMap<>(); public CommandData set(@NotNull String key, Object value) { @@ -15,8 +14,7 @@ public class CommandData { return this; } - @Nullable - public T get(@NotNull String key) { + public @Nullable T get(@NotNull String key) { return (T) dataMap.get(key); } @@ -24,8 +22,7 @@ public class CommandData { return dataMap.containsKey(key); } - @NotNull - public Map getDataMap() { + public @NotNull Map getDataMap() { return dataMap; } } diff --git a/src/main/java/net/minestom/server/command/builder/CommandDispatcher.java b/src/main/java/net/minestom/server/command/builder/CommandDispatcher.java index 1d35d7f66..1c9ed1544 100644 --- a/src/main/java/net/minestom/server/command/builder/CommandDispatcher.java +++ b/src/main/java/net/minestom/server/command/builder/CommandDispatcher.java @@ -20,11 +20,9 @@ import java.util.concurrent.TimeUnit; /** * Class responsible for parsing {@link Command}. */ -public class CommandDispatcher { - +public final class CommandDispatcher { private final Map commandMap = new HashMap<>(); private final Set commands = new HashSet<>(); - private final Cache cache = Caffeine.newBuilder() .expireAfterWrite(30, TimeUnit.SECONDS) .build(); @@ -37,7 +35,6 @@ public class CommandDispatcher { */ public void register(@NotNull Command command) { this.commandMap.put(command.getName().toLowerCase(), command); - // Register aliases final String[] aliases = command.getAliases(); if (aliases != null) { @@ -45,20 +42,17 @@ public class CommandDispatcher { this.commandMap.put(alias.toLowerCase(), command); } } - this.commands.add(command); } public void unregister(@NotNull Command command) { this.commandMap.remove(command.getName().toLowerCase()); - final String[] aliases = command.getAliases(); if (aliases != null) { for (String alias : aliases) { this.commandMap.remove(alias.toLowerCase()); } } - this.commands.remove(command); // Clear cache @@ -76,8 +70,7 @@ public class CommandDispatcher { * @return the {@link Command} associated with the name, null if not any */ public @Nullable Command findCommand(@NotNull String commandName) { - commandName = commandName.toLowerCase(); - return commandMap.getOrDefault(commandName, null); + return commandMap.getOrDefault(commandName.toLowerCase(), null); } /** @@ -90,9 +83,7 @@ public class CommandDispatcher { public @NotNull CommandResult execute(@NotNull CommandSender source, @NotNull String commandString) { CommandResult commandResult = parse(commandString); ParsedCommand parsedCommand = commandResult.parsedCommand; - if (parsedCommand != null) { - commandResult.commandData = parsedCommand.execute(source); - } + if (parsedCommand != null) commandResult.commandData = parsedCommand.execute(source); return commandResult; } @@ -117,16 +108,14 @@ public class CommandDispatcher { final String commandName = parts[0]; final CommandQueryResult commandQueryResult = CommandParser.findCommand(commandString); - // Check if the command exists if (commandQueryResult == null) { return CommandResult.of(CommandResult.Type.UNKNOWN, commandName); } - final Command command = commandQueryResult.command; CommandResult result = new CommandResult(); result.input = commandString; // Find the used syntax and fill CommandResult#type and CommandResult#parsedCommand - findParsedCommand(command, commandName, commandQueryResult.args, commandString, result); + findParsedCommand(commandQueryResult, result); // Cache result this.cache.put(commandString, result); @@ -134,32 +123,17 @@ public class CommandDispatcher { return result; } - private @Nullable ParsedCommand findParsedCommand(@NotNull Command command, - @NotNull String commandName, @NotNull String[] args, - @NotNull String commandString, - @NotNull CommandResult result) { - final boolean hasArgument = args.length > 0; - - // Search for subcommand - if (hasArgument) { - final String firstArgument = args[0]; - for (Command subcommand : command.getSubcommands()) { - if (Command.isValidName(subcommand, firstArgument)) { - return findParsedCommand(subcommand, - firstArgument, Arrays.copyOfRange(args, 1, args.length), - commandString, result); - } - } - } - - final String input = commandName + StringUtils.SPACE + String.join(StringUtils.SPACE, args); + private void findParsedCommand(@NotNull CommandQueryResult queryResult, + @NotNull CommandResult result) { + final Command command = queryResult.command(); + final String commandString = queryResult.input(); ParsedCommand parsedCommand = new ParsedCommand(); parsedCommand.command = command; parsedCommand.commandString = commandString; // The default executor should be used if no argument is provided - if (!hasArgument) { + if (queryResult.argsInput().isEmpty()) { Optional optionalSyntax = command.getSyntaxes() .stream() .filter(syntax -> syntax.getArguments().length == 0) @@ -170,21 +144,21 @@ public class CommandDispatcher { final CommandSyntax syntax = optionalSyntax.get(); parsedCommand.syntax = syntax; parsedCommand.executor = syntax.getExecutor(); - parsedCommand.context = new CommandContext(input); + parsedCommand.context = new CommandContext(commandString); result.type = CommandResult.Type.SUCCESS; result.parsedCommand = parsedCommand; - return parsedCommand; + return; } else { // No empty syntax, use default executor if any final CommandExecutor defaultExecutor = command.getDefaultExecutor(); if (defaultExecutor != null) { parsedCommand.executor = defaultExecutor; - parsedCommand.context = new CommandContext(input); + parsedCommand.context = new CommandContext(commandString); result.type = CommandResult.Type.SUCCESS; result.parsedCommand = parsedCommand; - return parsedCommand; + return; } } } @@ -200,12 +174,12 @@ public class CommandDispatcher { Int2ObjectRBTreeMap syntaxesSuggestions = new Int2ObjectRBTreeMap<>(Collections.reverseOrder()); for (CommandSyntax syntax : syntaxes) { - CommandParser.parse(syntax, syntax.getArguments(), args, commandString, validSyntaxes, syntaxesSuggestions); + CommandParser.parse(syntax, syntax.getArguments(), queryResult, validSyntaxes, syntaxesSuggestions); } // Check if there is at least one correct syntax if (!validSyntaxes.isEmpty()) { - CommandContext context = new CommandContext(input); + CommandContext context = new CommandContext(commandString); // Search the syntax with all perfect args final ValidSyntaxHolder finalValidSyntax = CommandParser.findMostCorrectSyntax(validSyntaxes, context); if (finalValidSyntax != null) { @@ -218,7 +192,7 @@ public class CommandDispatcher { result.type = CommandResult.Type.SUCCESS; result.parsedCommand = parsedCommand; - return parsedCommand; + return; } } @@ -238,13 +212,12 @@ public class CommandDispatcher { result.type = CommandResult.Type.INVALID_SYNTAX; result.parsedCommand = parsedCommand; - return parsedCommand; + return; } } // No syntax found result.type = CommandResult.Type.INVALID_SYNTAX; - result.parsedCommand = ParsedCommand.withDefaultExecutor(command, input); - return result.parsedCommand; + result.parsedCommand = ParsedCommand.withDefaultExecutor(command, commandString); } } diff --git a/src/main/java/net/minestom/server/command/builder/arguments/ArgumentGroup.java b/src/main/java/net/minestom/server/command/builder/arguments/ArgumentGroup.java index 9d4c8f659..b033d8de4 100644 --- a/src/main/java/net/minestom/server/command/builder/arguments/ArgumentGroup.java +++ b/src/main/java/net/minestom/server/command/builder/arguments/ArgumentGroup.java @@ -5,7 +5,6 @@ import net.minestom.server.command.builder.NodeMaker; import net.minestom.server.command.builder.exception.ArgumentSyntaxException; import net.minestom.server.command.builder.parser.CommandParser; import net.minestom.server.command.builder.parser.ValidSyntaxHolder; -import net.minestom.server.utils.StringUtils; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; @@ -26,14 +25,12 @@ public class ArgumentGroup extends Argument { @Override public CommandContext parse(@NotNull String input) throws ArgumentSyntaxException { List validSyntaxes = new ArrayList<>(); - CommandParser.parse(null, group, input.split(StringUtils.SPACE), input, validSyntaxes, null); + CommandParser.parse(null, group, input, input, validSyntaxes, null); CommandContext context = new CommandContext(input); CommandParser.findMostCorrectSyntax(validSyntaxes, context); - if (validSyntaxes.isEmpty()) { + if (validSyntaxes.isEmpty()) throw new ArgumentSyntaxException("Invalid arguments", input, INVALID_ARGUMENTS_ERROR); - } - return context; } diff --git a/src/main/java/net/minestom/server/command/builder/parser/ArgumentParser.java b/src/main/java/net/minestom/server/command/builder/parser/ArgumentParser.java index 39c246df1..000a0930f 100644 --- a/src/main/java/net/minestom/server/command/builder/parser/ArgumentParser.java +++ b/src/main/java/net/minestom/server/command/builder/parser/ArgumentParser.java @@ -2,7 +2,10 @@ package net.minestom.server.command.builder.parser; import net.minestom.server.command.builder.arguments.*; import net.minestom.server.command.builder.arguments.minecraft.*; -import net.minestom.server.command.builder.arguments.minecraft.registry.*; +import net.minestom.server.command.builder.arguments.minecraft.registry.ArgumentEnchantment; +import net.minestom.server.command.builder.arguments.minecraft.registry.ArgumentEntityType; +import net.minestom.server.command.builder.arguments.minecraft.registry.ArgumentParticle; +import net.minestom.server.command.builder.arguments.minecraft.registry.ArgumentPotionEffect; import net.minestom.server.command.builder.arguments.number.ArgumentDouble; import net.minestom.server.command.builder.arguments.number.ArgumentFloat; import net.minestom.server.command.builder.arguments.number.ArgumentInteger; @@ -41,10 +44,10 @@ public class ArgumentParser { ARGUMENT_FUNCTION_MAP.put("time", ArgumentTime::new); ARGUMENT_FUNCTION_MAP.put("enchantment", ArgumentEnchantment::new); ARGUMENT_FUNCTION_MAP.put("particle", ArgumentParticle::new); - ARGUMENT_FUNCTION_MAP.put("resourceLocation", ArgumentResourceLocation::new); + ARGUMENT_FUNCTION_MAP.put("resourcelocation", ArgumentResourceLocation::new); ARGUMENT_FUNCTION_MAP.put("potion", ArgumentPotionEffect::new); - ARGUMENT_FUNCTION_MAP.put("entityType", ArgumentEntityType::new); - ARGUMENT_FUNCTION_MAP.put("blockState", ArgumentBlockState::new); + ARGUMENT_FUNCTION_MAP.put("entitytype", ArgumentEntityType::new); + ARGUMENT_FUNCTION_MAP.put("blockstate", ArgumentBlockState::new); ARGUMENT_FUNCTION_MAP.put("intrange", ArgumentIntRange::new); ARGUMENT_FUNCTION_MAP.put("floatrange", ArgumentFloatRange::new); @@ -66,7 +69,6 @@ public class ArgumentParser { @ApiStatus.Experimental public static @NotNull Argument[] generate(@NotNull String format) { List> result = new ArrayList<>(); - // 0 = no state // 1 = inside angle bracket <> int state = 0; @@ -75,11 +77,9 @@ public class ArgumentParser { Function> argumentFunction = null; StringBuilder builder = new StringBuilder(); - // test: Integer String for (int i = 0; i < format.length(); i++) { - char c = format.charAt(i); - + final char c = format.charAt(i); // No state if (state == 0) { if (c == ' ') { @@ -133,17 +133,15 @@ public class ArgumentParser { result.add(new ArgumentLiteral(argument)); } } - return result.toArray(Argument[]::new); } - @Nullable - public static ArgumentResult validate(@NotNull Argument argument, - @NotNull Argument[] arguments, int argIndex, - @NotNull String[] inputArguments, int inputIndex) { - final boolean end = inputIndex == inputArguments.length; - if (end) // Stop if there is no input to analyze left - return null; + public static @Nullable ArgumentResult validate(@NotNull Argument argument, + @NotNull Argument[] arguments, int argIndex, + @NotNull String input, int inputIndex) { + final String[] inputArguments = input.split(StringUtils.SPACE); + // Stop if there is no input to analyze left + if (inputIndex == inputArguments.length) return null; // the parsed argument value, null if incorrect Object parsedValue = null; @@ -162,13 +160,10 @@ public class ArgumentParser { // Argument is supposed to take the rest of the command input for (int i = inputIndex; i < inputArguments.length; i++) { final String arg = inputArguments[i]; - if (builder.length() > 0) - builder.append(StringUtils.SPACE); + if (builder.length() > 0) builder.append(StringUtils.SPACE); builder.append(arg); } - rawArg = builder.toString(); - try { parsedValue = argument.parse(rawArg); correct = true; @@ -181,29 +176,22 @@ public class ArgumentParser { StringBuilder builder = new StringBuilder(); for (int i = inputIndex; i < inputArguments.length; i++) { builder.append(inputArguments[i]); - rawArg = builder.toString(); - try { parsedValue = argument.parse(rawArg); - // Prevent quitting the parsing too soon if the argument // does not allow space final boolean lastArgumentIteration = argIndex + 1 == arguments.length; if (lastArgumentIteration && i + 1 < inputArguments.length) { - if (!argument.allowSpace()) - break; + if (!argument.allowSpace()) break; builder.append(StringUtils.SPACE); continue; } - correct = true; - inputIndex = i + 1; break; } catch (ArgumentSyntaxException exception) { argumentSyntaxException = exception; - if (!argument.allowSpace()) { // rawArg should be the remaining for (int j = i + 1; j < inputArguments.length; j++) { @@ -247,5 +235,4 @@ public class ArgumentParser { // If correct public Object parsedValue; } - } diff --git a/src/main/java/net/minestom/server/command/builder/parser/CommandParser.java b/src/main/java/net/minestom/server/command/builder/parser/CommandParser.java index 5614af3c1..e4c855d90 100644 --- a/src/main/java/net/minestom/server/command/builder/parser/CommandParser.java +++ b/src/main/java/net/minestom/server/command/builder/parser/CommandParser.java @@ -11,7 +11,10 @@ import net.minestom.server.utils.StringUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.*; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.function.Predicate; import static net.minestom.server.command.builder.parser.ArgumentParser.validate; @@ -19,51 +22,43 @@ import static net.minestom.server.command.builder.parser.ArgumentParser.validate /** * Class used to parse complete command inputs. */ -public class CommandParser { - +public final class CommandParser { private static final CommandManager COMMAND_MANAGER = MinecraftServer.getCommandManager(); - @Nullable - public static CommandQueryResult findCommand(@Nullable Command parentCommand, @NotNull String commandName, @NotNull String[] args) { + public static @Nullable CommandQueryResult findCommand(@Nullable Command parentCommand, @NotNull String commandName, + @NotNull String argsInput, @NotNull String input) { Command command = parentCommand == null ? COMMAND_MANAGER.getDispatcher().findCommand(commandName) : parentCommand; - if (command == null) { - return null; - } - - CommandQueryResult commandQueryResult = new CommandQueryResult(); - commandQueryResult.command = command; - commandQueryResult.commandName = commandName; - commandQueryResult.args = args; - + if (command == null) return null; // Search for subcommand - if (args.length > 0) { - final String subCommandName = args[0]; + final int nextArgIndex = argsInput.indexOf(StringUtils.SPACE); + if (nextArgIndex != -1) { + final String subCommandName = argsInput.substring(0, nextArgIndex); for (Command subcommand : command.getSubcommands()) { if (Command.isValidName(subcommand, subCommandName)) { - final String[] subArgs = Arrays.copyOfRange(args, 1, args.length); - commandQueryResult.command = subcommand; - commandQueryResult.commandName = subCommandName; - commandQueryResult.args = subArgs; - return findCommand(subcommand, subCommandName, subArgs); + final String updated = StringUtils.trimLeft(argsInput.replaceFirst(commandName, "")); + return findCommand(subcommand, subCommandName, updated, input); } } } - - return commandQueryResult; + return new CommandQueryResult(command, argsInput, input); } - @Nullable - public static CommandQueryResult findCommand(@NotNull String input) { - final String[] parts = input.split(StringUtils.SPACE); - final String commandName = parts[0]; - - String[] args = new String[parts.length - 1]; - System.arraycopy(parts, 1, args, 0, args.length); - return CommandParser.findCommand(null, commandName, args); + public static @Nullable CommandQueryResult findCommand(@NotNull String input) { + final String commandName; + final String content; + final int commandNameEnd = input.indexOf(StringUtils.SPACE); + if (commandNameEnd != -1) { + commandName = input.substring(0, commandNameEnd); + content = StringUtils.trimLeft(input.replaceFirst(commandName, "")); + } else { + commandName = input; + content = ""; + } + return CommandParser.findCommand(null, commandName, content, input); } - public static void parse(@Nullable CommandSyntax syntax, @NotNull Argument[] commandArguments, @NotNull String[] inputArguments, - @NotNull String commandString, + public static void parse(@Nullable CommandSyntax syntax, @NotNull Argument[] commandArguments, + @NotNull CommandQueryResult queryResult, @Nullable List validSyntaxes, @Nullable Int2ObjectRBTreeMap syntaxesSuggestions) { final Map, ArgumentParser.ArgumentResult> argumentValueMap = new HashMap<>(); @@ -76,10 +71,8 @@ public class CommandParser { // Check the validity of the arguments... for (int argIndex = 0; argIndex < commandArguments.length; argIndex++) { final Argument argument = commandArguments[argIndex]; - ArgumentParser.ArgumentResult argumentResult = validate(argument, commandArguments, argIndex, inputArguments, inputIndex); - if (argumentResult == null) { - break; - } + ArgumentParser.ArgumentResult argumentResult = validate(argument, commandArguments, argIndex, queryResult.argsInput(), inputIndex); + if (argumentResult == null) break; // Update local var useRemaining = argumentResult.useRemaining; @@ -107,7 +100,7 @@ public class CommandParser { if (commandArguments.length == argumentValueMap.size() || useRemaining) { if (validSyntaxes != null) { ValidSyntaxHolder validSyntaxHolder = new ValidSyntaxHolder(); - validSyntaxHolder.commandString = commandString; + validSyntaxHolder.commandString = queryResult.input(); validSyntaxHolder.syntax = syntax; validSyntaxHolder.argumentResults = argumentValueMap; @@ -125,27 +118,20 @@ public class CommandParser { * @param context the recipient of the argument parsed values * @return the command syntax with all of its arguments correct and with the most arguments count, null if not any */ - @Nullable - public static ValidSyntaxHolder findMostCorrectSyntax(@NotNull List validSyntaxes, - @NotNull CommandContext context) { - if (validSyntaxes.isEmpty()) { - return null; - } + public static @Nullable ValidSyntaxHolder findMostCorrectSyntax(@NotNull List validSyntaxes, + @NotNull CommandContext context) { + if (validSyntaxes.isEmpty()) return null; ValidSyntaxHolder finalSyntax = null; int maxArguments = 0; CommandContext finalContext = null; - for (ValidSyntaxHolder validSyntaxHolder : validSyntaxes) { final Map, ArgumentParser.ArgumentResult> argsValues = validSyntaxHolder.argumentResults; - final int argsSize = argsValues.size(); - // Check if the syntax has more valid arguments if (argsSize > maxArguments) { finalSyntax = validSyntaxHolder; maxArguments = argsSize; - // Fill arguments map finalContext = new CommandContext(validSyntaxHolder.commandString); for (var entry : argsValues.entrySet()) { @@ -155,30 +141,21 @@ public class CommandParser { } } } - // Get the arguments values - if (finalSyntax != null) { - context.copy(finalContext); - } - + if (finalSyntax != null) context.copy(finalContext); return finalSyntax; } - @Nullable - public static ArgumentQueryResult findEligibleArgument(@NotNull Command command, String[] args, String commandString, - boolean trailingSpace, boolean forceCorrect, - Predicate syntaxPredicate, - Predicate> argumentPredicate) { - final Collection syntaxes = command.getSyntaxes(); - + public static @Nullable ArgumentQueryResult findEligibleArgument(@NotNull CommandQueryResult result, + boolean trailingSpace, boolean forceCorrect, + Predicate syntaxPredicate, + Predicate> argumentPredicate) { + final String[] args = result.argsInput().split(StringUtils.SPACE); Int2ObjectRBTreeMap suggestions = new Int2ObjectRBTreeMap<>(Collections.reverseOrder()); + for (CommandSyntax syntax : result.command().getSyntaxes()) { + if (!syntaxPredicate.test(syntax)) continue; - for (CommandSyntax syntax : syntaxes) { - if (!syntaxPredicate.test(syntax)) { - continue; - } - - final CommandContext context = new CommandContext(commandString); + final CommandContext context = new CommandContext(result.input()); final Argument[] commandArguments = syntax.getArguments(); int inputIndex = 0; @@ -187,7 +164,7 @@ public class CommandParser { int maxArgIndex = 0; for (int argIndex = 0; argIndex < commandArguments.length; argIndex++) { Argument argument = commandArguments[argIndex]; - ArgumentParser.ArgumentResult argumentResult = validate(argument, commandArguments, argIndex, args, inputIndex); + ArgumentParser.ArgumentResult argumentResult = validate(argument, commandArguments, argIndex, result.argsInput(), inputIndex); if (argumentResult == null) { // Nothing to analyze, create a dummy object argumentResult = new ArgumentParser.ArgumentResult(); @@ -219,29 +196,17 @@ public class CommandParser { } // Don't compute following arguments if the syntax is incorrect - if (!argumentResult.correct) { - break; - } + if (!argumentResult.correct) break; // Don't compute unrelated arguments final boolean isLast = inputIndex == args.length; - if (isLast && !trailingSpace) { - break; - } - - } - if (maxArg != null) { - suggestions.put(maxArgIndex, maxArg); + if (isLast && !trailingSpace) break; } + if (maxArg != null) suggestions.put(maxArgIndex, maxArg); } - if (suggestions.isEmpty()) { - // No suggestion - return null; - } - + if (suggestions.isEmpty()) return null; final int max = suggestions.firstIntKey(); return suggestions.get(max); } - } diff --git a/src/main/java/net/minestom/server/command/builder/parser/CommandQueryResult.java b/src/main/java/net/minestom/server/command/builder/parser/CommandQueryResult.java index a6f987b0e..522ad1270 100644 --- a/src/main/java/net/minestom/server/command/builder/parser/CommandQueryResult.java +++ b/src/main/java/net/minestom/server/command/builder/parser/CommandQueryResult.java @@ -2,8 +2,26 @@ package net.minestom.server.command.builder.parser; import net.minestom.server.command.builder.Command; -public class CommandQueryResult { - public Command command; - public String commandName; - public String[] args; +public final class CommandQueryResult { + private final Command command; + private final String argsInput; + private final String input; + + public CommandQueryResult(Command command, String argsInput, String input) { + this.command = command; + this.argsInput = argsInput; + this.input = input; + } + + public Command command() { + return command; + } + + public String argsInput() { + return argsInput; + } + + public String input() { + return input; + } } diff --git a/src/main/java/net/minestom/server/listener/TabCompleteListener.java b/src/main/java/net/minestom/server/listener/TabCompleteListener.java index 462978a50..ed4862ebe 100644 --- a/src/main/java/net/minestom/server/listener/TabCompleteListener.java +++ b/src/main/java/net/minestom/server/listener/TabCompleteListener.java @@ -27,13 +27,8 @@ public class TabCompleteListener { String args = commandString.replaceFirst(Pattern.quote(commandName), ""); final CommandQueryResult commandQueryResult = CommandParser.findCommand(commandString); - if (commandQueryResult == null) { - // Command not found - return; - } - - final ArgumentQueryResult queryResult = CommandParser.findEligibleArgument(commandQueryResult.command, - commandQueryResult.args, commandString, text.endsWith(StringUtils.SPACE), false, + if (commandQueryResult == null) return; + final ArgumentQueryResult queryResult = CommandParser.findEligibleArgument(commandQueryResult, text.endsWith(StringUtils.SPACE), false, CommandSyntax::hasSuggestion, Argument::hasSuggestion); if (queryResult == null) { // Suggestible argument not found diff --git a/src/main/java/net/minestom/server/utils/StringUtils.java b/src/main/java/net/minestom/server/utils/StringUtils.java index 870380cba..e443eb4db 100644 --- a/src/main/java/net/minestom/server/utils/StringUtils.java +++ b/src/main/java/net/minestom/server/utils/StringUtils.java @@ -21,6 +21,12 @@ public class StringUtils { return count; } + public static String trimLeft(String input) { + int i = 0; + while (i < input.length() && Character.isWhitespace(input.charAt(i))) i++; + return input.substring(i); + } + /** * Applies the Jaro-Winkler distance algorithm to the given strings, providing information about the * similarity of them.