跳到主要内容
ansicode
Kotlin

在 Kotlin 中使用 ANSI 转义码 —— \u001B、mordant、kotter、picocli

Kotlin 字符串字面量用 `\u001B` 表示 ESC 字节 —— Kotlin 没有 `\x` 十六进制转义,只有 `\u####` Unicode 转义(与 Java 一致)。`println("\u001B[31mred\u001B[0m")` 在 macOS / Linux 以及 Windows 10+(stdout 默认启用 VT)上按字节透传。需要顺手的颜色 + 表格 + 进度条 + Markdown 渲染时选 `mordant`,Kotlin 最受欢迎的 ANSI 库 —— 自动检测终端能力、尊重 `NO_COLOR`、被管道时优雅降级。需要完整交互 TUI 时 `kotter` 提供协程友好的 DSL。`picocli`(JVM CLI 参数解析器)为 `--help` 内建 ANSI 颜色,同时需要 CLI 解析时的自然选择。 **Gradle / IDE 注意**:IntelliJ Run 控制台启用 ANSI,但 Gradle daemon 默认剥离颜色 —— 传 `--console=plain`(或在 `gradle.properties` 设置 `org.gradle.console=plain`)以原样查看转义字节。Android Logcat 不解析 ANSI;目标为 Android 时使用 `mordant` 的 `Theme.PlainAscii` 或先做能力检测。

推荐库

  • mordant

    Kotlin 事实标准的 ANSI / TUI 库 —— 彩色文本、表格、进度条、Markdown 渲染、提示。自动检测 ANSI / 256 / TrueColor 能力,尊重 `NO_COLOR`,被管道时降级为纯文本。可在 Kotlin/JVM、Kotlin/Native、Kotlin/JS 上工作。

  • kotter

    现代 Kotlin 协程友好 DSL,用于构建交互 TUI —— `section { textLine(red("error")) }` 风格。专为动态更新输出(进度、动画、轮询状态)设计,无需 ncurses 风格的完整窗口 / 面板模型。

  • picocli

    成熟的基于注解的 JVM CLI 参数解析器,`--help` 输出内建 ANSI 颜色(`Picocli.Help.Ansi`)。Kotlin 友好。当你本来就需要参数解析时使用 —— 顺带免费拿到彩色帮助。

  • JLine 3

    Kotlin REPL、Groovy、Scala REPL 共享的较低层终端抽象 —— 行编辑、历史、补全、原始模式按键读取。需要能力检测(`Terminal.getType()`、鼠标、焦点事件)或构建 REPL 时使用。

常用写法

直接 println 配合 \u001B —— 注意不是 \x1b
// Kotlin has no \x hex escape — only \u#### Unicode (like Java).
// \x1b is a compile error inside string literals; use \u001B.

fun main() {
    println("\u001B[1;31merror:\u001B[0m permission denied")
    println("\u001B[33mwarn:\u001B[0m deprecated flag")
    println("\u001B[32mok:\u001B[0m 142 tests passed")

    // Define once, reuse — top-level constant:
    val ESC = "\u001B"
    println("${ESC}[38;2;255;128;0morange truecolor${ESC}[0m")
}
mordant —— Terminal + brightRed + Table
// build.gradle.kts:
//   implementation("com.github.ajalt.mordant:mordant:2.7.0")
import com.github.ajalt.mordant.rendering.TextColors.*
import com.github.ajalt.mordant.terminal.Terminal
import com.github.ajalt.mordant.table.table

fun main() {
    val t = Terminal()  // auto-detects capability + honours NO_COLOR
    t.println("${(brightRed + bold)("error:")} permission denied")
    t.println("${yellow("warn:")} deprecated flag")
    t.println("${green("ok:")} 142 tests passed")

    // Truecolor via hex:
    t.println(rgb("#ff8000")("orange truecolor"))

    // Tables are first-class:
    t.println(table {
        header { row("File", "Status") }
        body {
            row("app.log", green("ok"))
            row("db.log", red("missing"))
        }
    })
}
能力门控 —— NO_COLOR + System.console()
fun ansiCapable(): Boolean {
    // The Unix-standard kill switch — honour first.
    if (System.getenv("NO_COLOR") != null) return false
    // System.console() is null when stdout is redirected or piped —
    // the JVM's portable "is this a TTY?" check. Works on JVM and
    // Kotlin/JVM-Native; for Kotlin/Native use platform.posix.isatty.
    if (System.console() == null) return false
    return true
}

fun style(text: String, sgr: String) =
    if (ansiCapable()) "\u001B[${sgr}m${text}\u001B[0m" else text

fun main() {
    println(style("OK", "32"))
    println(style("FAIL", "1;31"))
}
Gradle daemon 会剥离 ANSI —— 加 --console=plain
// The #1 "my ANSI doesn't show up" surprise on Kotlin/JVM:
// Gradle's daemon wraps stdout and strips escape bytes by default.
//
//   # Force plain (verbatim) output for one invocation:
//   ./gradlew run --console=plain
//
// Or set it persistently in gradle.properties at the project root:
//
//   # gradle.properties
//   org.gradle.console=plain
//
// IntelliJ's Run console behaves differently — it parses ANSI inline,
// so running from the IDE will show colours even when Gradle CLI hides
// them. Android Logcat does NOT parse ANSI at all; for Android targets
// use mordant's Theme.PlainAscii or capability-gate the output:
//
//   val t = Terminal(
//       ansiLevel = if (isAndroidLogcat) AnsiLevel.NONE else null
//   )

相关序列

其他语言