Review AnsiMessageBuilder

- Rename isAnsiSupport to isAnsiSupported
- Rename AnsiMessageBuilder.builder to delegate
- Rename factory function
- Move buildAnsiMessage to top-level
- Move CommandSender.sendAnsiMessage to top-level
- Add String.dropAnsi(): String
This commit is contained in:
Karlatemp 2020-11-07 11:23:26 +08:00
parent 36d1480298
commit 81aa60fe5d
No known key found for this signature in database
GPG Key ID: 21FBDDF664FF06F8
5 changed files with 79 additions and 45 deletions

View File

@ -163,7 +163,7 @@ public interface MiraiConsole : CoroutineScope {
* @see [AnsiMessageBuilder] * @see [AnsiMessageBuilder]
*/ */
@ConsoleExperimentalApi @ConsoleExperimentalApi
public val isAnsiSupport: Boolean public val isAnsiSupported: Boolean
} }
/** /**

View File

@ -175,7 +175,7 @@ public interface MiraiConsoleImplementation : CoroutineScope {
* *
* : 若为 `true`, 建议携带 `org.fusesource.jansi:jansi` * : 若为 `true`, 建议携带 `org.fusesource.jansi:jansi`
*/ */
public val isAnsiSupport: Boolean get() = false public val isAnsiSupported: Boolean get() = false
public companion object { public companion object {
internal lateinit var instance: MiraiConsoleImplementation internal lateinit var instance: MiraiConsoleImplementation

View File

@ -82,7 +82,7 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI
override val dataStorageForBuiltIns: PluginDataStorage by instance::dataStorageForBuiltIns override val dataStorageForBuiltIns: PluginDataStorage by instance::dataStorageForBuiltIns
override val configStorageForBuiltIns: PluginDataStorage by instance::configStorageForBuiltIns override val configStorageForBuiltIns: PluginDataStorage by instance::configStorageForBuiltIns
override val consoleInput: ConsoleInput by instance::consoleInput 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 = override fun createLoginSolver(requesterBot: Long, configuration: BotConfiguration): LoginSolver =
instance.createLoginSolver(requesterBot, configuration) instance.createLoginSolver(requesterBot, configuration)

View File

@ -6,21 +6,27 @@
* *
* https://github.com/mamoe/mirai/blob/master/LICENSE * https://github.com/mamoe/mirai/blob/master/LICENSE
*/ */
@file:Suppress("unused", "MemberVisibilityCanBePrivate") @file:Suppress("unused", "MemberVisibilityCanBePrivate", "FunctionName")
package net.mamoe.mirai.console.util package net.mamoe.mirai.console.util
import net.mamoe.mirai.console.command.CommandSender import net.mamoe.mirai.console.command.CommandSender
import net.mamoe.mirai.console.command.ConsoleCommandSender import net.mamoe.mirai.console.command.ConsoleCommandSender
import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge
import net.mamoe.mirai.console.util.AnsiMessageBuilder.Companion.dropAnsi
public open class AnsiMessageBuilder internal constructor( public open class AnsiMessageBuilder internal constructor(
public val builder: StringBuilder public val delegate: StringBuilder
) : Appendable { ) : Appendable {
public inline fun <R> builder(action: StringBuilder.() -> R): R = builder.action() override fun toString(): String = delegate.toString()
override fun toString(): String = builder.toString()
/**
* 在添加 ansi code 的时候建议使用此方法.
*
* `noAnsi=true` 的时候会忽略此函数的调用
*/
public open fun ansi(code: String): AnsiMessageBuilder = append(code) public open fun ansi(code: String): AnsiMessageBuilder = append(code)
public open fun reset(): AnsiMessageBuilder = append(Color.RESET) public open fun reset(): AnsiMessageBuilder = append(Color.RESET)
public open fun white(): AnsiMessageBuilder = append(Color.WHITE) public open fun white(): AnsiMessageBuilder = append(Color.WHITE)
public open fun red(): AnsiMessageBuilder = append(Color.RED) public open fun red(): AnsiMessageBuilder = append(Color.RED)
@ -75,91 +81,119 @@ public open class AnsiMessageBuilder internal constructor(
} }
public companion object { 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, builder: StringBuilder,
noAnsi: Boolean = false noAnsi: Boolean = false
): AnsiMessageBuilder = if (noAnsi) { ): AnsiMessageBuilder = if (noAnsi) {
NoAnsiMessageBuilder(builder) NoAnsiMessageBuilder(builder)
} else AnsiMessageBuilder(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 noAnsi: Boolean = false
): AnsiMessageBuilder = ansiMessageBuilder(StringBuilder(size), noAnsi) ): AnsiMessageBuilder = invoke(StringBuilder(capacity), 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()
@ConsoleExperimentalApi @ConsoleExperimentalApi
public fun isAnsiSupport(sender: CommandSender): Boolean = @JvmStatic
public fun isAnsiSupported(sender: CommandSender): Boolean =
if (sender is ConsoleCommandSender) { if (sender is ConsoleCommandSender) {
MiraiConsoleImplementationBridge.isAnsiSupport MiraiConsoleImplementationBridge.isAnsiSupported
} else false } else false
public suspend fun CommandSender.sendAnsiMessage( public inline fun StringBuilder.appendAnsi(
size: Int = 16, noAnsi: Boolean = false,
builder: AnsiMessageBuilder.() -> Unit action: AnsiMessageBuilder.() -> Unit
) { ): AnsiMessageBuilder = invoke(this, noAnsi).apply(action)
sendMessage(buildAnsiMessage(size, noAnsi = !isAnsiSupport(this), builder))
}
} }
///////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////
override fun append(c: Char): AnsiMessageBuilder { override fun append(c: Char): AnsiMessageBuilder {
builder.append(c); return this delegate.append(c); return this
} }
override fun append(csq: CharSequence?): AnsiMessageBuilder { 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 { 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 { public fun append(any: Any?): AnsiMessageBuilder {
builder.append(any); return this delegate.append(any); return this
} }
public fun append(value: String): AnsiMessageBuilder { 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 { 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 { public fun append(value: Boolean): AnsiMessageBuilder {
builder.append(value); return this delegate.append(value); return this
} }
public fun append(value: Float): AnsiMessageBuilder { public fun append(value: Float): AnsiMessageBuilder {
builder.append(value); return this delegate.append(value); return this
} }
public fun append(value: Double): AnsiMessageBuilder { public fun append(value: Double): AnsiMessageBuilder {
builder.append(value); return this delegate.append(value); return this
} }
public fun append(value: Int): AnsiMessageBuilder { public fun append(value: Int): AnsiMessageBuilder {
builder.append(value); return this delegate.append(value); return this
} }
public fun append(value: Long): AnsiMessageBuilder { public fun append(value: Long): AnsiMessageBuilder {
builder.append(value); return this delegate.append(value); return this
} }
public fun append(value: Short): AnsiMessageBuilder { public fun append(value: Short): AnsiMessageBuilder {
builder.append(value); return this delegate.append(value); return this
} }
///////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////
} }
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()
)
}

View File

@ -75,7 +75,7 @@ class MiraiConsoleImplementationTerminal
MiraiConsole.mainLogger.error("Exception in coroutine $coroutineName", throwable) MiraiConsole.mainLogger.error("Exception in coroutine $coroutineName", throwable)
}) { }) {
override val consoleInput: ConsoleInput get() = ConsoleInputImpl 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 { override fun createLoginSolver(requesterBot: Long, configuration: BotConfiguration): LoginSolver {
return DefaultLoginSolver(input = { requestInput("LOGIN> ") }) return DefaultLoginSolver(input = { requestInput("LOGIN> ") })