From c9f3cd92dde86934411aa10bcef6976315efc058 Mon Sep 17 00:00:00 2001 From: "jiahua.liu" Date: Sat, 16 May 2020 18:15:37 +0800 Subject: [PATCH] change command structure --- .../mamoe/mirai/console/command/Command.kt | 64 ++++--- .../console/command/CommandDescriptor.kt | 176 ++---------------- .../mirai/console/command/CommandManager.kt | 57 ++++-- 3 files changed, 92 insertions(+), 205 deletions(-) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt index 13df88a9d..35a7391d3 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt @@ -20,33 +20,35 @@ internal const val FOR_BINARY_COMPATIBILITY = "for binary compatibility" /** * 指令 - * + * 通常情况下, 你的指令应继承 @see CompositeCommand/SimpleCommand * @see register 注册这个指令 */ interface Command { - val owner: CommandOwner val descriptor: CommandDescriptor - - /** - * 指令的默认执行方法 - * 当所有的 sub 方法均不满足时, 原始参数将送到此方法调用 - * 如果 arg 为 String, 他会被包装为 PlainText(AKA PlainMessage) - */ - @Permission(CommandPermission.Console::class) - suspend fun CommandSender.onDefault(args: List): Boolean - - /** - * 在更多的情况下, 你应当使用 @SubCommand 来注册一共 sub 指令 - */ - @Target(AnnotationTarget.FUNCTION) - annotation class SubCommand(val name:String) - /** * Permission of the command */ @Target(AnnotationTarget.FUNCTION) annotation class Permission(val permission:KClass<*>) + /** + * If a command is prefix optional + * e.g + * mute work as (/mute) if prefix optional or vise versa + */ + @Target(AnnotationTarget.CLASS) + annotation class PrefixOptional() +} + + +abstract class CompositeCommand(val name:String, val alias:Array = listOf()):Command{ + /** + * 你应当使用 @SubCommand 来注册 sub 指令 + */ + @Target(AnnotationTarget.FUNCTION) + annotation class SubCommand(val name:String) + + /** * Usage of the sub command * you should not include arg names, which will be insert automatically @@ -64,16 +66,30 @@ interface Command { annotation class Name(val name:String) - /** - * If a command is prefix optional - * e.g - * mute work as (/mute) if prefix optional or vise versa - */ - @Target(AnnotationTarget.CLASS) - annotation class PrefixOptional() + override val descriptor: CommandDescriptor by lazy { + CommandDescriptor() + } } +abstract class SimpleCommand(val name: String, val alias: Array = arrayOf() +):Command{ + override val descriptor: CommandDescriptor by lazy { + CommandDescriptor() + } + + /** + * 你必须实现onCommand方法 + */ +} + +abstract class RawCommand(name:String, alias: Array = arrayOf()):Command{ + override val descriptor: CommandDescriptor by lazy { + CommandDescriptor() + } + + +} /** * 解析完成的指令实际参数列表. 参数顺序与 [Command.descriptor] 的 [CommandDescriptor.params] 相同. */ diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandDescriptor.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandDescriptor.kt index 0a538400b..eced759c2 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandDescriptor.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/CommandDescriptor.kt @@ -12,29 +12,24 @@ import net.mamoe.mirai.message.data.SingleMessage * 这是指令系统较低级的 API. 大部分情况下请使用 [Command] */ class CommandDescriptor( - /** - * 子指令列表. 第一个元素为默认值. - */ + val names: Array = arrayOf(), + val subCommands: List, - /** - * 是否建议 console 将这个指令强制注册为需要带 [前缀][CommandPrefix] 的指令. - */ - val suggestForcePrefix: Boolean = true, + + val prefixOptional: Boolean = false, /** 覆盖内建的指令参数解析器环境. */ overrideContext: CommandParserContext = CommandParserContext.Empty ) { /** 子指令描述 */ inner class SubCommandDescriptor( - /** 子指令名, 如 "/mute group add" 中的 "group add". 当表示默认指令时为父指令名. */ - val subName: String, + /** 子指令名, 如 "/mute group add" 中的 "group add". 当表示默认指令时为父指令名. 包含别名*/ + val names: Array = arrayOf(), /** 用法说明 */ val usage: String, /** 指令参数列表, 有顺序. */ val params: List>, /** 指令说明 */ val description: String, - /** 指令别名 */ - val aliases: Array = arrayOf(), /** * 指令权限 * @see CommandPermission.or 要求其中一个权限 @@ -45,13 +40,14 @@ class CommandDescriptor( val onCommand: suspend (sender: CommandSender, args: CommandArgs) -> Boolean ) { init { - require(!(subName.startsWith(' ') || subName.endsWith(' '))) { "subName mustn't start or end with a caret" } - require(subName.isValidSubName()) { "subName mustn't contain any of $ILLEGAL_SUB_NAME_CHARS" } + names.forEach {subName -> + require(!(subName.startsWith(' ') || subName.endsWith(' '))) { "subName mustn't start or end with a caret" } + require(subName.isValidSubName()) { "subName mustn't contain any of $ILLEGAL_SUB_NAME_CHARS" } + } } @JvmField - internal val bakedSubNames: Array> = - listOf(subName, *aliases).map { it.bakeSubName() }.toTypedArray() + internal val bakedSubNames: Array> = names.map { it.bakeSubName() }.toTypedArray() internal inline val parent: CommandDescriptor get() = this@CommandDescriptor } @@ -82,156 +78,6 @@ fun CommandDescriptor.SubCommandDescriptor.checkArgs(args: CommandArgs) { } } - -/* -/** - * 构建一个 [CommandDescriptor] - */ -@Suppress("FunctionName") -inline fun CommandDescriptor( - /** - * 指令全名 - */ - vararg fullNameComponents: String, - block: CommandDescriptorBuilder.() -> Unit = {} -): CommandDescriptor = CommandDescriptorBuilder(*fullNameComponents).apply(block).build() - -class CommandDescriptorBuilder( - vararg fullName: String -) { - @PublishedApi - internal var fullName: Array = fullName.checkFullName("fullName") - - @PublishedApi - internal var context: CommandParserContext = CommandParserContext.Builtins - - @PublishedApi - internal var permission: CommandPermission = CommandPermission.Default - - @PublishedApi - internal var params: MutableList> = mutableListOf() - - @PublishedApi - internal var usage: String = "" - - @PublishedApi - internal var aliases: MutableList> = mutableListOf() - - @PublishedApi - internal var description: String = "" - - /** 增加指令参数解析器列表 */ - @JvmSynthetic - inline fun context(block: CommandParserContextBuilder.() -> Unit) { - this.context += CommandParserContext(block) - } - - /** 增加指令参数解析器列表 */ - @JvmSynthetic - inline fun context(context: CommandParserContext): CommandDescriptorBuilder = apply { - this.context += context - } - - /** 设置权限要求 */ - fun permission(permission: CommandPermission): CommandDescriptorBuilder = apply { - this.permission = permission - } - - /** 设置权限要求 */ - @JvmSynthetic - inline fun permission(crossinline block: CommandSender.() -> Boolean) { - this.permission = AnonymousCommandPermission(block) - } - - fun usage(message: String): CommandDescriptorBuilder = apply { - usage = message - } - - fun description(description: String): CommandDescriptorBuilder = apply { - this.description = description - } - - /** - * 添加一个别名 - * @param fullName 全名称. 见 [CommandDescriptor.fullName] - */ - fun alias(fullName: String): CommandDescriptorBuilder = apply { - this.aliases.add(fullName.checkFullName("fullName")) - } - - fun param(vararg params: CommandParam<*>): CommandDescriptorBuilder = apply { - this.params.addAll(params) - } - - @JvmSynthetic - fun param( - name: String?, - type: KClass, - overrideParser: CommandArgParser? = null - ): CommandDescriptorBuilder = apply { - this.params.add(CommandParam(name, type).apply { this._overrideParser = overrideParser }) - } - - fun param( - name: String?, - type: Class, - overrideParser: CommandArgParser? = null - ): CommandDescriptorBuilder = - param(name, type, overrideParser) - - inline fun param( - name: String? = null, - overrideParser: CommandArgParser? = null - ): CommandDescriptorBuilder = - param(name, T::class, overrideParser) - - @JvmSynthetic - fun param(vararg pairs: Pair>): CommandDescriptorBuilder = apply { - for (pair in pairs) { - this.params.add(CommandParam(pair.first, pair.second)) - } - } - - @JvmSynthetic - fun params(block: ParamBlock.() -> Unit): CommandDescriptorBuilder = apply { - ParamBlock(params).apply(block) - } - - @JvmSynthetic - fun param(vararg types: KClass<*>): CommandDescriptorBuilder = apply { - types.forEach { type -> params.add(CommandParam(null, type)) } - } - - fun param(vararg types: Class<*>): CommandDescriptorBuilder = apply { - types.forEach { type -> params.add(CommandParam(null, type.kotlin)) } - } - - fun build(): CommandDescriptor = - CommandDescriptor(fullName, usage, params, description, context, aliases.toTypedArray(), permission) -} - - -@Suppress("NON_PUBLIC_PRIMARY_CONSTRUCTOR_OF_INLINE_CLASS") -inline class ParamBlock internal constructor(@PublishedApi internal val list: MutableList>) { - /** 添加一个名称为 [this], 类型为 [klass] 的参数. 返回添加成功的对象 */ - infix fun String.typed(klass: KClass): CommandParam = - CommandParam(this, klass).also { list.add(it) } - - /** 指定 [CommandParam.overrideParser] */ - infix fun CommandParam.using(parser: CommandArgParser): CommandParam = - this.apply { this._overrideParser = parser } - - /** 覆盖 [CommandArgParser] */ - inline infix fun String.using(parser: CommandArgParser): CommandParam = - this typed T::class using parser -} - - */ - -/////// -/// internal - - internal fun Any.flattenCommandComponents(): List { val list = ArrayList() when (this) { 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 5437efa82..2f1debb7c 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 @@ -9,6 +9,7 @@ import net.mamoe.mirai.message.data.Message import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.utils.MiraiExperimentalAPI import java.util.concurrent.locks.ReentrantLock +import kotlin.reflect.KClass sealed class CommandOwner @@ -20,8 +21,7 @@ internal abstract class ConsoleCommandOwner : CommandOwner() val CommandOwner.registeredCommands: List get() = InternalCommandManager.registeredCommands.filter { it.owner == this } @get:JvmName("getCommandPrefix") -val CommandPrefix: String - get() = InternalCommandManager._commandPrefix +val CommandPrefix: String get() = InternalCommandManager.COMMAND_PREFIX fun CommandOwner.unregisterAllCommands() { for (registeredCommand in registeredCommands) { @@ -108,13 +108,6 @@ internal suspend fun List.executeCommand(origin: String, sender: CommandSen if (this.isEmpty()) return false val command = InternalCommandManager.matchCommand(origin) ?: return false TODO() - /* - command.descriptor.subCommands.forEach { sub -> - }.run { - sender.onDefault( - CommandArgs.parseFrom(command, sender, this@executeCommand.drop(command.fullName.size)) - ) - }*/ } internal infix fun Array.matchesBeginning(list: List): Boolean { @@ -125,19 +118,51 @@ internal infix fun Array.matchesBeginning(list: List): Boolean { } internal object InternalCommandManager { + const val COMMAND_PREFIX = "/" + + /** + * 全部注册的指令 + * /mute -> MuteCommand + * /jinyan -> MuteCommand + */ @JvmField - internal val registeredCommands: MutableList = mutableListOf() + internal val registeredCommands: MutableMap = mutableMapOf() + /** + * Command name of commands that are prefix optional + * mute -> MuteCommand + */ + private val quickMatchCommands: MutableMap = mutableMapOf() @JvmField internal val modifyLock = ReentrantLock() - internal var _commandPrefix: String = "/" - internal fun matchCommand(name: String): Command? { - return registeredCommands.firstOrNull { command -> - command.descriptor.base.bakedSubNames.any { - name.startsWith(it[0]) && (name.length <= it[0].length || name[it[0].length] == ' ') // 判断跟随空格 - } + /** + * 从原始的command中解析出Command对象 + */ + internal fun matchCommand(rawCommand: String): Command? { + if(!rawCommand.startsWith('/')){ + return quickMatchCommands[rawCommand + .substringBefore(' ') + .trim() + ] } + return registeredCommands[rawCommand + .substringBefore(' ') + .trim() + ] } + + /** + * 从解析好的第一个字节来获取Command对象 + */ + internal fun findCommand(name: String): Command? { + if(!name.startsWith('/')){ + return quickMatchCommands[name] + } + return registeredCommands[name] + } + + + } \ No newline at end of file