diff --git a/backend/mirai-console/src/MiraiConsole.kt b/backend/mirai-console/src/MiraiConsole.kt index 1df9d5505..35ce1b201 100644 --- a/backend/mirai-console/src/MiraiConsole.kt +++ b/backend/mirai-console/src/MiraiConsole.kt @@ -163,7 +163,7 @@ public interface MiraiConsole : CoroutineScope { * @see [AnsiMessageBuilder] */ @ConsoleExperimentalApi - public val isAnsiSupport: Boolean + public val isAnsiSupported: Boolean } /** diff --git a/backend/mirai-console/src/MiraiConsoleImplementation.kt b/backend/mirai-console/src/MiraiConsoleImplementation.kt index d0b0c8c10..f10414fa3 100644 --- a/backend/mirai-console/src/MiraiConsoleImplementation.kt +++ b/backend/mirai-console/src/MiraiConsoleImplementation.kt @@ -175,7 +175,7 @@ public interface MiraiConsoleImplementation : CoroutineScope { * * 注: 若为 `true`, 建议携带 `org.fusesource.jansi:jansi` */ - public val isAnsiSupport: Boolean get() = false + public val isAnsiSupported: Boolean get() = false public companion object { internal lateinit var instance: MiraiConsoleImplementation diff --git a/backend/mirai-console/src/internal/MiraiConsoleImplementationBridge.kt b/backend/mirai-console/src/internal/MiraiConsoleImplementationBridge.kt index ec3e78d9d..84a033718 100644 --- a/backend/mirai-console/src/internal/MiraiConsoleImplementationBridge.kt +++ b/backend/mirai-console/src/internal/MiraiConsoleImplementationBridge.kt @@ -82,7 +82,7 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI override val dataStorageForBuiltIns: PluginDataStorage by instance::dataStorageForBuiltIns override val configStorageForBuiltIns: PluginDataStorage by instance::configStorageForBuiltIns override val consoleInput: ConsoleInput by instance::consoleInput - override val isAnsiSupport: Boolean by instance::isAnsiSupport + override val isAnsiSupported: Boolean by instance::isAnsiSupported override fun createLoginSolver(requesterBot: Long, configuration: BotConfiguration): LoginSolver = instance.createLoginSolver(requesterBot, configuration) diff --git a/backend/mirai-console/src/util/AnsiMessageBuilder.kt b/backend/mirai-console/src/util/AnsiMessageBuilder.kt index a716774da..d198887b6 100644 --- a/backend/mirai-console/src/util/AnsiMessageBuilder.kt +++ b/backend/mirai-console/src/util/AnsiMessageBuilder.kt @@ -6,21 +6,27 @@ * * https://github.com/mamoe/mirai/blob/master/LICENSE */ -@file:Suppress("unused", "MemberVisibilityCanBePrivate") +@file:Suppress("unused", "MemberVisibilityCanBePrivate", "FunctionName") package net.mamoe.mirai.console.util import net.mamoe.mirai.console.command.CommandSender import net.mamoe.mirai.console.command.ConsoleCommandSender import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge +import net.mamoe.mirai.console.util.AnsiMessageBuilder.Companion.dropAnsi public open class AnsiMessageBuilder internal constructor( - public val builder: StringBuilder + public val delegate: StringBuilder ) : Appendable { - public inline fun builder(action: StringBuilder.() -> R): R = builder.action() - override fun toString(): String = builder.toString() + override fun toString(): String = delegate.toString() + /** + * 在添加 ansi code 的时候建议使用此方法. + * + * 在 `noAnsi=true` 的时候会忽略此函数的调用 + */ public open fun ansi(code: String): AnsiMessageBuilder = append(code) + public open fun reset(): AnsiMessageBuilder = append(Color.RESET) public open fun white(): AnsiMessageBuilder = append(Color.WHITE) public open fun red(): AnsiMessageBuilder = append(Color.RED) @@ -75,91 +81,119 @@ public open class AnsiMessageBuilder internal constructor( } public companion object { - public fun ansiMessageBuilder( + private val DROP_ANSI_PATTERN = """\u001b\[([0-9]+)(;[0-9]+)*m""".toRegex() + + @JvmStatic + public fun String.dropAnsi(): String = DROP_ANSI_PATTERN.replace(this, "") + + @JvmStatic + @JvmName("builder") // Java Factory Style + @JvmOverloads + public operator fun invoke( builder: StringBuilder, noAnsi: Boolean = false ): AnsiMessageBuilder = if (noAnsi) { NoAnsiMessageBuilder(builder) } else AnsiMessageBuilder(builder) - public fun ansiMessageBuilder( - size: Int = 16, + /** + * @param capacity [StringBuilder] 的初始化大小 + */ + @JvmStatic + @JvmName("builder") // Java Factory Style + @JvmOverloads + public operator fun invoke( + capacity: Int = 16, noAnsi: Boolean = false - ): AnsiMessageBuilder = ansiMessageBuilder(StringBuilder(size), noAnsi) - - public inline fun buildAnsiMessage( - builder: StringBuilder, - noAnsi: Boolean = false, - action: AnsiMessageBuilder.() -> Unit - ): String = ansiMessageBuilder(builder, noAnsi).apply(action).toString() - - public inline fun buildAnsiMessage( - size: Int = 16, - noAnsi: Boolean = false, - action: AnsiMessageBuilder.() -> Unit - ): String = ansiMessageBuilder(size, noAnsi).apply(action).toString() + ): AnsiMessageBuilder = invoke(StringBuilder(capacity), noAnsi) @ConsoleExperimentalApi - public fun isAnsiSupport(sender: CommandSender): Boolean = + @JvmStatic + public fun isAnsiSupported(sender: CommandSender): Boolean = if (sender is ConsoleCommandSender) { - MiraiConsoleImplementationBridge.isAnsiSupport + MiraiConsoleImplementationBridge.isAnsiSupported } else false - public suspend fun CommandSender.sendAnsiMessage( - size: Int = 16, - builder: AnsiMessageBuilder.() -> Unit - ) { - sendMessage(buildAnsiMessage(size, noAnsi = !isAnsiSupport(this), builder)) - } + public inline fun StringBuilder.appendAnsi( + noAnsi: Boolean = false, + action: AnsiMessageBuilder.() -> Unit + ): AnsiMessageBuilder = invoke(this, noAnsi).apply(action) + } ///////////////////////////////////////////////////////////////////////////////// override fun append(c: Char): AnsiMessageBuilder { - builder.append(c); return this + delegate.append(c); return this } override fun append(csq: CharSequence?): AnsiMessageBuilder { - builder.append(csq); return this + delegate.append(csq); return this } override fun append(csq: CharSequence?, start: Int, end: Int): AnsiMessageBuilder { - builder.append(csq, start, end); return this + delegate.append(csq, start, end); return this } public fun append(any: Any?): AnsiMessageBuilder { - builder.append(any); return this + delegate.append(any); return this } public fun append(value: String): AnsiMessageBuilder { - builder.append(value); return this + delegate.append(value); return this } public fun append(value: String, start: Int, end: Int): AnsiMessageBuilder { - builder.append(value, start, end); return this + delegate.append(value, start, end); return this } public fun append(value: Boolean): AnsiMessageBuilder { - builder.append(value); return this + delegate.append(value); return this } public fun append(value: Float): AnsiMessageBuilder { - builder.append(value); return this + delegate.append(value); return this } public fun append(value: Double): AnsiMessageBuilder { - builder.append(value); return this + delegate.append(value); return this } public fun append(value: Int): AnsiMessageBuilder { - builder.append(value); return this + delegate.append(value); return this } public fun append(value: Long): AnsiMessageBuilder { - builder.append(value); return this + delegate.append(value); return this } public fun append(value: Short): AnsiMessageBuilder { - builder.append(value); return this + delegate.append(value); return this } ///////////////////////////////////////////////////////////////////////////////// -} \ No newline at end of file +} + +public inline fun buildAnsiMessage( + capacity: Int = 16, + action: AnsiMessageBuilder.() -> Unit +): String = AnsiMessageBuilder(capacity, false).apply(action).toString() + +// 不在 top-level 使用者会得到 Internal error: Couldn't inline sendAnsiMessage +public suspend inline fun CommandSender.sendAnsiMessage( + capacity: Int = 16, + builder: AnsiMessageBuilder.() -> Unit +) { + sendMessage( + AnsiMessageBuilder(capacity, noAnsi = !AnsiMessageBuilder.isAnsiSupported(this)) + .apply(builder) + .toString() + ) +} + +public suspend inline fun CommandSender.sendAnsiMessage(message: String) { + sendMessage( + if (AnsiMessageBuilder.isAnsiSupported(this)) + message + else + message.dropAnsi() + ) +} diff --git a/frontend/mirai-console-terminal/src/MiraiConsoleImplementationTerminal.kt b/frontend/mirai-console-terminal/src/MiraiConsoleImplementationTerminal.kt index 4809d08d8..1884a0b39 100644 --- a/frontend/mirai-console-terminal/src/MiraiConsoleImplementationTerminal.kt +++ b/frontend/mirai-console-terminal/src/MiraiConsoleImplementationTerminal.kt @@ -75,7 +75,7 @@ class MiraiConsoleImplementationTerminal MiraiConsole.mainLogger.error("Exception in coroutine $coroutineName", throwable) }) { override val consoleInput: ConsoleInput get() = ConsoleInputImpl - override val isAnsiSupport: Boolean get() = true + override val isAnsiSupported: Boolean get() = true override fun createLoginSolver(requesterBot: Long, configuration: BotConfiguration): LoginSolver { return DefaultLoginSolver(input = { requestInput("LOGIN> ") })