diff --git a/.idea/misc.xml b/.idea/misc.xml index 03872dd..ecb0c83 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 1eb1391..af37b85 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -4,29 +4,30 @@ - - - - - - - false - true - false - true - - + - + + + true + true + false + false + + + + + + - + - - + + @@ -579,10 +593,11 @@ - - \ No newline at end of file diff --git a/TODO b/TODO index f3e61d5..bfd9608 100644 --- a/TODO +++ b/TODO @@ -1,9 +1,10 @@ +- version catalog - suggest next literal/argument - fix: ``` [22:05:39 ERROR]: Could not pass event AsyncTabCompleteEvent to CoreBukkit v1.0.0-SNAPSHOT java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0 - at core-bukkit-impl-all-1783368113525.jar//de.kentoj.kencommandapi.api.parser2.SuggestionCommandParser.getSuggestions(SuggestionCommandParser.java:39) ~[?:?] + at core-bukkit-impl-all-1783368113525.jar//de.kentoj.kencommandapi.api.parser.SuggestionCommandParser.getSuggestions(SuggestionCommandParser.java:39) ~[?:?] at core-bukkit-impl-all-1783368113525.jar//de.kentoj.kencommandapi.api.CommandHandler.getSuggestions(CommandHandler.java:44) ~[?:?] at core-bukkit-impl-all-1783368113525.jar//de.kentoj.kencommandapi.TabCompleteListener.onAsyncTabComplete(TabCompleteListener.java:27) ~[?:?] at co.aikar.timings.TimedEventExecutor.execute(TimedEventExecutor.java:80) ~[paper-api-1.21.11-R0.1-SNAPSHOT.jar:?] diff --git a/kencommandapi-bukkit/build.gradle.kts b/kencommandapi-bukkit/build.gradle.kts index 8a44f16..6c3efb3 100644 --- a/kencommandapi-bukkit/build.gradle.kts +++ b/kencommandapi-bukkit/build.gradle.kts @@ -5,14 +5,8 @@ plugins { id("scrow.java-library") } -repositories { - maven("https://repo.papermc.io/repository/maven-public/") { - name = "papermc" - } -} - dependencies { - compileOnly(project(":kencommandapi-core")) + api(project(":kencommandapi-core")) compileOnly("io.papermc.paper:paper-api:1.21.11-rc3-R0.1-SNAPSHOT") } diff --git a/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/TabCompleteListener.java b/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/TabCompleteListener.java index 70c0479..5a1179d 100644 --- a/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/TabCompleteListener.java +++ b/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/TabCompleteListener.java @@ -18,12 +18,19 @@ public class TabCompleteListener implements Listener { @EventHandler(ignoreCancelled = true) public void onAsyncTabComplete(AsyncTabCompleteEvent ev) { if (!ev.isCommand()) return; - var args = ev.getBuffer().substring(1).split(" "); + if (args.length == 0) return; + var label = args[0]; + { + // commands can be called with a ':' prefix. strip it. + int l = label.indexOf(':'); + if (l >= 0) + label = label.substring(l+1); + } var rootNode = commandAPI.getRegistered(label); if (rootNode == null) return; ev.setHandled(true); - ev.setCompletions(commandHandler.getSuggestions(rootNode, ev.getSender(), Arrays.copyOfRange(args, 1, args.length))); + ev.setCompletions(commandHandler.getSuggestions(rootNode, ev.getSender(), Arrays.copyOfRange(args, 1, args.length), ev.getBuffer().endsWith(" "))); } } diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/CommandHandler.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/CommandHandler.java index 9738558..c652460 100644 --- a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/CommandHandler.java +++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/CommandHandler.java @@ -2,12 +2,14 @@ package de.kentoj.kencommandapi.api; import de.kentoj.kencommandapi.api.invocation.CommandContext; import de.kentoj.kencommandapi.api.literal.RootLiteral; -import de.kentoj.kencommandapi.api.parser2.InvocationCommandParser; -import de.kentoj.kencommandapi.api.parser2.SuggestionCommandParser; +import de.kentoj.kencommandapi.api.parser.CommandParseHelper; +import de.kentoj.kencommandapi.api.parser.InvocationCommandParser; +import de.kentoj.kencommandapi.api.parser.SuggestionCommandParser; import de.kentoj.kencommandapi.api.platform.HasPermissionMethod; import de.kentoj.kencommandapi.api.platform.SendMessageMethod; import lombok.RequiredArgsConstructor; +import java.util.Arrays; import java.util.List; @RequiredArgsConstructor @@ -20,13 +22,14 @@ public class CommandHandler { public CommandHandler(SendMessageMethod sendMessageMethod, HasPermissionMethod hasPermissionMethod) { this.sendMessageMethod = sendMessageMethod; - invocationParser = new InvocationCommandParser<>(hasPermissionMethod); - suggestionParser = new SuggestionCommandParser<>(hasPermissionMethod); + var parseHelper = new CommandParseHelper<>(hasPermissionMethod); + invocationParser = new InvocationCommandParser<>(parseHelper); + suggestionParser = new SuggestionCommandParser<>(parseHelper); } @SuppressWarnings("CodeBlock2Expr") public void invoke(RootLiteral rootLiteral, T sender, String[] args) { - invocationParser.invoke(rootLiteral, sender, args) + invocationParser.parse(rootLiteral, sender, Arrays.stream(args).iterator()) .ifSuccessOrElse(data -> { var ctx = new CommandContext<>(sender, data.getArguments()); data.getExecutor().execute(ctx).whenComplete((result, ex) -> { @@ -40,7 +43,8 @@ public class CommandHandler { }); } - public List getSuggestions(RootLiteral rootNode, T sender, String[] args) { - return suggestionParser.getSuggestions(rootNode, sender, args); + public List getSuggestions(RootLiteral rootNode, T sender, String[] args, boolean trailingSpace) { + System.out.println("youre using the latest n greatest command parsing 3"); + return suggestionParser.suggest(rootNode, sender, Arrays.stream(args).iterator(), trailingSpace); } } \ No newline at end of file diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/literal/Literal.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/literal/Literal.java index 531905b..b017837 100644 --- a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/literal/Literal.java +++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/literal/Literal.java @@ -33,7 +33,6 @@ public interface Literal { /** * @throws IllegalArgumentException if there is already an argument with the same id * @throws IllegalStateException if a greedy argument was added to the literal previously - * @throws IllegalStateException if the literal already has a child literal assigned to it */ void addArgument(CommandArgument argument); @@ -50,7 +49,6 @@ public interface Literal { /** * @throws IllegalArgumentException if there is already a literal with the same name * @throws IllegalStateException if a greedy argument was added to the literal previously - * @throws IllegalStateException if the literal already has an argument assigned to it */ void addLiteral(@NotNull Literal literal); diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/parser/CommandParseHelper.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/parser/CommandParseHelper.java new file mode 100644 index 0000000..94c13c2 --- /dev/null +++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/parser/CommandParseHelper.java @@ -0,0 +1,26 @@ +package de.kentoj.kencommandapi.api.parser; + +import de.kentoj.kencommandapi.api.literal.Literal; +import de.kentoj.kencommandapi.api.platform.HasPermissionMethod; +import lombok.RequiredArgsConstructor; + +import java.util.Iterator; + +@RequiredArgsConstructor +public final class CommandParseHelper { + private final HasPermissionMethod hasPermission; + + public boolean hasPermission(T sender, Literal literal) { + if (literal.getPermission() == null) return true; + return hasPermission.test(sender, literal.getPermission()); + } + + public String parseInput(Iterator tokenIter, boolean isGreedy) { + if (!isGreedy) + return tokenIter.next(); + var builder = new StringBuilder(); + tokenIter.forEachRemaining(tok -> builder.append(" ").append(tok)); + builder.deleteCharAt(0); + return builder.toString(); + } +} \ No newline at end of file diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/parser/InvocationCommandParser.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/parser/InvocationCommandParser.java new file mode 100644 index 0000000..25697ba --- /dev/null +++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/parser/InvocationCommandParser.java @@ -0,0 +1,91 @@ +package de.kentoj.kencommandapi.api.parser; + +import com.leakyabstractions.result.api.Result; +import com.leakyabstractions.result.core.Results; +import de.kentoj.kencommandapi.api.argument.CommandArgument; +import de.kentoj.kencommandapi.api.invocation.CommandExecutor; +import de.kentoj.kencommandapi.api.invocation.ParsedArgument; +import de.kentoj.kencommandapi.api.literal.Literal; +import de.kentoj.kencommandapi.api.literal.RootLiteral; +import de.kentoj.kencommandapi.internal.ParsedArgumentImpl; +import lombok.RequiredArgsConstructor; +import lombok.Value; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +@RequiredArgsConstructor +public class InvocationCommandParser { + private final CommandParseHelper helper; + + public Result parse( + RootLiteral rootLiteral, + T sender, + Iterator tokenIter + ) { + Literal currentLiteral = rootLiteral; + Map> parsedArguments = new HashMap<>(); + + while (true) { + { + var result = processArguments(currentLiteral, sender, tokenIter) + .ifSuccess(parsedArguments::putAll); + if (result.hasFailure()) + return result.mapSuccess(__ -> null); + } + + if (!tokenIter.hasNext()) { + if (currentLiteral.getExecutor() == null) + return Results.failure("Literal expected"); + break; + } + + var literalName = tokenIter.next(); + var nextLiteral = currentLiteral.getLiteral(literalName); + var isIncompleteOrUnknown = nextLiteral == null || !helper.hasPermission(sender, nextLiteral); + if (isIncompleteOrUnknown) + return Results.failure("Unknown literal: " + literalName); + currentLiteral = nextLiteral; + } + + return Results.success(new ExecutionData(currentLiteral.getExecutor(), parsedArguments)); + } + + private Result>, String> processArguments( + Literal literal, + T sender, + Iterator tokenIter + ) { + Map> parsedArguments = new HashMap<>(); + + for (var _argument : literal.getArguments()) { + @SuppressWarnings("unchecked") + var argument = ((CommandArgument) _argument); + + Object value; + if (!tokenIter.hasNext()) { + if (argument.getDefaultValueProvider() == null) + return Results.failure("Argument expected: " + argument.getId()); + value = argument.getDefaultValueProvider().apply(sender); + } else { + var input = helper.parseInput(tokenIter, argument.getType().isGreedy()); + try { + value = argument.getType().parseInput(input); + } catch (IllegalArgumentException ex) { + return Results.failure("Illegal argument '" + input + "' for " + argument.getId() + ": " + ex.getMessage()); + } + } + + parsedArguments.put(argument.getId(), new ParsedArgumentImpl<>(value, argument)); + } + + return Results.success(parsedArguments); + } + + @Value + public class ExecutionData { + CommandExecutor executor; + Map> arguments; + } +} diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/parser/SuggestionCommandParser.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/parser/SuggestionCommandParser.java new file mode 100644 index 0000000..035b6b3 --- /dev/null +++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/parser/SuggestionCommandParser.java @@ -0,0 +1,98 @@ +package de.kentoj.kencommandapi.api.parser; + +import de.kentoj.kencommandapi.api.literal.Literal; +import de.kentoj.kencommandapi.api.literal.RootLiteral; +import lombok.RequiredArgsConstructor; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Stream; + +@RequiredArgsConstructor +public class SuggestionCommandParser { + + private final CommandParseHelper helper; + + public List suggest( + RootLiteral rootLiteral, + T sender, + Iterator tokenIter, + boolean suggestNext + ) { + Literal currentLiteral = rootLiteral; + Literal previousLiteral = null; + + while (true) { + var argumentSuggestions = processArguments(currentLiteral, tokenIter, sender, suggestNext); + if (argumentSuggestions != null) + return argumentSuggestions; + + if (!tokenIter.hasNext()) { + if (suggestNext || previousLiteral == null) + return suggestions(currentLiteral, sender).toList(); + return suggestions(previousLiteral, sender).toList(); + } + + var input = tokenIter.next(); + var nextLiteral = currentLiteral.getLiteral(input); + var isIncompleteOrUnknown = nextLiteral == null || !helper.hasPermission(sender, nextLiteral); + if (isIncompleteOrUnknown) { + var finalInput = input.toLowerCase(); + return suggestions(currentLiteral, sender) + .filter(name -> name.toLowerCase().startsWith(finalInput)) + .toList(); + } + + previousLiteral = currentLiteral; + currentLiteral = nextLiteral; + } + } + + private @Nullable List processArguments( + Literal literal, + Iterator tokenIter, + T sender, + boolean suggestNext + ) { + var argsIter = literal.getArguments().iterator(); + + /* + for each argument: + if no next token: suggest current + parse token + if not last token: skip + if nextToken: + if has next argument: suggest next argument + else: return null + return current argument's suggestions filtered + + */ + + while (argsIter.hasNext()) { + var argument = argsIter.next(); + if (!tokenIter.hasNext()) + return argument.getSuggestionProvider().suggest(sender); + var input = helper.parseInput(tokenIter, argument.getType().isGreedy()).toLowerCase(); + if (tokenIter.hasNext()) + continue; + if (suggestNext) { + if (argsIter.hasNext()) + return argsIter.next().getSuggestionProvider().suggest(sender); + return null; + } + return argument.getSuggestionProvider().suggest(sender).stream() + .filter(suggestion -> suggestion.toLowerCase().startsWith(input)) + .toList(); + } + return null; + } + + private Stream suggestions(Literal literal, T sender) { + return literal.getLiterals().stream() + .filter(lit -> helper.hasPermission(sender, lit)) + .map(Literal::getNames) + .flatMap(Arrays::stream); + } +} diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/parser2/AbstractCommandParser.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/parser2/AbstractCommandParser.java deleted file mode 100644 index 0564e56..0000000 --- a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/parser2/AbstractCommandParser.java +++ /dev/null @@ -1,39 +0,0 @@ -package de.kentoj.kencommandapi.api.parser2; - -import com.leakyabstractions.result.api.Result; -import com.leakyabstractions.result.core.Results; -import de.kentoj.kencommandapi.api.argument.CommandArgument; -import de.kentoj.kencommandapi.api.literal.Literal; -import de.kentoj.kencommandapi.api.platform.HasPermissionMethod; -import lombok.RequiredArgsConstructor; - -import java.util.Arrays; -import java.util.concurrent.atomic.AtomicInteger; - -@RequiredArgsConstructor -public abstract class AbstractCommandParser { - - private final HasPermissionMethod hasPermission; - - protected boolean hasPermission(T sender, Literal literal) { - if (literal.getPermission() == null) return true; - return hasPermission.test(sender, literal.getPermission()); - } - - protected String parseInput(String[] args, AtomicInteger index, boolean isGreedy) { - if (!isGreedy) - return args[index.getAndIncrement()]; - - var ret = String.join(" ", Arrays.copyOfRange(args, index.get(), args.length)); - index.set(args.length); - return ret; - } - - protected Result parseArgument(CommandArgument argument, String input) { - try { - return Results.success(argument.getType().parseInput(input)); - } catch (IllegalArgumentException ex) { - return Results.failure("Illegal argument '" + input + "' for " + argument.getId() + ": " + ex.getMessage()); - } - } -} \ No newline at end of file diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/parser2/InvocationCommandParser.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/parser2/InvocationCommandParser.java deleted file mode 100644 index af15745..0000000 --- a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/parser2/InvocationCommandParser.java +++ /dev/null @@ -1,74 +0,0 @@ -package de.kentoj.kencommandapi.api.parser2; - -import com.leakyabstractions.result.api.Result; -import com.leakyabstractions.result.core.Results; -import de.kentoj.kencommandapi.api.argument.CommandArgument; -import de.kentoj.kencommandapi.api.invocation.CommandExecutor; -import de.kentoj.kencommandapi.api.invocation.ParsedArgument; -import de.kentoj.kencommandapi.api.literal.Literal; -import de.kentoj.kencommandapi.api.literal.RootLiteral; -import de.kentoj.kencommandapi.api.platform.HasPermissionMethod; -import de.kentoj.kencommandapi.internal.ParsedArgumentImpl; -import lombok.Value; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; - -public class InvocationCommandParser extends AbstractCommandParser { - - public InvocationCommandParser(HasPermissionMethod hasPermission) { - super(hasPermission); - } - - public Result invoke( - RootLiteral rootLiteral, - T sender, - String[] args - ) { - Map> parsedArguments = new HashMap<>(); - Literal currentLiteral = rootLiteral; - AtomicInteger index = new AtomicInteger(0); - - while (true) { - for (var _argument : currentLiteral.getArguments()) { - @SuppressWarnings("unchecked") - var argument = ((CommandArgument) _argument); - - Object value; - if (index.get() >= args.length) { - if (argument.getDefaultValueProvider() == null) - return Results.failure("Argument expected: " + argument.getId()); - value = argument.getDefaultValueProvider().apply(sender); - } else { - var input = parseInput(args, index, argument.getType().isGreedy()); - var parseResult = parseArgument(argument, input); - if (parseResult.hasFailure()) return parseResult.mapSuccess(__ -> null); - value = parseResult.getSuccess().orElseThrow(); - } - - parsedArguments.put(argument.getId(), new ParsedArgumentImpl<>(value, argument)); - } - - if (index.get() >= args.length) { - if (currentLiteral.getExecutor() == null) - return Results.failure("Literal expected"); - break; - } - - var literalName = args[index.getAndIncrement()]; - var nextLiteral = currentLiteral.getLiteral(literalName); - if (nextLiteral == null || !hasPermission(sender, nextLiteral)) - return Results.failure("Unknown literal: " + literalName); - currentLiteral = nextLiteral; - } - - return Results.success(new ExecutionData(currentLiteral.getExecutor(), parsedArguments)); - } - - @Value - public class ExecutionData { - CommandExecutor executor; - Map> arguments; - } -} diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/parser2/SuggestionCommandParser.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/parser2/SuggestionCommandParser.java deleted file mode 100644 index c2d3a35..0000000 --- a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/parser2/SuggestionCommandParser.java +++ /dev/null @@ -1,60 +0,0 @@ -package de.kentoj.kencommandapi.api.parser2; - -import de.kentoj.kencommandapi.api.literal.Literal; -import de.kentoj.kencommandapi.api.literal.RootLiteral; -import de.kentoj.kencommandapi.api.platform.HasPermissionMethod; - -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Stream; - -public class SuggestionCommandParser extends AbstractCommandParser { - - public SuggestionCommandParser(HasPermissionMethod hasPermission) { - super(hasPermission); - } - - public List getSuggestions( - RootLiteral rootNode, - T sender, - String[] args - ) { - Literal currentLiteral = rootNode; - AtomicInteger index = new AtomicInteger(0); - - while (true) { - for (var argument : currentLiteral.getArguments()) { - // TODO somewhere suggest next argument at trailing space - if (index.getAndIncrement() >= args.length) - return argument.getSuggestionProvider().suggest(sender); - if (argument.getType().isGreedy()) - index.set(args.length); - } - - if (index.get() >= args.length) { - // TODO suggest next argument at trailing space - if (currentLiteral.getExecutor() == null) - return suggestions(currentLiteral, sender).toList(); - } - - var input = args[index.getAndIncrement()]; - var nextLiteral = currentLiteral.getLiteral(input); - if (nextLiteral == null || !hasPermission(sender, nextLiteral)) { - var finalInput = input.toLowerCase(); - return suggestions(currentLiteral, sender) - .filter(name -> name.toLowerCase().startsWith(finalInput)) - .toList(); - } - - currentLiteral = nextLiteral; - } - } - - private Stream suggestions(Literal literal, T sender) { - return literal.getLiterals().stream() - .filter(lit -> hasPermission(sender, lit)) - .map(Literal::getNames) - .flatMap(Arrays::stream); - } -} diff --git a/kencommandapi-core/src/test/java/de/kentoj/kencommandapi/api/parser/CommandHandlerTest.java b/kencommandapi-core/src/test/java/de/kentoj/kencommandapi/api/parser/CommandHandlerTest.java deleted file mode 100644 index 44f4df3..0000000 --- a/kencommandapi-core/src/test/java/de/kentoj/kencommandapi/api/parser/CommandHandlerTest.java +++ /dev/null @@ -1,41 +0,0 @@ -package de.kentoj.kencommandapi.api.parser; - -import de.kentoj.kencommandapi.api.CommandHandler; -import de.kentoj.kencommandapi.api.argument.CommandArgument; -import de.kentoj.kencommandapi.api.argument.types.StringArgumentType; -import de.kentoj.kencommandapi.api.literal.Literal; -import de.kentoj.kencommandapi.api.platform.MessageStyle; -import org.junit.jupiter.api.Test; - -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class CommandHandlerTest { - - @Test - void suggestArgument() { - var rootNode = Literal.rootLiteral(MessageStyle.PLAIN, "someRootNode"); - var arg = CommandArgument.arg("someArg", new StringArgumentType<>()); - arg.setSuggestionProvider(__ -> List.of("a", "b", "c")); - rootNode.addArgument(arg); - rootNode.setExecutor(ENV.EMPTY_EXECUTOR); - - var handler = new CommandHandler<>((__, ___) -> {}, (__, ___) -> true); - var suggestions = handler.getSuggestions(rootNode, ENV.SENDER, new String[0]); - assertEquals(List.of("a", "b", "c"), suggestions); - } - - @Test - void suggestLiterals() { - var rootNode = Literal.rootLiteral(MessageStyle.PLAIN, "someRootNode"); - var literal1 = Literal.literal("lit1"); - var literal2 = Literal.literal("lit2"); - rootNode.addLiteral(literal1); - rootNode.addLiteral(literal2); - - var handler = new CommandHandler<>((__, ___) -> {}, (__, ___) -> true); - var suggestions = handler.getSuggestions(rootNode, ENV.SENDER, new String[0]); - assertEquals(List.of("lit1", "lit2"), suggestions.stream().sorted().toList()); - } -} diff --git a/kencommandapi-core/src/test/java/de/kentoj/kencommandapi/api/parser/SuggestionCommandParserTest.java b/kencommandapi-core/src/test/java/de/kentoj/kencommandapi/api/parser/SuggestionCommandParserTest.java new file mode 100644 index 0000000..a183269 --- /dev/null +++ b/kencommandapi-core/src/test/java/de/kentoj/kencommandapi/api/parser/SuggestionCommandParserTest.java @@ -0,0 +1,87 @@ +package de.kentoj.kencommandapi.api.parser; + +import de.kentoj.kencommandapi.api.argument.CommandArgument; +import de.kentoj.kencommandapi.api.argument.types.StringArgumentType; +import de.kentoj.kencommandapi.api.literal.Literal; +import de.kentoj.kencommandapi.api.literal.RootLiteral; +import de.kentoj.kencommandapi.api.platform.MessageStyle; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class SuggestionCommandParserTest { + + private final SuggestionCommandParser suggestionParser; + private final RootLiteral rootLiteral; + + public SuggestionCommandParserTest() { + rootLiteral = Literal.rootLiteral(MessageStyle.PLAIN, "someRootNode"); + suggestionParser = new SuggestionCommandParser<>((__, ___) -> true); + } + + @Test + void suggestNextArgument() { + var arg = CommandArgument.arg("someArg", new StringArgumentType<>()); + arg.setSuggestionProvider(__ -> List.of("foo", "bar")); + rootLiteral.addArgument(arg); + + var suggestions = suggestionParser.getSuggestions(rootLiteral, ENV.SENDER, args("")); + assertEquals(Stream.of("foo", "bar").sorted().toList(), suggestions.stream().sorted().toList()); + } + + @Test + void suggestNextArgumentAtSpace() { + var uselessArg = CommandArgument.arg("someUselessArg", new StringArgumentType<>()); + var arg = CommandArgument.arg("someArg", new StringArgumentType<>()); + arg.setSuggestionProvider(__ -> List.of("foo", "bar")); + rootLiteral.addArgument(uselessArg); + rootLiteral.addArgument(arg); + + var suggestions = suggestionParser.getSuggestions(rootLiteral, ENV.SENDER, args("hi ")); + assertEquals(Stream.of("foo", "bar").sorted().toList(), suggestions.stream().sorted().toList()); + } + + @Test + void suggestCurrentArgument() { + var arg = CommandArgument.arg("someArg", new StringArgumentType<>()); + arg.setSuggestionProvider(__ -> List.of("foo", "bar")); + rootLiteral.addArgument(arg); + + var suggestions = suggestionParser.getSuggestions(rootLiteral, ENV.SENDER, args("f")); + assertEquals(List.of("foo"), suggestions); + } + + @Test + void suggestNextLiteral() { + var literalFoo = Literal.literal("foo"); + var literalBar = Literal.literal("bar"); + rootLiteral.addLiteral(literalFoo); + rootLiteral.addLiteral(literalBar); + + var suggestions = suggestionParser.getSuggestions(rootLiteral, ENV.SENDER, args("")); + assertEquals(Stream.of("foo", "bar").sorted().toList(), suggestions.stream().sorted().toList()); + } + + @Test + void suggestCurrentLiteral() { + var literalFoo = Literal.literal("foo"); + var literalBar = Literal.literal("bar"); + rootLiteral.addLiteral(literalFoo); + rootLiteral.addLiteral(literalBar); + + var suggestions = suggestionParser.getSuggestions(rootLiteral, ENV.SENDER, args("f")); + assertEquals(List.of("foo"), suggestions); + } + + private String[] args(String input) { + List args = new ArrayList<>(Arrays.asList(input.split(" "))); + if (input.endsWith(" ")) + args.add(""); + return args.toArray(new String[0]); + } +} diff --git a/kencommandapi-velocity/src/main/java/de/kentoj/kencommandapi/VelocityCommand.java b/kencommandapi-velocity/src/main/java/de/kentoj/kencommandapi/VelocityCommand.java index c640cf8..55c4256 100644 --- a/kencommandapi-velocity/src/main/java/de/kentoj/kencommandapi/VelocityCommand.java +++ b/kencommandapi-velocity/src/main/java/de/kentoj/kencommandapi/VelocityCommand.java @@ -1,7 +1,7 @@ package de.kentoj.kencommandapi; import com.velocitypowered.api.command.CommandSource; -import com.velocitypowered.api.command.SimpleCommand; +import com.velocitypowered.api.command.RawCommand; import de.kentoj.kencommandapi.api.CommandHandler; import de.kentoj.kencommandapi.api.literal.RootLiteral; import lombok.RequiredArgsConstructor; @@ -9,14 +9,15 @@ import lombok.RequiredArgsConstructor; import java.util.List; @RequiredArgsConstructor -public class VelocityCommand implements SimpleCommand { +public class VelocityCommand implements RawCommand { private final CommandHandler commandHandler; private final RootLiteral rootNode; @Override public void execute(Invocation invocation) { - commandHandler.invoke(rootNode, invocation.source(), invocation.arguments()); + var args = invocation.arguments().split(" "); + commandHandler.invoke(rootNode, invocation.source(), args); } @Override @@ -27,6 +28,7 @@ public class VelocityCommand implements SimpleCommand { @Override public List suggest(Invocation invocation) { - return commandHandler.getSuggestions(rootNode, invocation.source(), invocation.arguments()); + var args = invocation.arguments().split(" "); + return commandHandler.getSuggestions(rootNode, invocation.source(), args, invocation.arguments().endsWith(" ")); } } \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index dda4f9b..c31140a 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,5 +1,10 @@ pluginManagement { includeBuild("../build-logic") + repositories { + maven("https://repo.papermc.io/repository/maven-public/") { + name = "papermc" + } + } } plugins { @@ -8,9 +13,8 @@ plugins { dependencyResolutionManagement { repositories { - maven { + maven("https://repo.papermc.io/repository/maven-public/") { name = "papermc" - url = uri("https://repo.papermc.io/repository/maven-public/") } } }