mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-10 18:40:15 +08:00
Support vararg in command
This commit is contained in:
parent
87b56ade12
commit
d10f2b4bea
@ -19,8 +19,9 @@ 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.command.createOrFindCommandPermission
|
||||||
import net.mamoe.mirai.console.internal.data.typeOf0
|
import net.mamoe.mirai.console.internal.data.typeOf0
|
||||||
import net.mamoe.mirai.console.permission.Permission
|
import net.mamoe.mirai.console.permission.Permission
|
||||||
|
import net.mamoe.mirai.message.data.Message
|
||||||
import net.mamoe.mirai.message.data.MessageChain
|
import net.mamoe.mirai.message.data.MessageChain
|
||||||
import net.mamoe.mirai.message.data.MessageChainBuilder
|
import net.mamoe.mirai.message.data.buildMessageChain
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 无参数解析, 接收原生参数的指令.
|
* 无参数解析, 接收原生参数的指令.
|
||||||
@ -58,11 +59,11 @@ public abstract class RawCommand(
|
|||||||
override val overloads: List<CommandSignatureVariant> = listOf(
|
override val overloads: List<CommandSignatureVariant> = listOf(
|
||||||
CommandSignatureVariantImpl(
|
CommandSignatureVariantImpl(
|
||||||
receiverParameter = CommandReceiverParameter(false, typeOf0<CommandSender>()),
|
receiverParameter = CommandReceiverParameter(false, typeOf0<CommandSender>()),
|
||||||
valueParameters = listOf(AbstractCommandValueParameter.UserDefinedType.createRequired<MessageChain>("args", true))
|
valueParameters = listOf(AbstractCommandValueParameter.UserDefinedType.createRequired<Array<out Message>>("args", true))
|
||||||
) { call ->
|
) { call ->
|
||||||
val sender = call.caller
|
val sender = call.caller
|
||||||
val arguments = call.rawValueArguments
|
val arguments = call.rawValueArguments
|
||||||
sender.onCommand(arguments.mapTo(MessageChainBuilder()) { it.value }.build())
|
sender.onCommand(buildMessageChain { arguments.forEach { +it.value } })
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ import net.mamoe.mirai.console.command.parse.CommandValueArgument
|
|||||||
import net.mamoe.mirai.console.command.resolve.ResolvedCommandCall
|
import net.mamoe.mirai.console.command.resolve.ResolvedCommandCall
|
||||||
import net.mamoe.mirai.console.internal.data.classifierAsKClass
|
import net.mamoe.mirai.console.internal.data.classifierAsKClass
|
||||||
import net.mamoe.mirai.console.internal.data.classifierAsKClassOrNull
|
import net.mamoe.mirai.console.internal.data.classifierAsKClassOrNull
|
||||||
|
import net.mamoe.mirai.console.internal.data.typeOf0
|
||||||
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
|
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import kotlin.reflect.KFunction
|
import kotlin.reflect.KFunction
|
||||||
@ -173,6 +174,9 @@ public class CommandReceiverParameter<T : CommandSender>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal val ANY_TYPE = typeOf0<Any>()
|
||||||
|
internal val ARRAY_OUT_ANY_TYPE = typeOf0<Array<out Any?>>()
|
||||||
|
|
||||||
@ExperimentalCommandDescriptors
|
@ExperimentalCommandDescriptors
|
||||||
public sealed class AbstractCommandValueParameter<T> : CommandValueParameter<T>, AbstractCommandParameter<T>() {
|
public sealed class AbstractCommandValueParameter<T> : CommandValueParameter<T>, AbstractCommandParameter<T>() {
|
||||||
override fun toString(): String = buildString {
|
override fun toString(): String = buildString {
|
||||||
@ -184,8 +188,19 @@ public sealed class AbstractCommandValueParameter<T> : CommandValueParameter<T>,
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override fun accepting(argument: CommandValueArgument, commandArgumentContext: CommandArgumentContext?): ArgumentAcceptance {
|
public override fun accepting(argument: CommandValueArgument, commandArgumentContext: CommandArgumentContext?): ArgumentAcceptance {
|
||||||
val expectingType = this.type
|
if (isVararg) {
|
||||||
|
val arrayElementType = this.type.arguments.single() // Array<T>
|
||||||
|
return acceptingImpl(arrayElementType.type ?: ANY_TYPE, argument, commandArgumentContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
return acceptingImpl(this.type, argument, commandArgumentContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun acceptingImpl(
|
||||||
|
expectingType: KType,
|
||||||
|
argument: CommandValueArgument,
|
||||||
|
commandArgumentContext: CommandArgumentContext?,
|
||||||
|
): ArgumentAcceptance {
|
||||||
if (argument.type.isSubtypeOf(expectingType)) return ArgumentAcceptance.Direct
|
if (argument.type.isSubtypeOf(expectingType)) return ArgumentAcceptance.Direct
|
||||||
|
|
||||||
argument.typeVariants.associateWith { typeVariant ->
|
argument.typeVariants.associateWith { typeVariant ->
|
||||||
@ -239,7 +254,11 @@ public sealed class AbstractCommandValueParameter<T> : CommandValueParameter<T>,
|
|||||||
) : AbstractCommandValueParameter<T>() {
|
) : AbstractCommandValueParameter<T>() {
|
||||||
init {
|
init {
|
||||||
requireNotNull(type.classifierAsKClassOrNull()) {
|
requireNotNull(type.classifierAsKClassOrNull()) {
|
||||||
"CommandReceiverParameter.type.classifier must be KClass."
|
"type.classifier must be KClass."
|
||||||
|
}
|
||||||
|
if (isVararg)
|
||||||
|
check(type.isSubtypeOf(ARRAY_OUT_ANY_TYPE)) {
|
||||||
|
"type must be subtype of Array if vararg. Given $type."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,11 +11,10 @@ package net.mamoe.mirai.console.command.descriptor
|
|||||||
|
|
||||||
import net.mamoe.mirai.console.command.parse.CommandCall
|
import net.mamoe.mirai.console.command.parse.CommandCall
|
||||||
import net.mamoe.mirai.console.command.parse.CommandCallParser
|
import net.mamoe.mirai.console.command.parse.CommandCallParser
|
||||||
import net.mamoe.mirai.console.command.parse.RawCommandArgument
|
import net.mamoe.mirai.console.command.parse.CommandValueArgument
|
||||||
import net.mamoe.mirai.message.data.MessageChain
|
import net.mamoe.mirai.console.internal.data.castOrNull
|
||||||
import net.mamoe.mirai.message.data.MessageContent
|
import net.mamoe.mirai.console.internal.data.kClassQualifiedName
|
||||||
import net.mamoe.mirai.message.data.asMessageChain
|
import net.mamoe.mirai.message.data.*
|
||||||
import net.mamoe.mirai.message.data.content
|
|
||||||
import kotlin.reflect.KType
|
import kotlin.reflect.KType
|
||||||
import kotlin.reflect.typeOf
|
import kotlin.reflect.typeOf
|
||||||
|
|
||||||
@ -31,15 +30,18 @@ public interface TypeVariant<out OutType> {
|
|||||||
*/
|
*/
|
||||||
public val outType: KType
|
public val outType: KType
|
||||||
|
|
||||||
public fun mapValue(valueParameter: MessageContent): OutType
|
/**
|
||||||
|
* @see CommandValueArgument.value
|
||||||
|
*/
|
||||||
|
public fun mapValue(valueParameter: Message): OutType
|
||||||
|
|
||||||
public companion object {
|
public companion object {
|
||||||
@OptIn(ExperimentalStdlibApi::class)
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
@JvmSynthetic
|
@JvmSynthetic
|
||||||
public inline operator fun <reified OutType> invoke(crossinline block: (valueParameter: RawCommandArgument) -> OutType): TypeVariant<OutType> {
|
public inline operator fun <reified OutType> invoke(crossinline block: (valueParameter: Message) -> OutType): TypeVariant<OutType> {
|
||||||
return object : TypeVariant<OutType> {
|
return object : TypeVariant<OutType> {
|
||||||
override val outType: KType = typeOf<OutType>()
|
override val outType: KType = typeOf<OutType>()
|
||||||
override fun mapValue(valueParameter: MessageContent): OutType = block(valueParameter)
|
override fun mapValue(valueParameter: Message): OutType = block(valueParameter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -49,19 +51,20 @@ public interface TypeVariant<out OutType> {
|
|||||||
public object MessageContentTypeVariant : TypeVariant<MessageContent> {
|
public object MessageContentTypeVariant : TypeVariant<MessageContent> {
|
||||||
@OptIn(ExperimentalStdlibApi::class)
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
override val outType: KType = typeOf<MessageContent>()
|
override val outType: KType = typeOf<MessageContent>()
|
||||||
override fun mapValue(valueParameter: MessageContent): MessageContent = valueParameter
|
override fun mapValue(valueParameter: Message): MessageContent =
|
||||||
|
valueParameter.castOrNull<MessageContent>() ?: error("Accepts MessageContent only but given ${valueParameter.kClassQualifiedName}")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ExperimentalCommandDescriptors
|
@ExperimentalCommandDescriptors
|
||||||
public object MessageChainTypeVariant : TypeVariant<MessageChain> {
|
public object MessageChainTypeVariant : TypeVariant<MessageChain> {
|
||||||
@OptIn(ExperimentalStdlibApi::class)
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
override val outType: KType = typeOf<MessageChain>()
|
override val outType: KType = typeOf<MessageChain>()
|
||||||
override fun mapValue(valueParameter: MessageContent): MessageChain = valueParameter.asMessageChain()
|
override fun mapValue(valueParameter: Message): MessageChain = valueParameter.asMessageChain()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ExperimentalCommandDescriptors
|
@ExperimentalCommandDescriptors
|
||||||
public object ContentStringTypeVariant : TypeVariant<String> {
|
public object ContentStringTypeVariant : TypeVariant<String> {
|
||||||
@OptIn(ExperimentalStdlibApi::class)
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
override val outType: KType = typeOf<String>()
|
override val outType: KType = typeOf<String>()
|
||||||
override fun mapValue(valueParameter: MessageContent): String = valueParameter.content
|
override fun mapValue(valueParameter: Message): String = valueParameter.content
|
||||||
}
|
}
|
||||||
|
@ -12,18 +12,18 @@
|
|||||||
package net.mamoe.mirai.console.command.parse
|
package net.mamoe.mirai.console.command.parse
|
||||||
|
|
||||||
import net.mamoe.mirai.console.command.descriptor.*
|
import net.mamoe.mirai.console.command.descriptor.*
|
||||||
|
import net.mamoe.mirai.console.internal.data.castOrInternalError
|
||||||
|
import net.mamoe.mirai.console.internal.data.classifierAsKClass
|
||||||
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
|
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
|
||||||
|
import net.mamoe.mirai.message.data.Message
|
||||||
|
import net.mamoe.mirai.message.data.MessageChain
|
||||||
import net.mamoe.mirai.message.data.MessageContent
|
import net.mamoe.mirai.message.data.MessageContent
|
||||||
|
import net.mamoe.mirai.message.data.SingleMessage
|
||||||
import kotlin.reflect.KType
|
import kotlin.reflect.KType
|
||||||
import kotlin.reflect.full.isSubtypeOf
|
import kotlin.reflect.full.isSubtypeOf
|
||||||
import kotlin.reflect.typeOf
|
import kotlin.reflect.typeOf
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For developing use, to be inlined in the future.
|
|
||||||
*/
|
|
||||||
public typealias RawCommandArgument = MessageContent
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see CommandValueArgument
|
* @see CommandValueArgument
|
||||||
*/
|
*/
|
||||||
@ -36,7 +36,12 @@ public interface CommandArgument
|
|||||||
@ExperimentalCommandDescriptors
|
@ExperimentalCommandDescriptors
|
||||||
public interface CommandValueArgument : CommandArgument {
|
public interface CommandValueArgument : CommandArgument {
|
||||||
public val type: KType
|
public val type: KType
|
||||||
public val value: RawCommandArgument
|
|
||||||
|
/**
|
||||||
|
* [MessageContent] if single argument
|
||||||
|
* [MessageChain] is vararg
|
||||||
|
*/
|
||||||
|
public val value: Message
|
||||||
public val typeVariants: List<TypeVariant<*>>
|
public val typeVariants: List<TypeVariant<*>>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,7 +51,7 @@ public interface CommandValueArgument : CommandArgument {
|
|||||||
@ConsoleExperimentalApi
|
@ConsoleExperimentalApi
|
||||||
@ExperimentalCommandDescriptors
|
@ExperimentalCommandDescriptors
|
||||||
public data class DefaultCommandValueArgument(
|
public data class DefaultCommandValueArgument(
|
||||||
public override val value: RawCommandArgument,
|
public override val value: Message,
|
||||||
) : CommandValueArgument {
|
) : CommandValueArgument {
|
||||||
@OptIn(ExperimentalStdlibApi::class)
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
override val type: KType = typeOf<MessageContent>()
|
override val type: KType = typeOf<MessageContent>()
|
||||||
@ -73,6 +78,38 @@ public fun <T> CommandValueArgument.mapToType(type: KType): T =
|
|||||||
|
|
||||||
@ExperimentalCommandDescriptors
|
@ExperimentalCommandDescriptors
|
||||||
public fun <T> CommandValueArgument.mapToTypeOrNull(expectingType: KType): T? {
|
public fun <T> CommandValueArgument.mapToTypeOrNull(expectingType: KType): T? {
|
||||||
|
if (expectingType.isSubtypeOf(ARRAY_OUT_ANY_TYPE)) {
|
||||||
|
val arrayElementType = expectingType.arguments.single().type ?: ANY_TYPE
|
||||||
|
|
||||||
|
val result = ArrayList<Any?>()
|
||||||
|
|
||||||
|
when (val value = value) {
|
||||||
|
is MessageChain -> {
|
||||||
|
for (message in value) {
|
||||||
|
result.add(mapToTypeOrNullImpl(arrayElementType, message))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> { // single
|
||||||
|
value.castOrInternalError<SingleMessage>()
|
||||||
|
result.add(mapToTypeOrNullImpl(arrayElementType, value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
return result.toArray(arrayElementType.createArray(result.size)) as T
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
return mapToTypeOrNullImpl(expectingType, value) as T
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KType.createArray(size: Int): Array<Any?> {
|
||||||
|
return java.lang.reflect.Array.newInstance(this.classifierAsKClass().javaObjectType, size).castOrInternalError()
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCommandDescriptors::class)
|
||||||
|
private fun CommandValueArgument.mapToTypeOrNullImpl(expectingType: KType, value: Message): Any? {
|
||||||
@OptIn(ExperimentalStdlibApi::class)
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
val result = typeVariants
|
val result = typeVariants
|
||||||
.filter { it.outType.isSubtypeOf(expectingType) }
|
.filter { it.outType.isSubtypeOf(expectingType) }
|
||||||
@ -85,7 +122,7 @@ public fun <T> CommandValueArgument.mapToTypeOrNull(expectingType: KType): T? {
|
|||||||
else typeVariant
|
else typeVariant
|
||||||
}
|
}
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
return result.mapValue(value) as T
|
return result.mapValue(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ExperimentalCommandDescriptors
|
@ExperimentalCommandDescriptors
|
||||||
|
@ -6,9 +6,12 @@ import net.mamoe.mirai.console.command.descriptor.*
|
|||||||
import net.mamoe.mirai.console.command.descriptor.ArgumentAcceptance.Companion.isNotAcceptable
|
import net.mamoe.mirai.console.command.descriptor.ArgumentAcceptance.Companion.isNotAcceptable
|
||||||
import net.mamoe.mirai.console.command.parse.CommandCall
|
import net.mamoe.mirai.console.command.parse.CommandCall
|
||||||
import net.mamoe.mirai.console.command.parse.CommandValueArgument
|
import net.mamoe.mirai.console.command.parse.CommandValueArgument
|
||||||
|
import net.mamoe.mirai.console.command.parse.DefaultCommandValueArgument
|
||||||
import net.mamoe.mirai.console.extensions.CommandCallResolverProvider
|
import net.mamoe.mirai.console.extensions.CommandCallResolverProvider
|
||||||
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
|
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
|
||||||
import net.mamoe.mirai.console.util.safeCast
|
import net.mamoe.mirai.console.util.safeCast
|
||||||
|
import net.mamoe.mirai.message.data.EmptyMessageChain
|
||||||
|
import net.mamoe.mirai.message.data.asMessageChain
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builtin implementation of [CommandCallResolver]
|
* Builtin implementation of [CommandCallResolver]
|
||||||
@ -26,11 +29,16 @@ public object BuiltInCommandCallResolver : CommandCallResolver {
|
|||||||
|
|
||||||
val signature = resolveImpl(callee, valueArguments, context) ?: return null
|
val signature = resolveImpl(callee, valueArguments, context) ?: return null
|
||||||
|
|
||||||
return ResolvedCommandCallImpl(call.caller, callee, signature, call.valueArguments, context ?: EmptyCommandArgumentContext)
|
return ResolvedCommandCallImpl(call.caller,
|
||||||
|
callee,
|
||||||
|
signature.variant,
|
||||||
|
signature.zippedArguments.map { it.second },
|
||||||
|
context ?: EmptyCommandArgumentContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
private data class ResolveData(
|
private data class ResolveData(
|
||||||
val variant: CommandSignatureVariant,
|
val variant: CommandSignatureVariant,
|
||||||
|
val zippedArguments: List<Pair<AbstractCommandValueParameter<*>, CommandValueArgument>>,
|
||||||
val argumentAcceptances: List<ArgumentAcceptanceWithIndex>,
|
val argumentAcceptances: List<ArgumentAcceptanceWithIndex>,
|
||||||
val remainingParameters: List<AbstractCommandValueParameter<*>>,
|
val remainingParameters: List<AbstractCommandValueParameter<*>>,
|
||||||
) {
|
) {
|
||||||
@ -46,18 +54,45 @@ public object BuiltInCommandCallResolver : CommandCallResolver {
|
|||||||
callee: Command,
|
callee: Command,
|
||||||
valueArguments: List<CommandValueArgument>,
|
valueArguments: List<CommandValueArgument>,
|
||||||
context: CommandArgumentContext?,
|
context: CommandArgumentContext?,
|
||||||
): CommandSignatureVariant? {
|
): ResolveData? {
|
||||||
|
|
||||||
|
|
||||||
callee.overloads
|
callee.overloads
|
||||||
.mapNotNull l@{ signature ->
|
.mapNotNull l@{ signature ->
|
||||||
val zipped = signature.valueParameters.zip(valueArguments)
|
val valueParameters = signature.valueParameters
|
||||||
|
|
||||||
val remaining = signature.valueParameters.drop(zipped.size)
|
val zipped = valueParameters.zip(valueArguments).toMutableList()
|
||||||
|
|
||||||
if (remaining.any { !it.isOptional }) return@l null // not enough args
|
val remainingParameters = valueParameters.drop(zipped.size).toMutableList()
|
||||||
|
|
||||||
|
if (remainingParameters.any { !it.isOptional && !it.isVararg }) return@l null // not enough args. // vararg can be empty.
|
||||||
|
|
||||||
|
if (zipped.isEmpty()) {
|
||||||
|
ResolveData(
|
||||||
|
variant = signature,
|
||||||
|
zippedArguments = emptyList(),
|
||||||
|
argumentAcceptances = emptyList(),
|
||||||
|
remainingParameters = remainingParameters,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
if (valueArguments.size > valueParameters.size && zipped.last().first.isVararg) {
|
||||||
|
// merge vararg arguments
|
||||||
|
val (varargParameter, varargFirstArgument)
|
||||||
|
= zipped.removeLast()
|
||||||
|
|
||||||
|
zipped.add(varargParameter to DefaultCommandValueArgument(valueArguments.drop(zipped.size).map { it.value }.asMessageChain()))
|
||||||
|
} else {
|
||||||
|
// add default empty vararg argument
|
||||||
|
val remainingVararg = remainingParameters.find { it.isVararg }
|
||||||
|
if (remainingVararg != null) {
|
||||||
|
zipped.add(remainingVararg to DefaultCommandValueArgument(EmptyMessageChain))
|
||||||
|
remainingParameters.remove(remainingVararg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ResolveData(
|
ResolveData(
|
||||||
variant = signature,
|
variant = signature,
|
||||||
|
zippedArguments = zipped,
|
||||||
argumentAcceptances = zipped.mapIndexed { index, (parameter, argument) ->
|
argumentAcceptances = zipped.mapIndexed { index, (parameter, argument) ->
|
||||||
val accepting = parameter.accepting(argument, context)
|
val accepting = parameter.accepting(argument, context)
|
||||||
if (accepting.isNotAcceptable) {
|
if (accepting.isNotAcceptable) {
|
||||||
@ -65,13 +100,14 @@ public object BuiltInCommandCallResolver : CommandCallResolver {
|
|||||||
}
|
}
|
||||||
ArgumentAcceptanceWithIndex(index, accepting)
|
ArgumentAcceptanceWithIndex(index, accepting)
|
||||||
},
|
},
|
||||||
remainingParameters = remaining
|
remainingParameters = remainingParameters
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.also { result -> result.singleOrNull()?.let { return it.variant } }
|
}
|
||||||
|
.also { result -> result.singleOrNull()?.let { return it } }
|
||||||
.takeLongestMatches()
|
.takeLongestMatches()
|
||||||
.ifEmpty { return null }
|
.ifEmpty { return null }
|
||||||
.also { result -> result.singleOrNull()?.let { return it.variant } }
|
.also { result -> result.singleOrNull()?.let { return it } }
|
||||||
// take single ArgumentAcceptance.Direct
|
// take single ArgumentAcceptance.Direct
|
||||||
.also { list ->
|
.also { list ->
|
||||||
|
|
||||||
@ -79,7 +115,7 @@ public object BuiltInCommandCallResolver : CommandCallResolver {
|
|||||||
.flatMap { phase ->
|
.flatMap { phase ->
|
||||||
phase.argumentAcceptances.filter { it.acceptance is ArgumentAcceptance.Direct }.map { phase to it }
|
phase.argumentAcceptances.filter { it.acceptance is ArgumentAcceptance.Direct }.map { phase to it }
|
||||||
}
|
}
|
||||||
candidates.singleOrNull()?.let { return it.first.variant } // single Direct
|
candidates.singleOrNull()?.let { return it.first } // single Direct
|
||||||
if (candidates.distinctBy { it.second.index }.size != candidates.size) {
|
if (candidates.distinctBy { it.second.index }.size != candidates.size) {
|
||||||
// Resolution ambiguity
|
// Resolution ambiguity
|
||||||
/*
|
/*
|
||||||
|
@ -89,7 +89,7 @@ internal object Testing {
|
|||||||
internal var cont: Continuation<Any?>? = null
|
internal var cont: Continuation<Any?>? = null
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
suspend fun <R> withTesting(timeout: Long = 5000L, block: suspend () -> Unit): R {
|
suspend fun <R> withTesting(timeout: Long = 50000L, block: suspend () -> Unit): R {
|
||||||
@Suppress("RemoveExplicitTypeArguments") // bug
|
@Suppress("RemoveExplicitTypeArguments") // bug
|
||||||
return if (timeout != -1L) {
|
return if (timeout != -1L) {
|
||||||
withTimeout<R>(timeout) {
|
withTimeout<R>(timeout) {
|
||||||
|
@ -31,10 +31,7 @@ import net.mamoe.mirai.message.data.*
|
|||||||
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
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.*
|
||||||
import kotlin.test.assertFalse
|
|
||||||
import kotlin.test.assertSame
|
|
||||||
import kotlin.test.assertTrue
|
|
||||||
|
|
||||||
object TestCompositeCommand : CompositeCommand(
|
object TestCompositeCommand : CompositeCommand(
|
||||||
ConsoleCommandOwner,
|
ConsoleCommandOwner,
|
||||||
@ -293,6 +290,47 @@ internal class TestCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test vararg`() {
|
||||||
|
runBlocking {
|
||||||
|
val optionCommand = object : CompositeCommand(
|
||||||
|
ConsoleCommandOwner,
|
||||||
|
"test"
|
||||||
|
) {
|
||||||
|
@SubCommand
|
||||||
|
fun vararg(arg1: Int, vararg x: String) {
|
||||||
|
assertEquals(1, arg1)
|
||||||
|
Testing.ok(x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
optionCommand.withRegistration {
|
||||||
|
assertArrayEquals(
|
||||||
|
emptyArray<String>(),
|
||||||
|
withTesting {
|
||||||
|
assertSuccess(sender.executeCommand("/test vararg 1"))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
assertArrayEquals(
|
||||||
|
arrayOf("s"),
|
||||||
|
withTesting<Array<String>> {
|
||||||
|
assertSuccess(sender.executeCommand("/test vararg 1 s"))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
assertArrayEquals(
|
||||||
|
arrayOf("s", "s", "s"),
|
||||||
|
withTesting {
|
||||||
|
assertSuccess(sender.executeCommand("/test vararg 1 s s s"))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> assertArrayEquals(expected: Array<out T>, actual: Array<out T>, message: String? = null) {
|
||||||
|
asserter.assertEquals(message, expected.contentToString(), actual.contentToString())
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalCommandDescriptors::class)
|
@OptIn(ExperimentalCommandDescriptors::class)
|
||||||
|
@ -15,8 +15,12 @@ import kotlinx.coroutines.CoroutineName
|
|||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import net.mamoe.mirai.console.MiraiConsole
|
import net.mamoe.mirai.console.MiraiConsole
|
||||||
import net.mamoe.mirai.console.command.*
|
import net.mamoe.mirai.console.command.BuiltInCommands
|
||||||
|
import net.mamoe.mirai.console.command.CommandExecuteStatus
|
||||||
|
import net.mamoe.mirai.console.command.CommandManager
|
||||||
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommand
|
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommand
|
||||||
|
import net.mamoe.mirai.console.command.ConsoleCommandSender
|
||||||
|
import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors
|
||||||
import net.mamoe.mirai.console.terminal.noconsole.NoConsole
|
import net.mamoe.mirai.console.terminal.noconsole.NoConsole
|
||||||
import net.mamoe.mirai.console.util.ConsoleInternalApi
|
import net.mamoe.mirai.console.util.ConsoleInternalApi
|
||||||
import net.mamoe.mirai.console.util.requestInput
|
import net.mamoe.mirai.console.util.requestInput
|
||||||
@ -26,7 +30,7 @@ import org.jline.reader.UserInterruptException
|
|||||||
|
|
||||||
val consoleLogger by lazy { DefaultLogger("console") }
|
val consoleLogger by lazy { DefaultLogger("console") }
|
||||||
|
|
||||||
@OptIn(ConsoleInternalApi::class, ConsoleTerminalExperimentalApi::class)
|
@OptIn(ConsoleInternalApi::class, ConsoleTerminalExperimentalApi::class, ExperimentalCommandDescriptors::class)
|
||||||
internal fun startupConsoleThread() {
|
internal fun startupConsoleThread() {
|
||||||
if (terminal is NoConsole) return
|
if (terminal is NoConsole) return
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user