reworked parsing

This commit is contained in:
kento2 2026-07-06 22:08:48 +02:00
parent 8897579a12
commit ac6a95232e
22 changed files with 360 additions and 441 deletions

156
.idea/workspace.xml generated
View file

@ -5,15 +5,28 @@
</component>
<component name="ChangeListManager">
<list default="true" id="6414223f-1761-4683-97f5-9dab59d6c44a" name="Changes" comment=".">
<change afterPath="$PROJECT_DIR$/kencommandapi-core/src/test/java/de/kentoj/kencommandapi/api/parser/CommandHandlerTest.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/kencommandapi-core/src/test/java/de/kentoj/kencommandapi/api/parser/CommandParserTest.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/kencommandapi-core/src/test/java/de/kentoj/kencommandapi/api/parser/ENV.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/parser2/AbstractCommandParser.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/parser2/InvocationCommandParser.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/parser2/SuggestionCommandParser.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/kencommandapi-core/build.gradle.kts" beforeDir="false" afterPath="$PROJECT_DIR$/kencommandapi-core/build.gradle.kts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/parser/CommandParser.java" beforeDir="false" afterPath="$PROJECT_DIR$/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/parser/CommandParser.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../build-logic/convention/src/main/kotlin/scrow/BaseDependenciesConvention.kt" beforeDir="false" afterPath="$PROJECT_DIR$/../build-logic/convention/src/main/kotlin/scrow/BaseDependenciesConvention.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../build-logic/convention/src/main/kotlin/scrow/ScrowPublishConvention.kt" beforeDir="false" afterPath="$PROJECT_DIR$/../build-logic/convention/src/main/kotlin/scrow/ScrowPublishConvention.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../build-logic/convention/src/main/kotlin/scrow/configureScrowRepo.kt" beforeDir="false" afterPath="$PROJECT_DIR$/../build-logic/convention/src/main/kotlin/scrow/configureScrowRepo.kt" afterDir="false" />
<change beforePath="$PROJECT_DIR$/TODO" beforeDir="false" afterPath="$PROJECT_DIR$/TODO" afterDir="false" />
<change beforePath="$PROJECT_DIR$/build.gradle.kts" beforeDir="false" afterPath="$PROJECT_DIR$/build.gradle.kts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/BukkitKenCommand.java" beforeDir="false" afterPath="$PROJECT_DIR$/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/BukkitKenCommand.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/CommandAPI.java" beforeDir="false" afterPath="$PROJECT_DIR$/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/CommandAPI.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/TabCompleteListener.java" beforeDir="false" afterPath="$PROJECT_DIR$/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/TabCompleteListener.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/CommandHandler.java" beforeDir="false" afterPath="$PROJECT_DIR$/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/CommandHandler.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/node/CommandNode.java" beforeDir="false" afterPath="$PROJECT_DIR$/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/literal/Literal.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/node/RootCommandNode.java" beforeDir="false" afterPath="$PROJECT_DIR$/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/literal/RootLiteral.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/parser/CommandParser.java" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/parser/ParseResult.java" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/platform/HasPermissionMethod.java" beforeDir="false" afterPath="$PROJECT_DIR$/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/platform/HasPermissionMethod.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/CommandNodeImpl.java" beforeDir="false" afterPath="$PROJECT_DIR$/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/LiteralImpl.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/RootCommandNodeImpl.java" beforeDir="false" afterPath="$PROJECT_DIR$/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/RootLiteralImpl.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/kencommandapi-core/src/test/java/de/kentoj/kencommandapi/api/parser/CommandHandlerTest.java" beforeDir="false" afterPath="$PROJECT_DIR$/kencommandapi-core/src/test/java/de/kentoj/kencommandapi/api/parser/CommandHandlerTest.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/kencommandapi-core/src/test/java/de/kentoj/kencommandapi/api/parser/CommandParserTest.java" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/kencommandapi-core/src/test/java/de/kentoj/kencommandapi/api/parser/ENV.java" beforeDir="false" afterPath="$PROJECT_DIR$/kencommandapi-core/src/test/java/de/kentoj/kencommandapi/api/parser/ENV.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/kencommandapi-velocity/src/main/java/de/kentoj/kencommandapi/CommandAPI.java" beforeDir="false" afterPath="$PROJECT_DIR$/kencommandapi-velocity/src/main/java/de/kentoj/kencommandapi/CommandAPI.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/kencommandapi-velocity/src/main/java/de/kentoj/kencommandapi/VelocityCommand.java" beforeDir="false" afterPath="$PROJECT_DIR$/kencommandapi-velocity/src/main/java/de/kentoj/kencommandapi/VelocityCommand.java" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@ -58,6 +71,13 @@
<item name="KenCommandAPI" type="2d1252cf:ModuleNode" />
<item name="Tasks" type="e4a08cd1:TasksNode" />
</path>
<path>
<item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
<item name="KenCommandAPI" type="f1a62948:ProjectNode" />
<item name="KenCommandAPI" type="2d1252cf:ModuleNode" />
<item name="Tasks" type="e4a08cd1:TasksNode" />
<item name="build" type="c8890929:TasksNode$1" />
</path>
<path>
<item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" />
<item name="KenCommandAPI" type="f1a62948:ProjectNode" />
@ -114,41 +134,41 @@
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"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",
"Gradle.CommandParserTest.checkArgumentExpected.executor": "Run",
"Gradle.Download Sources.executor": "Run",
"Gradle.KenCommandAPI [:de.kencommand.Main.main()].executor": "Run",
"Gradle.KenCommandAPI [assemble].executor": "Run",
"Gradle.KenCommandAPI [buildEnvironment].executor": "Run",
"Gradle.KenCommandAPI [build].executor": "Run",
"Gradle.KenCommandAPI [clean test].executor": "Run",
"Gradle.KenCommandAPI [clean].executor": "Run",
"Gradle.KenCommandAPI [publishAllPublicationsToScrowRepository].executor": "Run",
"Gradle.KenCommandAPI [publish].executor": "Run",
"Gradle.KenCommandAPI [test].executor": "Run",
"Gradle.KenCommandAPI:kencommandapi-bukkit [build].executor": "Run",
"Gradle.KenCommandAPI:kencommandapi-bukkit [generatePomFileForKenCommandAPIBukkitPublication].executor": "Run",
"Gradle.KenCommandAPI:kencommandapi-bukkit [publishAllPublicationsToScrowRepository].executor": "Run",
"Gradle.KenCommandAPI:kencommandapi-bukkit [publish].executor": "Run",
"Gradle.KenCommandAPI:kencommandapi-velocity [publish].executor": "Run",
"Gradle.kencommandapi-core [build].executor": "Run",
"ModuleVcsDetector.initialDetectionPerformed": "true",
"RunOnceActivity.ShowReadmeOnStart": "true",
"RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
"RunOnceActivity.git.unshallow": "true",
"git-widget-placeholder": "master",
"kotlin-language-version-configured": "true",
"last_opened_file_path": "/home/a/src/scrow/KenCommandAPI/kencommandapi-core",
"project.structure.last.edited": "Modules",
"project.structure.proportion": "0.15",
"project.structure.side.proportion": "0.6268199",
"settings.editor.selected.configurable": "preferences.pluginManager"
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;Gradle./home/a/src/kento2/KenCommandAPI/kencommandapi-core [:kencommandapi-core:de.kentoj.kencommandapi.Main.main()].executor&quot;: &quot;Run&quot;,
&quot;Gradle./home/a/src/kento2/KenCommandAPI/kencommandapi-core [build].executor&quot;: &quot;Run&quot;,
&quot;Gradle./home/a/src/kento2/KenCommandAPI/kencommandapi-core [publish].executor&quot;: &quot;Run&quot;,
&quot;Gradle.CommandParserTest.checkArgumentExpected.executor&quot;: &quot;Run&quot;,
&quot;Gradle.Download Sources.executor&quot;: &quot;Run&quot;,
&quot;Gradle.KenCommandAPI [:de.kencommand.Main.main()].executor&quot;: &quot;Run&quot;,
&quot;Gradle.KenCommandAPI [assemble].executor&quot;: &quot;Run&quot;,
&quot;Gradle.KenCommandAPI [buildEnvironment].executor&quot;: &quot;Run&quot;,
&quot;Gradle.KenCommandAPI [build].executor&quot;: &quot;Run&quot;,
&quot;Gradle.KenCommandAPI [clean test].executor&quot;: &quot;Run&quot;,
&quot;Gradle.KenCommandAPI [clean].executor&quot;: &quot;Run&quot;,
&quot;Gradle.KenCommandAPI [publishAllPublicationsToScrowRepository].executor&quot;: &quot;Run&quot;,
&quot;Gradle.KenCommandAPI [publish].executor&quot;: &quot;Run&quot;,
&quot;Gradle.KenCommandAPI [test].executor&quot;: &quot;Run&quot;,
&quot;Gradle.KenCommandAPI:kencommandapi-bukkit [build].executor&quot;: &quot;Run&quot;,
&quot;Gradle.KenCommandAPI:kencommandapi-bukkit [generatePomFileForKenCommandAPIBukkitPublication].executor&quot;: &quot;Run&quot;,
&quot;Gradle.KenCommandAPI:kencommandapi-bukkit [publishAllPublicationsToScrowRepository].executor&quot;: &quot;Run&quot;,
&quot;Gradle.KenCommandAPI:kencommandapi-bukkit [publish].executor&quot;: &quot;Run&quot;,
&quot;Gradle.KenCommandAPI:kencommandapi-velocity [publish].executor&quot;: &quot;Run&quot;,
&quot;Gradle.kencommandapi-core [build].executor&quot;: &quot;Run&quot;,
&quot;ModuleVcsDetector.initialDetectionPerformed&quot;: &quot;true&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252&quot;: &quot;true&quot;,
&quot;RunOnceActivity.git.unshallow&quot;: &quot;true&quot;,
&quot;git-widget-placeholder&quot;: &quot;master&quot;,
&quot;kotlin-language-version-configured&quot;: &quot;true&quot;,
&quot;last_opened_file_path&quot;: &quot;/home/a/src/scrow/core&quot;,
&quot;project.structure.last.edited&quot;: &quot;Modules&quot;,
&quot;project.structure.proportion&quot;: &quot;0.15&quot;,
&quot;project.structure.side.proportion&quot;: &quot;0.6268199&quot;,
&quot;settings.editor.selected.configurable&quot;: &quot;preferences.pluginManager&quot;
}
}]]></component>
}</component>
<component name="RecentsManager">
<key name="CreateClassDialog.RecentsKey">
<recent name="de.kentoj.kencommandapi.api" />
@ -166,11 +186,11 @@
<recent name="$PROJECT_DIR$/kencommandapi-core" />
</key>
<key name="CopyClassDialog.RECENTS_KEY">
<recent name="de.kentoj.kencommandapi.api.parser2" />
<recent name="de.kentoj.kencommandapi.api.invocation" />
<recent name="de.kentoj.kencommandapi.internal" />
<recent name="de.kentoj.kencommandapi.api" />
<recent name="de.kentoj.kencommandapi.impl" />
<recent name="de.kentoj.kencommandapi.api.structure.argument.types" />
</key>
</component>
<component name="RunAnythingCache">
@ -182,7 +202,7 @@
<command value="gradlew test" />
</option>
</component>
<component name="RunManager" selected="Gradle.KenCommandAPI [test]">
<component name="RunManager" selected="Gradle.KenCommandAPI [publish]">
<configuration name="CommandParserTest.checkArgumentExpected" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
<ExternalSystemSettings>
<option name="executionName" />
@ -207,6 +227,28 @@
<RunAsTest>true</RunAsTest>
<method v="2" />
</configuration>
<configuration name="KenCommandAPI [build]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="build" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
<configuration name="KenCommandAPI [clean test]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
<ExternalSystemSettings>
<option name="executionName" />
@ -230,28 +272,6 @@
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
<configuration name="KenCommandAPI [publishAllPublicationsToScrowRepository]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="publishAllPublicationsToScrowRepository" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
<configuration name="KenCommandAPI [publish]" type="GradleRunConfiguration" factoryName="Gradle" temporary="true">
<ExternalSystemSettings>
<option name="executionName" />
@ -298,11 +318,11 @@
</configuration>
<recent_temporary>
<list>
<item itemvalue="Gradle.KenCommandAPI [publish]" />
<item itemvalue="Gradle.KenCommandAPI [build]" />
<item itemvalue="Gradle.KenCommandAPI [test]" />
<item itemvalue="Gradle.KenCommandAPI [clean test]" />
<item itemvalue="Gradle.CommandParserTest.checkArgumentExpected" />
<item itemvalue="Gradle.KenCommandAPI [publish]" />
<item itemvalue="Gradle.KenCommandAPI [publishAllPublicationsToScrowRepository]" />
</list>
</recent_temporary>
</component>

