Fix CompositeCommand; Rename CommandParam to CommandParameter

This commit is contained in:
Him188 2020-08-20 14:26:44 +08:00
parent 2843f91d02
commit d240ba7b83
5 changed files with 46 additions and 28 deletions

View File

@ -146,6 +146,7 @@ public interface CommandManager {
override fun Command.findDuplicate(): Command? = CommandManagerImpl.run { findDuplicate() } override fun Command.findDuplicate(): Command? = CommandManagerImpl.run { findDuplicate() }
override fun Command.unregister(): Boolean = CommandManagerImpl.run { unregister() } override fun Command.unregister(): Boolean = CommandManagerImpl.run { unregister() }
override fun Command.isRegistered(): Boolean = CommandManagerImpl.run { isRegistered() } override fun Command.isRegistered(): Boolean = CommandManagerImpl.run { isRegistered() }
override val commandPrefix: String get() = CommandManagerImpl.commandPrefix
override suspend fun CommandSender.executeCommand(vararg messages: Any): Command? = override suspend fun CommandSender.executeCommand(vararg messages: Any): Command? =
CommandManagerImpl.run { executeCommand(*messages) } CommandManagerImpl.run { executeCommand(*messages) }
@ -196,4 +197,4 @@ public abstract class PluginCommandOwner(
/** /**
* 代表控制台所有者. 所有的 mirai-console 内建的指令都属于 [ConsoleCommandOwner]. * 代表控制台所有者. 所有的 mirai-console 内建的指令都属于 [ConsoleCommandOwner].
*/ */
public object ConsoleCommandOwner : CommandOwner() internal object ConsoleCommandOwner : CommandOwner()

View File

@ -15,9 +15,9 @@ import net.mamoe.mirai.console.command.CompositeCommand
import java.lang.reflect.Parameter import java.lang.reflect.Parameter
import kotlin.reflect.KClass import kotlin.reflect.KClass
internal fun Parameter.toCommandParam(): CommandParam<*> { internal fun Parameter.toCommandParam(): CommandParameter<*> {
val name = getAnnotation(CompositeCommand.Name::class.java) val name = getAnnotation(CompositeCommand.Name::class.java)
return CommandParam( return CommandParameter(
name?.value ?: this.name name?.value ?: this.name
?: throw IllegalArgumentException("Cannot construct CommandParam from a unnamed param"), ?: throw IllegalArgumentException("Cannot construct CommandParam from a unnamed param"),
this.type.kotlin this.type.kotlin
@ -28,7 +28,7 @@ internal fun Parameter.toCommandParam(): CommandParam<*> {
* 指令形式参数. * 指令形式参数.
* @see toCommandParam * @see toCommandParam
*/ */
internal data class CommandParam<T : Any>( internal data class CommandParameter<T : Any>(
/** /**
* 参数名. 不允许重复. * 参数名. 不允许重复.
*/ */

View File

@ -12,10 +12,9 @@
package net.mamoe.mirai.console.internal.command package net.mamoe.mirai.console.internal.command
import net.mamoe.mirai.console.command.* import net.mamoe.mirai.console.command.*
import net.mamoe.mirai.console.command.Command.Companion.primaryName
import net.mamoe.mirai.console.command.description.CommandArgumentContext import net.mamoe.mirai.console.command.description.CommandArgumentContext
import net.mamoe.mirai.console.command.description.CommandArgumentContextAware import net.mamoe.mirai.console.command.description.CommandArgumentContextAware
import net.mamoe.mirai.console.command.description.CommandParam import net.mamoe.mirai.console.command.description.CommandParameter
import net.mamoe.mirai.message.data.PlainText import net.mamoe.mirai.message.data.PlainText
import net.mamoe.mirai.message.data.SingleMessage import net.mamoe.mirai.message.data.SingleMessage
import kotlin.reflect.KAnnotatedElement import kotlin.reflect.KAnnotatedElement
@ -90,7 +89,7 @@ internal abstract class AbstractReflectionCommand @JvmOverloads constructor(
}.map { function -> }.map { function ->
createSubCommand(function, context) createSubCommand(function, context)
}.toTypedArray().also { }.toTypedArray().also {
_usage = it.firstOrNull()?.usage ?: description _usage = it.createUsage(this)
}.also { checkSubCommand(it) } }.also { checkSubCommand(it) }
} }
@ -112,16 +111,16 @@ internal abstract class AbstractReflectionCommand @JvmOverloads constructor(
val onCommand: suspend (sender: CommandSender, rawArgs: Array<out Any>) -> Unit val onCommand: suspend (sender: CommandSender, rawArgs: Array<out Any>) -> Unit
) )
internal class SubCommandDescriptor( internal inner class SubCommandDescriptor(
val names: Array<out String>, val names: Array<out String>,
val params: Array<CommandParam<*>>, val params: Array<CommandParameter<*>>,
val description: String, val description: String,
val permission: CommandPermission, val permission: CommandPermission,
val onCommand: suspend (sender: CommandSender, parsedArgs: Array<out Any>) -> Boolean, val onCommand: suspend (sender: CommandSender, parsedArgs: Array<out Any>) -> Boolean,
val context: CommandArgumentContext, val context: CommandArgumentContext
val usage: String
) { ) {
internal suspend inline fun parseAndExecute( val usage: String = createUsage(this@AbstractReflectionCommand)
internal suspend fun parseAndExecute(
sender: CommandSender, sender: CommandSender,
argsWithSubCommandNameNotRemoved: Array<out Any>, argsWithSubCommandNameNotRemoved: Array<out Any>,
removeSubName: Boolean removeSubName: Boolean
@ -159,7 +158,7 @@ internal abstract class AbstractReflectionCommand @JvmOverloads constructor(
* @param rawArgs 元素类型必须为 [SingleMessage] [String], 且已经经过扁平化处理. 否则抛出异常 [IllegalArgumentException] * @param rawArgs 元素类型必须为 [SingleMessage] [String], 且已经经过扁平化处理. 否则抛出异常 [IllegalArgumentException]
*/ */
internal fun matchSubCommand(rawArgs: Array<out Any>): SubCommandDescriptor? { internal fun matchSubCommand(rawArgs: Array<out Any>): SubCommandDescriptor? {
val maxCount = rawArgs.size - 1 val maxCount = rawArgs.size
var cur = 0 var cur = 0
bakedCommandNameToSubDescriptorArray.forEach { (name, descriptor) -> bakedCommandNameToSubDescriptorArray.forEach { (name, descriptor) ->
if (name.size != cur) { if (name.size != cur) {
@ -209,6 +208,29 @@ internal inline fun <T : Any> KClass<out T>.getInstance(): T {
internal val KClass<*>.qualifiedNameOrTip: String get() = this.qualifiedName ?: "<anonymous class>" internal val KClass<*>.qualifiedNameOrTip: String get() = this.qualifiedName ?: "<anonymous class>"
internal fun Array<AbstractReflectionCommand.SubCommandDescriptor>.createUsage(baseCommand: AbstractReflectionCommand): String =
buildString {
appendLine(baseCommand.description)
appendLine()
for (subCommandDescriptor in this@createUsage) {
appendLine(subCommandDescriptor.usage)
}
}.trimEnd()
internal fun AbstractReflectionCommand.SubCommandDescriptor.createUsage(baseCommand: AbstractReflectionCommand): String =
buildString {
if (!baseCommand.prefixOptional) {
append(CommandManager.commandPrefix)
}
append(names.first())
append(" ")
append(params.joinToString(" ") { "<${it.name}>" })
append(" ")
append(description)
appendLine()
}.trimEnd()
internal fun AbstractReflectionCommand.createSubCommand( internal fun AbstractReflectionCommand.createSubCommand(
function: KFunction<*>, function: KFunction<*>,
context: CommandArgumentContext context: CommandArgumentContext
@ -216,7 +238,7 @@ internal fun AbstractReflectionCommand.createSubCommand(
val notStatic = !function.hasAnnotation<JvmStatic>() val notStatic = !function.hasAnnotation<JvmStatic>()
val overridePermission = function.findAnnotation<CompositeCommand.Permission>()//optional val overridePermission = function.findAnnotation<CompositeCommand.Permission>()//optional
val subDescription = val subDescription =
function.findAnnotation<CompositeCommand.Description>()?.value ?: "<no description available>" function.findAnnotation<CompositeCommand.Description>()?.value ?: ""
fun KClass<*>.isValidReturnType(): Boolean { fun KClass<*>.isValidReturnType(): Boolean {
return when (this) { return when (this) {
@ -269,26 +291,20 @@ internal fun AbstractReflectionCommand.createSubCommand(
} }
} }
val buildUsage = StringBuilder(this.description).append(": \n")
//map parameter //map parameter
val params = parameters.map { param -> val params = parameters.map { param ->
buildUsage.append("/$primaryName ")
if (param.isOptional) error("optional parameters are not yet supported. (at ${this::class.qualifiedNameOrTip}.${function.name}.$param)") if (param.isOptional) error("optional parameters are not yet supported. (at ${this::class.qualifiedNameOrTip}.${function.name}.$param)")
val argName = param.findAnnotation<CompositeCommand.Name>()?.value ?: param.name ?: "unknown" val paramName = param.findAnnotation<CompositeCommand.Name>()?.value ?: param.name ?: "unknown"
buildUsage.append("<").append(argName).append("> ").append(" ") CommandParameter(
CommandParam( paramName,
argName,
(param.type.classifier as? KClass<*>) (param.type.classifier as? KClass<*>)
?: throw IllegalArgumentException("unsolved type reference from param " + param.name + ". (at ${this::class.qualifiedNameOrTip}.${function.name}.$param)") ?: throw IllegalArgumentException("unsolved type reference from param " + param.name + ". (at ${this::class.qualifiedNameOrTip}.${function.name}.$param)")
) )
}.toTypedArray() }.toTypedArray()
buildUsage.append(subDescription).append("\n") return SubCommandDescriptor(
return AbstractReflectionCommand.SubCommandDescriptor(
commandName, commandName,
params, params,
subDescription, subDescription,
@ -309,7 +325,6 @@ internal fun AbstractReflectionCommand.createSubCommand(
result as? Boolean ?: true // Unit, void is considered as true. result as? Boolean ?: true // Unit, void is considered as true.
}, },
context = context, context = context
usage = buildUsage.toString()
) )
} }

View File

@ -15,6 +15,7 @@ import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeout import kotlinx.coroutines.withTimeout
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
import net.mamoe.mirai.console.MiraiConsoleImplementation.Companion.start import net.mamoe.mirai.console.MiraiConsoleImplementation.Companion.start
import net.mamoe.mirai.console.command.CommandManager
import net.mamoe.mirai.console.command.ConsoleCommandSender import net.mamoe.mirai.console.command.ConsoleCommandSender
import net.mamoe.mirai.console.plugin.DeferredPluginLoader import net.mamoe.mirai.console.plugin.DeferredPluginLoader
import net.mamoe.mirai.console.plugin.PluginLoader import net.mamoe.mirai.console.plugin.PluginLoader
@ -54,6 +55,7 @@ fun initTestEnvironment() {
override val settingStorageForBuiltIns: SettingStorage get() = MemorySettingStorage() override val settingStorageForBuiltIns: SettingStorage get() = MemorySettingStorage()
override val coroutineContext: CoroutineContext = SupervisorJob() override val coroutineContext: CoroutineContext = SupervisorJob()
}.start() }.start()
CommandManager
} }
internal object Testing { internal object Testing {

View File

@ -27,8 +27,8 @@ import net.mamoe.mirai.console.command.description.CommandArgumentParser
import net.mamoe.mirai.console.initTestEnvironment import net.mamoe.mirai.console.initTestEnvironment
import net.mamoe.mirai.console.internal.command.flattenCommandComponents import net.mamoe.mirai.console.internal.command.flattenCommandComponents
import net.mamoe.mirai.message.data.Image import net.mamoe.mirai.message.data.Image
import net.mamoe.mirai.message.data.PlainText
import net.mamoe.mirai.message.data.SingleMessage import net.mamoe.mirai.message.data.SingleMessage
import net.mamoe.mirai.message.data.toMessage
import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
@ -107,7 +107,7 @@ internal class TestCommand {
@Test @Test
fun testSimpleArgsSplitting() = runBlocking { fun testSimpleArgsSplitting() = runBlocking {
assertEquals(arrayOf("test", "ttt", "tt").contentToString(), withTesting<Array<String>> { assertEquals(arrayOf("test", "ttt", "tt").contentToString(), withTesting<Array<String>> {
TestSimpleCommand.execute(sender, "test ttt tt".toMessage()) TestSimpleCommand.execute(sender, PlainText("test ttt tt"))
}.contentToString()) }.contentToString())
} }