diff --git a/.bazelrc b/.bazelrc
deleted file mode 100644
index b6254e5..0000000
--- a/.bazelrc
+++ /dev/null
@@ -1,7 +0,0 @@
-build --java_language_version=25
-build --java_runtime_version=25
-build --tool_java_language_version=25
-build --tool_java_runtime_version=25
-build --define=maven_repo=https://repo.lab0x13.site/snapshots
-build --remote_cache=https://cache.lab0x13.site
-build --remote_upload_local_results=false
\ No newline at end of file
diff --git a/.forgejo/workflows/publish-artifact.yml b/.forgejo/workflows/publish-artifact.yml
deleted file mode 100644
index 8423916..0000000
--- a/.forgejo/workflows/publish-artifact.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-on:
- push:
- branches: [master]
-
-jobs:
- build:
- runs-on: docker
- container:
- image: "git.lab0x13.site/scrow/bazel-java:0.5"
- steps:
- - uses: actions/checkout@v7
- - id: build
- run: |
- bazel build //bukkit:bukkit.publish //core:core.publish //velocity:velocity.publish \
- --remote_cache='https://${{ secrets.CACHE_USERNAME }}:${{ secrets.CACHE_PASSWORD }}@cache.lab0x13.site' --remote_upload_local_results
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index bcca68a..1fac4d5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,10 +1,9 @@
-/.idea/inspectionProfiles
-/MODULE.bazel.lock
-/.bazelbsp
-/bazel-bin
-/bazel-out
-/bazel-KenCommandAPI
-/bazel-testlogs
+.gradle
+build/
+!gradle/wrapper/gradle-wrapper.jar
+!**/src/main/**/build/
+!**/src/test/**/build/
+.kotlin
### IntelliJ IDEA ###
.idea/modules.xml
diff --git a/.idea/bazelVersionCache.xml b/.idea/bazelVersionCache.xml
deleted file mode 100644
index 8501e9b..0000000
--- a/.idea/bazelVersionCache.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index 3e3960b..a0486d1 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -1,4 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/jsonSchemas.xml b/.idea/jsonSchemas.xml
deleted file mode 100644
index 270275b..0000000
--- a/.idea/jsonSchemas.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 6b56b62..03872dd 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml
deleted file mode 100644
index de9b6c3..0000000
--- a/.idea/runConfigurations.xml
+++ /dev/null
@@ -1,59 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
index 35eb1dd..0b33847 100644
--- a/.idea/vcs.xml
+++ b/.idea/vcs.xml
@@ -2,5 +2,6 @@
+
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 6fbbbcb..1eb1391 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -3,49 +3,30 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -104,6 +85,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -124,7 +118,6 @@
-
@@ -143,14 +136,6 @@
{
"keyToString": {
- "Bazel.//bukkit:bukkit.publish.executor": "Run",
- "Bazel.//core:core.publish.executor": "Run",
- "Bazel.//kencommandapi-velocity:velocity.publish.executor": "Run",
- "Bazel.//velocity:velocity.publish.executor": "Run",
- "Bazel.Run //kencommandapi-bukkit:bukkit.publish.executor": "Run",
- "Bazel.Run //kencommandapi-core:core.publish.executor": "Run",
- "Bazel.Run //kencommandapi-core:export.publish.executor": "Run",
- "Bazel.Run //kencommandapi-velocity:velocity.publish.executor": "Run",
"Gradle./home/a/src/kento2/KenCommandAPI/kencommandapi-core [:kencommandapi-core:de.kentoj.kencommandapi.Main.main()].executor": "Run",
"Gradle./home/a/src/kento2/KenCommandAPI/kencommandapi-core [build].executor": "Run",
"Gradle./home/a/src/kento2/KenCommandAPI/kencommandapi-core [publish].executor": "Run",
@@ -162,7 +147,6 @@
"Gradle.KenCommandAPI [build].executor": "Run",
"Gradle.KenCommandAPI [clean test].executor": "Run",
"Gradle.KenCommandAPI [clean].executor": "Run",
- "Gradle.KenCommandAPI [javadoc].executor": "Run",
"Gradle.KenCommandAPI [publishAllPublicationsToScrowRepository].executor": "Run",
"Gradle.KenCommandAPI [publish].executor": "Run",
"Gradle.KenCommandAPI [test].executor": "Run",
@@ -176,21 +160,12 @@
"RunOnceActivity.ShowReadmeOnStart": "true",
"RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
"RunOnceActivity.git.unshallow": "true",
- "RunOnceActivity.typescript.service.memoryLimit.init": "true",
- "SHARE_PROJECT_CONFIGURATION_FILES": "true",
- "bazel.project.detector.is.bazel.project": "true",
- "codeWithMe.voiceChat.enabledByDefault": "false",
- "com.intellij.ide.FileNotInSourceRootChecker.no.check": "true",
"git-widget-placeholder": "master",
"kotlin-language-version-configured": "true",
- "last_opened_file_path": "/home/a/src/random/rules_jvm_external",
- "node.js.detected.package.jshint": "true",
- "node.js.selected.package.jshint": "",
- "nodejs_package_manager_path": "npm",
+ "last_opened_file_path": "/home/a/src/scrow/core",
"project.structure.last.edited": "Modules",
"project.structure.proportion": "0.15",
"project.structure.side.proportion": "0.6268199",
- "remote.dev.show.docker.config.missing.jdk.notification": "false",
"settings.editor.selected.configurable": "preferences.pluginManager"
}
}
@@ -203,26 +178,19 @@
-
-
-
+
-
-
-
-
-
-
+
@@ -232,62 +200,34 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- @//bukkit:bukkit.publish
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+ true
+ false
+ true
-
-
- @//core:core.publish
-
-
-
-
-
-
- @//kencommandapi-velocity:velocity.publish
-
-
-
-
-
-
- @//velocity:velocity.publish
-
-
-
-
-
+
@@ -298,49 +238,94 @@
-
+
true
true
- false
false
false
- false
- false
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ true
+ false
+ false
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ true
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ true
+ false
+ false
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
@@ -576,9 +561,6 @@
-
-
-
@@ -597,17 +579,10 @@
+
-
-
-
-
-
-
-
-
-
+
\ No newline at end of file
diff --git a/MODULE.bazel b/MODULE.bazel
deleted file mode 100644
index a792429..0000000
--- a/MODULE.bazel
+++ /dev/null
@@ -1,30 +0,0 @@
-bazel_dep(name = "rules_java", version = "9.7.0")
-bazel_dep(name = "rules_jvm_external", version = "7.1")
-
-git_override(
- module_name = "rules_jvm_external",
- remote = "http://forge.kentoj.de/scrow/rules_jvm_external.git",
-)
-
-maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
-maven.install(
- name = "maven",
- artifacts = [
- "com.leakyabstractions:result-api:1.0.1.0",
- "com.leakyabstractions:result:1.0.2.0",
- "org.jetbrains:annotations:26.0.2-1",
- "com.google.guava:guava:33.5.0-jre",
- "org.projectlombok:lombok:1.18.46",
- "org.slf4j:slf4j-api:2.0.18",
- "org.slf4j:slf4j-simple:2.0.18",
- "net.kyori:adventure-api:5.2.0",
- "io.papermc.paper:paper-api:[26.2.build,)",
- "com.velocitypowered:velocity-api:4.1.0-20260802.091736-8",
- ],
- repositories = [
- "https://repo.papermc.io/repository/maven-public/",
- ],
- known_contributing_modules = ["protobuf"],
-)
-
-use_repo(maven, "maven")
\ No newline at end of file
diff --git a/README.md b/README.md
index c66dba4..4dd5331 100644
--- a/README.md
+++ b/README.md
@@ -1,97 +1,2 @@
-## TODO
-
-CI/CD to publish automatically
-
-## Build
-
-after installing bazel:
-```shell
-bazel build //bukkit
-bazel build //core
-bazel build //velocity
-```
-
-## Limitations
-
-- argument's can't require specific permissions.
-
-## Design
-
-Commands are trees of Literals.
-Literals can have arguments.
-Literals can also have Executors.
-
-arguments can be optional by giving them a default value.
-arguments can have suggestions.
-
-`/feed `:
-- feed is the root literal
-- `` is an argument: it's value is a User Object
-
-To allow the use of `/feed` without giving a user's name, i.e. to make the argument optional, we need to assign the
-user-argument a default value.
-
-### An example command
-
-Let's say we want to make a command that allows seeing how many coins another User has.
-If we don't specify a user, it should instead show the coins the sender has.
-
-### As a tree
-
-Visualized as a tree, we have a Node/Literal `coins` with an alias `balance` with an optional argument called `user`.
-To make an argument optional, it must have a default value.
-
-```text
-literal:coins/balance
-- has argument:user
-- has executor:getCoins
-```
-
-### As code
-
-```java
-import de.kentoj.kencommandapi.api.literal.RootLiteral;
-
-/**
- * Show the amount coins a user has.
- * If no user is given, it should show the amount the sender of the command has.
- */
-public class CoinsCommand {
-
- private final RootLiteral rootLiteral;
- private final CommandArgumentSpec userArg;
-
- public CoinsCommand() {
- userArg = CommandArgumentSpec.builder("user", new UserArgumentType())
- .withRequirement(p -> !p.hasPermission("economy.immune.getcoins"),
- "you can't see that player's balance")
- .withDefaultValue(CommandContext::sender)
- .build();
- rootLiteral = Literal.builder("coins", "balance")
- .withPermission("economy.command.coins")
- .withArgument(userArg)
- .withSyncExecutor(this::getCoins)
- .asRootLiteral(MessageStyle.PLAIN);
- }
-
- private Result getCoins(CommandContext ctx) {
- var user = ctx.getArg(userArg);
- ctx.getSender().sendMessage(user.name() + " has " + user.coins() + " coins");
- return Results.success(new Object());
- }
-
- public RootLiteral rootLiteral() {
- return rootLiteral;
- }
-}
-```
-
-If we wanted to report an error, we could do for example `return Results.failure("That user doesn't exist")`.
-However, if we have a smart UserArgumentType, it automatically makes sure that the user exists.
-Additionally, we can also a Requirement in the CommandArgumentSpec.
-
-The registration of the command is platform-dependant. Typically, a class called CommandAPI can be used.
-```java
-CommandAPI api = new CommandAPI();
-register(new CoinsCommand().rootLiteral());
-```
\ No newline at end of file
+example usage:
+http://forge.kentoj.de/scrow/core/src/commit/a55d9857738425e099fe9650cc1a705ceb2ece00/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/economy
\ No newline at end of file
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..f3e61d5
--- /dev/null
+++ b/TODO
@@ -0,0 +1,10 @@
+- 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.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:?]
+```
\ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts
new file mode 100644
index 0000000..b51ec73
--- /dev/null
+++ b/build.gradle.kts
@@ -0,0 +1,7 @@
+group = "de.kentoj.scrow"
+version = "0.33-SNAPSHOT"
+
+subprojects {
+ group = rootProject.group
+ version = rootProject.version
+}
diff --git a/bukkit/BUILD b/bukkit/BUILD
deleted file mode 100644
index 47dc8a8..0000000
--- a/bukkit/BUILD
+++ /dev/null
@@ -1,15 +0,0 @@
-load("@rules_jvm_external//:defs.bzl", "artifact", "java_export")
-
-java_export(
- name = "bukkit",
- maven_coordinates = "de.kentoj.scrow:kencommandapi-bukkit:0.34-SNAPSHOT",
- srcs = glob(["src/main/**/*.java"]),
- deps = [
- "//core",
- artifact("org.jetbrains:annotations"),
- artifact("io.papermc.paper:paper-api"),
- ],
- exports = [
- "//core",
- ],
-)
\ No newline at end of file
diff --git a/bukkit/src/main/java/de/kentoj/kencommandapi/BukkitKenCommand.java b/bukkit/src/main/java/de/kentoj/kencommandapi/BukkitKenCommand.java
deleted file mode 100644
index 410b141..0000000
--- a/bukkit/src/main/java/de/kentoj/kencommandapi/BukkitKenCommand.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package de.kentoj.kencommandapi;
-
-import de.kentoj.kencommandapi.api.CommandHandler;
-import de.kentoj.kencommandapi.api.literal.RootLiteral;
-import de.kentoj.kencommandapi.api.platform.MessageStyle;
-import org.bukkit.command.Command;
-import org.bukkit.command.CommandSender;
-import org.jetbrains.annotations.NotNull;
-
-import java.util.Arrays;
-import java.util.stream.Stream;
-
-public class BukkitKenCommand extends Command {
-
- private final RootLiteral rootLiteral;
- private final CommandHandler commandHandler;
- private final Class senderType;
-
- protected BukkitKenCommand(CommandHandler commandHandler, RootLiteral rootLiteral, Class senderType) {
- super(rootLiteral.names()[0]);
- this.commandHandler = commandHandler;
- this.rootLiteral = rootLiteral;
- this.senderType = senderType;
-
- this.setAliases(Arrays.asList(rootLiteral.names()));
- this.setPermission(rootLiteral.permission());
- }
-
- @Override
- public boolean execute(@NotNull CommandSender commandSender, @NotNull String commandLabel, @NotNull String[] args) {
- if (!senderType.isInstance(commandSender)) {
- commandSender.sendMessage(MessageStyle.PLAIN.err("You can't use that command"));
- return true;
- }
- T sender = senderType.cast(commandSender);
-
- commandHandler.invoke(rootLiteral, sender, Stream.of(args).filter(str -> !str.isEmpty()));
- return true;
- }
-}
diff --git a/bukkit/src/main/java/de/kentoj/kencommandapi/TabCompleteListener.java b/bukkit/src/main/java/de/kentoj/kencommandapi/TabCompleteListener.java
deleted file mode 100644
index 6d6e080..0000000
--- a/bukkit/src/main/java/de/kentoj/kencommandapi/TabCompleteListener.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package de.kentoj.kencommandapi;
-
-import com.destroystokyo.paper.event.server.AsyncTabCompleteEvent;
-import de.kentoj.kencommandapi.api.CommandHandler;
-import org.bukkit.command.CommandSender;
-import org.bukkit.event.EventHandler;
-import org.bukkit.event.Listener;
-
-import java.util.Arrays;
-
-public class TabCompleteListener implements Listener {
-
- private final CommandAPI commandAPI;
- private final CommandHandler commandHandler;
- private final Class senderType;
-
- public TabCompleteListener(CommandAPI commandAPI, CommandHandler commandHandler, Class senderType) {
- this.commandAPI = commandAPI;
- this.commandHandler = commandHandler;
- this.senderType = senderType;
- }
-
- @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 literal = commandAPI.getRegistered(label);
- if (literal == null) return;
-
- if (!senderType.isInstance(ev.getSender()))
- return;
-
- T sender = senderType.cast(ev.getSender());
- ev.setHandled(true);
- ev.setCompletions(commandHandler.getSuggestions(literal, sender, Arrays.copyOfRange(args, 1, args.length), ev.getBuffer().endsWith(" ")));
- }
-}
diff --git a/bukkit/src/main/java/de/kentoj/kencommandapi/type/OfflinePlayerArgumentType.java b/bukkit/src/main/java/de/kentoj/kencommandapi/type/OfflinePlayerArgumentType.java
deleted file mode 100644
index 5b17dd0..0000000
--- a/bukkit/src/main/java/de/kentoj/kencommandapi/type/OfflinePlayerArgumentType.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package de.kentoj.kencommandapi.type;
-
-import de.kentoj.kencommandapi.api.argument.ArgumentType;
-import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider;
-import de.kentoj.kencommandapi.suggestionprovider.PlayerSuggestionProvider;
-import org.bukkit.Bukkit;
-import org.bukkit.OfflinePlayer;
-
-import java.util.UUID;
-
-public class OfflinePlayerArgumentType implements ArgumentType {
-
- @Override
- public OfflinePlayer parseInputUnchecked(String string) {
- // UUIDs are always 36 chars
- if (string.length() == 36) {
- try {
- return Bukkit.getOfflinePlayer(UUID.fromString(string));
- } catch (IllegalArgumentException ex) {
- throw new IllegalArgumentException("Invalid UUID");
- }
- }
-
- OfflinePlayer player = Bukkit.getPlayerExact(string);
- if (player == null || (!player.hasPlayedBefore() && !player.isOnline()))
- throw new IllegalArgumentException("Player not found");
- return player;
- }
-
- @Override
- public SuggestionProvider getDefaultSuggestionProvider() {
- return new PlayerSuggestionProvider<>();
- }
-}
diff --git a/bukkit/src/main/java/de/kentoj/kencommandapi/type/PlayerArgumentType.java b/bukkit/src/main/java/de/kentoj/kencommandapi/type/PlayerArgumentType.java
deleted file mode 100644
index 2b4065e..0000000
--- a/bukkit/src/main/java/de/kentoj/kencommandapi/type/PlayerArgumentType.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package de.kentoj.kencommandapi.type;
-
-import de.kentoj.kencommandapi.api.argument.ArgumentType;
-import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider;
-import de.kentoj.kencommandapi.suggestionprovider.PlayerSuggestionProvider;
-import org.bukkit.Bukkit;
-import org.bukkit.entity.Player;
-
-public class PlayerArgumentType implements ArgumentType {
-
- @Override
- public Player parseInputUnchecked(String string) {
- var player = Bukkit.getPlayer(string);
- if (player == null) throw new IllegalArgumentException("player not online");
- return player;
- }
-
- @Override
- public SuggestionProvider getDefaultSuggestionProvider() {
- return new PlayerSuggestionProvider<>();
- }
-}
diff --git a/core/BUILD b/core/BUILD
deleted file mode 100644
index 03f0de0..0000000
--- a/core/BUILD
+++ /dev/null
@@ -1,16 +0,0 @@
-load("@rules_jvm_external//:defs.bzl", "artifact", "java_export")
-
-java_export(
- name = "core",
- maven_coordinates = "de.kentoj.scrow:kencommandapi-core:0.34-SNAPSHOT",
- srcs = glob(["src/main/**/*.java"]),
- visibility = ["//visibility:public"],
- deps = [
- artifact("com.leakyabstractions:result-api"),
- artifact("com.leakyabstractions:result"),
- artifact("org.jetbrains:annotations"),
- artifact("com.google.guava:guava"),
- artifact("org.slf4j:slf4j-api"),
- artifact("net.kyori:adventure-api"),
- ],
-)
\ No newline at end of file
diff --git a/core/src/main/java/de/kentoj/kencommandapi/api/CommandHandler.java b/core/src/main/java/de/kentoj/kencommandapi/api/CommandHandler.java
deleted file mode 100644
index f728250..0000000
--- a/core/src/main/java/de/kentoj/kencommandapi/api/CommandHandler.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package de.kentoj.kencommandapi.api;
-
-import de.kentoj.kencommandapi.api.literal.RootLiteral;
-import de.kentoj.kencommandapi.api.platform.HasPermissionMethod;
-import de.kentoj.kencommandapi.api.platform.SendMessageMethod;
-import de.kentoj.kencommandapi.internal.parser.CommandParseHelper;
-import de.kentoj.kencommandapi.internal.parser.InvocationCommandParser;
-import de.kentoj.kencommandapi.internal.parser.SuggestionCommandParser;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.stream.Stream;
-
-public class CommandHandler {
-
- private final SendMessageMethod sendMessageMethod;
- private final InvocationCommandParser invocationParser;
- private final SuggestionCommandParser suggestionParser;
-
- public CommandHandler(SendMessageMethod sendMessageMethod, HasPermissionMethod hasPermissionMethod) {
- this.sendMessageMethod = sendMessageMethod;
- var parseHelper = new CommandParseHelper<>(hasPermissionMethod);
- invocationParser = new InvocationCommandParser<>(parseHelper);
- suggestionParser = new SuggestionCommandParser<>(parseHelper);
- }
-
- public void invoke(RootLiteral rootLiteral, T sender, Stream args) {
- invocationParser.parse(rootLiteral, sender, args.toList().iterator())
- .ifSuccessOrElse(data -> exec(rootLiteral, data),
- err -> sendMessageMethod.send(sender, rootLiteral.messageStyle().err(err)));
- }
-
- private void exec(RootLiteral rootLiteral, InvocationCommandParser.ExecutionData data) {
- var ctx = data.context();
- data.executor().execute(ctx)
- .whenComplete((result, ex) -> {
- if (ex != null)
- sendMessageMethod.send(ctx.sender(), rootLiteral.messageStyle().exception(ex));
- result.ifFailure(err ->
- sendMessageMethod.send(ctx.sender(), rootLiteral.messageStyle().err(err)));
- });
- }
-
- public List getSuggestions(RootLiteral rootNode, T sender, String[] args, boolean trailingSpace) {
- return suggestionParser.suggest(rootNode, sender, Arrays.stream(args).iterator(), trailingSpace);
- }
-}
\ No newline at end of file
diff --git a/core/src/main/java/de/kentoj/kencommandapi/api/argument/ArgumentRequirement.java b/core/src/main/java/de/kentoj/kencommandapi/api/argument/ArgumentRequirement.java
deleted file mode 100644
index 47a8976..0000000
--- a/core/src/main/java/de/kentoj/kencommandapi/api/argument/ArgumentRequirement.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package de.kentoj.kencommandapi.api.argument;
-
-import java.util.function.Predicate;
-
-/**
- * @param type of the parsed argument value
- */
-public record ArgumentRequirement(
- Predicate predicate,
- String message
-) {
-}
diff --git a/core/src/main/java/de/kentoj/kencommandapi/api/argument/CommandArgument.java b/core/src/main/java/de/kentoj/kencommandapi/api/argument/CommandArgument.java
deleted file mode 100644
index 8e79a06..0000000
--- a/core/src/main/java/de/kentoj/kencommandapi/api/argument/CommandArgument.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package de.kentoj.kencommandapi.api.argument;
-
-public interface CommandArgument {
-
- V value();
-
- CommandArgumentSpec argument();
-}
diff --git a/core/src/main/java/de/kentoj/kencommandapi/api/argument/CommandArgumentImpl.java b/core/src/main/java/de/kentoj/kencommandapi/api/argument/CommandArgumentImpl.java
deleted file mode 100644
index 71bd78a..0000000
--- a/core/src/main/java/de/kentoj/kencommandapi/api/argument/CommandArgumentImpl.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package de.kentoj.kencommandapi.api.argument;
-
-public record CommandArgumentImpl(
- V value,
- CommandArgumentSpec argument
-) implements CommandArgument {
-}
diff --git a/core/src/main/java/de/kentoj/kencommandapi/api/argument/CommandArgumentSpec.java b/core/src/main/java/de/kentoj/kencommandapi/api/argument/CommandArgumentSpec.java
deleted file mode 100644
index 726ad14..0000000
--- a/core/src/main/java/de/kentoj/kencommandapi/api/argument/CommandArgumentSpec.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package de.kentoj.kencommandapi.api.argument;
-
-import de.kentoj.kencommandapi.api.invocation.CommandContext;
-import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.function.Function;
-
-public sealed interface CommandArgumentSpec permits CommandArgumentSpecImpl {
-
- static CommandArgumentSpecBuilder builder(String id, ArgumentType type) {
- return new CommandArgumentSpecBuilder<>(id, type);
- }
-
- String id();
-
- ArgumentType type();
-
- /**
- * @throws IllegalArgumentException if string invalid or if a requirement is not met
- */
- V parseInput(String rawInput);
-
- @Nullable Function, V> defaultValueProvider();
-
- /**
- * @return the explicitly set SuggestionProvider or the default suggestions for the type if unset/null
- */
- /* explicit @NotNull to avoid confusion */
- @NotNull SuggestionProvider suggestionProvider();
-}
diff --git a/core/src/main/java/de/kentoj/kencommandapi/api/argument/CommandArgumentSpecBuilder.java b/core/src/main/java/de/kentoj/kencommandapi/api/argument/CommandArgumentSpecBuilder.java
deleted file mode 100644
index fa767e0..0000000
--- a/core/src/main/java/de/kentoj/kencommandapi/api/argument/CommandArgumentSpecBuilder.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package de.kentoj.kencommandapi.api.argument;
-
-import de.kentoj.kencommandapi.api.invocation.CommandContext;
-import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.function.Function;
-import java.util.function.Predicate;
-
-/**
- * @param type of the command sender
- * @param type of the value
- */
-public class CommandArgumentSpecBuilder {
-
- private final String id;
- private final ArgumentType type;
- private @Nullable Function, V> defaultValueProvider;
- private @Nullable SuggestionProvider suggestionProvider;
- private final List> requirements = new ArrayList<>();
-
- public CommandArgumentSpecBuilder(String id, ArgumentType type) {
- this.id = id;
- this.type = type;
- }
-
- public CommandArgumentSpecBuilder withRequirement(Predicate predicate, String message) {
- requirements.add(new ArgumentRequirement<>(predicate, message));
- return this;
- }
-
- public CommandArgumentSpecBuilder withDefaultValue(@Nullable Function, V> defaultValueProvider) {
- this.defaultValueProvider = defaultValueProvider;
- return this;
- }
-
- public CommandArgumentSpecBuilder withSuggestionProvider(@Nullable SuggestionProvider suggestionProvider) {
- this.suggestionProvider = suggestionProvider;
- return this;
- }
-
- public CommandArgumentSpec build() {
- return new CommandArgumentSpecImpl<>(id, type, requirements, defaultValueProvider, suggestionProvider);
- }
-}
diff --git a/core/src/main/java/de/kentoj/kencommandapi/api/argument/CommandArgumentSpecImpl.java b/core/src/main/java/de/kentoj/kencommandapi/api/argument/CommandArgumentSpecImpl.java
deleted file mode 100644
index 2f80387..0000000
--- a/core/src/main/java/de/kentoj/kencommandapi/api/argument/CommandArgumentSpecImpl.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package de.kentoj.kencommandapi.api.argument;
-
-import de.kentoj.kencommandapi.api.invocation.CommandContext;
-import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.List;
-import java.util.function.Function;
-
-public record CommandArgumentSpecImpl(
- String id,
- ArgumentType type,
- List> requirements,
- Function, V> defaultValueProvider,
- @Nullable SuggestionProvider suggestionProvider
-) implements CommandArgumentSpec {
-
- @Override
- public V parseInput(String rawInput) {
- var parsed = type.parseInputUnchecked(rawInput);
- for (var requirement : requirements) {
- if (!requirement.predicate().test(parsed))
- throw new IllegalArgumentException(requirement.message());
- }
- return parsed;
- }
-
- @Override
- public @Nullable Function, V> defaultValueProvider() {
- return defaultValueProvider;
- }
-
- @Override
- public @NotNull SuggestionProvider suggestionProvider() {
- return suggestionProvider != null ? suggestionProvider : type.getDefaultSuggestionProvider();
- }
-}
diff --git a/core/src/main/java/de/kentoj/kencommandapi/api/argument/types/StringArgumentType.java b/core/src/main/java/de/kentoj/kencommandapi/api/argument/types/StringArgumentType.java
deleted file mode 100644
index 6e0ee6a..0000000
--- a/core/src/main/java/de/kentoj/kencommandapi/api/argument/types/StringArgumentType.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package de.kentoj.kencommandapi.api.argument.types;
-
-import de.kentoj.kencommandapi.api.argument.ArgumentType;
-import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider;
-
-public record StringArgumentType(boolean greedy) implements ArgumentType {
-
- @Override
- public String parseInputUnchecked(String string) {
- return string;
- }
-
- @Override
- public SuggestionProvider getDefaultSuggestionProvider() {
- return SuggestionProvider.none();
- }
-}
diff --git a/core/src/main/java/de/kentoj/kencommandapi/api/invocation/CommandContext.java b/core/src/main/java/de/kentoj/kencommandapi/api/invocation/CommandContext.java
deleted file mode 100644
index 471b14a..0000000
--- a/core/src/main/java/de/kentoj/kencommandapi/api/invocation/CommandContext.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package de.kentoj.kencommandapi.api.invocation;
-
-import de.kentoj.kencommandapi.api.argument.CommandArgument;
-import de.kentoj.kencommandapi.api.argument.CommandArgumentSpec;
-
-import java.util.Map;
-
-public interface CommandContext {
-
- T sender();
-
- CommandArgument getParsedArgument(String id);
-
- /**
- * @return the value of the argument with the specified id
- * @param the type of the value of the argument with the specified id
- */
- @SuppressWarnings("unchecked")
- default V getArg(String id) {
- return (V) getParsedArgument(id).value();
- }
-
- /**
- * @param arg the argument whose value to return
- * @return the value of the argument
- * @param the type of the value of the argument
- */
- default V getArg(CommandArgumentSpec arg) {
- return getArg(arg.id());
- }
-
- default CommandArgument getParsedArgument(CommandArgumentSpec arg) {
- return getParsedArgument(arg.id());
- }
-
- Map> parsedArguments();
-}
diff --git a/core/src/main/java/de/kentoj/kencommandapi/api/literal/Literal.java b/core/src/main/java/de/kentoj/kencommandapi/api/literal/Literal.java
deleted file mode 100644
index 57d9d76..0000000
--- a/core/src/main/java/de/kentoj/kencommandapi/api/literal/Literal.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package de.kentoj.kencommandapi.api.literal;
-
-import de.kentoj.kencommandapi.api.argument.CommandArgumentSpec;
-import de.kentoj.kencommandapi.api.invocation.CommandExecutor;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.List;
-
-public interface Literal {
- static LiteralBuilder builder(String... name) {
- return new LiteralBuilder<>(name);
- }
-
- @NotNull String[] names();
-
- /**
- * @return what the literal does
- */
- @Nullable String description();
-
- List> arguments();
-
- @Nullable CommandExecutor executor();
-
- @Nullable Literal getLiteral(@NotNull String name);
-
- @NotNull List> literals();
-
- @Nullable String permission();
-}
diff --git a/core/src/main/java/de/kentoj/kencommandapi/api/literal/LiteralBuilder.java b/core/src/main/java/de/kentoj/kencommandapi/api/literal/LiteralBuilder.java
deleted file mode 100644
index ec18fbb..0000000
--- a/core/src/main/java/de/kentoj/kencommandapi/api/literal/LiteralBuilder.java
+++ /dev/null
@@ -1,91 +0,0 @@
-package de.kentoj.kencommandapi.api.literal;
-
-import de.kentoj.kencommandapi.api.argument.CommandArgumentSpec;
-import de.kentoj.kencommandapi.api.invocation.CommandExecutor;
-import de.kentoj.kencommandapi.api.invocation.SyncCommandExecutor;
-import de.kentoj.kencommandapi.api.platform.MessageStyle;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import static com.google.common.base.Preconditions.checkArgument;
-
-public final class LiteralBuilder {
-
- private final String[] names;
- private @Nullable String description;
- private @Nullable CommandExecutor executor;
- private @Nullable String permission;
- private final List> arguments = new ArrayList<>();
- private final List> literals = new ArrayList<>();
-
- LiteralBuilder(String... names) {
- checkArgument(names.length > 0, "at least one name is required");
- this.names = names;
- }
-
- /**
- * @param description what the literal does
- */
- public LiteralBuilder withDescription(@Nullable String description) {
- this.description = description;
- return this;
- }
-
- public LiteralBuilder withExecutor(@Nullable CommandExecutor executor) {
- this.executor = executor;
- return this;
- }
-
- public LiteralBuilder withSyncExecutor(@Nullable SyncCommandExecutor executor) {
- return withExecutor(executor);
- }
-
- public LiteralBuilder withPermission(@Nullable String permission) {
- this.permission = permission;
- return this;
- }
-
- /**
- * @throws IllegalArgumentException if there is already an argument with the same id
- * @throws IllegalStateException if a greedy argument was added to the literal previously
- */
- public LiteralBuilder withArgument(CommandArgumentSpec argument) {
- checkArgument(!hasGreedyArg(), "cannot add an argument after a greedy argument");
- if (arguments.stream().anyMatch(arg -> arg.id().equals(argument.id())))
- throw new IllegalArgumentException("cannot add multiple arguments with the same id");
-
- this.arguments.add(argument);
- return this;
- }
-
- /**
- * @throws IllegalArgumentException if there is already a literal with the same name
- * @throws IllegalStateException if a greedy argument was added to the literal previously
- */
- public LiteralBuilder withLiteral(Literal literal) {
- checkArgument(!hasGreedyArg(), "cannot add a literal after a greedy argument");
- for (String name : literal.names())
- for (var l : literals)
- if (l.getLiteral(name) != null)
- throw new IllegalArgumentException("literal with same name already exists");
-
- this.literals.add(literal);
- return this;
- }
-
- public RootLiteral asRootLiteral(MessageStyle messageStyle) {
- return new RootLiteralImpl<>(names, description, literals, arguments, executor, permission, messageStyle);
- }
-
- public Literal build() {
- return new LiteralImpl<>(names, description, literals, arguments, executor, permission);
- }
-
- private boolean hasGreedyArg() {
- if (arguments.isEmpty()) return false;
- var arg = arguments.getLast();
- return arg != null && arg.type().greedy();
- }
-}
diff --git a/core/src/main/java/de/kentoj/kencommandapi/api/literal/LiteralImpl.java b/core/src/main/java/de/kentoj/kencommandapi/api/literal/LiteralImpl.java
deleted file mode 100644
index 0a26c1c..0000000
--- a/core/src/main/java/de/kentoj/kencommandapi/api/literal/LiteralImpl.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package de.kentoj.kencommandapi.api.literal;
-
-import de.kentoj.kencommandapi.api.argument.CommandArgumentSpec;
-import de.kentoj.kencommandapi.api.invocation.CommandExecutor;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.List;
-
-/**
- * @param description what the literal does
- */
-record LiteralImpl(
- String[] names,
- String description,
- List> literals,
- List> arguments,
- CommandExecutor executor,
- @Nullable String permission
-) implements Literal {
-
- @Override
- public @Nullable Literal getLiteral(@NotNull String name) {
- for (Literal literal : literals) {
- for (var literalName : literal.names()) {
- if (literalName.equalsIgnoreCase(name))
- return literal;
- }
- }
- return null;
- }
-}
diff --git a/core/src/main/java/de/kentoj/kencommandapi/api/literal/RootLiteral.java b/core/src/main/java/de/kentoj/kencommandapi/api/literal/RootLiteral.java
deleted file mode 100644
index 42321f6..0000000
--- a/core/src/main/java/de/kentoj/kencommandapi/api/literal/RootLiteral.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package de.kentoj.kencommandapi.api.literal;
-
-import de.kentoj.kencommandapi.api.platform.MessageStyle;
-
-public sealed interface RootLiteral extends Literal permits RootLiteralImpl {
-
- MessageStyle messageStyle();
-}
diff --git a/core/src/main/java/de/kentoj/kencommandapi/api/literal/RootLiteralImpl.java b/core/src/main/java/de/kentoj/kencommandapi/api/literal/RootLiteralImpl.java
deleted file mode 100644
index d49b511..0000000
--- a/core/src/main/java/de/kentoj/kencommandapi/api/literal/RootLiteralImpl.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package de.kentoj.kencommandapi.api.literal;
-
-import de.kentoj.kencommandapi.api.argument.CommandArgumentSpec;
-import de.kentoj.kencommandapi.api.invocation.CommandExecutor;
-import de.kentoj.kencommandapi.api.platform.MessageStyle;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.List;
-
-record RootLiteralImpl(
- String[] names,
- String description,
- List> literals,
- List> arguments,
- CommandExecutor executor,
- @Nullable String permission,
- MessageStyle messageStyle
-) implements RootLiteral {
- @Override
- public @Nullable Literal getLiteral(@NotNull String name) {
- for (var l : literals) {
- for (var lName : l.names())
- if (lName.equalsIgnoreCase(name))
- return l;
- }
- return null;
- }
-}
diff --git a/core/src/main/java/de/kentoj/kencommandapi/internal/CommandContextImpl.java b/core/src/main/java/de/kentoj/kencommandapi/internal/CommandContextImpl.java
deleted file mode 100644
index 5b8db98..0000000
--- a/core/src/main/java/de/kentoj/kencommandapi/internal/CommandContextImpl.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package de.kentoj.kencommandapi.internal;
-
-import de.kentoj.kencommandapi.api.argument.CommandArgument;
-import de.kentoj.kencommandapi.api.invocation.CommandContext;
-
-import java.util.Map;
-
-public record CommandContextImpl(
- T sender,
- Map> parsedArguments
-) implements CommandContext {
-
- @Override
- public CommandArgument getParsedArgument(String id) {
- try {
- //noinspection unchecked
- return (CommandArgument) parsedArguments.get(id);
- } catch (ClassCastException e) {
- throw new RuntimeException("requested argument with wrong type", e);
- }
- }
-}
diff --git a/core/src/main/java/de/kentoj/kencommandapi/internal/parser/CommandParseHelper.java b/core/src/main/java/de/kentoj/kencommandapi/internal/parser/CommandParseHelper.java
deleted file mode 100644
index 17b7774..0000000
--- a/core/src/main/java/de/kentoj/kencommandapi/internal/parser/CommandParseHelper.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package de.kentoj.kencommandapi.internal.parser;
-
-import de.kentoj.kencommandapi.api.literal.Literal;
-import de.kentoj.kencommandapi.api.platform.HasPermissionMethod;
-
-import java.util.Iterator;
-
-public final class CommandParseHelper {
- private final HasPermissionMethod hasPermission;
-
- public CommandParseHelper(HasPermissionMethod hasPermission) {
- this.hasPermission = hasPermission;
- }
-
- public boolean hasPermission(T sender, Literal literal) {
- if (literal.permission() == null) return true;
- return hasPermission.test(sender, literal.permission());
- }
-
- 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/core/src/main/java/de/kentoj/kencommandapi/internal/parser/InvocationCommandParser.java b/core/src/main/java/de/kentoj/kencommandapi/internal/parser/InvocationCommandParser.java
deleted file mode 100644
index 8809dea..0000000
--- a/core/src/main/java/de/kentoj/kencommandapi/internal/parser/InvocationCommandParser.java
+++ /dev/null
@@ -1,108 +0,0 @@
-package de.kentoj.kencommandapi.internal.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.argument.CommandArgumentImpl;
-import de.kentoj.kencommandapi.api.argument.CommandArgumentSpec;
-import de.kentoj.kencommandapi.api.invocation.CommandContext;
-import de.kentoj.kencommandapi.api.invocation.CommandExecutor;
-import de.kentoj.kencommandapi.api.literal.Literal;
-import de.kentoj.kencommandapi.internal.CommandContextImpl;
-
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-
-import static java.util.Objects.requireNonNull;
-
-public class InvocationCommandParser {
-
- private final CommandParseHelper helper;
-
- public InvocationCommandParser(CommandParseHelper helper) {
- this.helper = helper;
- }
-
- public Result parse(
- Literal rootLiteral,
- T sender,
- Iterator tokenIter
- ) {
- Literal currentLiteral = rootLiteral;
- var ctx = new CommandContextImpl<>(sender, new HashMap<>());
-
- while (true) {
- {
- var result = processArguments(currentLiteral, ctx, tokenIter)
- .ifSuccess(ctx.parsedArguments()::putAll);
- if (result.hasFailure())
- return result.mapSuccess(_ -> null);
- }
-
- if (!tokenIter.hasNext()) {
- if (currentLiteral.executor() == 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.executor(), ctx));
- }
-
- private Result>, String> processArguments(
- Literal literal,
- CommandContext ctx,
- Iterator tokenIter
- ) {
- Map> parsedArguments = new HashMap<>();
-
- for (var _argument : literal.arguments()) {
- @SuppressWarnings("unchecked")
- var argument = ((CommandArgumentSpec) _argument);
-
- Object value;
- if (!tokenIter.hasNext()) {
- if (argument.defaultValueProvider() == null)
- return Results.failure("Argument expected: " + argument.id());
- value = requireNonNull(argument.defaultValueProvider()).apply(ctx);
- } else {
- var input = helper.parseInput(tokenIter, argument.type().greedy());
- try {
- value = argument.parseInput(input);
- } catch (IllegalArgumentException ex) {
- return Results.failure("Illegal argument '" + input + "' for " + argument.id() + ": " + ex.getMessage());
- }
- }
-
- parsedArguments.put(argument.id(), new CommandArgumentImpl<>(value, argument));
- }
-
- return Results.success(parsedArguments);
- }
-
- public class ExecutionData {
- private final CommandExecutor executor;
- private final CommandContext context;
-
- public ExecutionData(CommandExecutor executor, CommandContext context) {
- this.executor = executor;
- this.context = context;
- }
-
- public CommandExecutor executor() {
- return executor;
- }
-
- public CommandContext context() {
- return context;
- }
- }
-}
diff --git a/core/src/main/java/de/kentoj/kencommandapi/internal/parser/SuggestionCommandParser.java b/core/src/main/java/de/kentoj/kencommandapi/internal/parser/SuggestionCommandParser.java
deleted file mode 100644
index 088a740..0000000
--- a/core/src/main/java/de/kentoj/kencommandapi/internal/parser/SuggestionCommandParser.java
+++ /dev/null
@@ -1,99 +0,0 @@
-package de.kentoj.kencommandapi.internal.parser;
-
-import de.kentoj.kencommandapi.api.literal.Literal;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.List;
-import java.util.stream.Stream;
-
-public class SuggestionCommandParser {
-
- private final CommandParseHelper helper;
-
- public SuggestionCommandParser(CommandParseHelper helper) {
- this.helper = helper;
- }
-
- public List suggest(
- Literal 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.arguments().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.suggestionProvider().suggest(sender);
- var input = helper.parseInput(tokenIter, argument.type().greedy()).toLowerCase();
- if (tokenIter.hasNext())
- continue;
- if (suggestNext) {
- if (argsIter.hasNext())
- return argsIter.next().suggestionProvider().suggest(sender);
- return null;
- }
- return argument.suggestionProvider().suggest(sender).stream()
- .filter(suggestion -> suggestion.toLowerCase().startsWith(input))
- .toList();
- }
- return null;
- }
-
- private Stream suggestions(Literal literal, T sender) {
- return literal.literals().stream()
- .filter(lit -> helper.hasPermission(sender, lit))
- .map(Literal::names)
- .flatMap(Arrays::stream);
- }
-}
diff --git a/core/src/test/java/de/kentoj/kencommandapi/api/parser/SuggestionCommandParserTest.java b/core/src/test/java/de/kentoj/kencommandapi/api/parser/SuggestionCommandParserTest.java
deleted file mode 100644
index 37c5725..0000000
--- a/core/src/test/java/de/kentoj/kencommandapi/api/parser/SuggestionCommandParserTest.java
+++ /dev/null
@@ -1,88 +0,0 @@
-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.Literal;
-import de.kentoj.kencommandapi.api.platform.MessageStyle;
-import de.kentoj.kencommandapi.internal.parser.SuggestionCommandParser;
-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 Literal 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/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..249e583
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..19ca496
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Tue Apr 14 21:48:06 CEST 2026
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000..1b6c787
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,234 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
+
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
+
+APP_NAME="Gradle"
+APP_BASE_NAME=${0##*/}
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+ echo "$*"
+} >&2
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD=$JAVA_HOME/jre/sh/java
+ else
+ JAVACMD=$JAVA_HOME/bin/java
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD=java
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+# Collect all arguments for the java command;
+# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+# shell script including quotes and variable substitutions, so put them in
+# double quotes to make sure that they get re-expanded; and
+# * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..ac1b06f
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/kencommandapi-bukkit/build.gradle.kts b/kencommandapi-bukkit/build.gradle.kts
new file mode 100644
index 0000000..8a44f16
--- /dev/null
+++ b/kencommandapi-bukkit/build.gradle.kts
@@ -0,0 +1,28 @@
+plugins {
+ id("java")
+ id("scrow.scrow-publish")
+ id("scrow.base-dependencies")
+ id("scrow.java-library")
+}
+
+repositories {
+ maven("https://repo.papermc.io/repository/maven-public/") {
+ name = "papermc"
+ }
+}
+
+dependencies {
+ compileOnly(project(":kencommandapi-core"))
+ compileOnly("io.papermc.paper:paper-api:1.21.11-rc3-R0.1-SNAPSHOT")
+}
+
+publishing {
+ publications {
+ create("KenCommandAPI") {
+ groupId = rootProject.group.toString()
+ version = rootProject.version.toString()
+ artifactId = project.name
+ from(components["java"])
+ }
+ }
+}
diff --git a/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/BukkitKenCommand.java b/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/BukkitKenCommand.java
new file mode 100644
index 0000000..25dbe36
--- /dev/null
+++ b/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/BukkitKenCommand.java
@@ -0,0 +1,32 @@
+package de.kentoj.kencommandapi;
+
+import de.kentoj.kencommandapi.api.CommandHandler;
+import de.kentoj.kencommandapi.api.literal.RootLiteral;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Arrays;
+
+public class BukkitKenCommand extends Command {
+
+ private final RootLiteral rootNode;
+ private final CommandHandler commandHandler;
+
+ protected BukkitKenCommand(
+ CommandHandler commandHandler,
+ RootLiteral rootLiteral
+ ) {
+ super(rootLiteral.getNames()[0]);
+ this.commandHandler = commandHandler;
+ this.rootNode = rootLiteral;
+ this.setAliases(Arrays.asList(rootLiteral.getNames()));
+ this.setPermission(rootLiteral.getPermission());
+ }
+
+ @Override
+ public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) {
+ commandHandler.invoke(rootNode, sender, args);
+ return true;
+ }
+}
diff --git a/bukkit/src/main/java/de/kentoj/kencommandapi/CommandAPI.java b/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/CommandAPI.java
similarity index 66%
rename from bukkit/src/main/java/de/kentoj/kencommandapi/CommandAPI.java
rename to kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/CommandAPI.java
index 8ea24c5..9c27132 100644
--- a/bukkit/src/main/java/de/kentoj/kencommandapi/CommandAPI.java
+++ b/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/CommandAPI.java
@@ -13,17 +13,15 @@ import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
-public class CommandAPI {
+public class CommandAPI {
- private final CommandHandler commandHandler;
- private final List> registeredNodes = new ArrayList<>();
+ private final CommandHandler commandHandler;
+ private final List> registeredNodes = new ArrayList<>();
private final SimpleCommandMap commandMap;
- private final Class senderType;
- public CommandAPI(Plugin plugin, Class senderType) {
- this.senderType = senderType;
+ public CommandAPI(Plugin plugin) {
this.commandHandler = new CommandHandler<>(CommandSender::sendMessage, Permissible::hasPermission);
- Bukkit.getPluginManager().registerEvents(new TabCompleteListener<>(this, commandHandler, senderType), plugin);
+ Bukkit.getPluginManager().registerEvents(new TabCompleteListener(this, commandHandler), plugin);
try {
Field field = Bukkit.getPluginManager().getClass().getDeclaredField("commandMap");
@@ -34,17 +32,17 @@ public class CommandAPI {
}
}
- @Nullable RootLiteral getRegistered(String name) {
+ @Nullable RootLiteral getRegistered(String name) {
for (var node : registeredNodes) {
- for (String nodeName : node.names()) {
+ for (String nodeName : node.getNames()) {
if (nodeName.equals(name)) return node;
}
}
return null;
}
- public void register(RootLiteral rootNode) {
+ public void register(RootLiteral rootNode) {
registeredNodes.add(rootNode);
- commandMap.register(rootNode.names()[0], new BukkitKenCommand<>(commandHandler, rootNode, senderType));
+ commandMap.register(rootNode.getNames()[0], new BukkitKenCommand(commandHandler, rootNode));
}
}
diff --git a/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/TabCompleteListener.java b/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/TabCompleteListener.java
new file mode 100644
index 0000000..70c0479
--- /dev/null
+++ b/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/TabCompleteListener.java
@@ -0,0 +1,29 @@
+package de.kentoj.kencommandapi;
+
+import com.destroystokyo.paper.event.server.AsyncTabCompleteEvent;
+import de.kentoj.kencommandapi.api.CommandHandler;
+import lombok.RequiredArgsConstructor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+
+import java.util.Arrays;
+
+@RequiredArgsConstructor
+public class TabCompleteListener implements Listener {
+
+ private final CommandAPI commandAPI;
+ private final CommandHandler commandHandler;
+
+ @EventHandler(ignoreCancelled = true)
+ public void onAsyncTabComplete(AsyncTabCompleteEvent ev) {
+ if (!ev.isCommand()) return;
+
+ var args = ev.getBuffer().substring(1).split(" ");
+ var label = args[0];
+ 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)));
+ }
+}
diff --git a/bukkit/src/main/java/de/kentoj/kencommandapi/suggestionprovider/PlayerSuggestionProvider.java b/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/suggestionprovider/PlayerSuggestionProvider.java
similarity index 65%
rename from bukkit/src/main/java/de/kentoj/kencommandapi/suggestionprovider/PlayerSuggestionProvider.java
rename to kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/suggestionprovider/PlayerSuggestionProvider.java
index c6880c6..3db53a4 100644
--- a/bukkit/src/main/java/de/kentoj/kencommandapi/suggestionprovider/PlayerSuggestionProvider.java
+++ b/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/suggestionprovider/PlayerSuggestionProvider.java
@@ -2,13 +2,14 @@ package de.kentoj.kencommandapi.suggestionprovider;
import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider;
import org.bukkit.Bukkit;
+import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.List;
-public final class PlayerSuggestionProvider implements SuggestionProvider {
+public class PlayerSuggestionProvider implements SuggestionProvider {
@Override
- public List suggest(T __) {
+ public List suggest(CommandSender __) {
return Bukkit.getOnlinePlayers().stream()
.map(Player::getName)
.toList();
diff --git a/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/type/OfflinePlayerArgumentType.java b/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/type/OfflinePlayerArgumentType.java
new file mode 100644
index 0000000..1b74400
--- /dev/null
+++ b/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/type/OfflinePlayerArgumentType.java
@@ -0,0 +1,40 @@
+package de.kentoj.kencommandapi.type;
+
+import de.kentoj.kencommandapi.api.argument.ArgumentType;
+import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider;
+import de.kentoj.kencommandapi.suggestionprovider.PlayerSuggestionProvider;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import org.bukkit.Bukkit;
+import org.bukkit.OfflinePlayer;
+import org.bukkit.command.CommandSender;
+
+import java.util.UUID;
+
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public class OfflinePlayerArgumentType implements ArgumentType {
+
+ @Getter(lazy = true)
+ private static final OfflinePlayerArgumentType instance = new OfflinePlayerArgumentType();
+
+ // TODO make work with player names on offline players
+ @Override
+ public OfflinePlayer parseInput(String string) {
+ OfflinePlayer player = Bukkit.getPlayerExact(string);
+ if (player == null) {
+ try {
+ player = Bukkit.getOfflinePlayer(UUID.fromString(string));
+ } catch (IllegalArgumentException ex) {
+ throw new IllegalArgumentException("not an UUID or online player");
+ }
+ }
+ if (!player.hasPlayedBefore() && !player.isOnline()) throw new IllegalArgumentException("Player not found");
+ return player;
+ }
+
+ @Override
+ public SuggestionProvider defaultSuggestionProvider() {
+ return new PlayerSuggestionProvider();
+ }
+}
diff --git a/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/type/PlayerArgumentType.java b/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/type/PlayerArgumentType.java
new file mode 100644
index 0000000..fe75d27
--- /dev/null
+++ b/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/type/PlayerArgumentType.java
@@ -0,0 +1,30 @@
+package de.kentoj.kencommandapi.type;
+
+import de.kentoj.kencommandapi.api.argument.ArgumentType;
+import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider;
+import de.kentoj.kencommandapi.suggestionprovider.PlayerSuggestionProvider;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import org.bukkit.Bukkit;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public class PlayerArgumentType implements ArgumentType {
+
+ @Getter(lazy = true)
+ private static final PlayerArgumentType instance = new PlayerArgumentType();
+
+ @Override
+ public Player parseInput(String string) {
+ var player = Bukkit.getPlayer(string);
+ if (player == null) throw new IllegalArgumentException("player not online");
+ return player;
+ }
+
+ @Override
+ public SuggestionProvider defaultSuggestionProvider() {
+ return new PlayerSuggestionProvider();
+ }
+}
diff --git a/kencommandapi-core/build.gradle.kts b/kencommandapi-core/build.gradle.kts
new file mode 100644
index 0000000..b88b7c4
--- /dev/null
+++ b/kencommandapi-core/build.gradle.kts
@@ -0,0 +1,40 @@
+plugins {
+ id("java")
+ id("scrow.scrow-publish")
+ id("scrow.base-dependencies")
+ id("scrow.java-library")
+}
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ // https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api
+ testImplementation("org.junit.jupiter:junit-jupiter-api:6.1.1")
+ // https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-engine
+ testImplementation("org.junit.jupiter:junit-jupiter-engine:6.1.1")
+ // https://mvnrepository.com/artifact/org.junit.platform/junit-platform-launcher
+ testImplementation("org.junit.platform:junit-platform-launcher:6.1.1")
+ testImplementation("net.kyori:adventure-api:5.2.0")
+
+ compileOnly("net.kyori:adventure-api:5.2.0")
+}
+
+tasks.test {
+ useJUnitPlatform()
+ testLogging {
+ showStandardStreams = true
+ }
+}
+
+publishing {
+ publications {
+ create("KenCommandAPI") {
+ groupId = rootProject.group.toString()
+ version = rootProject.version.toString()
+ artifactId = project.name
+ from(components["java"])
+ }
+ }
+}
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
new file mode 100644
index 0000000..9738558
--- /dev/null
+++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/CommandHandler.java
@@ -0,0 +1,46 @@
+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.platform.HasPermissionMethod;
+import de.kentoj.kencommandapi.api.platform.SendMessageMethod;
+import lombok.RequiredArgsConstructor;
+
+import java.util.List;
+
+@RequiredArgsConstructor
+public class CommandHandler {
+
+ private final SendMessageMethod sendMessageMethod;
+
+ private final InvocationCommandParser invocationParser;
+ private final SuggestionCommandParser suggestionParser;
+
+ public CommandHandler(SendMessageMethod sendMessageMethod, HasPermissionMethod hasPermissionMethod) {
+ this.sendMessageMethod = sendMessageMethod;
+ invocationParser = new InvocationCommandParser<>(hasPermissionMethod);
+ suggestionParser = new SuggestionCommandParser<>(hasPermissionMethod);
+ }
+
+ @SuppressWarnings("CodeBlock2Expr")
+ public void invoke(RootLiteral rootLiteral, T sender, String[] args) {
+ invocationParser.invoke(rootLiteral, sender, args)
+ .ifSuccessOrElse(data -> {
+ var ctx = new CommandContext<>(sender, data.getArguments());
+ data.getExecutor().execute(ctx).whenComplete((result, ex) -> {
+ if (ex != null)
+ sendMessageMethod.send(sender, rootLiteral.getMessageStyle().exception(ex));
+ result.ifFailure(err ->
+ sendMessageMethod.send(sender, rootLiteral.getMessageStyle().err(err)));
+ });
+ }, err -> {
+ sendMessageMethod.send(sender, rootLiteral.getMessageStyle().err(err));
+ });
+ }
+
+ public List getSuggestions(RootLiteral rootNode, T sender, String[] args) {
+ return suggestionParser.getSuggestions(rootNode, sender, args);
+ }
+}
\ No newline at end of file
diff --git a/core/src/main/java/de/kentoj/kencommandapi/api/argument/ArgumentType.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/ArgumentType.java
similarity index 51%
rename from core/src/main/java/de/kentoj/kencommandapi/api/argument/ArgumentType.java
rename to kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/ArgumentType.java
index 360a3fc..00cda59 100644
--- a/core/src/main/java/de/kentoj/kencommandapi/api/argument/ArgumentType.java
+++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/ArgumentType.java
@@ -2,31 +2,21 @@ package de.kentoj.kencommandapi.api.argument;
import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider;
-import java.util.function.Predicate;
-
public interface ArgumentType {
/**
- * parses the given input and returns it.
- * Unlike {@link CommandArgumentSpec#parseInput(String)}, this does not check any additional requirements.
- *
* @throws IllegalArgumentException if string invalid
- * @see CommandArgumentSpec#parseInput(String)
- * @see CommandArgumentSpecBuilder#withRequirement(Predicate, String)
*/
- V parseInputUnchecked(String input) throws IllegalArgumentException;
+ V parseInput(String string);
- /**
- * @see CommandArgumentSpec#suggestionProvider()
- */
- SuggestionProvider getDefaultSuggestionProvider();
+ SuggestionProvider defaultSuggestionProvider();
/**
* @return whether this argument is greedy. Greedy arguments consume all remaining input instead of a single word.
* For example, given the 2 words "hello" "world" as input, a greedy argument parses "hello world" as a single value.
* Greedy arguments may not be followed by another argument or a literal
*/
- default boolean greedy() {
+ default boolean isGreedy() {
return false;
}
}
diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/CommandArgument.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/CommandArgument.java
new file mode 100644
index 0000000..45967db
--- /dev/null
+++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/CommandArgument.java
@@ -0,0 +1,30 @@
+package de.kentoj.kencommandapi.api.argument;
+
+import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider;
+import de.kentoj.kencommandapi.internal.CommandArgumentImpl;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.function.Function;
+
+public interface CommandArgument {
+
+ static CommandArgument arg(String id, ArgumentType type) {
+ return new CommandArgumentImpl<>(id, type);
+ }
+
+ String getId();
+
+ ArgumentType getType();
+
+ void setDefaultValueProvider(@Nullable Function defaultValueProvider);
+
+ @Nullable Function getDefaultValueProvider();
+
+ void setSuggestionProvider(@Nullable SuggestionProvider suggestionProvider);
+
+ /**
+ * @return the explicitly set SuggestionProvider or the default suggestions for the type if unset/null
+ */
+ @NotNull SuggestionProvider getSuggestionProvider();
+}
diff --git a/core/src/main/java/de/kentoj/kencommandapi/api/argument/types/IntegerArgumentType.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/types/IntegerArgumentType.java
similarity index 60%
rename from core/src/main/java/de/kentoj/kencommandapi/api/argument/types/IntegerArgumentType.java
rename to kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/types/IntegerArgumentType.java
index 5f81e03..80d6ee8 100644
--- a/core/src/main/java/de/kentoj/kencommandapi/api/argument/types/IntegerArgumentType.java
+++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/types/IntegerArgumentType.java
@@ -2,16 +2,17 @@ package de.kentoj.kencommandapi.api.argument.types;
import de.kentoj.kencommandapi.api.argument.ArgumentType;
import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider;
+import de.kentoj.kencommandapi.api.suggestion.NoSuggestionProvider;
public class IntegerArgumentType implements ArgumentType {
@Override
- public Integer parseInputUnchecked(String string) {
+ public Integer parseInput(String string) {
return Integer.parseInt(string);
}
@Override
- public SuggestionProvider getDefaultSuggestionProvider() {
- return SuggestionProvider.none();
+ public SuggestionProvider defaultSuggestionProvider() {
+ return new NoSuggestionProvider<>();
}
}
diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/types/StringArgumentType.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/types/StringArgumentType.java
new file mode 100644
index 0000000..5ea49d5
--- /dev/null
+++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/types/StringArgumentType.java
@@ -0,0 +1,31 @@
+package de.kentoj.kencommandapi.api.argument.types;
+
+import de.kentoj.kencommandapi.api.argument.ArgumentType;
+import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider;
+import de.kentoj.kencommandapi.api.suggestion.NoSuggestionProvider;
+import lombok.RequiredArgsConstructor;
+
+@RequiredArgsConstructor
+public class StringArgumentType implements ArgumentType {
+
+ private final boolean isGreedy;
+
+ public StringArgumentType() {
+ this(false);
+ }
+
+ @Override
+ public String parseInput(String string) {
+ return string;
+ }
+
+ @Override
+ public SuggestionProvider defaultSuggestionProvider() {
+ return new NoSuggestionProvider<>();
+ }
+
+ @Override
+ public boolean isGreedy() {
+ return this.isGreedy;
+ }
+}
diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/invocation/CommandContext.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/invocation/CommandContext.java
new file mode 100644
index 0000000..8d8a248
--- /dev/null
+++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/invocation/CommandContext.java
@@ -0,0 +1,37 @@
+package de.kentoj.kencommandapi.api.invocation;
+
+import de.kentoj.kencommandapi.api.argument.CommandArgument;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+import java.util.Map;
+
+@RequiredArgsConstructor
+public class CommandContext {
+ @Getter
+ private final T sender;
+ @Getter
+ private final Map> parsedArguments;
+
+ public ParsedArgument getParsedArgument(String name) {
+ try {
+ //noinspection unchecked
+ return (ParsedArgument) parsedArguments.get(name);
+ } catch (ClassCastException e) {
+ throw new RuntimeException("requested argument with wrong type", e);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public V getArg(String name) {
+ return (V) getParsedArgument(name).getValue();
+ }
+
+ public V getArg(CommandArgument arg) {
+ return getArg(arg.getId());
+ }
+
+ public ParsedArgument getParsedArgument(CommandArgument arg) {
+ return getParsedArgument(arg.getId());
+ }
+}
diff --git a/core/src/main/java/de/kentoj/kencommandapi/api/invocation/CommandExecutor.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/invocation/CommandExecutor.java
similarity index 100%
rename from core/src/main/java/de/kentoj/kencommandapi/api/invocation/CommandExecutor.java
rename to kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/invocation/CommandExecutor.java
diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/invocation/ParsedArgument.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/invocation/ParsedArgument.java
new file mode 100644
index 0000000..6412823
--- /dev/null
+++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/invocation/ParsedArgument.java
@@ -0,0 +1,10 @@
+package de.kentoj.kencommandapi.api.invocation;
+
+import de.kentoj.kencommandapi.api.argument.CommandArgument;
+
+public interface ParsedArgument {
+
+ V getValue();
+
+ CommandArgument getArgument();
+}
diff --git a/core/src/main/java/de/kentoj/kencommandapi/api/invocation/SyncCommandExecutor.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/invocation/SyncCommandExecutor.java
similarity index 100%
rename from core/src/main/java/de/kentoj/kencommandapi/api/invocation/SyncCommandExecutor.java
rename to kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/invocation/SyncCommandExecutor.java
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
new file mode 100644
index 0000000..531905b
--- /dev/null
+++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/literal/Literal.java
@@ -0,0 +1,64 @@
+package de.kentoj.kencommandapi.api.literal;
+
+import de.kentoj.kencommandapi.api.platform.MessageStyle;
+import de.kentoj.kencommandapi.api.argument.CommandArgument;
+import de.kentoj.kencommandapi.api.invocation.CommandExecutor;
+import de.kentoj.kencommandapi.internal.LiteralImpl;
+import de.kentoj.kencommandapi.internal.RootLiteralImpl;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+import java.util.Set;
+
+public interface Literal {
+ static Literal literal(String... name) {
+ return new LiteralImpl<>(name);
+ }
+
+ static RootLiteral