5
TODO
View file

@ -1,4 +1 @@
- use version catalog
- fix missing ArgumentExpected error at first arg or smth (maybe all args are shifted ???)
- make sure permissions of literals work
- fix suggestions/tab completion
- suggest next literal/argument

View file

@ -1,5 +1,5 @@
group = "de.kentoj.scrow"
version = "0.32"
version = "0.33-SNAPSHOT"
subprojects {
group = rootProject.group

View file

@ -1,7 +1,7 @@
package de.kentoj.kencommandapi;
import de.kentoj.kencommandapi.api.CommandHandler;
import de.kentoj.kencommandapi.api.node.RootCommandNode;
import de.kentoj.kencommandapi.api.literal.RootLiteral;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
@ -10,18 +10,18 @@ import java.util.Arrays;
public class BukkitKenCommand extends Command {
private final RootCommandNode<CommandSender> rootNode;
private final RootLiteral<CommandSender> rootNode;
private final CommandHandler<CommandSender> commandHandler;
protected BukkitKenCommand(
CommandHandler<CommandSender> commandHandler,
RootCommandNode<CommandSender> rootNode
RootLiteral<CommandSender> rootLiteral
) {
super(rootNode.getNames()[0]);
super(rootLiteral.getNames()[0]);
this.commandHandler = commandHandler;
this.rootNode = rootNode;
this.setAliases(Arrays.asList(rootNode.getNames()));
this.setPermission(rootNode.getPermission());
this.rootNode = rootLiteral;
this.setAliases(Arrays.asList(rootLiteral.getNames()));
this.setPermission(rootLiteral.getPermission());
}
@Override

View file

@ -1,8 +1,7 @@
package de.kentoj.kencommandapi;
import de.kentoj.kencommandapi.api.CommandHandler;
import de.kentoj.kencommandapi.api.node.RootCommandNode;
import de.kentoj.kencommandapi.api.parser.CommandParser;
import de.kentoj.kencommandapi.api.literal.RootLiteral;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.command.SimpleCommandMap;
@ -17,12 +16,11 @@ import java.util.List;
public class CommandAPI {
private final CommandHandler<CommandSender> commandHandler;
private final List<RootCommandNode<CommandSender>> registeredNodes = new ArrayList<>();
private final List<RootLiteral<CommandSender>> registeredNodes = new ArrayList<>();
private final SimpleCommandMap commandMap;
public CommandAPI(Plugin plugin) {
this.commandHandler = new CommandHandler<>(new CommandParser<>(Permissible::hasPermission),
CommandSender::sendMessage, Permissible::hasPermission);
this.commandHandler = new CommandHandler<>(CommandSender::sendMessage, Permissible::hasPermission);
Bukkit.getPluginManager().registerEvents(new TabCompleteListener(this, commandHandler), plugin);
try {
@ -34,7 +32,7 @@ public class CommandAPI {
}
}
@Nullable RootCommandNode<CommandSender> getRegistered(String name) {
@Nullable RootLiteral<CommandSender> getRegistered(String name) {
for (var node : registeredNodes) {
for (String nodeName : node.getNames()) {
if (nodeName.equals(name)) return node;
@ -43,7 +41,7 @@ public class CommandAPI {
return null;
}
public void register(RootCommandNode<CommandSender> rootNode) {
public void register(RootLiteral<CommandSender> rootNode) {
registeredNodes.add(rootNode);
commandMap.register(rootNode.getNames()[0], new BukkitKenCommand(commandHandler, rootNode));
}

View file

@ -7,6 +7,8 @@ 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 {
@ -17,11 +19,11 @@ public class TabCompleteListener implements Listener {
public void onAsyncTabComplete(AsyncTabCompleteEvent ev) {
if (!ev.isCommand()) return;
var args = ev.getBuffer().split(" ");
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(), args));
ev.setCompletions(commandHandler.getSuggestions(rootNode, ev.getSender(), Arrays.copyOfRange(args, 1, args.length)));
}
}

View file

@ -1,66 +1,46 @@
package de.kentoj.kencommandapi.api;
import de.kentoj.kencommandapi.api.invocation.CommandContext;
import de.kentoj.kencommandapi.api.node.CommandNode;
import de.kentoj.kencommandapi.api.node.RootCommandNode;
import de.kentoj.kencommandapi.api.parser.CommandParser;
import de.kentoj.kencommandapi.api.parser.ParseResult;
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 org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.List;
@RequiredArgsConstructor
public class CommandHandler<T> {
private final CommandParser<T> commandParser;
private final SendMessageMethod<T> sendMessageMethod;
private final HasPermissionMethod<T> hasPermissionMethod;
public void invoke(RootCommandNode<T> rootNode, T sender, String[] args) {
var parseResult = commandParser.parseLiteral(rootNode, sender, args, true);
if (!(parseResult instanceof ParseResult.Success<T>)) {
sendMessageMethod.send(sender, rootNode.getMessageStyle().err(parseResult.getMessage()));
return;
}
private final InvocationCommandParser<T> invocationParser;
private final SuggestionCommandParser<T> suggestionParser;
var ctx = new CommandContext<>(sender, ((ParseResult.Success<T>) parseResult).getParsedArguments());
assert parseResult.getLiteral().getExecutor() != null;
parseResult.getLiteral().getExecutor().execute(ctx).whenComplete((result, ex) -> {
if (ex != null)
sendMessageMethod.send(sender, rootNode.getMessageStyle().exception(ex));
result.ifFailure(failure ->
sendMessageMethod.send(sender, rootNode.getMessageStyle().err(failure)));
});
public CommandHandler(SendMessageMethod<T> sendMessageMethod, HasPermissionMethod<T> hasPermissionMethod) {
this.sendMessageMethod = sendMessageMethod;
invocationParser = new InvocationCommandParser<>(hasPermissionMethod);
suggestionParser = new SuggestionCommandParser<>(hasPermissionMethod);
}
public List<String> getSuggestions(RootCommandNode<T> rootNode, T sender, String[] args) {
var parseResult = commandParser.parseLiteral(rootNode, sender, args, false);
if (parseResult instanceof ParseResult.ArgumentExpected<T>) {
var arg = ((ParseResult.ArgumentExpected<T>) parseResult).getArgument();
return arg.getSuggestionProvider().suggest(sender);
}
String filter = null;
if (parseResult instanceof ParseResult.UnknownLiteral<T>)
filter = ((ParseResult.UnknownLiteral<T>) parseResult).getInput();
else if (parseResult instanceof ParseResult.NoPermission<T>)
filter = ((ParseResult.NoPermission<T>) parseResult).getInput();
return getSuggestions(sender, parseResult.getLiteral(), filter);
@SuppressWarnings("CodeBlock2Expr")
public void invoke(RootLiteral<T> 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));
});
}
private List<String> getSuggestions(T sender, CommandNode<T> literal, @Nullable String filter) {
var stream = literal.getLiterals().stream()
.filter(lit -> lit.getPermission() != null && hasPermissionMethod.hasPermission(sender, lit.getPermission()))
.map(CommandNode::getNames)
.flatMap(Arrays::stream);
if (filter != null)
stream = stream.filter(f -> f.toLowerCase().contains(filter.toLowerCase()));
return stream.toList();
public List<String> getSuggestions(RootLiteral<T> rootNode, T sender, String[] args) {
return suggestionParser.getSuggestions(rootNode, sender, args);
}
}

View file

@ -1,23 +1,23 @@
package de.kentoj.kencommandapi.api.node;
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.CommandNodeImpl;
import de.kentoj.kencommandapi.internal.RootCommandNodeImpl;
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 CommandNode<T> {
static <T> CommandNode<T> node(String... name) {
return new CommandNodeImpl<>(name);
public interface Literal<T> {
static <T> Literal<T> literal(String... name) {
return new LiteralImpl<>(name);
}
static <T> RootCommandNode<T> rootNode(MessageStyle messageStyle, String...name) {
return new RootCommandNodeImpl<>(messageStyle, name);
static <T> RootLiteral<T> rootLiteral(MessageStyle messageStyle, String...name) {
return new RootLiteralImpl<>(messageStyle, name);
}
@NotNull String[] getNames();
@ -32,7 +32,8 @@ public interface CommandNode<T> {
/**
* @throws IllegalArgumentException if there is already an argument with the same id
* @throws IllegalStateException if a greedy argument was added to the node previously
* @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
*/
<V> void addArgument(CommandArgument<T, V> argument);
@ -48,13 +49,14 @@ public interface CommandNode<T> {
/**
* @throws IllegalArgumentException if there is already a literal with the same name
* @throws IllegalStateException if a greedy argument was added to the node previously
* @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 CommandNode<T> commandNode);
void addLiteral(@NotNull Literal<T> literal);
@Nullable CommandNode<T> getLiteral(@NotNull String name);
@Nullable Literal<T> getLiteral(@NotNull String name);
@NotNull Set<CommandNode<T>> getLiterals();
@NotNull Set<Literal<T>> getLiterals();
@Nullable String getPermission();

View file

@ -1,8 +1,8 @@
package de.kentoj.kencommandapi.api.node;
package de.kentoj.kencommandapi.api.literal;
import de.kentoj.kencommandapi.api.platform.MessageStyle;
public interface RootCommandNode<T> extends CommandNode<T> {
public interface RootLiteral<T> extends Literal<T> {
MessageStyle getMessageStyle();

View file

@ -1,103 +0,0 @@
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.ParsedArgument;
import de.kentoj.kencommandapi.api.node.CommandNode;
import de.kentoj.kencommandapi.api.node.RootCommandNode;
import de.kentoj.kencommandapi.api.platform.HasPermissionMethod;
import de.kentoj.kencommandapi.internal.ParsedArgumentImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@RequiredArgsConstructor
public class CommandParser<T> {
private final HasPermissionMethod<T> hasPermissionMethod;
public ParseResult<T> parseLiteral(RootCommandNode<T> rootNode, T sender, String[] args, boolean parseArgs) {
CommandNode<T> cur = rootNode;
final Iterator<String> argStrIter = Arrays.stream(args).iterator();
final Map<String, ParsedArgument<T, ?>> parsedArguments = new HashMap<>();
while (true) {
for (var nodeArg : cur.getArguments()) {
var parsed = parseArgument(rootNode, nodeArg, argStrIter, sender, parseArgs);
if (parsed.hasFailure())
return parsed.getFailure().orElseThrow();
if (parseArgs)
parsedArguments.put(nodeArg.getId(), parsed.getSuccess().orElseThrow());
}
if (!argStrIter.hasNext()) {
if (cur.getExecutor() == null)
return new ParseResult.LiteralExpected<>(cur);
return new ParseResult.Success<>(cur, parsedArguments);
}
var argStr = argStrIter.next();
var nextNode = cur.getLiteral(argStr);
if (nextNode == null)
return new ParseResult.UnknownLiteral<>(cur, argStr);
if (nextNode.getPermission() != null && !hasPermissionMethod.hasPermission(sender, nextNode.getPermission()))
return new ParseResult.NoPermission<>(cur, argStr);
cur = nextNode;
}
}
@SuppressWarnings("unchecked")
private Result<ParsedArgument<T, Object>, ParseResult<T>> parseArgument(
CommandNode<T> node,
CommandArgument<T, ?> nodeArg,
Iterator<String> args,
T sender,
boolean parseArgs
) {
if (!args.hasNext()) {
if (nodeArg.getDefaultValueProvider() == null)
return Results.failure(new ParseResult.ArgumentExpected<>(node, nodeArg));
var value = nodeArg.getDefaultValueProvider().apply(sender);
return Results.success(toParsed(value, nodeArg));
}
Object value = null;
var argStr = getRawArgument(args, nodeArg);
if (parseArgs) {
try {
value = nodeArg.getType().parseInput(argStr);
} catch (IllegalArgumentException ex) {
return Results.failure(new ParseResult.IllegalArgument<>(node, nodeArg, ex.getMessage(), argStr));
}
}
var parsedArg = new ParsedArgumentImpl<>(value, (CommandArgument<T, Object>) nodeArg);
return Results.success(parsedArg);
}
@SuppressWarnings("unchecked")
private ParsedArgument<T, Object> toParsed(Object value, CommandArgument<T, ?> arg) {
return new ParsedArgumentImpl<>(value, (CommandArgument<T, Object>) arg);
}
private String getRawArgument(Iterator<String> args, CommandArgument<T, ?> arg) {
StringBuilder rawArgument = new StringBuilder();
if (arg.getType().isGreedy()) {
while (args.hasNext()) {
rawArgument.append(" ");
rawArgument.append(args.next());
}
rawArgument.deleteCharAt(0);
} else {
rawArgument = new StringBuilder(args.next());
}
return rawArgument.toString();
}
}

View file

@ -1,114 +0,0 @@
package de.kentoj.kencommandapi.api.parser;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import de.kentoj.kencommandapi.api.invocation.ParsedArgument;
import de.kentoj.kencommandapi.api.node.CommandNode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.util.Map;
public abstract sealed class ParseResult<T> permits ParseResult.IllegalArgument, ParseResult.ArgumentExpected,
ParseResult.LiteralExpected, ParseResult.NoPermission, ParseResult.Success, ParseResult.UnknownLiteral {
/**
* @return last successfully literal
*/
public abstract CommandNode<T> getLiteral();
public abstract String getMessage();
@RequiredArgsConstructor
public static final class ArgumentExpected<T> extends ParseResult<T> {
@Getter
private final CommandNode<T> literal;
@Getter
private final CommandArgument<T, ?> argument;
@Override
public String getMessage() {
return "Argument expected: " + argument.getId();
}
}
@RequiredArgsConstructor
public static final class UnknownLiteral<T> extends ParseResult<T> {
private final CommandNode<T> previousLiteral;
@Getter
private final String input;
@Override
public String getMessage() {
return "Unknown subcommand: " + input;
}
@Override
public CommandNode<T> getLiteral() {
return previousLiteral;
}
}
@RequiredArgsConstructor
public static final class LiteralExpected<T> extends ParseResult<T> {
@Getter
private final CommandNode<T> literal;
@Override
public String getMessage() {
return "Subcommand expected";
}
}
@RequiredArgsConstructor
public static final class Success<T> extends ParseResult<T> {
@Getter
private final CommandNode<T> literal;
@Getter
private final Map<String, ParsedArgument<T, ?>> parsedArguments;
@Override
public String getMessage() {
return "";
}
}
@RequiredArgsConstructor
public static final class NoPermission<T> extends ParseResult<T> {
@Getter
private final CommandNode<T> literal;
@Getter
private final String input;
@Override
public String getMessage() {
return "Unknown subcommand: " + input;
}
}
@RequiredArgsConstructor
public static final class IllegalArgument<T> extends ParseResult<T> {
@Getter
private final CommandNode<T> literal;
private final CommandArgument<T, ?> argument;
private final String message;
/**
* Input passed to the parser
*/
@Getter
private final String argStr;
public IllegalArgument(CommandNode<T> literal, CommandArgument<T, ?> argument, Throwable throwable, String argStr) {
this.literal = literal;
this.argument = argument;
this.argStr = argStr;
while (throwable.getMessage() == null && throwable.getCause() != null)
throwable = throwable.getCause();
message = throwable.getMessage();
}
@Override
public String getMessage() {
return "Illegal argument '" + argStr + "' for " + argument.getId() + ": " + message;
}
}
}

View file

@ -0,0 +1,39 @@
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<T> {
private final HasPermissionMethod<T> hasPermission;
protected boolean hasPermission(T sender, Literal<T> 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<Object, String> parseArgument(CommandArgument<T, Object> 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());
}
}
}

View file

@ -0,0 +1,74 @@
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<T> extends AbstractCommandParser<T> {
public InvocationCommandParser(HasPermissionMethod<T> hasPermission) {
super(hasPermission);
}
public Result<ExecutionData, String> invoke(
RootLiteral<T> rootLiteral,
T sender,
String[] args
) {
Map<String, ParsedArgument<T, ?>> parsedArguments = new HashMap<>();
Literal<T> currentLiteral = rootLiteral;
AtomicInteger index = new AtomicInteger(0);
while (true) {
for (var _argument : currentLiteral.getArguments()) {
@SuppressWarnings("unchecked")
var argument = ((CommandArgument<T, Object>) _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<T> executor;
Map<String, ParsedArgument<T, ?>> arguments;
}
}

View file

@ -0,0 +1,60 @@
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<T> extends AbstractCommandParser<T> {
public SuggestionCommandParser(HasPermissionMethod<T> hasPermission) {
super(hasPermission);
}
public List<String> getSuggestions(
RootLiteral<T> rootNode,
T sender,
String[] args
) {
Literal<T> 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<String> suggestions(Literal<T> literal, T sender) {
return literal.getLiterals().stream()
.filter(lit -> hasPermission(sender, lit))
.map(Literal::getNames)
.flatMap(Arrays::stream);
}
}

View file

@ -2,5 +2,5 @@ package de.kentoj.kencommandapi.api.platform;
@FunctionalInterface
public interface HasPermissionMethod<T> {
boolean hasPermission(T sender, String permission);
boolean test(T sender, String permission);
}

View file

@ -1,6 +1,6 @@
package de.kentoj.kencommandapi.internal;
import de.kentoj.kencommandapi.api.node.CommandNode;
import de.kentoj.kencommandapi.api.literal.Literal;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import de.kentoj.kencommandapi.api.invocation.CommandExecutor;
import lombok.Getter;
@ -13,12 +13,12 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class CommandNodeImpl<T> implements CommandNode<T> {
public class LiteralImpl<T> implements Literal<T> {
@Getter
private final String[] names;
@Getter
private final Set<CommandNode<T>> literals = new HashSet<>();
private final Set<Literal<T>> literals = new HashSet<>();
@Getter
private final List<CommandArgument<T, ?>> arguments = new ArrayList<>();
@ -34,22 +34,22 @@ public class CommandNodeImpl<T> implements CommandNode<T> {
@Setter
private @Nullable String permission = null;
public CommandNodeImpl(String[] names) {
public LiteralImpl(String[] names) {
if (names.length == 0) throw new IllegalArgumentException("at least one name is required");
this.names = names;
}
@Override
public void addLiteral(@NotNull CommandNode<T> commandNode) {
public void addLiteral(@NotNull Literal<T> literal) {
if (isGreedy()) throw new IllegalStateException("can't add literal to a node with a greedy argument");
for (String name : commandNode.getNames())
for (String name : literal.getNames())
if (getLiteral(name) != null)
throw new IllegalArgumentException("literal with same name already exists");
literals.add(commandNode);
literals.add(literal);
}
@Override
public CommandNode<T> getLiteral(@NotNull String name) {
public Literal<T> getLiteral(@NotNull String name) {
for (var literal : literals)
for (String literalName : literal.getNames())
if (literalName.equalsIgnoreCase(name)) return literal;

View file

@ -1,16 +1,16 @@
package de.kentoj.kencommandapi.internal;
import de.kentoj.kencommandapi.api.platform.MessageStyle;
import de.kentoj.kencommandapi.api.node.RootCommandNode;
import de.kentoj.kencommandapi.api.literal.RootLiteral;
import lombok.Getter;
import lombok.Setter;
public class RootCommandNodeImpl<T> extends CommandNodeImpl<T> implements RootCommandNode<T> {
public class RootLiteralImpl<T> extends LiteralImpl<T> implements RootLiteral<T> {
@Setter
@Getter
private MessageStyle messageStyle;
public RootCommandNodeImpl(MessageStyle messageStyle, String[] names) {
public RootLiteralImpl(MessageStyle messageStyle, String[] names) {
super(names);
this.messageStyle = messageStyle;
}

View file

@ -3,7 +3,7 @@ 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.node.CommandNode;
import de.kentoj.kencommandapi.api.literal.Literal;
import de.kentoj.kencommandapi.api.platform.MessageStyle;
import org.junit.jupiter.api.Test;
@ -14,19 +14,28 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
public class CommandHandlerTest {
@Test
void testSuggestions() {
var rootNode = CommandNode.rootNode(MessageStyle.PLAIN, "someRootNode");
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<>(
new CommandParser<>((__, ___) -> true),
(__, ___) -> {},
(__, ___) -> true
);
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());
}
}

View file

@ -1,43 +0,0 @@
package de.kentoj.kencommandapi.api.parser;
import com.leakyabstractions.result.core.Results;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import de.kentoj.kencommandapi.api.argument.types.StringArgumentType;
import de.kentoj.kencommandapi.api.invocation.CommandExecutor;
import de.kentoj.kencommandapi.api.invocation.ParsedArgument;
import de.kentoj.kencommandapi.api.node.CommandNode;
import de.kentoj.kencommandapi.api.node.RootCommandNode;
import de.kentoj.kencommandapi.api.platform.MessageStyle;
import org.junit.jupiter.api.Test;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
public class CommandParserTest {
@Test
void checkArgumentExpected() {
RootCommandNode<Object> node = CommandNode.rootNode(MessageStyle.PLAIN, "someRootNode");
var arg = CommandArgument.arg("someArg", new StringArgumentType<>(false));
node.addArgument(arg);
var parsed = ENV.PARSER.parseLiteral(node, ENV.SENDER, new String[]{}, false);
assertInstanceOf(ParseResult.ArgumentExpected.class, parsed);
}
@Test
void checkGreedyStrings() {
RootCommandNode<Object> node = CommandNode.rootNode(MessageStyle.PLAIN, "someRootNode");
var arg = CommandArgument.arg("someArg", new StringArgumentType<>(true));
node.addArgument(arg);
node.setExecutor(ENV.EMPTY_EXECUTOR);
var parsed = ENV.PARSER.parseLiteral(node, ENV.SENDER, new String[]{"part1", "part2"}, true);
Map<String, ParsedArgument<Object, ?>> parsedStr = ((ParseResult.Success<Object>) parsed).getParsedArguments();
String str = (String) parsedStr.get("someArg").getValue();
assertEquals("part1 part2", str);
}
}

View file

@ -1,12 +1,14 @@
package de.kentoj.kencommandapi.api.parser;
import com.leakyabstractions.result.core.Results;
import de.kentoj.kencommandapi.api.CommandHandler;
import de.kentoj.kencommandapi.api.invocation.CommandExecutor;
import java.util.concurrent.CompletableFuture;
public class ENV {
public static final CommandParser<Object> PARSER = new CommandParser<>((__, ___) -> true);
public static final CommandHandler<Object> PARSER =
new CommandHandler<>((__, ___) -> {}, (__, ___) -> true);
public static final Object SENDER = new Object();
public static final CommandExecutor<Object> EMPTY_EXECUTOR = __ ->
CompletableFuture.completedFuture(Results.success(new Object()));

View file

@ -3,8 +3,7 @@ package de.kentoj.kencommandapi;
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.ProxyServer;
import de.kentoj.kencommandapi.api.CommandHandler;
import de.kentoj.kencommandapi.api.node.RootCommandNode;
import de.kentoj.kencommandapi.api.parser.CommandParser;
import de.kentoj.kencommandapi.api.literal.RootLiteral;
import lombok.RequiredArgsConstructor;
import java.util.Arrays;
@ -13,13 +12,10 @@ import java.util.Arrays;
public class CommandAPI {
private final ProxyServer server;
private final CommandHandler<CommandSource> commandHandler = new CommandHandler<>(
new CommandParser<>(CommandSource::hasPermission),
CommandSource::sendMessage,
CommandSource::hasPermission
);
private final CommandHandler<CommandSource> commandHandler =
new CommandHandler<>(CommandSource::sendMessage, CommandSource::hasPermission);
public void register(RootCommandNode<CommandSource> rootNode) {
public void register(RootLiteral<CommandSource> rootNode) {
var cm = server.getCommandManager();
var meta = cm.metaBuilder(rootNode.getNames()[0])
.aliases(Arrays.copyOfRange(rootNode.getNames(), 1, rootNode.getNames().length))

View file

@ -3,7 +3,7 @@ package de.kentoj.kencommandapi;
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.command.SimpleCommand;
import de.kentoj.kencommandapi.api.CommandHandler;
import de.kentoj.kencommandapi.api.node.RootCommandNode;
import de.kentoj.kencommandapi.api.literal.RootLiteral;
import lombok.RequiredArgsConstructor;
import java.util.List;
@ -12,7 +12,7 @@ import java.util.List;
public class VelocityCommand implements SimpleCommand {
private final CommandHandler<CommandSource> commandHandler;
private final RootCommandNode<CommandSource> rootNode;
private final RootLiteral<CommandSource> rootNode;
@Override
public void execute(Invocation invocation) {