diff --git a/mirai-console/backend/mirai-console/src/command/AbstractSubCommandGroup.kt b/mirai-console/backend/mirai-console/src/command/AbstractSubCommandGroup.kt index a1d791eddb7..5e51cb95e96 100644 --- a/mirai-console/backend/mirai-console/src/command/AbstractSubCommandGroup.kt +++ b/mirai-console/backend/mirai-console/src/command/AbstractSubCommandGroup.kt @@ -10,15 +10,8 @@ package net.mamoe.mirai.console.command import net.mamoe.mirai.console.command.descriptor.* -import net.mamoe.mirai.console.compiler.common.ResolveContext -import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.RESTRICTED_CONSOLE_COMMAND_OWNER import net.mamoe.mirai.console.internal.command.GroupedCommandSubCommandAnnotationResolver import net.mamoe.mirai.console.internal.command.SubCommandReflector -import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.COMMAND_NAME -import net.mamoe.mirai.console.util.ConsoleExperimentalApi -import kotlin.annotation.AnnotationRetention.RUNTIME -import kotlin.annotation.AnnotationTarget.FUNCTION -import kotlin.annotation.AnnotationTarget.PROPERTY public abstract class AbstractSubCommandGroup( overrideContext: CommandArgumentContext = EmptyCommandArgumentContext, @@ -33,35 +26,6 @@ public abstract class AbstractSubCommandGroup( } } - /** - * 标记一个属性为子指令集合 - */ - @Retention(RUNTIME) - @Target(PROPERTY) - protected annotation class AnotherCombinedCommand( - ) - - /** - * 标记一个函数为子指令, 当 [value] 为空时使用函数名. - * @param value 子指令名 - */ - @Retention(RUNTIME) - @Target(FUNCTION) - protected annotation class AnotherSubCommand( - @ResolveContext(COMMAND_NAME) vararg val value: String = [], - ) - - /** 指令描述 */ - @Retention(RUNTIME) - @Target(FUNCTION) - protected annotation class AnotherDescription(val value: String) - - /** 参数名, 由具体Command决定用途 */ - @ConsoleExperimentalApi("Classname might change") - @Retention(RUNTIME) - @Target(AnnotationTarget.VALUE_PARAMETER) - protected annotation class AnotherName(val value: String) - /** * 智能参数解析环境 */ // open since 2.12 diff --git a/mirai-console/backend/mirai-console/src/command/BuiltInCommands.kt b/mirai-console/backend/mirai-console/src/command/BuiltInCommands.kt index c2a27bb9d8c..581186fc42f 100644 --- a/mirai-console/backend/mirai-console/src/command/BuiltInCommands.kt +++ b/mirai-console/backend/mirai-console/src/command/BuiltInCommands.kt @@ -23,6 +23,9 @@ import net.mamoe.mirai.console.MiraiConsoleImplementation import net.mamoe.mirai.console.MiraiConsoleImplementation.ConsoleDataScope.Companion.get import net.mamoe.mirai.console.command.CommandManager.INSTANCE.allRegisteredCommands import net.mamoe.mirai.console.command.CommandManager.INSTANCE.register +import net.mamoe.mirai.console.command.SubCommandGroup.Description +import net.mamoe.mirai.console.command.SubCommandGroup.Name +import net.mamoe.mirai.console.command.SubCommandGroup.SubCommand import net.mamoe.mirai.console.command.descriptor.CommandArgumentParserException import net.mamoe.mirai.console.command.descriptor.CommandValueArgumentParser.Companion.map import net.mamoe.mirai.console.command.descriptor.PermissionIdValueArgumentParser diff --git a/mirai-console/backend/mirai-console/src/command/CompositeCommand.kt b/mirai-console/backend/mirai-console/src/command/CompositeCommand.kt index 5743d44e5e4..d619a17adfb 100644 --- a/mirai-console/backend/mirai-console/src/command/CompositeCommand.kt +++ b/mirai-console/backend/mirai-console/src/command/CompositeCommand.kt @@ -18,9 +18,9 @@ import net.mamoe.mirai.console.internal.command.CommandReflector import net.mamoe.mirai.console.internal.command.CompositeCommandSubCommandAnnotationResolver import net.mamoe.mirai.console.permission.Permission import net.mamoe.mirai.console.util.ConsoleExperimentalApi +import kotlin.DeprecationLevel.* import kotlin.annotation.AnnotationRetention.RUNTIME import kotlin.annotation.AnnotationTarget.FUNCTION -import kotlin.annotation.AnnotationTarget.PROPERTY /** * 复合指令. 指令注册时候会通过反射构造指令解析器. @@ -92,7 +92,7 @@ public abstract class CompositeCommand( parentPermission: Permission = owner.parentPermission, overrideContext: CommandArgumentContext = EmptyCommandArgumentContext, ) : Command, AbstractCommand(owner, primaryName, secondaryNames = secondaryNames, description, parentPermission), - CommandArgumentContextAware { + CommandArgumentContextAware, SubCommandGroup { private val reflector by lazy { CommandReflector(this, CompositeCommandSubCommandAnnotationResolver) } @@ -116,34 +116,30 @@ public abstract class CompositeCommand( */ // open since 2.12 public override val context: CommandArgumentContext = CommandArgumentContext.Builtins + overrideContext - /** - * 标记一个属性为子指令集合 - */ - @Retention(RUNTIME) - @Target(PROPERTY) - protected annotation class CombinedCommand( - ) - /** +/* *//** * 标记一个函数为子指令, 当 [value] 为空时使用函数名. * @param value 子指令名 - */ + *//* @Retention(RUNTIME) @Target(FUNCTION) + @Deprecated(level = HIDDEN, message = "use SubCommandGroup.SubCommand") protected annotation class SubCommand( @ResolveContext(COMMAND_NAME) vararg val value: String = [], ) - /** 指令描述 */ + *//** 指令描述 *//* @Retention(RUNTIME) @Target(FUNCTION) + @Deprecated(level = HIDDEN, message = "use SubCommandGroup.Description") protected annotation class Description(val value: String) - /** 参数名, 将参与构成 [usage] */ + *//** 参数名, 将参与构成 [usage] *//* @ConsoleExperimentalApi("Classname might change") @Retention(RUNTIME) @Target(AnnotationTarget.VALUE_PARAMETER) - protected annotation class Name(val value: String) + @Deprecated(level = HIDDEN, message = "use SubCommandGroup.Name") + protected annotation class Name(val value: String)*/ } diff --git a/mirai-console/backend/mirai-console/src/command/SubCommandGroup.kt b/mirai-console/backend/mirai-console/src/command/SubCommandGroup.kt index 8b336e012b5..c84c754161c 100644 --- a/mirai-console/backend/mirai-console/src/command/SubCommandGroup.kt +++ b/mirai-console/backend/mirai-console/src/command/SubCommandGroup.kt @@ -2,6 +2,7 @@ package net.mamoe.mirai.console.command import net.mamoe.mirai.console.command.descriptor.CommandSignatureFromKFunction import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors +import net.mamoe.mirai.console.compiler.common.ResolveContext import net.mamoe.mirai.console.util.ConsoleExperimentalApi public interface SubCommandGroup { @@ -12,4 +13,34 @@ public interface SubCommandGroup { @ExperimentalCommandDescriptors public val overloads: List<@JvmWildcard CommandSignatureFromKFunction> + /** + * 标记一个属性为子指令集合,且使用flat策略 + */ + @Retention(AnnotationRetention.RUNTIME) + @Target(AnnotationTarget.PROPERTY) + public annotation class FlattenSubCommands( + ) + + /** + * 1. 标记一个函数为子指令, 当 [value] 为空时使用函数名. + * 2. 标记一个属性为子指令集合,且使用sub策略 + * @param value 子指令名 + */ + @Retention(AnnotationRetention.RUNTIME) + @Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY) + public annotation class SubCommand( + @ResolveContext(ResolveContext.Kind.COMMAND_NAME) vararg val value: String = [], + ) + + /** 指令描述 */ + @Retention(AnnotationRetention.RUNTIME) + @Target(AnnotationTarget.FUNCTION) + public annotation class Description(val value: String) + + /** 参数名, 由具体Command决定用途 */ + @ConsoleExperimentalApi("Classname might change") + @Retention(AnnotationRetention.RUNTIME) + @Target(AnnotationTarget.VALUE_PARAMETER) + public annotation class Name(val value: String) + } \ No newline at end of file diff --git a/mirai-console/backend/mirai-console/src/internal/command/CommandReflector.kt b/mirai-console/backend/mirai-console/src/internal/command/CommandReflector.kt index 6e77d693750..06503d25987 100644 --- a/mirai-console/backend/mirai-console/src/internal/command/CommandReflector.kt +++ b/mirai-console/backend/mirai-console/src/internal/command/CommandReflector.kt @@ -67,56 +67,75 @@ internal fun Any.flattenCommandComponents(): MessageChain = buildMessageChain { @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") internal object CompositeCommandSubCommandAnnotationResolver : SubCommandAnnotationResolver { - override fun hasAnnotation(ownerCommand: Command, function: KFunction<*>) = - function.hasAnnotation() + override fun isDeclaredSubCommand(ownerCommand: Command, function: KFunction<*>) = + function.hasAnnotation() - override fun getSubCommandNames(ownerCommand: Command, function: KFunction<*>): Array { - val annotated = function.findAnnotation()!!.value + override fun getDeclaredSubCommandNames(ownerCommand: Command, function: KFunction<*>): Array { + val annotated = function.findAnnotation()!!.value return if (annotated.isEmpty()) arrayOf(function.name) else annotated } override fun getAnnotatedName(ownerCommand: Command, parameter: KParameter): String? = - parameter.findAnnotation()?.value + parameter.findAnnotation()?.value override fun getDescription(ownerCommand: Command, function: KFunction<*>): String? = - function.findAnnotation()?.value - - override fun isCombinedCommand(command: Command, kProperty: KProperty<*>): Boolean = - kProperty.hasAnnotation() - + function.findAnnotation()?.value + + override fun isCombinedSubCommands(command: Command, kProperty: KProperty<*>): Boolean = + kProperty.hasAnnotation() || kProperty.hasAnnotation() + + override fun getCombinedAdditionNames(command: Command, kProperty: KProperty<*>): Array { + return if (kProperty.hasAnnotation()) { + emptyArray() + } else { + val annotated = kProperty.findAnnotation()!!.value + if (annotated.isEmpty()) arrayOf(kProperty.name) + else annotated + } + } } @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") internal object GroupedCommandSubCommandAnnotationResolver : SubCommandAnnotationResolver { - override fun hasAnnotation(ownerCommand: Any, function: KFunction<*>) = - function.hasAnnotation() + override fun isDeclaredSubCommand(ownerCommand: Any, function: KFunction<*>) = + function.hasAnnotation() - override fun getSubCommandNames(ownerCommand: Any, function: KFunction<*>): Array { - val annotated = function.findAnnotation()!!.value + override fun getDeclaredSubCommandNames(ownerCommand: Any, function: KFunction<*>): Array { + val annotated = function.findAnnotation()!!.value return if (annotated.isEmpty()) arrayOf(function.name) else annotated } override fun getAnnotatedName(ownerCommand: Any, parameter: KParameter): String? = - parameter.findAnnotation()?.value + parameter.findAnnotation()?.value override fun getDescription(ownerCommand: Any, function: KFunction<*>): String? = - function.findAnnotation()?.value - - override fun isCombinedCommand(command: Any, kProperty: KProperty<*>): Boolean = - kProperty.hasAnnotation() + function.findAnnotation()?.value + + override fun isCombinedSubCommands(command: Any, kProperty: KProperty<*>): Boolean = + kProperty.hasAnnotation() || kProperty.hasAnnotation() + + override fun getCombinedAdditionNames(command: Any, kProperty: KProperty<*>): Array { + return if (kProperty.hasAnnotation()) { + emptyArray() + } else { + val annotated = kProperty.findAnnotation()!!.value + if (annotated.isEmpty()) arrayOf(kProperty.name) + else annotated + } + } } @Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") internal object SimpleCommandSubCommandAnnotationResolver : SubCommandAnnotationResolver { - override fun hasAnnotation(ownerCommand: Command, function: KFunction<*>) = + override fun isDeclaredSubCommand(ownerCommand: Command, function: KFunction<*>) = function.hasAnnotation() - override fun getSubCommandNames(ownerCommand: Command, function: KFunction<*>): Array = + override fun getDeclaredSubCommandNames(ownerCommand: Command, function: KFunction<*>): Array = emptyArray() override fun getAnnotatedName(ownerCommand: Command, parameter: KParameter): String? = @@ -125,15 +144,20 @@ internal object SimpleCommandSubCommandAnnotationResolver : override fun getDescription(ownerCommand: Command, function: KFunction<*>): String = ownerCommand.description - override fun isCombinedCommand(command: Command, kProperty: KProperty<*>): Boolean = false + override fun isCombinedSubCommands(command: Command, kProperty: KProperty<*>): Boolean = false + + override fun getCombinedAdditionNames(command: Command, kProperty: KProperty<*>): Array = + emptyArray() + } internal interface SubCommandAnnotationResolver { - fun hasAnnotation(ownerCommand: T, function: KFunction<*>): Boolean - fun getSubCommandNames(ownerCommand: T, function: KFunction<*>): Array + fun isDeclaredSubCommand(ownerCommand: T, function: KFunction<*>): Boolean + fun getDeclaredSubCommandNames(ownerCommand: T, function: KFunction<*>): Array fun getAnnotatedName(ownerCommand: T, parameter: KParameter): String? fun getDescription(ownerCommand: T, function: KFunction<*>): String? - fun isCombinedCommand(command: T, kProperty: KProperty<*>): Boolean + fun isCombinedSubCommands(command: T, kProperty: KProperty<*>): Boolean + fun getCombinedAdditionNames(command: T, kProperty: KProperty<*>): Array } @ConsoleExperimentalApi @@ -232,8 +256,9 @@ internal class SubCommandReflector( throw IllegalCommandDeclarationException(owner, this, message) } - private fun KProperty<*>.isSubCommandProviderProperty(): Boolean = annotationResolver.isCombinedCommand(owner, this) - private fun KFunction<*>.isSubCommandFunction(): Boolean = annotationResolver.hasAnnotation(owner, this) + private fun KProperty<*>.isSubCommandProviderProperty(): Boolean = annotationResolver.isCombinedSubCommands(owner, this) + private fun KFunction<*>.isSubCommandFunction(): Boolean = annotationResolver.isDeclaredSubCommand(owner, this) + private fun KProperty<*>.getCombinedAdditionNames(): Array = annotationResolver.getCombinedAdditionNames(owner, this) private fun KFunction<*>.checkExtensionReceiver() { this.extensionReceiverParameter?.let { receiver -> val classifier = receiver.type.classifierAsKClassOrNull() @@ -246,7 +271,7 @@ internal class SubCommandReflector( } private fun KFunction<*>.checkNames() { - val names = annotationResolver.getSubCommandNames(owner, this) + val names = annotationResolver.getDeclaredSubCommandNames(owner, this) for (name in names) { ILLEGAL_SUB_NAME_CHARS.find { it in name }?.let { illegalDeclaration("'$it' is forbidden in command name.") @@ -304,6 +329,92 @@ internal class SubCommandReflector( throw SubcommandDeclarationClashException(owner, clashes.value.map { it.first }) } + private fun generateCommandSignatureFromKFunctionImplWithAdditionName(name: String?, origin: CommandSignatureFromKFunction): CommandSignatureFromKFunctionImpl { + val functionNameAsValueParameter = + name?.split(' ')?.mapIndexed { index, s -> createStringConstantParameterForName(index, s) } + .orEmpty() + + return CommandSignatureFromKFunctionImpl( + receiverParameter = origin.receiverParameter, + valueParameters = functionNameAsValueParameter + origin.valueParameters, + originFunction = origin.originFunction + ) { call -> + origin.call(call) + } + } + + private fun generateCommandSignatureFromKFunctionImplWithAdditionName(name: String?, function: KFunction<*>): CommandSignatureFromKFunctionImpl { + val functionNameAsValueParameter = + name?.split(' ')?.mapIndexed { index, s -> createStringConstantParameterForName(index, s) } + .orEmpty() + + val valueParameters = function.valueParameters.toMutableList() + var receiverParameter = function.extensionReceiverParameter + if (receiverParameter == null && valueParameters.isNotEmpty()) { + val valueFirstParameter = valueParameters[0] + val classifier = valueFirstParameter.type.classifierAsKClassOrNull() + if (classifier != null && isAcceptableReceiverType(classifier) + ) { + receiverParameter = valueFirstParameter + valueParameters.removeAt(0) + } + } + + val functionValueParameters = + valueParameters.associateBy { it.toUserDefinedCommandParameter() } + + return CommandSignatureFromKFunctionImpl( + receiverParameter = receiverParameter?.toCommandReceiverParameter(), + valueParameters = functionNameAsValueParameter + functionValueParameters.keys, + originFunction = function + ) { call -> + val args = LinkedHashMap() + + for ((commandParameter, value) in call.resolvedValueArguments) { + if (commandParameter is AbstractCommandValueParameter.StringConstant) { + continue + } + val functionParameter = + functionValueParameters[commandParameter] + ?: error("Could not find a corresponding function parameter '${commandParameter.name}'") + args[functionParameter] = value + } + + val instanceParameter = function.instanceParameter + if (instanceParameter != null) { + check(instanceParameter.type.classifierAsKClass().isInstance(owner)) { + "Bad command call resolved. " + + "Function expects instance parameter ${instanceParameter.type} whereas actual instance is ${owner::class}." + } + args[instanceParameter] = owner + } + + if (receiverParameter != null) { + + val receiverType = receiverParameter.type.classifierAsKClass() + + if (receiverType.isSubclassOf(CommandContext::class)) { + args[receiverParameter] = CommandContextImpl(call.caller, call.originalMessage) + } else { + check(receiverType.isInstance(call.caller)) { + "Bad command call resolved. " + + "Function expects receiver parameter ${receiverParameter.type} whereas actual is ${call.caller::class}." + } + args[receiverParameter] = call.caller + } + + } + + // mirai-console#341 + if (function.isSuspend) { + function.callSuspendBy(args) + } else { + runBIO { function.callBy(args) } + } + } + } + + @Throws(IllegalCommandDeclarationException::class) override fun findSubCommands(): List { val fromMemberFunctions = owner::class.functions // exclude static later @@ -313,91 +424,32 @@ internal class SubCommandReflector( .onEach { it.checkModifiers() } .onEach { it.checkNames() } .flatMap { function -> - val names = annotationResolver.getSubCommandNames(owner, function) + val names = annotationResolver.getDeclaredSubCommandNames(owner, function) if (names.isEmpty()) sequenceOf(createMapEntry(null, function)) else names.associateWith { function }.asSequence() } .map { (name, function) -> - - val functionNameAsValueParameter = - name?.split(' ')?.mapIndexed { index, s -> createStringConstantParameterForName(index, s) } - .orEmpty() - - val valueParameters = function.valueParameters.toMutableList() - var receiverParameter = function.extensionReceiverParameter - if (receiverParameter == null && valueParameters.isNotEmpty()) { - val valueFirstParameter = valueParameters[0] - val classifier = valueFirstParameter.type.classifierAsKClassOrNull() - if (classifier != null && isAcceptableReceiverType(classifier) - ) { - receiverParameter = valueFirstParameter - valueParameters.removeAt(0) - } - } - - val functionValueParameters = - valueParameters.associateBy { it.toUserDefinedCommandParameter() } - - CommandSignatureFromKFunctionImpl( - receiverParameter = receiverParameter?.toCommandReceiverParameter(), - valueParameters = functionNameAsValueParameter + functionValueParameters.keys, - originFunction = function - ) { call -> - val args = LinkedHashMap() - - for ((commandParameter, value) in call.resolvedValueArguments) { - if (commandParameter is AbstractCommandValueParameter.StringConstant) { - continue - } - val functionParameter = - functionValueParameters[commandParameter] - ?: error("Could not find a corresponding function parameter '${commandParameter.name}'") - args[functionParameter] = value - } - - val instanceParameter = function.instanceParameter - if (instanceParameter != null) { - check(instanceParameter.type.classifierAsKClass().isInstance(owner)) { - "Bad command call resolved. " + - "Function expects instance parameter ${instanceParameter.type} whereas actual instance is ${owner::class}." - } - args[instanceParameter] = owner - } - - if (receiverParameter != null) { - - val receiverType = receiverParameter.type.classifierAsKClass() - - if (receiverType.isSubclassOf(CommandContext::class)) { - args[receiverParameter] = CommandContextImpl(call.caller, call.originalMessage) - } else { - check(receiverType.isInstance(call.caller)) { - "Bad command call resolved. " + - "Function expects receiver parameter ${receiverParameter.type} whereas actual is ${call.caller::class}." - } - args[receiverParameter] = call.caller - } - - } - - // mirai-console#341 - if (function.isSuspend) { - function.callSuspendBy(args) - } else { - runBIO { function.callBy(args) } - } - } + generateCommandSignatureFromKFunctionImplWithAdditionName(name, function) }.toList() val fromMemberProperties = owner::class.declaredMemberProperties .asSequence() .filter { it.isSubCommandProviderProperty() } - .map { it.getter.call(owner) } - .filter { it is SubCommandGroup } - .flatMap { property -> - property as SubCommandGroup - property.overloads - }.toList() + .filter { it.getter.call(owner) is SubCommandGroup } + .flatMap { + val names = it.getCombinedAdditionNames() + val originOverloads = (it.getter.call(owner) as SubCommandGroup).overloads + if (names.isEmpty()) sequenceOf(createMapEntry(null, originOverloads)) + else names.associateWith { originOverloads }.asSequence() + } + .map { (name, originOverloads) -> + originOverloads + .map { originOverload -> + generateCommandSignatureFromKFunctionImplWithAdditionName(name, originOverload) + } + } + .flatten() + .toList() val list: MutableList = ArrayList() list.addAll(fromMemberFunctions) diff --git a/mirai-console/backend/mirai-console/test/command/CommandContextTest.kt b/mirai-console/backend/mirai-console/test/command/CommandContextTest.kt index 1a59e80b605..761c1137d23 100644 --- a/mirai-console/backend/mirai-console/test/command/CommandContextTest.kt +++ b/mirai-console/backend/mirai-console/test/command/CommandContextTest.kt @@ -18,6 +18,7 @@ import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors import net.mamoe.mirai.console.command.java.JCompositeCommand import net.mamoe.mirai.console.command.java.JRawCommand import net.mamoe.mirai.console.command.java.JSimpleCommand +import net.mamoe.mirai.console.command.SubCommandGroup.SubCommand import net.mamoe.mirai.console.internal.data.classifierAsKClass import net.mamoe.mirai.message.data.* import net.mamoe.mirai.utils.safeCast diff --git a/mirai-console/backend/mirai-console/test/command/CommandValueArgumentContextTest.kt b/mirai-console/backend/mirai-console/test/command/CommandValueArgumentContextTest.kt index c67ed826422..3b163904dbf 100644 --- a/mirai-console/backend/mirai-console/test/command/CommandValueArgumentContextTest.kt +++ b/mirai-console/backend/mirai-console/test/command/CommandValueArgumentContextTest.kt @@ -19,6 +19,7 @@ import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors import net.mamoe.mirai.console.command.descriptor.buildCommandArgumentContext import net.mamoe.mirai.console.command.java.JCompositeCommand import net.mamoe.mirai.console.command.java.JSimpleCommand +import net.mamoe.mirai.console.command.SubCommandGroup.SubCommand import net.mamoe.mirai.console.internal.data.classifierAsKClass import net.mamoe.mirai.message.data.Image import net.mamoe.mirai.message.data.MessageContent diff --git a/mirai-console/backend/mirai-console/test/command/InstanceTestCommand.kt b/mirai-console/backend/mirai-console/test/command/InstanceTestCommand.kt index 9a0e56622df..31d1f91e533 100644 --- a/mirai-console/backend/mirai-console/test/command/InstanceTestCommand.kt +++ b/mirai-console/backend/mirai-console/test/command/InstanceTestCommand.kt @@ -24,6 +24,7 @@ import net.mamoe.mirai.console.command.CommandManager.INSTANCE.unregisterCommand import net.mamoe.mirai.console.command.descriptor.CommandValueArgumentParser import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors import net.mamoe.mirai.console.command.descriptor.buildCommandArgumentContext + import net.mamoe.mirai.console.internal.command.CommandManagerImpl import net.mamoe.mirai.console.internal.command.flattenCommandComponents import net.mamoe.mirai.console.permission.PermissionService.Companion.permit @@ -34,43 +35,43 @@ import java.time.temporal.TemporalAccessor import kotlin.reflect.KClass import kotlin.test.* +import net.mamoe.mirai.console.command.SubCommandGroup.SubCommand +import net.mamoe.mirai.console.command.SubCommandGroup.FlattenSubCommands - - -class TestContainerCompositeCommand : CompositeCommand( - owner, - "testContainerComposite", "tsPC" +class MyUnifiedCommand : CompositeCommand( + owner, "testMyUnifiedCommand", "tsMUC" ) { - - class TopGroup : AbstractSubCommandGroup() { - - class NestGroup : AbstractSubCommandGroup() { - @AnotherSubCommand - fun foo2(seconds: Int) { - Testing.ok(seconds) - } + // 插件一个模块的部分功能 + class ModuleAPart1 : AbstractSubCommandGroup() { + @SubCommand + fun function1(arg0: Int) { + Testing.ok(arg0) } + } - @AnotherCombinedCommand - val provider: SubCommandGroup = NestGroup() - - @AnotherSubCommand - fun foo1(seconds: Int) { - Testing.ok(seconds) + // 插件一个模块的另一部分功能 + class ModuleAPart2 : AbstractSubCommandGroup() { + @SubCommand + fun function2(arg0: Int) { + Testing.ok(arg0) } } - @CombinedCommand - val provider: SubCommandGroup = TopGroup() - - @SubCommand - fun foo0(seconds: Int) { - Testing.ok(seconds) + class ModuleACommandGroup: AbstractSubCommandGroup() { + @SubCommand // 与在函数上标注这个注解类似, 它会带 `part1` 这个名称前缀来注册指令. 需要执行 /base part1 function1 + val part1 = ModuleAPart1() + @SubCommand("part1NewName") // 也可以使用 SubCommand 的参数来覆盖名称 /base part1NewName function1 + val part1b = ModuleAPart1() + @FlattenSubCommands // 新增, 不带前缀注册指令, 执行 /base function2 + val part2 = ModuleAPart2() } + @FlattenSubCommands + val moduleA = ModuleACommandGroup() + @SubCommand - fun containerBar(seconds: Int) { - Testing.ok(seconds) + fun about(arg0: Int) { + Testing.ok(arg0) } } @@ -203,7 +204,7 @@ internal class InstanceTestCommand : AbstractConsoleInstanceTest() { private val simpleCommand by lazy { TestSimpleCommand() } private val rawCommand by lazy { TestRawCommand() } private val compositeCommand by lazy { TestCompositeCommand() } - private val containerCompositeCommand by lazy { TestContainerCompositeCommand() } + private val unifiedCompositeCommand by lazy { MyUnifiedCommand() } @BeforeTest fun grantPermission() { @@ -542,15 +543,18 @@ internal class InstanceTestCommand : AbstractConsoleInstanceTest() { @Test fun `container composite command executing`() = runBlocking { - containerCompositeCommand.withRegistration { + unifiedCompositeCommand.withRegistration { assertEquals(0, withTesting { - assertSuccess(containerCompositeCommand.execute(sender, "foo0 0")) + assertSuccess(unifiedCompositeCommand.execute(sender, "part1 function1 0")) }) - assertEquals(1, withTesting { - assertSuccess(containerCompositeCommand.execute(sender, "foo1 1")) + assertEquals(0, withTesting { + assertSuccess(unifiedCompositeCommand.execute(sender, "part1NewName function1 0")) + }) + assertEquals(0, withTesting { + assertSuccess(unifiedCompositeCommand.execute(sender, "function2 0")) }) - assertEquals(2, withTesting { - assertSuccess(containerCompositeCommand.execute(sender, "foo2 2")) + assertEquals(0, withTesting { + assertSuccess(unifiedCompositeCommand.execute(sender, "about 0")) }) } }