Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import dev.jorel.commandapi.arguments.AbstractArgument;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
Expand Down Expand Up @@ -60,6 +61,41 @@ public Impl then(final AbstractArgumentTree<?, Argument, CommandSender> tree) {
return instance();
}

/**
* Creates a chain of child branches starting at this node
* <p>
* {@code thenNested(a, b, c)} is equivalent to {@link #then}{@code (a.then(b.then(c)))}.
*
* @param trees The child branches to add in a chain.
* @return this tree node
*/
public final Impl thenNested(List<AbstractArgumentTree<?, Argument, CommandSender>> trees) {
int length = trees.size();
if (length == 0) {
return instance();
}

AbstractArgumentTree<?, Argument, CommandSender> combined = trees.get(length - 1);
for (int i = length - 2; i >= 0; i--) {
combined = trees.get(i).then(combined);
}

return then(combined);
}

/**
* Creates a chain of child branches starting at this node
* <p>
* {@code thenNested(a, b, c)} is equivalent to {@link #then}{@code (a.then(b.then(c)))}.
*
* @param trees The child branches to add in a chain.
* @return this tree node
*/
@SafeVarargs
public final Impl thenNested(final AbstractArgumentTree<?, Argument, CommandSender>... trees) {
return thenNested(Arrays.asList(trees));
}

