From 084e2c5c55bf16a821f9d4be3eb889fb1bf1e509 Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 23 Oct 2020 13:40:16 +0800 Subject: [PATCH] Fix command call --- .../console/command/CommandExecuteResult.kt | 14 ++--- .../mirai/console/command/CommandManager.kt | 4 +- .../mirai/console/command/SimpleCommand.kt | 12 +++-- .../internal/command/CommandManagerImpl.kt | 6 +-- .../command/CompositeCommandInternal.kt | 10 ++-- .../mirai/console/command/TestCommand.kt | 51 +++++++++++-------- 6 files changed, 56 insertions(+), 41 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandExecuteResult.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandExecuteResult.kt index 94b9b1f66..9697dd4d7 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandExecuteResult.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandExecuteResult.kt @@ -90,9 +90,9 @@ public sealed class CommandExecuteResult { } /** 没有匹配的指令 */ - public class CommandNotFound( + public class UnresolvedCall( /** 尝试执行的指令名 */ - public override val commandName: String + public override val commandName: String, ) : CommandExecuteResult() { /** 指令执行时发生的错误, 总是 `null` */ public override val exception: Nothing? get() = null @@ -196,19 +196,19 @@ public fun CommandExecuteResult.isPermissionDenied(): Boolean { } /** - * 当 [this] 为 [CommandExecuteResult.CommandNotFound] 时返回 `true` + * 当 [this] 为 [CommandExecuteResult.UnresolvedCall] 时返回 `true` */ @JvmSynthetic public fun CommandExecuteResult.isCommandNotFound(): Boolean { contract { - returns(true) implies (this@isCommandNotFound is CommandExecuteResult.CommandNotFound) - returns(false) implies (this@isCommandNotFound !is CommandExecuteResult.CommandNotFound) + returns(true) implies (this@isCommandNotFound is CommandExecuteResult.UnresolvedCall) + returns(false) implies (this@isCommandNotFound !is CommandExecuteResult.UnresolvedCall) } - return this is CommandExecuteResult.CommandNotFound + return this is CommandExecuteResult.UnresolvedCall } /** - * 当 [this] 为 [CommandExecuteResult.ExecutionFailed], [CommandExecuteResult.IllegalArgument] 或 [CommandExecuteResult.CommandNotFound] 时返回 `true` + * 当 [this] 为 [CommandExecuteResult.ExecutionFailed], [CommandExecuteResult.IllegalArgument] 或 [CommandExecuteResult.UnresolvedCall] 时返回 `true` */ @JvmSynthetic public fun CommandExecuteResult.isFailure(): Boolean { diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandManager.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandManager.kt index 73970d2d6..f5d0ad07d 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandManager.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandManager.kt @@ -228,8 +228,8 @@ internal suspend fun executeCommandImpl( caller: CommandSender, checkPermission: Boolean, ): CommandExecuteResult = with(receiver) { - val call = message.asMessageChain().parseCommandCall(caller) ?: return CommandExecuteResult.CommandNotFound("") - val resolved = call.resolve() ?: return CommandExecuteResult.CommandNotFound(call.calleeName) + val call = message.asMessageChain().parseCommandCall(caller) ?: return CommandExecuteResult.UnresolvedCall("") + val resolved = call.resolve() ?: return CommandExecuteResult.UnresolvedCall(call.calleeName) val command = resolved.callee diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/SimpleCommand.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/SimpleCommand.kt index 60e67ca32..9288f3430 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/SimpleCommand.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/SimpleCommand.kt @@ -62,12 +62,16 @@ public abstract class SimpleCommand( CommandArgumentContextAware { @ExperimentalCommandDescriptors - override val overloads: List = listOf( - CommandSignatureVariantImpl(listOf(CommandValueParameter.UserDefinedType.createRequired("args", true))) { call -> + override val overloads: List by lazy { + CommandSignatureVariantImpl( + valueParameters = subCommands.single().params.map { + CommandValueParameter.UserDefinedType(it.name, null, isOptional = false, isVararg = false, type = it.type) + } + ) { call -> val sender = call.caller subCommands.single().onCommand(sender, call.resolvedValueArguments) - } - ) + }.let { listOf(it) } + } /** * 自动根据带有 [Handler] 注解的函数签名生成 [usage]. 也可以被覆盖. diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CommandManagerImpl.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CommandManagerImpl.kt index 11b55aeb1..dcffd9aaa 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CommandManagerImpl.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CommandManagerImpl.kt @@ -81,7 +81,7 @@ internal object CommandManagerImpl : CommandManager, CoroutineScope by MiraiCons sender.catchExecutionException(result.exception) intercept() } - is CommandExecuteResult.CommandNotFound -> { + is CommandExecuteResult.UnresolvedCall -> { // noop } } @@ -140,11 +140,11 @@ internal object CommandManagerImpl : CommandManager, CoroutineScope by MiraiCons override fun Command.unregister(): Boolean = modifyLock.withLock { if (this.prefixOptional) { this.allNames.forEach { - optionalPrefixCommandMap.remove(it) + optionalPrefixCommandMap.remove(it.toLowerCase()) } } this.allNames.forEach { - requiredPrefixCommandMap.remove(it) + requiredPrefixCommandMap.remove(it.toLowerCase()) } _registeredCommands.remove(this) } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CompositeCommandInternal.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CompositeCommandInternal.kt index ede2cef02..a401e3c19 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CompositeCommandInternal.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/command/CompositeCommandInternal.kt @@ -13,6 +13,7 @@ package net.mamoe.mirai.console.internal.command import net.mamoe.mirai.console.command.* import net.mamoe.mirai.console.command.descriptor.* +import net.mamoe.mirai.console.internal.command.hasAnnotation import net.mamoe.mirai.console.permission.Permission import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.PlainText @@ -22,10 +23,7 @@ import kotlin.reflect.KAnnotatedElement import kotlin.reflect.KClass import kotlin.reflect.KFunction import kotlin.reflect.KParameter -import kotlin.reflect.full.callSuspendBy -import kotlin.reflect.full.declaredFunctions -import kotlin.reflect.full.findAnnotation -import kotlin.reflect.full.isSubclassOf +import kotlin.reflect.full.* internal object CompositeCommandSubCommandAnnotationResolver : AbstractReflectionCommand.SubCommandAnnotationResolver { @@ -322,7 +320,9 @@ internal fun AbstractReflectionCommand.createSubCommand( subDescription, // overridePermission?.value permission,//overridePermission?.value?.let { PermissionService.INSTANCE[PermissionId.parseFromString(it)] } ?: permission, onCommand = { _: CommandSender, args -> - val result = function.callSuspendBy(parameters.zip(args).toMap()) + val p = parameters.zip(args).toMap(LinkedHashMap()) + if (notStatic) p[function.instanceParameter!!] = this@createSubCommand + val result = function.callSuspendBy(p) checkNotNull(result) { "sub command return value is null (at ${this::class.qualifiedName}.${function.name})" } diff --git a/backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/command/TestCommand.kt b/backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/command/TestCommand.kt index 76c680bbf..4e11719bc 100644 --- a/backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/command/TestCommand.kt +++ b/backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/command/TestCommand.kt @@ -73,6 +73,7 @@ internal class TestCommand { fun testRegister() { try { ConsoleCommandOwner.unregisterAllCommands() // builtins + TestSimpleCommand.unregister() assertTrue(TestCompositeCommand.register()) assertFalse(TestCompositeCommand.register()) @@ -80,7 +81,9 @@ internal class TestCommand { assertEquals(1, ConsoleCommandOwner.registeredCommands.size) assertEquals(1, CommandManagerImpl._registeredCommands.size) - assertEquals(2, CommandManagerImpl.requiredPrefixCommandMap.size) + assertEquals(2, + CommandManagerImpl.requiredPrefixCommandMap.size, + CommandManagerImpl.requiredPrefixCommandMap.entries.joinToString { it.toString() }) } finally { TestCompositeCommand.unregister() } @@ -107,24 +110,28 @@ internal class TestCommand { @Test fun testSimpleArgsSplitting() = runBlocking { - assertEquals(arrayOf("test", "ttt", "tt").joinToString(), withTesting { - assertSuccess(TestSimpleCommand.execute(sender, PlainText("test ttt tt"))) - }.joinToString()) + TestSimpleCommand.withRegistration { + assertEquals(arrayOf("test", "ttt", "tt").joinToString(), withTesting { + assertSuccess(TestSimpleCommand.execute(sender, PlainText("test ttt tt"))) + }.joinToString()) + } } val image = Image("/f8f1ab55-bf8e-4236-b55e-955848d7069f") @Test fun `PlainText and Image args splitting`() = runBlocking { - val result = withTesting { - assertSuccess(TestSimpleCommand.execute(sender, buildMessageChain { - +"test" - +image - +"tt" - })) + TestSimpleCommand.withRegistration { + val result = withTesting { + assertSuccess(TestSimpleCommand.execute(sender, buildMessageChain { + +"test" + +image + +"tt" + })) + } + assertEquals(arrayOf("test", image, "tt").joinToString(), result.toTypedArray().joinToString()) + assertSame(image, result[1]) } - assertEquals(arrayOf("test", image, "tt").joinToString(), result.toTypedArray().joinToString()) - assertSame(image, result[1]) } @Test @@ -147,9 +154,11 @@ internal class TestCommand { @Test fun `composite command executing`() = runBlocking { - assertEquals(1, withTesting { - assertSuccess(TestCompositeCommand.execute(sender, "mute 1")) - }) + TestCompositeCommand.withRegistration { + assertEquals(1, withTesting { + assertSuccess(TestCompositeCommand.execute(sender, "mute 1")) + }) + } } @Test @@ -187,7 +196,7 @@ internal class TestCommand { fun `composite sub command parsing`() { runBlocking { class MyClass( - val value: Int + val value: Int, ) val composite = object : CompositeCommand( @@ -215,10 +224,12 @@ internal class TestCommand { composite.withRegistration { assertEquals(333, withTesting { execute(sender, "mute 333") }.value) assertEquals(2, withTesting { - execute(sender, buildMessageChain { - +"mute" - +image - }) + assertSuccess( + execute(sender, buildMessageChain { + +"mute" + +image + }) + ) }.value) } }