mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-11 02:50:15 +08:00
Command execution
This commit is contained in:
parent
258e6ce13a
commit
2dda8a4a7e
@ -13,10 +13,15 @@ package net.mamoe.mirai.console.command
|
|||||||
|
|
||||||
import net.mamoe.mirai.console.command.description.CommandParam
|
import net.mamoe.mirai.console.command.description.CommandParam
|
||||||
import net.mamoe.mirai.console.command.description.CommandParserContext
|
import net.mamoe.mirai.console.command.description.CommandParserContext
|
||||||
|
import net.mamoe.mirai.console.command.description.EmptyCommandParserContext
|
||||||
import net.mamoe.mirai.console.command.description.plus
|
import net.mamoe.mirai.console.command.description.plus
|
||||||
|
import net.mamoe.mirai.console.plugins.MyArg
|
||||||
import net.mamoe.mirai.message.data.PlainText
|
import net.mamoe.mirai.message.data.PlainText
|
||||||
import net.mamoe.mirai.message.data.SingleMessage
|
import net.mamoe.mirai.message.data.SingleMessage
|
||||||
|
import kotlin.reflect.KAnnotatedElement
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
import kotlin.reflect.full.declaredFunctions
|
||||||
|
import kotlin.reflect.full.findAnnotation
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 指令
|
* 指令
|
||||||
@ -24,7 +29,8 @@ import kotlin.reflect.KClass
|
|||||||
* @see register 注册这个指令
|
* @see register 注册这个指令
|
||||||
*/
|
*/
|
||||||
interface Command {
|
interface Command {
|
||||||
val names: Array<String>
|
val names: Array<out String>
|
||||||
|
val usage: String
|
||||||
val description: String
|
val description: String
|
||||||
val permission: CommandPermission
|
val permission: CommandPermission
|
||||||
val prefixOptional: Boolean
|
val prefixOptional: Boolean
|
||||||
@ -45,51 +51,70 @@ interface Command {
|
|||||||
*/
|
*/
|
||||||
abstract class CompositeCommand @JvmOverloads constructor(
|
abstract class CompositeCommand @JvmOverloads constructor(
|
||||||
override val owner: CommandOwner,
|
override val owner: CommandOwner,
|
||||||
override val names: Array<String>,
|
override vararg val names: String,
|
||||||
override val description: String,
|
override val description: String,
|
||||||
|
override val permission: CommandPermission = CommandPermission.Default,
|
||||||
override val prefixOptional: Boolean = false,
|
override val prefixOptional: Boolean = false,
|
||||||
overrideContext: CommandParserContext
|
overrideContext: CommandParserContext = EmptyCommandParserContext
|
||||||
) : Command {
|
) : Command {
|
||||||
val context: CommandParserContext = CommandParserContext.Builtins + overrideContext
|
val context: CommandParserContext = CommandParserContext.Builtins + overrideContext
|
||||||
|
override val usage: String by lazy { TODO() }
|
||||||
|
|
||||||
/**
|
/** 指定子指令要求的权限 */
|
||||||
* Permission of the command
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
*/
|
|
||||||
@Target(AnnotationTarget.FUNCTION)
|
@Target(AnnotationTarget.FUNCTION)
|
||||||
annotation class Permission(val permission: KClass<out Permission>)
|
annotation class Permission(val permission: KClass<out Permission>)
|
||||||
|
|
||||||
/**
|
/** 标记一个函数为子指令 */
|
||||||
* 你应当使用 @SubCommand 来注册 sub 指令
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
*/
|
|
||||||
@Target(AnnotationTarget.FUNCTION)
|
@Target(AnnotationTarget.FUNCTION)
|
||||||
annotation class SubCommand(val name: String)
|
annotation class SubCommand(val name: String)
|
||||||
|
|
||||||
|
/** 指令描述 */
|
||||||
/**
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
* Usage of the sub command
|
|
||||||
* you should not include arg names, which will be insert automatically
|
|
||||||
*/
|
|
||||||
@Target(AnnotationTarget.FUNCTION)
|
@Target(AnnotationTarget.FUNCTION)
|
||||||
annotation class Usage(val usage: String)
|
annotation class Description(val description: String)
|
||||||
|
|
||||||
/**
|
/** 参数名, 将参与构成 [usage] */
|
||||||
* name of the parameter
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
*
|
|
||||||
* by default available
|
|
||||||
*/
|
|
||||||
@Target(AnnotationTarget.VALUE_PARAMETER)
|
@Target(AnnotationTarget.VALUE_PARAMETER)
|
||||||
annotation class Name(val name: String)
|
annotation class Name(val name: String)
|
||||||
|
|
||||||
final override suspend fun onCommand(sender: CommandSender, args: Array<out Any>) {
|
final override suspend fun onCommand(sender: CommandSender, args: Array<out Any>) {
|
||||||
matchSubCommand(args).parseAndExecute(sender, args)
|
matchSubCommand(args)?.parseAndExecute(sender, args) ?: kotlin.run {
|
||||||
|
defaultSubCommand.onCommand(sender, args)
|
||||||
|
}
|
||||||
subCommands
|
subCommands
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val defaultSubCommand: SubCommandDescriptor by lazy {
|
internal val defaultSubCommand: DefaultSubCommandDescriptor by lazy {
|
||||||
TODO()
|
DefaultSubCommandDescriptor(
|
||||||
|
"",
|
||||||
|
CommandPermission.Default,
|
||||||
|
onCommand = block { sender: CommandSender, args: Array<out Any> ->
|
||||||
|
println("default finally got args: ${args.joinToString()}")
|
||||||
|
true
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val subCommands: Array<SubCommandDescriptor> by lazy {
|
internal val subCommands: Array<SubCommandDescriptor> by lazy {
|
||||||
TODO()
|
this::class.declaredFunctions.filter { it.hasAnnotation<SubCommand>() }.map { function ->
|
||||||
|
SubCommandDescriptor(
|
||||||
|
arrayOf(function.name),
|
||||||
|
arrayOf(CommandParam("p", MyArg::class)),
|
||||||
|
"",
|
||||||
|
CommandPermission.Default,
|
||||||
|
onCommand = block { sender: CommandSender, args: Array<out Any> ->
|
||||||
|
println("subname finally gor args: ${args.joinToString()}")
|
||||||
|
true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}.toTypedArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun block(block: suspend (CommandSender, Array<out Any>) -> Boolean): suspend (CommandSender, Array<out Any>) -> Boolean {
|
||||||
|
return block
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
@ -103,12 +128,18 @@ abstract class CompositeCommand @JvmOverloads constructor(
|
|||||||
map.toSortedMap(Comparator { o1, o2 -> o1!!.contentHashCode() - o2!!.contentHashCode() })
|
map.toSortedMap(Comparator { o1, o2 -> o1!!.contentHashCode() - o2!!.contentHashCode() })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal inner class DefaultSubCommandDescriptor(
|
||||||
|
val description: String,
|
||||||
|
val permission: CommandPermission,
|
||||||
|
val onCommand: suspend (sender: CommandSender, rawArgs: Array<out Any>) -> Boolean
|
||||||
|
)
|
||||||
|
|
||||||
internal inner class SubCommandDescriptor(
|
internal inner class SubCommandDescriptor(
|
||||||
val names: Array<String>,
|
val names: Array<String>,
|
||||||
val params: Array<CommandParam<*>>,
|
val params: Array<CommandParam<*>>,
|
||||||
val description: String,
|
val description: String,
|
||||||
val permission: CommandPermission,
|
val permission: CommandPermission,
|
||||||
val onCommand: suspend (sender: CommandSender, parsedArgs: List<Any>) -> Boolean
|
val onCommand: suspend (sender: CommandSender, parsedArgs: Array<out Any>) -> Boolean
|
||||||
) {
|
) {
|
||||||
internal suspend inline fun parseAndExecute(
|
internal suspend inline fun parseAndExecute(
|
||||||
sender: CommandSender,
|
sender: CommandSender,
|
||||||
@ -121,10 +152,11 @@ abstract class CompositeCommand @JvmOverloads constructor(
|
|||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
internal val bakedSubNames: Array<Array<String>> = names.map { it.bakeSubName() }.toTypedArray()
|
internal val bakedSubNames: Array<Array<String>> = names.map { it.bakeSubName() }.toTypedArray()
|
||||||
private fun parseArgs(sender: CommandSender, rawArgs: Array<out Any>, offset: Int): List<Any> {
|
private fun parseArgs(sender: CommandSender, rawArgs: Array<out Any>, offset: Int): Array<out Any> {
|
||||||
require(rawArgs.size >= offset + this.params.size) { "No enough args. Required ${params.size}, but given ${rawArgs.size}" }
|
require(rawArgs.size >= offset + this.params.size) { "No enough args. Required ${params.size}, but given ${rawArgs.size - offset}" }
|
||||||
|
|
||||||
return this.params.mapIndexed { index, param ->
|
return Array(this.params.size) { index ->
|
||||||
|
val param = params[index]
|
||||||
val rawArg = rawArgs[offset + index]
|
val rawArg = rawArgs[offset + index]
|
||||||
when (rawArg) {
|
when (rawArg) {
|
||||||
is String -> context[param.type]?.parse(rawArg, sender)
|
is String -> context[param.type]?.parse(rawArg, sender)
|
||||||
@ -138,18 +170,18 @@ abstract class CompositeCommand @JvmOverloads constructor(
|
|||||||
/**
|
/**
|
||||||
* @param rawArgs 元素类型必须为 [SingleMessage] 或 [String], 且已经经过扁平化处理. 否则抛出异常 [IllegalArgumentException]
|
* @param rawArgs 元素类型必须为 [SingleMessage] 或 [String], 且已经经过扁平化处理. 否则抛出异常 [IllegalArgumentException]
|
||||||
*/
|
*/
|
||||||
internal fun matchSubCommand(rawArgs: Array<out Any>): SubCommandDescriptor {
|
internal fun matchSubCommand(rawArgs: Array<out Any>): SubCommandDescriptor? {
|
||||||
val maxCount = rawArgs.size - 1
|
val maxCount = rawArgs.size - 1
|
||||||
var cur = 0
|
var cur = 0
|
||||||
bakedCommandNameToSubDescriptorArray.forEach { (name, descriptor) ->
|
bakedCommandNameToSubDescriptorArray.forEach { (name, descriptor) ->
|
||||||
if (name.size != cur) {
|
if (name.size != cur) {
|
||||||
if (cur++ == maxCount) return defaultSubCommand
|
if (cur++ == maxCount) return null
|
||||||
}
|
}
|
||||||
if (name.contentEqualsOffset(rawArgs, offset = cur)) {
|
if (name.contentEqualsOffset(rawArgs, length = cur)) {
|
||||||
return descriptor
|
return descriptor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return defaultSubCommand
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,9 +203,9 @@ abstract class RawCommand(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun <T> Array<T>.contentEqualsOffset(other: Array<out Any>, offset: Int): Boolean {
|
private fun <T> Array<T>.contentEqualsOffset(other: Array<out Any>, length: Int): Boolean {
|
||||||
for (index in other.indices) {
|
repeat(length) { index ->
|
||||||
if (other[index + offset].toString() != this[index]) {
|
if (other[index].toString() != this[index]) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -186,12 +218,17 @@ internal fun String.bakeSubName(): Array<String> = split(' ').filterNot { it.isB
|
|||||||
|
|
||||||
internal fun Any.flattenCommandComponents(): ArrayList<Any> {
|
internal fun Any.flattenCommandComponents(): ArrayList<Any> {
|
||||||
val list = ArrayList<Any>()
|
val list = ArrayList<Any>()
|
||||||
when (this) {
|
when (this::class.java) {
|
||||||
is String -> list.addAll(split(' ').filterNot { it.isBlank() })
|
String::class.java -> (this as String).splitToSequence(' ').filterNot { it.isBlank() }.forEach { list.add(it) }
|
||||||
is PlainText -> list.addAll(content.flattenCommandComponents())
|
PlainText::class.java -> (this as PlainText).content.splitToSequence(' ').filterNot { it.isBlank() }
|
||||||
is SingleMessage -> list.add(this)
|
.forEach { list.add(it) }
|
||||||
is Iterable<*> -> this.asSequence().forEach { if (it != null) list.addAll(it.flattenCommandComponents()) }
|
SingleMessage::class.java -> list.add(this as SingleMessage)
|
||||||
|
Array<Any>::class.java -> (this as Array<*>).forEach { if (it != null) list.addAll(it.flattenCommandComponents()) }
|
||||||
|
Iterable::class.java -> (this as Iterable<*>).forEach { if (it != null) list.addAll(it.flattenCommandComponents()) }
|
||||||
else -> list.add(this.toString())
|
else -> list.add(this.toString())
|
||||||
}
|
}
|
||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal inline fun <reified T : Annotation> KAnnotatedElement.hasAnnotation(): Boolean =
|
||||||
|
findAnnotation<T>() != null
|
||||||
|
@ -7,9 +7,12 @@ import kotlinx.atomicfu.locks.withLock
|
|||||||
import net.mamoe.mirai.console.plugins.PluginBase
|
import net.mamoe.mirai.console.plugins.PluginBase
|
||||||
import net.mamoe.mirai.message.data.Message
|
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.SingleMessage
|
||||||
|
|
||||||
sealed class CommandOwner
|
sealed class CommandOwner
|
||||||
|
|
||||||
|
object TestCommandOwner : CommandOwner()
|
||||||
|
|
||||||
abstract class PluginCommandOwner(plugin: PluginBase) : CommandOwner()
|
abstract class PluginCommandOwner(plugin: PluginBase) : CommandOwner()
|
||||||
|
|
||||||
// 由前端实现
|
// 由前端实现
|
||||||
@ -36,6 +39,15 @@ fun CommandOwner.unregisterAllCommands() {
|
|||||||
fun Command.register(): Boolean = InternalCommandManager.modifyLock.withLock {
|
fun Command.register(): Boolean = InternalCommandManager.modifyLock.withLock {
|
||||||
if (findDuplicate() != null) return false
|
if (findDuplicate() != null) return false
|
||||||
InternalCommandManager.registeredCommands.add(this@register)
|
InternalCommandManager.registeredCommands.add(this@register)
|
||||||
|
if (this.prefixOptional) {
|
||||||
|
for (name in this.names) {
|
||||||
|
InternalCommandManager.optionalPrefixCommandMap[name] = this
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (name in this.names) {
|
||||||
|
InternalCommandManager.requiredPrefixCommandMap[name] = this
|
||||||
|
}
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,8 +72,12 @@ fun Command.unregister(): Boolean = InternalCommandManager.modifyLock.withLock {
|
|||||||
* @param messages 接受 [String] 或 [Message], 其他对象将会被 [Any.toString]
|
* @param messages 接受 [String] 或 [Message], 其他对象将会被 [Any.toString]
|
||||||
* @return 是否成功解析到指令. 返回 `false` 代表无任何指令匹配
|
* @return 是否成功解析到指令. 返回 `false` 代表无任何指令匹配
|
||||||
*/
|
*/
|
||||||
suspend fun CommandSender.executeCommand(vararg messages: Any): Boolean =
|
suspend fun CommandSender.executeCommand(vararg messages: Any): Boolean {
|
||||||
executeCommandInternal(messages) { messages.getOrNull(it) }
|
if (messages.isEmpty()) return false
|
||||||
|
return executeCommandInternal(
|
||||||
|
messages,
|
||||||
|
messages[0].let { if (it is SingleMessage) it.toString() else it.toString().substringBefore(' ') })
|
||||||
|
}
|
||||||
|
|
||||||
internal inline fun <reified T> List<T>.dropToTypedArray(n: Int): Array<T> = Array(size - n) { this[n + it] }
|
internal inline fun <reified T> List<T>.dropToTypedArray(n: Int): Array<T> = Array(size - n) { this[n + it] }
|
||||||
|
|
||||||
@ -69,14 +85,16 @@ internal inline fun <reified T> List<T>.dropToTypedArray(n: Int): Array<T> = Arr
|
|||||||
* 解析并执行一个指令
|
* 解析并执行一个指令
|
||||||
* @return 是否成功解析到指令. 返回 `false` 代表无任何指令匹配
|
* @return 是否成功解析到指令. 返回 `false` 代表无任何指令匹配
|
||||||
*/
|
*/
|
||||||
suspend fun CommandSender.executeCommand(message: MessageChain): Boolean =
|
suspend fun CommandSender.executeCommand(message: MessageChain): Boolean {
|
||||||
executeCommandInternal(message) { message.getOrNull(it) }
|
if (message.isEmpty()) return false
|
||||||
|
return executeCommandInternal(message, message[0].toString())
|
||||||
|
}
|
||||||
|
|
||||||
internal suspend inline fun CommandSender.executeCommandInternal(
|
internal suspend inline fun CommandSender.executeCommandInternal(
|
||||||
messages: Any,
|
messages: Any,
|
||||||
iterator: (index: Int) -> Any?
|
commandName: String
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val command = InternalCommandManager.matchCommand(getCommandName(iterator)) ?: return false
|
val command = InternalCommandManager.matchCommand(commandName) ?: return false
|
||||||
val rawInput = messages.flattenCommandComponents()
|
val rawInput = messages.flattenCommandComponents()
|
||||||
command.onCommand(this, rawInput.dropToTypedArray(1))
|
command.onCommand(this, rawInput.dropToTypedArray(1))
|
||||||
return true
|
return true
|
||||||
|
@ -54,7 +54,10 @@ object FloatArgParser : CommandArgParser<Float>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
object StringArgParser : CommandArgParser<String>() {
|
object StringArgParser : CommandArgParser<String>() {
|
||||||
override fun parse(raw: String, sender: CommandSender): String = raw
|
override fun parse(raw: String, sender: CommandSender): String {
|
||||||
|
println("STRING PARSER! $raw")
|
||||||
|
return raw
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object BooleanArgParser : CommandArgParser<Boolean>() {
|
object BooleanArgParser : CommandArgParser<Boolean>() {
|
||||||
|
@ -54,16 +54,16 @@ interface CommandParserContext {
|
|||||||
Bot::class with ExistBotArgParser
|
Bot::class with ExistBotArgParser
|
||||||
Friend::class with ExistFriendArgParser
|
Friend::class with ExistFriendArgParser
|
||||||
})
|
})
|
||||||
|
|
||||||
object Empty : CommandParserContext by CustomCommandParserContext(listOf())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object EmptyCommandParserContext : CommandParserContext by CustomCommandParserContext(listOf())
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 合并两个 [CommandParserContext], [replacer] 将会替换 [this] 中重复的 parser.
|
* 合并两个 [CommandParserContext], [replacer] 将会替换 [this] 中重复的 parser.
|
||||||
*/
|
*/
|
||||||
operator fun CommandParserContext.plus(replacer: CommandParserContext): CommandParserContext {
|
operator fun CommandParserContext.plus(replacer: CommandParserContext): CommandParserContext {
|
||||||
if (replacer == CommandParserContext.Empty) return this
|
if (replacer == EmptyCommandParserContext) return this
|
||||||
if (this == CommandParserContext.Empty) return replacer
|
if (this == EmptyCommandParserContext) return replacer
|
||||||
return object : CommandParserContext {
|
return object : CommandParserContext {
|
||||||
override fun <T : Any> get(klass: KClass<out T>): CommandArgParser<T>? = replacer[klass] ?: this@plus[klass]
|
override fun <T : Any> get(klass: KClass<out T>): CommandArgParser<T>? = replacer[klass] ?: this@plus[klass]
|
||||||
override fun toList(): List<ParserPair<*>> = replacer.toList() + this@plus.toList()
|
override fun toList(): List<ParserPair<*>> = replacer.toList() + this@plus.toList()
|
||||||
@ -75,7 +75,7 @@ operator fun CommandParserContext.plus(replacer: CommandParserContext): CommandP
|
|||||||
*/
|
*/
|
||||||
operator fun CommandParserContext.plus(replacer: List<ParserPair<*>>): CommandParserContext {
|
operator fun CommandParserContext.plus(replacer: List<ParserPair<*>>): CommandParserContext {
|
||||||
if (replacer.isEmpty()) return this
|
if (replacer.isEmpty()) return this
|
||||||
if (this == CommandParserContext.Empty) return CustomCommandParserContext(replacer)
|
if (this == EmptyCommandParserContext) return CustomCommandParserContext(replacer)
|
||||||
return object : CommandParserContext {
|
return object : CommandParserContext {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
override fun <T : Any> get(klass: KClass<out T>): CommandArgParser<T>? =
|
override fun <T : Any> get(klass: KClass<out T>): CommandArgParser<T>? =
|
||||||
|
@ -3,14 +3,15 @@
|
|||||||
package net.mamoe.mirai.console.command.description
|
package net.mamoe.mirai.console.command.description
|
||||||
|
|
||||||
import net.mamoe.mirai.console.command.Command
|
import net.mamoe.mirai.console.command.Command
|
||||||
|
import net.mamoe.mirai.console.command.CompositeCommand
|
||||||
|
import java.lang.reflect.Parameter
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import kotlin.reflect.KParameter
|
|
||||||
|
|
||||||
internal fun KParameter.toCommandParam(): CommandParam<*> {
|
internal fun Parameter.toCommandParam(): CommandParam<*> {
|
||||||
|
val name = getAnnotation(CompositeCommand.Name::class.java)
|
||||||
return CommandParam(
|
return CommandParam(
|
||||||
this.name ?: throw IllegalArgumentException("Cannot construct CommandParam from a unnamed param"),
|
name?.name ?: this.name ?: throw IllegalArgumentException("Cannot construct CommandParam from a unnamed param"),
|
||||||
this.type.classifier as? KClass<*>
|
this.type.kotlin
|
||||||
?: throw IllegalArgumentException("Cannot construct CommandParam from a type parameter")
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,23 +19,6 @@ internal infix fun Array<String>.matchesBeginning(list: List<Any>): Boolean {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private val SYMBOL_MISSING_CARET = String(byteArrayOf())
|
|
||||||
|
|
||||||
internal inline fun getCommandName(iterator: (index: Int) -> Any?): String = buildString {
|
|
||||||
repeat(Int.MAX_VALUE) { index ->
|
|
||||||
val next = iterator(index) ?: return@buildString
|
|
||||||
|
|
||||||
val str = next.toString()
|
|
||||||
val before = str.substringBefore(' ', SYMBOL_MISSING_CARET)
|
|
||||||
if (before === SYMBOL_MISSING_CARET) {
|
|
||||||
append(str)
|
|
||||||
} else {
|
|
||||||
append(before)
|
|
||||||
return@buildString
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal object InternalCommandManager {
|
internal object InternalCommandManager {
|
||||||
const val COMMAND_PREFIX = "/"
|
const val COMMAND_PREFIX = "/"
|
||||||
|
|
||||||
@ -66,13 +49,13 @@ internal object InternalCommandManager {
|
|||||||
*/
|
*/
|
||||||
internal fun matchCommand(rawCommand: String): Command? {
|
internal fun matchCommand(rawCommand: String): Command? {
|
||||||
if (rawCommand.startsWith(COMMAND_PREFIX)) {
|
if (rawCommand.startsWith(COMMAND_PREFIX)) {
|
||||||
return requiredPrefixCommandMap[rawCommand]
|
return requiredPrefixCommandMap[rawCommand.substringAfter(COMMAND_PREFIX)]
|
||||||
}
|
}
|
||||||
return optionalPrefixCommandMap[rawCommand]
|
return optionalPrefixCommandMap[rawCommand]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal infix fun <T> Array<T>.intersects(other: Array<T>): Boolean {
|
internal infix fun <T> Array<out T>.intersects(other: Array<out T>): Boolean {
|
||||||
val max = this.size.coerceAtMost(other.size)
|
val max = this.size.coerceAtMost(other.size)
|
||||||
for (i in 0 until max) {
|
for (i in 0 until max) {
|
||||||
if (this[i] == other[i]) return true
|
if (this[i] == other[i]) return true
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 Mamoe Technologies and contributors.
|
||||||
|
*
|
||||||
|
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||||
|
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||||
|
*
|
||||||
|
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.mamoe.mirai.console.command
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
|
object TestCompositeCommand : CompositeCommand(
|
||||||
|
TestCommandOwner,
|
||||||
|
"name1", "name2",
|
||||||
|
description = """
|
||||||
|
desc
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
internal class TestComposite {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testRegister() {
|
||||||
|
TestCompositeCommand.register()
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user