Review AnsiMessageBuilder

This commit is contained in:
Him188 2020-12-01 09:07:11 +08:00
parent 0a2e103bf3
commit b87a5390b9

View File

@ -13,11 +13,23 @@ 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.asAnsiMessageBuilder
import net.mamoe.mirai.console.util.AnsiMessageBuilder.Companion.dropAnsi
import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.message.MessageReceipt
import java.io.Serializable
/**
* @see buildAnsiMessage
* @see sendAnsiMessage
* @see AnsiMessageBuilder
* @see AnsiMessageBuilder.create
* @see asAnsiMessageBuilder
* @since 1.1
*/
public open class AnsiMessageBuilder public constructor(
public val delegate: StringBuilder
) : Appendable {
public val delegate: StringBuilder,
) : Appendable, Serializable, Comparable<AnsiMessageBuilder> {
override fun toString(): String = delegate.toString()
/**
@ -29,7 +41,7 @@ public open class AnsiMessageBuilder public constructor(
*
* @param code Ansi 操作码
*
* @see from
* @see asAnsiMessageBuilder
* @see create
*/
public open fun ansi(code: String): AnsiMessageBuilder = append(code)
@ -50,69 +62,7 @@ public open class AnsiMessageBuilder public constructor(
public open fun lightPurple(): AnsiMessageBuilder = append(Color.LIGHT_PURPLE)
public open fun lightCyan(): AnsiMessageBuilder = append(Color.LIGHT_CYAN)
internal object Color {
const val RESET = "\u001b[0m"
const val WHITE = "\u001b[30m"
const val RED = "\u001b[31m"
const val EMERALD_GREEN = "\u001b[32m"
const val GOLD = "\u001b[33m"
const val BLUE = "\u001b[34m"
const val PURPLE = "\u001b[35m"
const val GREEN = "\u001b[36m"
const val GRAY = "\u001b[90m"
const val LIGHT_RED = "\u001b[91m"
const val LIGHT_GREEN = "\u001b[92m"
const val LIGHT_YELLOW = "\u001b[93m"
const val LIGHT_BLUE = "\u001b[94m"
const val LIGHT_PURPLE = "\u001b[95m"
const val LIGHT_CYAN = "\u001b[96m"
}
internal class NoAnsiMessageBuilder(builder: StringBuilder) : AnsiMessageBuilder(builder) {
override fun reset(): AnsiMessageBuilder = this
override fun white(): AnsiMessageBuilder = this
override fun red(): AnsiMessageBuilder = this
override fun emeraldGreen(): AnsiMessageBuilder = this
override fun gold(): AnsiMessageBuilder = this
override fun blue(): AnsiMessageBuilder = this
override fun purple(): AnsiMessageBuilder = this
override fun green(): AnsiMessageBuilder = this
override fun gray(): AnsiMessageBuilder = this
override fun lightRed(): AnsiMessageBuilder = this
override fun lightGreen(): AnsiMessageBuilder = this
override fun lightYellow(): AnsiMessageBuilder = this
override fun lightBlue(): AnsiMessageBuilder = this
override fun lightPurple(): AnsiMessageBuilder = this
override fun lightCyan(): AnsiMessageBuilder = this
override fun ansi(code: String): AnsiMessageBuilder = this
}
public companion object {
// CSI序列由ESC [、若干个包括0个“参数字节”、若干个“中间字节”以及一个“最终字节”组成。各部分的字符范围如下
//
// CSI序列在ESC [之后各个组成部分的字符范围[12]:5.4
// 组成部分 字符范围 ASCII
// 参数字节 0x300x3F 09:;<=>?
// 中间字节 0x200x2F 空格、!"#$%&'()*+,-./
// 最终字节 0x400x7E @AZ[\]^_`az{|}~
//
// @see https://zh.wikipedia.org/wiki/ANSI%E8%BD%AC%E4%B9%89%E5%BA%8F%E5%88%97#CSI%E5%BA%8F%E5%88%97
@Suppress("RegExpRedundantEscape")
private val DROP_CSI_PATTERN = """\u001b\[([\u0030-\u003F])*?([\u0020-\u002F])*?[\u0040-\u007E]""".toRegex()
// 序列具有不同的长度。所有序列都以ASCII字符ESC27 / 十六进制 0x1B开头
// 第二个字节则是0x400x5FASCII @AZ[\]^_范围内的字符。[12]:5.3.a
//
// 标准规定在8位环境中这两个字节的序列可以合并为0x80-0x9F范围内的单个字节详情请参阅C1控制字符集
// 但是在现代设备上这些代码通常用于其他目的例如UTF-8的一部分或CP-1252字符因此并不使用这种合并的方式。
//
// 除ESC之外的其他C0代码通常是BELBSCRLFFFTABVTSO和SI在输出时也可能会产生与某些控制序列相似或相同的效果。
//
// @see https://zh.wikipedia.org/wiki/ANSI%E8%BD%AC%E4%B9%89%E5%BA%8F%E5%88%97#%E8%BD%AC%E4%B9%89%E5%BA%8F%E5%88%97
//
// 注: 缺少详细资料, 只能认定 ansi 长度固定为二字节 (CSI除外)
private val DROP_ANSI_PATTERN = """\u001b[\u0040\u005F]""".toRegex()
/**
* [String] 中剔除 ansi 控制符
*/
@ -122,30 +72,28 @@ public open class AnsiMessageBuilder public constructor(
.replace(DROP_ANSI_PATTERN, "")
/**
* 使用 [builder] 封装一个 [AnsiMessageBuilder]
* 使用 [this] 封装一个 [AnsiMessageBuilder]
*
* @param noAnsi `true` 时忽略全部与 ansi 有关的方法的调用
*/
@JvmStatic
@JvmOverloads
public fun from(
builder: StringBuilder,
noAnsi: Boolean = false
): AnsiMessageBuilder = if (noAnsi) {
NoAnsiMessageBuilder(builder)
} else AnsiMessageBuilder(builder)
@JvmName("from")
public fun StringBuilder.asAnsiMessageBuilder(noAnsi: Boolean = false): AnsiMessageBuilder =
if (noAnsi) NoAnsiMessageBuilder(this) else AnsiMessageBuilder(this)
/**
* @param capacity [StringBuilder] 的初始化大小
*
* @param noAnsi `true` 时忽略全部与 ansi 有关的方法的调用
* @see AnsiMessageBuilder
*/
@JvmStatic
@JvmOverloads
public fun create(
capacity: Int = 16,
noAnsi: Boolean = false
): AnsiMessageBuilder = from(StringBuilder(capacity), noAnsi)
noAnsi: Boolean = false,
): AnsiMessageBuilder = StringBuilder(capacity).asAnsiMessageBuilder(noAnsi)
/**
* 判断 [sender] 是否支持带 ansi 控制符的正确显示
@ -161,11 +109,12 @@ public open class AnsiMessageBuilder public constructor(
* [StringBuilder] 追加 ansi 控制符
*/
public inline fun StringBuilder.appendAnsi(
action: AnsiMessageBuilder.() -> Unit
): AnsiMessageBuilder = from(this).apply(action)
action: AnsiMessageBuilder.() -> Unit,
): AnsiMessageBuilder = this.asAnsiMessageBuilder().apply(action)
}
override fun compareTo(other: AnsiMessageBuilder): Int = this.delegate.compareTo(other.delegate)
/////////////////////////////////////////////////////////////////////////////////
override fun append(c: Char): AnsiMessageBuilder = apply { delegate.append(c) }
override fun append(csq: CharSequence?): AnsiMessageBuilder = apply { delegate.append(csq) }
@ -184,31 +133,38 @@ public open class AnsiMessageBuilder public constructor(
/**
* @param capacity [StringBuilder] 初始化大小
* @see AnsiMessageBuilder.create
* @since 1.1
*/
@JvmSynthetic
public fun AnsiMessageBuilder(capacity: Int = 16): AnsiMessageBuilder = AnsiMessageBuilder(StringBuilder(capacity))
/**
* 构建一条 ansi 信息
* 构建一条 ANSI 信息
*
* @see [AnsiMessageBuilder]
* @see AnsiMessageBuilder
* @since 1.1
*/
@JvmSynthetic
public inline fun buildAnsiMessage(
capacity: Int = 16,
action: AnsiMessageBuilder.() -> Unit
action: AnsiMessageBuilder.() -> Unit,
): String = AnsiMessageBuilder.create(capacity, false).apply(action).toString()
// 不在 top-level 使用者会得到 Internal error: Couldn't inline sendAnsiMessage
/**
* [CommandSender] 发送一条带有 ansi 控制符的信息
* [CommandSender] 发送一条带有 ANSI 控制符的信息
*
* @see [AnsiMessageBuilder]
* @see AnsiMessageBuilder
* @since 1.1
*/
@JvmSynthetic
public suspend inline fun CommandSender.sendAnsiMessage(
capacity: Int = 16,
builder: AnsiMessageBuilder.() -> Unit
) {
sendMessage(
builder: AnsiMessageBuilder.() -> Unit,
): MessageReceipt<Contact>? {
return sendMessage(
AnsiMessageBuilder.create(capacity, noAnsi = !AnsiMessageBuilder.isAnsiSupported(this))
.apply(builder)
.toString()
@ -216,15 +172,81 @@ public suspend inline fun CommandSender.sendAnsiMessage(
}
/**
* [CommandSender] 发送一条带有 ansi 控制符的信息
* [CommandSender] 发送一条带有 ANSI 控制符的信息
*
* @see [AnsiMessageBuilder.Companion.dropAnsi]
* @see AnsiMessageBuilder.Companion.dropAnsi
* @since 1.1
*/
public suspend inline fun CommandSender.sendAnsiMessage(message: String) {
sendMessage(
if (AnsiMessageBuilder.isAnsiSupported(this))
message
else
message.dropAnsi()
@JvmSynthetic
public suspend inline fun CommandSender.sendAnsiMessage(message: String): MessageReceipt<Contact>? {
return sendMessage(
if (AnsiMessageBuilder.isAnsiSupported(this)) message
else message.dropAnsi()
)
}
// internals
// CSI序列由ESC [、若干个包括0个“参数字节”、若干个“中间字节”以及一个“最终字节”组成。各部分的字符范围如下
//
// CSI序列在ESC [之后各个组成部分的字符范围[12]:5.4
// 组成部分 字符范围 ASCII
// 参数字节 0x300x3F 09:;<=>?
// 中间字节 0x200x2F 空格、!"#$%&'()*+,-./
// 最终字节 0x400x7E @AZ[\]^_`az{|}~
//
// @see https://zh.wikipedia.org/wiki/ANSI%E8%BD%AC%E4%B9%89%E5%BA%8F%E5%88%97#CSI%E5%BA%8F%E5%88%97
@Suppress("RegExpRedundantEscape")
private val DROP_CSI_PATTERN = """\u001b\[([\u0030-\u003F])*?([\u0020-\u002F])*?[\u0040-\u007E]""".toRegex()
// 序列具有不同的长度。所有序列都以ASCII字符ESC27 / 十六进制 0x1B开头
// 第二个字节则是0x400x5FASCII @AZ[\]^_范围内的字符。[12]:5.3.a
//
// 标准规定在8位环境中这两个字节的序列可以合并为0x80-0x9F范围内的单个字节详情请参阅C1控制字符集
// 但是在现代设备上这些代码通常用于其他目的例如UTF-8的一部分或CP-1252字符因此并不使用这种合并的方式。
//
// 除ESC之外的其他C0代码通常是BELBSCRLFFFTABVTSO和SI在输出时也可能会产生与某些控制序列相似或相同的效果。
//
// @see https://zh.wikipedia.org/wiki/ANSI%E8%BD%AC%E4%B9%89%E5%BA%8F%E5%88%97#%E8%BD%AC%E4%B9%89%E5%BA%8F%E5%88%97
//
// 注: 缺少详细资料, 只能认定 ansi 长度固定为二字节 (CSI除外)
private val DROP_ANSI_PATTERN = """\u001b[\u0040\u005F]""".toRegex()
private object Color {
const val RESET = "\u001b[0m"
const val WHITE = "\u001b[30m"
const val RED = "\u001b[31m"
const val EMERALD_GREEN = "\u001b[32m"
const val GOLD = "\u001b[33m"
const val BLUE = "\u001b[34m"
const val PURPLE = "\u001b[35m"
const val GREEN = "\u001b[36m"
const val GRAY = "\u001b[90m"
const val LIGHT_RED = "\u001b[91m"
const val LIGHT_GREEN = "\u001b[92m"
const val LIGHT_YELLOW = "\u001b[93m"
const val LIGHT_BLUE = "\u001b[94m"
const val LIGHT_PURPLE = "\u001b[95m"
const val LIGHT_CYAN = "\u001b[96m"
}
private class NoAnsiMessageBuilder(builder: StringBuilder) : AnsiMessageBuilder(builder) {
override fun reset(): AnsiMessageBuilder = this
override fun white(): AnsiMessageBuilder = this
override fun red(): AnsiMessageBuilder = this
override fun emeraldGreen(): AnsiMessageBuilder = this
override fun gold(): AnsiMessageBuilder = this
override fun blue(): AnsiMessageBuilder = this
override fun purple(): AnsiMessageBuilder = this
override fun green(): AnsiMessageBuilder = this
override fun gray(): AnsiMessageBuilder = this
override fun lightRed(): AnsiMessageBuilder = this
override fun lightGreen(): AnsiMessageBuilder = this
override fun lightYellow(): AnsiMessageBuilder = this
override fun lightBlue(): AnsiMessageBuilder = this
override fun lightPurple(): AnsiMessageBuilder = this
override fun lightCyan(): AnsiMessageBuilder = this
override fun ansi(code: String): AnsiMessageBuilder = this
}