mirror of
https://github.com/mamoe/mirai.git
synced 2025-04-24 20:43:33 +08:00
update about SubCommandGroup
This commit is contained in:
parent
7ace541597
commit
8b3085125b
mirai-console/backend/mirai-console
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)*/
|
||||
}
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
||||
}
|
@ -67,56 +67,75 @@ internal fun Any.flattenCommandComponents(): MessageChain = buildMessageChain {
|
||||
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
|
||||
internal object CompositeCommandSubCommandAnnotationResolver :
|
||||
SubCommandAnnotationResolver<Command> {
|
||||
override fun hasAnnotation(ownerCommand: Command, function: KFunction<*>) =
|
||||
function.hasAnnotation<CompositeCommand.SubCommand>()
|
||||
override fun isDeclaredSubCommand(ownerCommand: Command, function: KFunction<*>) =
|
||||
function.hasAnnotation<SubCommandGroup.SubCommand>()
|
||||
|
||||
override fun getSubCommandNames(ownerCommand: Command, function: KFunction<*>): Array<out String> {
|
||||
val annotated = function.findAnnotation<CompositeCommand.SubCommand>()!!.value
|
||||
override fun getDeclaredSubCommandNames(ownerCommand: Command, function: KFunction<*>): Array<out String> {
|
||||
val annotated = function.findAnnotation<SubCommandGroup.SubCommand>()!!.value
|
||||
return if (annotated.isEmpty()) arrayOf(function.name)
|
||||
else annotated
|
||||
}
|
||||
|
||||
override fun getAnnotatedName(ownerCommand: Command, parameter: KParameter): String? =
|
||||
parameter.findAnnotation<CompositeCommand.Name>()?.value
|
||||
parameter.findAnnotation<SubCommandGroup.Name>()?.value
|
||||
|
||||
override fun getDescription(ownerCommand: Command, function: KFunction<*>): String? =
|
||||
function.findAnnotation<CompositeCommand.Description>()?.value
|
||||
function.findAnnotation<SubCommandGroup.Description>()?.value
|
||||
|
||||
override fun isCombinedCommand(command: Command, kProperty: KProperty<*>): Boolean =
|
||||
kProperty.hasAnnotation<CompositeCommand.CombinedCommand>()
|
||||
override fun isCombinedSubCommands(command: Command, kProperty: KProperty<*>): Boolean =
|
||||
kProperty.hasAnnotation<SubCommandGroup.FlattenSubCommands>() || kProperty.hasAnnotation<SubCommandGroup.SubCommand>()
|
||||
|
||||
override fun getCombinedAdditionNames(command: Command, kProperty: KProperty<*>): Array<out String> {
|
||||
return if (kProperty.hasAnnotation<SubCommandGroup.FlattenSubCommands>()) {
|
||||
emptyArray()
|
||||
} else {
|
||||
val annotated = kProperty.findAnnotation<SubCommandGroup.SubCommand>()!!.value
|
||||
if (annotated.isEmpty()) arrayOf(kProperty.name)
|
||||
else annotated
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
|
||||
internal object GroupedCommandSubCommandAnnotationResolver :
|
||||
SubCommandAnnotationResolver<Any> {
|
||||
override fun hasAnnotation(ownerCommand: Any, function: KFunction<*>) =
|
||||
function.hasAnnotation<AbstractSubCommandGroup.AnotherSubCommand>()
|
||||
override fun isDeclaredSubCommand(ownerCommand: Any, function: KFunction<*>) =
|
||||
function.hasAnnotation<SubCommandGroup.SubCommand>()
|
||||
|
||||
override fun getSubCommandNames(ownerCommand: Any, function: KFunction<*>): Array<out String> {
|
||||
val annotated = function.findAnnotation<AbstractSubCommandGroup.AnotherSubCommand>()!!.value
|
||||
override fun getDeclaredSubCommandNames(ownerCommand: Any, function: KFunction<*>): Array<out String> {
|
||||
val annotated = function.findAnnotation<SubCommandGroup.SubCommand>()!!.value
|
||||
return if (annotated.isEmpty()) arrayOf(function.name)
|
||||
else annotated
|
||||
}
|
||||
|
||||
override fun getAnnotatedName(ownerCommand: Any, parameter: KParameter): String? =
|
||||
parameter.findAnnotation<AbstractSubCommandGroup.AnotherName>()?.value
|
||||
parameter.findAnnotation<SubCommandGroup.Name>()?.value
|
||||
|
||||
override fun getDescription(ownerCommand: Any, function: KFunction<*>): String? =
|
||||
function.findAnnotation<AbstractSubCommandGroup.AnotherDescription>()?.value
|
||||
function.findAnnotation<SubCommandGroup.Description>()?.value
|
||||
|
||||
override fun isCombinedCommand(command: Any, kProperty: KProperty<*>): Boolean =
|
||||
kProperty.hasAnnotation<AbstractSubCommandGroup.AnotherCombinedCommand>()
|
||||
override fun isCombinedSubCommands(command: Any, kProperty: KProperty<*>): Boolean =
|
||||
kProperty.hasAnnotation<SubCommandGroup.FlattenSubCommands>() || kProperty.hasAnnotation<SubCommandGroup.SubCommand>()
|
||||
|
||||
override fun getCombinedAdditionNames(command: Any, kProperty: KProperty<*>): Array<out String> {
|
||||
return if (kProperty.hasAnnotation<SubCommandGroup.FlattenSubCommands>()) {
|
||||
emptyArray()
|
||||
} else {
|
||||
val annotated = kProperty.findAnnotation<SubCommandGroup.SubCommand>()!!.value
|
||||
if (annotated.isEmpty()) arrayOf(kProperty.name)
|
||||
else annotated
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
|
||||
internal object SimpleCommandSubCommandAnnotationResolver :
|
||||
SubCommandAnnotationResolver<Command> {
|
||||
override fun hasAnnotation(ownerCommand: Command, function: KFunction<*>) =
|
||||
override fun isDeclaredSubCommand(ownerCommand: Command, function: KFunction<*>) =
|
||||
function.hasAnnotation<SimpleCommand.Handler>()
|
||||
|
||||
override fun getSubCommandNames(ownerCommand: Command, function: KFunction<*>): Array<out String> =
|
||||
override fun getDeclaredSubCommandNames(ownerCommand: Command, function: KFunction<*>): Array<out String> =
|
||||
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<out String> =
|
||||
emptyArray()
|
||||
|
||||
}
|
||||
|
||||
internal interface SubCommandAnnotationResolver<T> {
|
||||
fun hasAnnotation(ownerCommand: T, function: KFunction<*>): Boolean
|
||||
fun getSubCommandNames(ownerCommand: T, function: KFunction<*>): Array<out String>
|
||||
fun isDeclaredSubCommand(ownerCommand: T, function: KFunction<*>): Boolean
|
||||
fun getDeclaredSubCommandNames(ownerCommand: T, function: KFunction<*>): Array<out String>
|
||||
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<out String>
|
||||
}
|
||||
|
||||
@ConsoleExperimentalApi
|
||||
@ -232,8 +256,9 @@ internal class SubCommandReflector<T: Any>(
|
||||
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<out String> = annotationResolver.getCombinedAdditionNames(owner, this)
|
||||
private fun KFunction<*>.checkExtensionReceiver() {
|
||||
this.extensionReceiverParameter?.let { receiver ->
|
||||
val classifier = receiver.type.classifierAsKClassOrNull()
|
||||
@ -246,7 +271,7 @@ internal class SubCommandReflector<T: Any>(
|
||||
}
|
||||
|
||||
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<T: Any>(
|
||||
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<KParameter, Any?>()
|
||||
|
||||
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<CommandSignatureFromKFunction> {
|
||||
val fromMemberFunctions = owner::class.functions // exclude static later
|
||||
@ -313,91 +424,32 @@ internal class SubCommandReflector<T: Any>(
|
||||
.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<KParameter, Any?>()
|
||||
|
||||
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<CommandSignatureFromKFunction> = ArrayList()
|
||||
list.addAll(fromMemberFunctions)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@AnotherCombinedCommand
|
||||
val provider: SubCommandGroup = NestGroup()
|
||||
|
||||
@AnotherSubCommand
|
||||
fun foo1(seconds: Int) {
|
||||
Testing.ok(seconds)
|
||||
// 插件一个模块的部分功能
|
||||
class ModuleAPart1 : AbstractSubCommandGroup() {
|
||||
@SubCommand
|
||||
fun function1(arg0: Int) {
|
||||
Testing.ok(arg0)
|
||||
}
|
||||
}
|
||||
|
||||
@CombinedCommand
|
||||
val provider: SubCommandGroup = TopGroup()
|
||||
|
||||
@SubCommand
|
||||
fun foo0(seconds: Int) {
|
||||
Testing.ok(seconds)
|
||||
// 插件一个模块的另一部分功能
|
||||
class ModuleAPart2 : AbstractSubCommandGroup() {
|
||||
@SubCommand
|
||||
fun function2(arg0: Int) {
|
||||
Testing.ok(arg0)
|
||||
}
|
||||
}
|
||||
|
||||
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(2, withTesting {
|
||||
assertSuccess(containerCompositeCommand.execute(sender, "foo2 2"))
|
||||
assertEquals(0, withTesting {
|
||||
assertSuccess(unifiedCompositeCommand.execute(sender, "function2 0"))
|
||||
})
|
||||
assertEquals(0, withTesting {
|
||||
assertSuccess(unifiedCompositeCommand.execute(sender, "about 0"))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user