mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-25 15:40:28 +08:00
Rework command reflection:
- Remove AbstractReflectionCommand - Introduce CommandReflector - Misc improvements
This commit is contained in:
parent
8b75e47f58
commit
4aa996a417
@ -26,8 +26,8 @@ import net.mamoe.mirai.console.command.CommandSender.Companion.asTempCommandSend
|
||||
import net.mamoe.mirai.console.command.CommandSender.Companion.toCommandSender
|
||||
import net.mamoe.mirai.console.command.descriptor.CommandArgumentParserException
|
||||
import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge
|
||||
import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip
|
||||
import net.mamoe.mirai.console.internal.data.castOrNull
|
||||
import net.mamoe.mirai.console.internal.data.qualifiedNameOrTip
|
||||
import net.mamoe.mirai.console.internal.plugin.rootCauseOrSelf
|
||||
import net.mamoe.mirai.console.permission.AbstractPermitteeId
|
||||
import net.mamoe.mirai.console.permission.Permittee
|
||||
|
@ -20,11 +20,10 @@ 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.COMMAND_NAME
|
||||
import net.mamoe.mirai.console.internal.command.AbstractReflectionCommand
|
||||
import net.mamoe.mirai.console.internal.command.CompositeCommandSubCommandAnnotationResolver
|
||||
import net.mamoe.mirai.console.internal.command.CommandReflector
|
||||
import net.mamoe.mirai.console.internal.command.SimpleCommandSubCommandAnnotationResolver
|
||||
import net.mamoe.mirai.console.permission.Permission
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
|
||||
import net.mamoe.mirai.message.data.MessageChain
|
||||
import kotlin.annotation.AnnotationRetention.RUNTIME
|
||||
import kotlin.annotation.AnnotationTarget.FUNCTION
|
||||
|
||||
@ -90,13 +89,23 @@ public abstract class CompositeCommand(
|
||||
parentPermission: Permission = owner.parentPermission,
|
||||
prefixOptional: Boolean = false,
|
||||
overrideContext: CommandArgumentContext = EmptyCommandArgumentContext,
|
||||
) : Command, AbstractReflectionCommand(owner, primaryName, secondaryNames = secondaryNames, description, parentPermission, prefixOptional),
|
||||
) : Command, AbstractCommand(owner, primaryName, secondaryNames = secondaryNames, description, parentPermission, prefixOptional),
|
||||
CommandArgumentContextAware {
|
||||
|
||||
private val reflector by lazy { CommandReflector(this, SimpleCommandSubCommandAnnotationResolver) }
|
||||
|
||||
@ExperimentalCommandDescriptors
|
||||
public final override val overloads: List<CommandSignatureVariantFromKFunction> by lazy {
|
||||
reflector.findSubCommands()
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动根据带有 [SubCommand] 注解的函数签名生成 [usage]. 也可以被覆盖.
|
||||
*/
|
||||
public override val usage: String get() = super.usage
|
||||
public override val usage: String by lazy {
|
||||
@OptIn(ExperimentalCommandDescriptors::class)
|
||||
reflector.generateUsage(overloads)
|
||||
}
|
||||
|
||||
/**
|
||||
* [CommandValueArgumentParser] 的环境
|
||||
@ -123,33 +132,6 @@ public abstract class CompositeCommand(
|
||||
@Retention(RUNTIME)
|
||||
@Target(AnnotationTarget.VALUE_PARAMETER)
|
||||
protected annotation class Name(val value: String)
|
||||
|
||||
@OptIn(ExperimentalCommandDescriptors::class)
|
||||
override val overloads: List<CommandSignatureVariant> by lazy {
|
||||
subCommands.flatMap { desc ->
|
||||
desc.bakedSubNames.map { names ->
|
||||
CommandSignatureVariantImpl(
|
||||
valueParameters =
|
||||
names.mapIndexed { index, s -> CommandValueParameter.StringConstant("p$index", s) } + desc.params.map {
|
||||
CommandValueParameter.UserDefinedType(it.name, null,
|
||||
isOptional = false,
|
||||
isVararg = false,
|
||||
type = it.type)
|
||||
},
|
||||
onCall = { resolvedCommandCall ->
|
||||
desc.onCommand(resolvedCommandCall.caller, resolvedCommandCall.resolvedValueArguments.drop(names.size))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override suspend fun CommandSender.onDefault(rawArgs: MessageChain) {
|
||||
sendMessage(usage)
|
||||
}
|
||||
|
||||
internal final override val subCommandAnnotationResolver: SubCommandAnnotationResolver
|
||||
get() = CompositeCommandSubCommandAnnotationResolver
|
||||
}
|
||||
|
||||
|
||||
|
@ -12,14 +12,12 @@
|
||||
package net.mamoe.mirai.console.command
|
||||
|
||||
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommand
|
||||
import net.mamoe.mirai.console.command.descriptor.CommandSignatureVariant
|
||||
import net.mamoe.mirai.console.command.descriptor.CommandSignatureVariantImpl
|
||||
import net.mamoe.mirai.console.command.descriptor.CommandValueParameter
|
||||
import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors
|
||||
import net.mamoe.mirai.console.command.descriptor.*
|
||||
import net.mamoe.mirai.console.command.java.JRawCommand
|
||||
import net.mamoe.mirai.console.compiler.common.ResolveContext
|
||||
import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.COMMAND_NAME
|
||||
import net.mamoe.mirai.console.internal.command.createOrFindCommandPermission
|
||||
import net.mamoe.mirai.console.internal.data.typeOf0
|
||||
import net.mamoe.mirai.console.permission.Permission
|
||||
import net.mamoe.mirai.message.data.MessageChain
|
||||
import net.mamoe.mirai.message.data.MessageChainBuilder
|
||||
@ -58,7 +56,10 @@ public abstract class RawCommand(
|
||||
|
||||
@ExperimentalCommandDescriptors
|
||||
override val overloads: List<CommandSignatureVariant> = listOf(
|
||||
CommandSignatureVariantImpl(listOf(CommandValueParameter.UserDefinedType.createRequired<MessageChain>("args", true))) { call ->
|
||||
CommandSignatureVariantImpl(
|
||||
receiverParameter = CommandReceiverParameter(false, typeOf0<CommandSender>()),
|
||||
valueParameters = listOf(CommandValueParameter.UserDefinedType.createRequired<MessageChain>("args", true))
|
||||
) { call ->
|
||||
val sender = call.caller
|
||||
val arguments = call.rawValueArguments
|
||||
sender.onCommand(arguments.mapTo(MessageChainBuilder()) { it.value }.build())
|
||||
|
@ -22,10 +22,13 @@ import net.mamoe.mirai.console.command.descriptor.*
|
||||
import net.mamoe.mirai.console.command.java.JSimpleCommand
|
||||
import net.mamoe.mirai.console.compiler.common.ResolveContext
|
||||
import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.COMMAND_NAME
|
||||
import net.mamoe.mirai.console.internal.command.AbstractReflectionCommand
|
||||
import net.mamoe.mirai.console.internal.command.CommandReflector
|
||||
import net.mamoe.mirai.console.internal.command.IllegalCommandDeclarationException
|
||||
import net.mamoe.mirai.console.internal.command.SimpleCommandSubCommandAnnotationResolver
|
||||
import net.mamoe.mirai.console.permission.Permission
|
||||
import net.mamoe.mirai.message.data.MessageChain
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
|
||||
import kotlin.annotation.AnnotationTarget.FUNCTION
|
||||
import kotlin.annotation.AnnotationTarget.VALUE_PARAMETER
|
||||
|
||||
/**
|
||||
* 简单的, 支持参数自动解析的指令.
|
||||
@ -58,47 +61,41 @@ public abstract class SimpleCommand(
|
||||
parentPermission: Permission = owner.parentPermission,
|
||||
prefixOptional: Boolean = false,
|
||||
overrideContext: CommandArgumentContext = EmptyCommandArgumentContext,
|
||||
) : Command, AbstractReflectionCommand(owner, primaryName, secondaryNames = secondaryNames, description, parentPermission, prefixOptional),
|
||||
) : Command, AbstractCommand(owner, primaryName, secondaryNames = secondaryNames, description, parentPermission, prefixOptional),
|
||||
CommandArgumentContextAware {
|
||||
|
||||
private val reflector by lazy { CommandReflector(this, SimpleCommandSubCommandAnnotationResolver) }
|
||||
|
||||
@ExperimentalCommandDescriptors
|
||||
override val overloads: List<CommandSignatureVariant> 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) }
|
||||
public final override val overloads: List<CommandSignatureVariantFromKFunction> by lazy {
|
||||
reflector.findSubCommands().also {
|
||||
if (it.isEmpty())
|
||||
throw IllegalCommandDeclarationException(this, "SimpleCommand must have at least one subcommand, whereas zero present.")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动根据带有 [Handler] 注解的函数签名生成 [usage]. 也可以被覆盖.
|
||||
*/
|
||||
public override val usage: String get() = super.usage
|
||||
public override val usage: String by lazy {
|
||||
@OptIn(ExperimentalCommandDescriptors::class)
|
||||
reflector.generateUsage(overloads)
|
||||
}
|
||||
|
||||
/**
|
||||
* 标注指令处理器
|
||||
*/
|
||||
@Target(FUNCTION)
|
||||
protected annotation class Handler
|
||||
|
||||
/** 参数名, 将参与构成 [usage] */
|
||||
@ConsoleExperimentalApi("Classname might change")
|
||||
@Target(VALUE_PARAMETER)
|
||||
protected annotation class Name(val value: String)
|
||||
|
||||
/**
|
||||
* 指令参数环境. 默认为 [CommandArgumentContext.Builtins] `+` `overrideContext`
|
||||
*/
|
||||
public override val context: CommandArgumentContext = CommandArgumentContext.Builtins + overrideContext
|
||||
|
||||
internal override fun checkSubCommand(subCommands: Array<SubCommandDescriptor>) {
|
||||
super.checkSubCommand(subCommands)
|
||||
check(subCommands.size == 1) { "There can only be exactly one function annotated with Handler at this moment as overloading is not yet supported." }
|
||||
}
|
||||
|
||||
@Deprecated("prohibited", level = DeprecationLevel.HIDDEN)
|
||||
internal override suspend fun CommandSender.onDefault(rawArgs: MessageChain) {
|
||||
sendMessage(usage)
|
||||
}
|
||||
|
||||
internal final override val subCommandAnnotationResolver: SubCommandAnnotationResolver
|
||||
get() = SimpleCommandSubCommandAnnotationResolver
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
package net.mamoe.mirai.console.command.descriptor
|
||||
|
||||
import net.mamoe.mirai.console.command.CommandSender
|
||||
import net.mamoe.mirai.console.command.descriptor.ArgumentAcceptance.Companion.isAcceptable
|
||||
import net.mamoe.mirai.console.command.descriptor.CommandValueParameter.UserDefinedType.Companion.createOptional
|
||||
import net.mamoe.mirai.console.command.descriptor.CommandValueParameter.UserDefinedType.Companion.createRequired
|
||||
@ -18,6 +19,7 @@ import net.mamoe.mirai.console.internal.data.classifierAsKClassOrNull
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
|
||||
import net.mamoe.mirai.console.util.safeCast
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KFunction
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.full.isSubtypeOf
|
||||
import kotlin.reflect.typeOf
|
||||
@ -27,13 +29,23 @@ import kotlin.reflect.typeOf
|
||||
*/
|
||||
@ExperimentalCommandDescriptors
|
||||
public interface CommandSignatureVariant {
|
||||
@ConsoleExperimentalApi
|
||||
public val receiverParameter: CommandReceiverParameter<out CommandSender>?
|
||||
|
||||
public val valueParameters: List<CommandValueParameter<*>>
|
||||
|
||||
public suspend fun call(resolvedCommandCall: ResolvedCommandCall)
|
||||
}
|
||||
|
||||
@ConsoleExperimentalApi
|
||||
@ExperimentalCommandDescriptors
|
||||
public class CommandSignatureVariantImpl(
|
||||
public interface CommandSignatureVariantFromKFunction : CommandSignatureVariant {
|
||||
public val originFunction: KFunction<*>
|
||||
}
|
||||
|
||||
@ExperimentalCommandDescriptors
|
||||
public open class CommandSignatureVariantImpl(
|
||||
override val receiverParameter: CommandReceiverParameter<out CommandSender>?,
|
||||
override val valueParameters: List<CommandValueParameter<*>>,
|
||||
private val onCall: suspend CommandSignatureVariantImpl.(resolvedCommandCall: ResolvedCommandCall) -> Unit,
|
||||
) : CommandSignatureVariant {
|
||||
@ -42,25 +54,46 @@ public class CommandSignatureVariantImpl(
|
||||
}
|
||||
}
|
||||
|
||||
@ConsoleExperimentalApi
|
||||
@ExperimentalCommandDescriptors
|
||||
public open class CommandSignatureVariantFromKFunctionImpl(
|
||||
override val receiverParameter: CommandReceiverParameter<out CommandSender>?,
|
||||
override val valueParameters: List<CommandValueParameter<*>>,
|
||||
override val originFunction: KFunction<*>,
|
||||
private val onCall: suspend CommandSignatureVariantFromKFunctionImpl.(resolvedCommandCall: ResolvedCommandCall) -> Unit,
|
||||
) : CommandSignatureVariantFromKFunction {
|
||||
override suspend fun call(resolvedCommandCall: ResolvedCommandCall) {
|
||||
return onCall(resolvedCommandCall)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inherited instances must be [CommandValueParameter]
|
||||
* Inherited instances must be [ICommandValueParameter] or [CommandReceiverParameter]
|
||||
*/
|
||||
@ExperimentalCommandDescriptors
|
||||
public interface ICommandParameter<T : Any?> {
|
||||
public val name: String
|
||||
public val name: String?
|
||||
|
||||
/**
|
||||
* If [isOptional] is `false`, [defaultValue] is always `null`.
|
||||
* Otherwise [defaultValue] may be `null` iff [T] is nullable.
|
||||
*/
|
||||
public val defaultValue: T?
|
||||
public val isOptional: Boolean
|
||||
|
||||
/**
|
||||
* Reified type of [T]
|
||||
*/
|
||||
public val type: KType
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherited instances must be [CommandValueParameter]
|
||||
*/
|
||||
@ExperimentalCommandDescriptors
|
||||
public interface ICommandValueParameter<T : Any?> : ICommandParameter<T> {
|
||||
|
||||
/**
|
||||
* If [isOptional] is `false`, [defaultValue] is always `null`.
|
||||
* Otherwise [defaultValue] may be `null` iff [T] is nullable.
|
||||
*/
|
||||
public val defaultValue: T?
|
||||
|
||||
public val isVararg: Boolean
|
||||
|
||||
@ -105,9 +138,21 @@ public sealed class ArgumentAcceptance(
|
||||
}
|
||||
}
|
||||
|
||||
@ExperimentalCommandDescriptors
|
||||
public class CommandReceiverParameter<T : CommandSender>(
|
||||
override val isOptional: Boolean,
|
||||
override val type: KType,
|
||||
) : ICommandParameter<T> {
|
||||
override val name: String get() = PARAMETER_NAME
|
||||
|
||||
public companion object {
|
||||
public const val PARAMETER_NAME: String = "<receiver>"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ExperimentalCommandDescriptors
|
||||
public sealed class CommandValueParameter<T> : ICommandParameter<T> {
|
||||
public sealed class CommandValueParameter<T> : ICommandValueParameter<T> {
|
||||
internal fun validate() { // // TODO: 2020/10/18 net.mamoe.mirai.console.command.descriptor.CommandValueParameter.validate$mirai_console_mirai_console_main
|
||||
require(type.classifier?.safeCast<KClass<*>>()?.isInstance(defaultValue) == true) {
|
||||
"defaultValue is not instance of type"
|
||||
@ -132,8 +177,10 @@ public sealed class CommandValueParameter<T> : ICommandParameter<T> {
|
||||
return ArgumentAcceptance.Impossible
|
||||
}
|
||||
|
||||
@ConsoleExperimentalApi
|
||||
public class StringConstant(
|
||||
public override val name: String,
|
||||
@ConsoleExperimentalApi
|
||||
public override val name: String?,
|
||||
public val expectingValue: String,
|
||||
) : CommandValueParameter<String>() {
|
||||
public override val type: KType get() = STRING_TYPE
|
||||
@ -152,7 +199,7 @@ public sealed class CommandValueParameter<T> : ICommandParameter<T> {
|
||||
* @see createRequired
|
||||
*/
|
||||
public class UserDefinedType<T>(
|
||||
public override val name: String,
|
||||
public override val name: String?,
|
||||
public override val defaultValue: T?,
|
||||
public override val isOptional: Boolean,
|
||||
public override val isVararg: Boolean,
|
||||
|
@ -13,8 +13,8 @@ package net.mamoe.mirai.console.command.descriptor
|
||||
|
||||
import net.mamoe.mirai.console.command.parse.CommandCall
|
||||
import net.mamoe.mirai.console.command.parse.CommandValueArgument
|
||||
import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip
|
||||
import net.mamoe.mirai.console.internal.data.classifierAsKClassOrNull
|
||||
import net.mamoe.mirai.console.internal.data.qualifiedNameOrTip
|
||||
import kotlin.reflect.KType
|
||||
|
||||
|
||||
|
@ -14,7 +14,7 @@ package net.mamoe.mirai.console.data
|
||||
import kotlinx.atomicfu.atomic
|
||||
import kotlinx.coroutines.*
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip
|
||||
import net.mamoe.mirai.console.internal.data.qualifiedNameOrTip
|
||||
import net.mamoe.mirai.console.internal.plugin.updateWhen
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
|
||||
import net.mamoe.mirai.utils.*
|
||||
|
@ -16,6 +16,7 @@ import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.console.command.*
|
||||
import net.mamoe.mirai.console.command.Command.Companion.allNames
|
||||
import net.mamoe.mirai.console.command.CommandSender.Companion.toCommandSender
|
||||
import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors
|
||||
import net.mamoe.mirai.console.util.CoroutineScopeUtils.childScope
|
||||
import net.mamoe.mirai.event.Listener
|
||||
import net.mamoe.mirai.event.subscribeAlways
|
||||
@ -24,6 +25,7 @@ import net.mamoe.mirai.message.data.content
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
|
||||
@OptIn(ExperimentalCommandDescriptors::class)
|
||||
internal object CommandManagerImpl : CommandManager, CoroutineScope by MiraiConsole.childScope("CommandManagerImpl") {
|
||||
private val logger: MiraiLogger by lazy {
|
||||
MiraiConsole.createLogger("command")
|
||||
@ -102,7 +104,9 @@ internal object CommandManagerImpl : CommandManager, CoroutineScope by MiraiCons
|
||||
}
|
||||
|
||||
override fun Command.register(override: Boolean): Boolean {
|
||||
if (this is CompositeCommand) this.subCommands // init lazy
|
||||
if (this is CompositeCommand) {
|
||||
this.overloads // init lazy
|
||||
}
|
||||
kotlin.runCatching {
|
||||
this.permission // init lazy
|
||||
this.secondaryNames // init lazy
|
||||
|
@ -0,0 +1,215 @@
|
||||
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.data.classifierAsKClass
|
||||
import net.mamoe.mirai.console.internal.data.classifierAsKClassOrNull
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
|
||||
import net.mamoe.mirai.message.data.MessageChain
|
||||
import net.mamoe.mirai.message.data.PlainText
|
||||
import net.mamoe.mirai.message.data.SingleMessage
|
||||
import net.mamoe.mirai.message.data.buildMessageChain
|
||||
import kotlin.reflect.KFunction
|
||||
import kotlin.reflect.KParameter
|
||||
import kotlin.reflect.KVisibility
|
||||
import kotlin.reflect.full.*
|
||||
|
||||
|
||||
internal val ILLEGAL_SUB_NAME_CHARS = "\\/!@#$%^&*()_+-={}[];':\",.<>?`~".toCharArray()
|
||||
|
||||
internal fun Any.flattenCommandComponents(): MessageChain = buildMessageChain {
|
||||
when (this@flattenCommandComponents) {
|
||||
is PlainText -> this@flattenCommandComponents.content.splitToSequence(' ').filterNot { it.isBlank() }
|
||||
.forEach { +PlainText(it) }
|
||||
is CharSequence -> this@flattenCommandComponents.splitToSequence(' ').filterNot { it.isBlank() }
|
||||
.forEach { +PlainText(it) }
|
||||
is SingleMessage -> add(this@flattenCommandComponents)
|
||||
is Array<*> -> this@flattenCommandComponents.forEach { if (it != null) addAll(it.flattenCommandComponents()) }
|
||||
is Iterable<*> -> this@flattenCommandComponents.forEach { if (it != null) addAll(it.flattenCommandComponents()) }
|
||||
else -> add(this@flattenCommandComponents.toString())
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
|
||||
internal object CompositeCommandSubCommandAnnotationResolver :
|
||||
SubCommandAnnotationResolver {
|
||||
override fun hasAnnotation(ownerCommand: Command, function: KFunction<*>) =
|
||||
function.hasAnnotation<CompositeCommand.SubCommand>()
|
||||
|
||||
override fun getSubCommandNames(ownerCommand: Command, function: KFunction<*>): Array<out String> =
|
||||
function.findAnnotation<CompositeCommand.SubCommand>()!!.value
|
||||
|
||||
override fun getAnnotatedName(ownerCommand: Command, parameter: KParameter): String? =
|
||||
parameter.findAnnotation<CompositeCommand.Name>()?.value
|
||||
|
||||
override fun getDescription(ownerCommand: Command, function: KFunction<*>): String? =
|
||||
function.findAnnotation<CompositeCommand.Description>()?.value
|
||||
}
|
||||
|
||||
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
|
||||
internal object SimpleCommandSubCommandAnnotationResolver :
|
||||
SubCommandAnnotationResolver {
|
||||
override fun hasAnnotation(ownerCommand: Command, function: KFunction<*>) =
|
||||
function.hasAnnotation<SimpleCommand.Handler>()
|
||||
|
||||
override fun getSubCommandNames(ownerCommand: Command, function: KFunction<*>): Array<out String> =
|
||||
ownerCommand.secondaryNames
|
||||
|
||||
override fun getAnnotatedName(ownerCommand: Command, parameter: KParameter): String? =
|
||||
parameter.findAnnotation<SimpleCommand.Name>()?.value
|
||||
|
||||
override fun getDescription(ownerCommand: Command, function: KFunction<*>): String? =
|
||||
ownerCommand.description
|
||||
}
|
||||
|
||||
internal interface SubCommandAnnotationResolver {
|
||||
fun hasAnnotation(ownerCommand: Command, function: KFunction<*>): Boolean
|
||||
fun getSubCommandNames(ownerCommand: Command, function: KFunction<*>): Array<out String>
|
||||
fun getAnnotatedName(ownerCommand: Command, parameter: KParameter): String?
|
||||
fun getDescription(ownerCommand: Command, function: KFunction<*>): String?
|
||||
}
|
||||
|
||||
@ConsoleExperimentalApi
|
||||
public class IllegalCommandDeclarationException : Exception {
|
||||
public override val message: String?
|
||||
|
||||
public constructor(
|
||||
ownerCommand: Command,
|
||||
correspondingFunction: KFunction<*>,
|
||||
message: String?,
|
||||
) : super("Illegal command declaration: ${correspondingFunction.name} declared in ${ownerCommand::class.qualifiedName}") {
|
||||
this.message = message
|
||||
}
|
||||
|
||||
public constructor(
|
||||
ownerCommand: Command,
|
||||
message: String?,
|
||||
) : super("Illegal command declaration: ${ownerCommand::class.qualifiedName}") {
|
||||
this.message = message
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCommandDescriptors::class)
|
||||
internal class CommandReflector(
|
||||
val command: Command,
|
||||
val annotationResolver: SubCommandAnnotationResolver,
|
||||
) {
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
private inline fun KFunction<*>.illegalDeclaration(
|
||||
message: String,
|
||||
): Nothing {
|
||||
throw IllegalCommandDeclarationException(command, this, message)
|
||||
}
|
||||
|
||||
private fun KFunction<*>.isSubCommandFunction(): Boolean = annotationResolver.hasAnnotation(command, this)
|
||||
private fun KFunction<*>.checkExtensionReceiver() {
|
||||
this.extensionReceiverParameter?.let { receiver ->
|
||||
if (receiver.type.classifierAsKClassOrNull()?.isSubclassOf(CommandSender::class) != true) {
|
||||
illegalDeclaration("Extension receiver parameter type is not subclass of CommandSender.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun KFunction<*>.checkNames() {
|
||||
val names = annotationResolver.getSubCommandNames(command, this)
|
||||
for (name in names) {
|
||||
ILLEGAL_SUB_NAME_CHARS.find { it in name }?.let {
|
||||
illegalDeclaration("'$it' is forbidden in command name.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun KFunction<*>.checkModifiers() {
|
||||
if (isInline) illegalDeclaration("Command function cannot be inline")
|
||||
if (visibility == KVisibility.PRIVATE) illegalDeclaration("Command function must be accessible from Mirai Console, that is, effectively public.")
|
||||
if (this.hasAnnotation<JvmStatic>()) illegalDeclaration("Command function must not be static.")
|
||||
|
||||
// should we allow abstract?
|
||||
|
||||
// if (isAbstract) illegalDeclaration("Command function cannot be abstract")
|
||||
}
|
||||
|
||||
fun generateUsage(overloads: Iterable<CommandSignatureVariantFromKFunction>): String {
|
||||
return overloads.joinToString("\n") { subcommand ->
|
||||
buildString {
|
||||
if (command.prefixOptional) {
|
||||
append("(")
|
||||
append(CommandManager.commandPrefix)
|
||||
append(")")
|
||||
} else {
|
||||
append(CommandManager.commandPrefix)
|
||||
}
|
||||
if (command is CompositeCommand) {
|
||||
append(command.primaryName)
|
||||
append(" ")
|
||||
}
|
||||
append(subcommand.valueParameters.joinToString(" ") { it.render() })
|
||||
annotationResolver.getDescription(command, subcommand.originFunction).let { description ->
|
||||
append(" ")
|
||||
append(description)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
|
||||
private fun <T> CommandValueParameter<T>.render(): String {
|
||||
return when (this) {
|
||||
is CommandValueParameter.Extended,
|
||||
is CommandValueParameter.UserDefinedType<*>,
|
||||
-> {
|
||||
"<${this.name ?: this.type.classifierAsKClass().simpleName}>"
|
||||
}
|
||||
is CommandValueParameter.StringConstant -> {
|
||||
this.expectingValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(IllegalCommandDeclarationException::class)
|
||||
fun findSubCommands(): List<CommandSignatureVariantFromKFunctionImpl> {
|
||||
return command::class.functions // exclude static later
|
||||
.asSequence()
|
||||
.filter { it.isSubCommandFunction() }
|
||||
.onEach { it.checkExtensionReceiver() }
|
||||
.onEach { it.checkModifiers() }
|
||||
.onEach { it.checkNames() }
|
||||
.map { function ->
|
||||
|
||||
val functionNameAsValueParameter =
|
||||
annotationResolver.getSubCommandNames(command, function).map { createStringConstantParameter(it) }
|
||||
|
||||
val functionValueParameters =
|
||||
function.valueParameters.map { it.toUserDefinedCommandParameter() }
|
||||
|
||||
CommandSignatureVariantFromKFunctionImpl(
|
||||
receiverParameter = function.extensionReceiverParameter?.toCommandReceiverParameter(),
|
||||
valueParameters = functionNameAsValueParameter + functionValueParameters,
|
||||
originFunction = function
|
||||
) { call ->
|
||||
function.callSuspend(command, *call.resolvedValueArguments.toTypedArray())
|
||||
}
|
||||
}.toList()
|
||||
}
|
||||
|
||||
private fun KParameter.toCommandReceiverParameter(): CommandReceiverParameter<out CommandSender>? {
|
||||
check(!this.isVararg) { "Receiver cannot be vararg." }
|
||||
check(this.type.classifierAsKClass().isSubclassOf(CommandSender::class)) { "Receiver must be subclass of CommandSender" }
|
||||
|
||||
return CommandReceiverParameter(this.type.isMarkedNullable, this.type)
|
||||
}
|
||||
|
||||
private fun createStringConstantParameter(expectingValue: String): CommandValueParameter.StringConstant {
|
||||
return CommandValueParameter.StringConstant(null, expectingValue)
|
||||
}
|
||||
|
||||
private fun KParameter.toUserDefinedCommandParameter(): CommandValueParameter.UserDefinedType<KParameter> {
|
||||
return CommandValueParameter.UserDefinedType(nameForCommandParameter(), this, this.isOptional, this.isVararg, this.type)
|
||||
}
|
||||
|
||||
private fun KParameter.nameForCommandParameter(): String? = annotationResolver.getAnnotatedName(command, this) ?: this.name
|
||||
}
|
@ -1,334 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:Suppress("NOTHING_TO_INLINE", "MemberVisibilityCanBePrivate", "INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
|
||||
|
||||
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
|
||||
import net.mamoe.mirai.message.data.SingleMessage
|
||||
import net.mamoe.mirai.message.data.buildMessageChain
|
||||
import kotlin.reflect.KAnnotatedElement
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KFunction
|
||||
import kotlin.reflect.KParameter
|
||||
import kotlin.reflect.full.*
|
||||
|
||||
internal object CompositeCommandSubCommandAnnotationResolver :
|
||||
AbstractReflectionCommand.SubCommandAnnotationResolver {
|
||||
override fun hasAnnotation(baseCommand: AbstractReflectionCommand, function: KFunction<*>) =
|
||||
function.hasAnnotation<CompositeCommand.SubCommand>()
|
||||
|
||||
override fun getSubCommandNames(baseCommand: AbstractReflectionCommand, function: KFunction<*>): Array<out String> =
|
||||
function.findAnnotation<CompositeCommand.SubCommand>()!!.value
|
||||
}
|
||||
|
||||
internal object SimpleCommandSubCommandAnnotationResolver :
|
||||
AbstractReflectionCommand.SubCommandAnnotationResolver {
|
||||
override fun hasAnnotation(baseCommand: AbstractReflectionCommand, function: KFunction<*>) =
|
||||
function.hasAnnotation<SimpleCommand.Handler>()
|
||||
|
||||
override fun getSubCommandNames(baseCommand: AbstractReflectionCommand, function: KFunction<*>): Array<out String> =
|
||||
baseCommand.secondaryNames
|
||||
}
|
||||
|
||||
internal abstract class AbstractReflectionCommand
|
||||
@JvmOverloads constructor(
|
||||
owner: CommandOwner,
|
||||
primaryName: String,
|
||||
secondaryNames: Array<out String>,
|
||||
description: String = "<no description available>",
|
||||
parentPermission: Permission = owner.parentPermission,
|
||||
prefixOptional: Boolean = false,
|
||||
) : Command, AbstractCommand(
|
||||
owner,
|
||||
primaryName = primaryName,
|
||||
secondaryNames = secondaryNames,
|
||||
description = description,
|
||||
parentPermission = parentPermission,
|
||||
prefixOptional = prefixOptional
|
||||
), CommandArgumentContextAware {
|
||||
internal abstract val subCommandAnnotationResolver: SubCommandAnnotationResolver
|
||||
|
||||
@JvmField
|
||||
@Suppress("PropertyName")
|
||||
internal var _usage: String = "<not yet initialized>"
|
||||
|
||||
override val usage: String // initialized by subCommand reflection
|
||||
get() {
|
||||
subCommands // ensure init
|
||||
return _usage
|
||||
}
|
||||
|
||||
abstract suspend fun CommandSender.onDefault(rawArgs: MessageChain)
|
||||
|
||||
internal val defaultSubCommand: DefaultSubCommandDescriptor by lazy {
|
||||
DefaultSubCommandDescriptor(
|
||||
"",
|
||||
createOrFindCommandPermission(parentPermission),
|
||||
onCommand = { sender: CommandSender, args: MessageChain ->
|
||||
sender.onDefault(args)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
internal open fun checkSubCommand(subCommands: Array<SubCommandDescriptor>) {
|
||||
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCommandDescriptors::class)
|
||||
private fun <T : Any> CommandParameter<T>.toCommandValueParameter(): CommandValueParameter<T> {
|
||||
return CommandValueParameter.UserDefinedType<T>(name, null, false, false, type)
|
||||
}
|
||||
|
||||
|
||||
@OptIn(ExperimentalCommandDescriptors::class)
|
||||
override val overloads: List<CommandSignatureVariant> by lazy {
|
||||
subCommands.map { desc ->
|
||||
CommandSignatureVariantImpl(desc.params.map { it.toCommandValueParameter() }) { call ->
|
||||
desc.onCommand(call.caller, call.resolvedValueArguments)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface SubCommandAnnotationResolver {
|
||||
fun hasAnnotation(baseCommand: AbstractReflectionCommand, function: KFunction<*>): Boolean
|
||||
fun getSubCommandNames(baseCommand: AbstractReflectionCommand, function: KFunction<*>): Array<out String>
|
||||
}
|
||||
|
||||
internal val subCommands: Array<SubCommandDescriptor> by lazy {
|
||||
this::class.declaredFunctions.filter { subCommandAnnotationResolver.hasAnnotation(this, it) }
|
||||
.also { subCommandFunctions ->
|
||||
// overloading not yet supported
|
||||
val overloadFunction = subCommandFunctions.groupBy { it.name }.entries.firstOrNull { it.value.size > 1 }
|
||||
if (overloadFunction != null) {
|
||||
error("Sub command overloading is not yet supported. (at ${this::class.qualifiedNameOrTip}.${overloadFunction.key})")
|
||||
}
|
||||
}.map { function ->
|
||||
createSubCommand(function, context)
|
||||
}.toTypedArray().also {
|
||||
_usage = it.createUsage(this)
|
||||
}.also { checkSubCommand(it) }
|
||||
}
|
||||
|
||||
internal val bakedCommandNameToSubDescriptorArray: Map<Array<String>, SubCommandDescriptor> by lazy {
|
||||
kotlin.run {
|
||||
val map = LinkedHashMap<Array<String>, SubCommandDescriptor>(subCommands.size * 2)
|
||||
for (descriptor in subCommands) {
|
||||
for (name in descriptor.bakedSubNames) {
|
||||
map[name] = descriptor
|
||||
}
|
||||
}
|
||||
map.toSortedMap { o1, o2 -> o1!!.contentHashCode() - o2!!.contentHashCode() }
|
||||
}
|
||||
}
|
||||
|
||||
internal class DefaultSubCommandDescriptor(
|
||||
val description: String,
|
||||
val permission: Permission,
|
||||
val onCommand: suspend (sender: CommandSender, rawArgs: MessageChain) -> Unit,
|
||||
)
|
||||
|
||||
internal inner class SubCommandDescriptor(
|
||||
val names: Array<out String>,
|
||||
val params: Array<CommandParameter<*>>,
|
||||
val description: String,
|
||||
val permission: Permission,
|
||||
val onCommand: suspend (sender: CommandSender, parsedArgs: List<Any?>) -> Boolean,
|
||||
val context: CommandArgumentContext,
|
||||
val argumentBuilder: (sender: CommandSender) -> MutableMap<KParameter, Any?>,
|
||||
) {
|
||||
val usage: String = createUsage(this@AbstractReflectionCommand)
|
||||
|
||||
private fun KParameter.isOptional(): Boolean {
|
||||
return isOptional || this.type.isMarkedNullable
|
||||
}
|
||||
|
||||
val minimalArgumentsSize = params.count {
|
||||
!it.parameter.isOptional()
|
||||
}
|
||||
|
||||
@JvmField
|
||||
internal val bakedSubNames: Array<Array<String>> = names.map { it.bakeSubName() }.toTypedArray()
|
||||
}
|
||||
}
|
||||
|
||||
internal fun <T> Array<T>.contentEqualsOffset(other: MessageChain, length: Int): Boolean {
|
||||
repeat(length) { index ->
|
||||
if (!other[index].toString().equals(this[index].toString(), ignoreCase = true)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
internal val ILLEGAL_SUB_NAME_CHARS = "\\/!@#$%^&*()_+-={}[];':\",.<>?`~".toCharArray()
|
||||
internal fun String.isValidSubName(): Boolean = ILLEGAL_SUB_NAME_CHARS.none { it in this }
|
||||
internal fun String.bakeSubName(): Array<String> = split(' ').filterNot { it.isBlank() }.toTypedArray()
|
||||
|
||||
internal fun Any.flattenCommandComponents(): MessageChain = buildMessageChain {
|
||||
when (this@flattenCommandComponents) {
|
||||
is PlainText -> this@flattenCommandComponents.content.splitToSequence(' ').filterNot { it.isBlank() }
|
||||
.forEach { +PlainText(it) }
|
||||
is CharSequence -> this@flattenCommandComponents.splitToSequence(' ').filterNot { it.isBlank() }
|
||||
.forEach { +PlainText(it) }
|
||||
is SingleMessage -> add(this@flattenCommandComponents)
|
||||
is Array<*> -> this@flattenCommandComponents.forEach { if (it != null) addAll(it.flattenCommandComponents()) }
|
||||
is Iterable<*> -> this@flattenCommandComponents.forEach { if (it != null) addAll(it.flattenCommandComponents()) }
|
||||
else -> add(this@flattenCommandComponents.toString())
|
||||
}
|
||||
}
|
||||
|
||||
internal inline fun <reified T : Annotation> KAnnotatedElement.hasAnnotation(): Boolean =
|
||||
findAnnotation<T>() != null
|
||||
|
||||
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("(")
|
||||
append(CommandManager.commandPrefix)
|
||||
append(")")
|
||||
} else {
|
||||
append(CommandManager.commandPrefix)
|
||||
}
|
||||
if (baseCommand is CompositeCommand) {
|
||||
append(baseCommand.primaryName)
|
||||
append(" ")
|
||||
}
|
||||
append(names.first())
|
||||
append(" ")
|
||||
append(params.joinToString(" ") { "<${it.name}>" })
|
||||
append(" ")
|
||||
append(description)
|
||||
appendLine()
|
||||
}.trimEnd()
|
||||
|
||||
internal fun <T1, R1, R2> ((T1) -> R1).then(then: (T1, R1) -> R2): ((T1) -> R2) {
|
||||
return { a -> then.invoke(a, (this@then(a))) }
|
||||
}
|
||||
|
||||
internal fun AbstractReflectionCommand.createSubCommand(
|
||||
function: KFunction<*>,
|
||||
context: CommandArgumentContext,
|
||||
): AbstractReflectionCommand.SubCommandDescriptor {
|
||||
val notStatic = !function.hasAnnotation<JvmStatic>()
|
||||
//val overridePermission = null//function.findAnnotation<CompositeCommand.PermissionId>()//optional
|
||||
val subDescription =
|
||||
function.findAnnotation<CompositeCommand.Description>()?.value ?: ""
|
||||
|
||||
fun KClass<*>.isValidReturnType(): Boolean {
|
||||
return when (this) {
|
||||
Boolean::class, Void::class, Unit::class, Nothing::class -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
check((function.returnType.classifier as? KClass<*>)?.isValidReturnType() == true) {
|
||||
error("Return type of sub command ${function.name} must be one of the following: kotlin.Boolean, java.lang.Boolean, kotlin.Unit (including implicit), kotlin.Nothing, boolean or void (at ${this::class.qualifiedNameOrTip}.${function.name})")
|
||||
}
|
||||
|
||||
check(!function.returnType.isMarkedNullable) {
|
||||
error("Return type of sub command ${function.name} must not be marked nullable in Kotlin, and must be marked with @NotNull or @NonNull explicitly in Java. (at ${this::class.qualifiedNameOrTip}.${function.name})")
|
||||
}
|
||||
var argumentBuilder: (sender: CommandSender) -> MutableMap<KParameter, Any?> = { HashMap() }
|
||||
val parameters = function.parameters.toMutableList()
|
||||
|
||||
if (notStatic) {
|
||||
val type = parameters.removeAt(0) // instance
|
||||
argumentBuilder = argumentBuilder.then { _, map ->
|
||||
map[type] = this@createSubCommand
|
||||
map
|
||||
}
|
||||
}
|
||||
|
||||
check(parameters.isNotEmpty()) {
|
||||
"Parameters of sub command ${function.name} must not be empty. (Must have CommandSender as its receiver or first parameter or absent, followed by naturally typed params) (at ${this::class.qualifiedNameOrTip}.${function.name})"
|
||||
}
|
||||
|
||||
parameters.forEach { param ->
|
||||
check(!param.isVararg) {
|
||||
"Parameter $param must not be vararg. (at ${this::class.qualifiedNameOrTip}.${function.name}.$param)"
|
||||
}
|
||||
}
|
||||
|
||||
(parameters.first()).let { receiver ->
|
||||
if ((receiver.type.classifier as? KClass<*>)?.isSubclassOf(CommandSender::class) == true) {
|
||||
val senderType = parameters.removeAt(0)
|
||||
argumentBuilder = argumentBuilder.then { sender, map ->
|
||||
map[senderType] = sender
|
||||
map
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val commandName =
|
||||
subCommandAnnotationResolver.getSubCommandNames(this, function)
|
||||
.let { namesFromAnnotation ->
|
||||
if (namesFromAnnotation.isNotEmpty()) {
|
||||
namesFromAnnotation.map(String::toLowerCase).toTypedArray()
|
||||
} else arrayOf(function.name.toLowerCase())
|
||||
}.also { names ->
|
||||
names.forEach {
|
||||
check(it.isValidSubName()) {
|
||||
"Name of sub command ${function.name} is invalid"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//map parameter
|
||||
val params = parameters.map { param ->
|
||||
|
||||
// if (param.isOptional) error("optional parameters are not yet supported. (at ${this::class.qualifiedNameOrTip}.${function.name}.$param)")
|
||||
|
||||
val paramName = param.findAnnotation<CompositeCommand.Name>()?.value ?: param.name ?: "unknown"
|
||||
CommandParameter<Any>(
|
||||
paramName,
|
||||
param.type,
|
||||
param
|
||||
)
|
||||
}.toTypedArray()
|
||||
|
||||
// TODO: 2020/09/19 检查 optional/nullable 是否都在最后
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return SubCommandDescriptor(
|
||||
commandName,
|
||||
params as Array<CommandParameter<*>>,
|
||||
subDescription, // overridePermission?.value
|
||||
permission,//overridePermission?.value?.let { PermissionService.INSTANCE[PermissionId.parseFromString(it)] } ?: permission,
|
||||
onCommand = { _: CommandSender, args ->
|
||||
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})" }
|
||||
|
||||
result as? Boolean ?: true // Unit, void is considered as true.
|
||||
},
|
||||
context = context,
|
||||
argumentBuilder = argumentBuilder
|
||||
)
|
||||
}
|
@ -12,7 +12,6 @@ package net.mamoe.mirai.console.internal.data
|
||||
import kotlinx.serialization.json.Json
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.console.data.*
|
||||
import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
import net.mamoe.mirai.utils.SilentLogger
|
||||
|
@ -11,14 +11,15 @@ package net.mamoe.mirai.console.internal.data
|
||||
|
||||
import net.mamoe.mirai.console.data.PluginData
|
||||
import net.mamoe.mirai.console.data.ValueName
|
||||
import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KParameter
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.*
|
||||
import kotlin.reflect.full.findAnnotation
|
||||
import kotlin.reflect.full.isSubclassOf
|
||||
|
||||
internal val KClass<*>.qualifiedNameOrTip: String get() = this.qualifiedName ?: "<anonymous class>"
|
||||
|
||||
internal inline fun <reified T : Annotation> KAnnotatedElement.hasAnnotation(): Boolean =
|
||||
findAnnotation<T>() != null
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
internal inline fun <reified T : Any> KType.toKClass(): KClass<out T> {
|
||||
val clazz = requireNotNull(classifier as? KClass<T>) { "Unsupported classifier: $classifier" }
|
||||
|
@ -16,7 +16,6 @@ import net.mamoe.mirai.console.data.PluginData
|
||||
import net.mamoe.mirai.console.data.SerializableValue.Companion.serializableValueWith
|
||||
import net.mamoe.mirai.console.data.SerializerAwareValue
|
||||
import net.mamoe.mirai.console.data.valueFromKType
|
||||
import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip
|
||||
import kotlin.contracts.contract
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KType
|
||||
|
@ -12,7 +12,7 @@
|
||||
package net.mamoe.mirai.console.util
|
||||
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip
|
||||
import net.mamoe.mirai.console.internal.data.qualifiedNameOrTip
|
||||
import net.mamoe.mirai.contact.*
|
||||
|
||||
/**
|
||||
|
@ -261,7 +261,7 @@ internal class TestCommand {
|
||||
"testOptional"
|
||||
) {
|
||||
@SubCommand
|
||||
fun optional(arg1: String, arg2: String = "Here is optional", arg3: String?) {
|
||||
fun optional(arg1: String, arg2: String = "Here is optional", arg3: String? = null) {
|
||||
println(arg1)
|
||||
println(arg2)
|
||||
println(arg3)
|
||||
|
Loading…
Reference in New Issue
Block a user