User-friendly prompt for command execute failure

This commit is contained in:
Him188 2020-11-16 15:31:04 +08:00
parent 46ff28050f
commit 58c91e80fb

View File

@ -7,20 +7,31 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE * https://github.com/mamoe/mirai/blob/master/LICENSE
*/ */
@file:OptIn(ExperimentalCommandDescriptors::class)
package net.mamoe.mirai.console.terminal package net.mamoe.mirai.console.terminal
import kotlinx.coroutines.* import kotlinx.coroutines.*
import net.mamoe.mirai.console.MiraiConsole import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.console.command.* import net.mamoe.mirai.console.command.*
import net.mamoe.mirai.console.command.CommandExecuteResult.* import net.mamoe.mirai.console.command.CommandExecuteResult.*
import net.mamoe.mirai.console.command.descriptor.AbstractCommandValueParameter.StringConstant
import net.mamoe.mirai.console.command.descriptor.CommandReceiverParameter
import net.mamoe.mirai.console.command.descriptor.CommandValueParameter
import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors
import net.mamoe.mirai.console.command.parse.CommandCall
import net.mamoe.mirai.console.command.parse.CommandValueArgument
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.cast
import net.mamoe.mirai.console.util.requestInput import net.mamoe.mirai.console.util.requestInput
import net.mamoe.mirai.console.util.safeCast
import net.mamoe.mirai.utils.DefaultLogger import net.mamoe.mirai.utils.DefaultLogger
import net.mamoe.mirai.utils.warning import net.mamoe.mirai.utils.warning
import org.jline.reader.EndOfFileException import org.jline.reader.EndOfFileException
import org.jline.reader.UserInterruptException import org.jline.reader.UserInterruptException
import kotlin.reflect.KClass
import kotlin.reflect.full.isSubclassOf
val consoleLogger by lazy { DefaultLogger("console") } val consoleLogger by lazy { DefaultLogger("console") }
@ -84,7 +95,7 @@ internal fun startupConsoleThread() {
consoleLogger.warning { "权限不足." } consoleLogger.warning { "权限不足." }
} }
is UnmatchedSignature -> { is UnmatchedSignature -> {
consoleLogger.warning { "参数不匹配: " + result.failureReasons.joinToString("\n") { it.render() } } consoleLogger.warning { "参数不匹配, 你是否想执行: \n" + result.failureReasons.render(result.command, result.call) }
} }
is Failure -> { is Failure -> {
consoleLogger.warning { result.toString() } consoleLogger.warning { result.toString() }
@ -108,14 +119,37 @@ internal fun startupConsoleThread() {
} }
@OptIn(ExperimentalCommandDescriptors::class) @OptIn(ExperimentalCommandDescriptors::class)
internal fun UnmatchedCommandSignature.render(): String { private fun List<UnmatchedCommandSignature>.render(command: Command, call: CommandCall): String {
return this.signature.toString() + " ${failureReason.render()}" val list =
this.filter lambda@{ signature ->
if (signature.failureReason.safeCast<FailureReason.InapplicableValueArgument>()?.parameter is StringConstant) return@lambda false
if (signature.signature.valueParameters.anyStringConstantUnmatched(call.valueArguments)) return@lambda false
true
}
if (list.isEmpty()) {
return command.usage
}
return list.joinToString("\n") { it.render(command) }
}
private fun List<CommandValueParameter<*>>.anyStringConstantUnmatched(arguments: List<CommandValueArgument>): Boolean {
return this.zip(arguments).any { (parameter, argument) ->
parameter is StringConstant && !parameter.accepts(argument, null)
}
}
@OptIn(ExperimentalCommandDescriptors::class)
internal fun UnmatchedCommandSignature.render(command: Command): String {
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
val usage = net.mamoe.mirai.console.internal.command.CommandReflector.generateUsage(command, null, listOf(this.signature))
return usage.trim() + " (${failureReason.render()})"
} }
@OptIn(ExperimentalCommandDescriptors::class) @OptIn(ExperimentalCommandDescriptors::class)
internal fun FailureReason.render(): String { internal fun FailureReason.render(): String {
return when (this) { return when (this) {
is FailureReason.InapplicableArgument -> "参数类型错误" is FailureReason.InapplicableArgument -> "参数类型错误"
is FailureReason.InapplicableReceiverArgument -> "需要由 ${this.parameter.renderAsName()} 执行"
is FailureReason.TooManyArguments -> "参数过多" is FailureReason.TooManyArguments -> "参数过多"
is FailureReason.NotEnoughArguments -> "参数不足" is FailureReason.NotEnoughArguments -> "参数不足"
is FailureReason.ResolutionAmbiguity -> "调用歧义" is FailureReason.ResolutionAmbiguity -> "调用歧义"
@ -125,3 +159,19 @@ internal fun FailureReason.render(): String {
} }
} }
} }
@OptIn(ExperimentalCommandDescriptors::class)
internal fun CommandReceiverParameter<*>.renderAsName(): String {
val classifier = this.type.classifier.cast<KClass<out CommandSender>>()
return when {
classifier.isSubclassOf(ConsoleCommandSender::class) -> "控制台"
classifier.isSubclassOf(FriendCommandSenderOnMessage::class) -> "好友私聊"
classifier.isSubclassOf(FriendCommandSender::class) -> "好友"
classifier.isSubclassOf(MemberCommandSenderOnMessage::class) -> "群内发言"
classifier.isSubclassOf(MemberCommandSender::class) -> "群成员"
classifier.isSubclassOf(TempCommandSenderOnMessage::class) -> "临时会话"
classifier.isSubclassOf(TempCommandSender::class) -> "临时好友"
classifier.isSubclassOf(UserCommandSender::class) -> "用户"
else -> classifier.simpleName ?: classifier.toString()
}
}