List<Execution<CommandSender, Argument>> getExecutions() {
List<Execution<CommandSender, Argument>> executions = new ArrayList<>();
// If this is executable, add its execution
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import dev.jorel.commandapi.arguments.AbstractArgument;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
Expand Down Expand Up @@ -43,6 +44,41 @@ public Impl then(final AbstractArgumentTree<?, Argument, CommandSender> tree) {
return instance();
}

/**
* Creates a chain of child branches starting at this node
* <p>
* {@code thenNested(a, b, c)} is equivalent to {@link #then}{@code (a.then(b.then(c)))}.
*
* @param trees The child branches to add in a chain.
* @return this tree node
*/
public final Impl thenNested(List<AbstractArgumentTree<?, Argument, CommandSender>> trees) {
int length = trees.size();
if (length == 0) {
return instance();
}

AbstractArgumentTree<?, Argument, CommandSender> combined = trees.get(length - 1);
for (int i = length - 2; i >= 0; i--) {
combined = trees.get(i).then(combined);
}

return then(combined);
}

/**
* Creates a chain of child branches starting at this node
* <p>
* {@code thenNested(a, b, c)} is equivalent to {@link #then}{@code (a.then(b.then(c)))}.
*
* @param trees The child branches to add in a chain.
* @return this tree node
*/
@SafeVarargs
public final Impl thenNested(final AbstractArgumentTree<?, Argument, CommandSender>... trees) {
return thenNested(Arrays.asList(trees));
}

/**
* Registers the command with a given namespace
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import org.bukkit.command.CommandSender
import org.bukkit.plugin.java.JavaPlugin
import java.util.function.Predicate

const val WILL_NOT_REGISTER = "will-not-reg"

inline fun commandTree(name: String, tree: CommandTree.() -> Unit = {}) = CommandTree(name).apply(tree).register()
inline fun commandTree(name: String, namespace: String, tree: CommandTree.() -> Unit = {}) = CommandTree(name).apply(tree).register(namespace)
inline fun commandTree(name: String, namespace: JavaPlugin, tree: CommandTree.() -> Unit = {}) = CommandTree(name).apply(tree).register(namespace)
Expand All @@ -17,6 +19,18 @@ inline fun CommandTree.argument(base: Argument<*>, block: Argument<*>.() -> Unit

inline fun CommandTree.optionalArgument(base: Argument<*>, optional: Boolean = false, block: Argument<*>.() -> Unit = {}): CommandTree = then(base.setOptional(true).setOptional(optional).apply(block))

inline fun CommandTree.nestedArguments(vararg arguments: Argument<*>,block: Argument<*>.() -> Unit = {}): CommandTree = thenNested(*arguments.also { it.last().apply(block) })
inline fun CommandTree.nested(block: CommandTree.() -> Unit): CommandTree {
val arguments = mutableListOf<AbstractArgumentTree<*, Argument<*>?, CommandSender?>?>()
object : CommandTree(WILL_NOT_REGISTER) {
override fun then(tree: AbstractArgumentTree<*, Argument<*>?, CommandSender?>?): CommandTree? {
arguments.add(tree)
return this
}
}.block()
return thenNested(arguments)
}

// Integer arguments
inline fun CommandTree.integerArgument(nodeName: String, min: Int = Int.MIN_VALUE, max: Int = Int.MAX_VALUE, optional: Boolean = false, block: Argument<*>.() -> Unit = {}): CommandTree = then(IntegerArgument(nodeName, min, max).setOptional(optional).apply(block))
inline fun CommandTree.integerRangeArgument(nodeName: String, optional: Boolean = false, block: Argument<*>.() -> Unit = {}): CommandTree = then(IntegerRangeArgument(nodeName).setOptional(optional).apply(block))
Expand Down Expand Up @@ -123,6 +137,18 @@ inline fun Argument<*>.argument(base: Argument<*>, optional: Boolean = false, bl

inline fun Argument<*>.optionalArgument(base: Argument<*>, optional: Boolean = false, block: Argument<*>.() -> Unit = {}): Argument<*> = then(base.setOptional(true).setOptional(optional).apply(block))

inline fun Argument<*>.nestedArguments(vararg arguments: Argument<*>, block: Argument<*>.() -> Unit = {}): Argument<*> = thenNested(*arguments.also { it.last().apply(block) })
inline fun Argument<*>.nested(block: Argument<*>.() -> Unit): Argument<*> {
val arguments = mutableListOf<AbstractArgumentTree<*, Argument<*>?, CommandSender?>?>()
object : LiteralArgument(WILL_NOT_REGISTER) {
override fun then(tree: AbstractArgumentTree<*, Argument<*>?, CommandSender?>?): Argument<String?>? {
arguments.add(tree)
return this
}
}.block()
return thenNested(arguments)
}

// Integer arguments
inline fun Argument<*>.integerArgument(nodeName: String, min: Int = Int.MIN_VALUE, max: Int = Int.MAX_VALUE, optional: Boolean = false, block: Argument<*>.() -> Unit = {}): Argument<*> = then(IntegerArgument(nodeName, min, max).setOptional(optional).apply(block))
inline fun Argument<*>.integerRangeArgument(nodeName: String, optional: Boolean = false, block: Argument<*>.() -> Unit = {}): Argument<*> = then(IntegerRangeArgument(nodeName).setOptional(optional).apply(block))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package dev.jorel.commandapi.test.dsltests

import be.seeseemelk.mockbukkit.entity.PlayerMock
import dev.jorel.commandapi.arguments.LiteralArgument
import dev.jorel.commandapi.kotlindsl.commandTree
import dev.jorel.commandapi.kotlindsl.literalArgument
import dev.jorel.commandapi.kotlindsl.nested
import dev.jorel.commandapi.kotlindsl.nestedArguments
import dev.jorel.commandapi.kotlindsl.playerExecutor
import dev.jorel.commandapi.test.Mut
import dev.jorel.commandapi.test.TestBase
Expand Down Expand Up @@ -95,4 +98,89 @@ class CommandTreeTests : TestBase() {
assertNoMoreResults(results)
}

@Test
fun testThenNestedHalfDSLVer() {
val results: Mut<String> = Mut.of()

commandTree("test") {
nestedArguments(
LiteralArgument("one"),
LiteralArgument("two"),
LiteralArgument("three")
) {
playerExecutor { player, _ ->
results.set("/test one two three")
}
nestedArguments(
LiteralArgument("four"),
LiteralArgument("five")
) {
playerExecutor { player, _ ->
results.set("/test one two three four five")
}
}
}
}

val player: PlayerMock = server.addPlayer()

// /test one two three
server.dispatchCommand(player, "test one two three")
assertEquals("/test one two three", results.get())

// /test one two three four five
server.dispatchCommand(player, "test one two three four five")
assertEquals("/test one two three four five", results.get())

// /test
assertCommandFailsWith(player, "test", "Unknown or incomplete command, see below for error at position 4: test<--[HERE]")

// /test three
assertCommandFailsWith(player, "test three", "Incorrect argument for command at position 5: test <--[HERE]")

assertNoMoreResults(results)
}

@Test
fun testThenNestedFullDSLVer() {
val results: Mut<String> = Mut.of()

commandTree("test") {
nested {
literalArgument("one")
literalArgument("two")
literalArgument("three") {
playerExecutor { player, _ ->
results.set("/test one two three")
}
nested {
literalArgument("four")
literalArgument("five") {
playerExecutor { player, _ ->
results.set("/test one two three four five")
}
}
}
}
}
}

val player: PlayerMock = server.addPlayer()

// /test one two three
server.dispatchCommand(player, "test one two three")
assertEquals("/test one two three", results.get())

// /test one two three four five
server.dispatchCommand(player, "test one two three four five")
assertEquals("/test one two three four five", results.get())

// /test
assertCommandFailsWith(player, "test", "Unknown or incomplete command, see below for error at position 4: test<--[HERE]")

// /test three
assertCommandFailsWith(player, "test three", "Incorrect argument for command at position 5: test <--[HERE]")

assertNoMoreResults(results)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -707,4 +707,45 @@ void testCommandTreeDuplicateNodeNameException() {
getDispatcherString()
);
}
}

@Test
void testCommandTreeThenNested() {
// Make sure dispatcher is cleared from any previous tests
CommandAPIHandler.getInstance().writeDispatcherToFile();

// Register a command using the new `thenNested` method
new CommandTree("test").thenNested(
new LiteralArgument("a"),
new LiteralArgument("b"),
new LiteralArgument("c")
.executesPlayer(P_EXEC)
).register();

// Command added to tree
assertEquals("""
{
"type": "root",
"children": {
"test": {
"type": "literal",
"children": {
"a": {
"type": "literal",
"children": {
"b": {
"type": "literal",
"children": {
"c": {
"type": "literal",
"executable": true
}
}
}
}
}
}
}
}
}""", getDispatcherString());
}
}
Loading
Loading