跳到主要内容
ansicode
Julia

在 Julia 中使用 ANSI 转义码 —— \e、printstyled、Crayons.jl

Julia 的字符串字面量直接支持 `\e` —— 除 bash、zsh、perl、php 之外少数几种把反斜杠-e 解析为字节 27 的语言之一。`"\e[31m"` 与 `"\x1b[31m"`(十六进制)、`"\033[31m"`(八进制)、`"\u1b[31m"`(Unicode)完全等价 —— 任选其一即可。`println("\e[1;31merror:\e[0m permission denied")` 在 Julia ≥ 0.6 的所有版本,以及 macOS、Linux、BSD、Windows 10+ 的 Conhost 1709+ / Windows Terminal(均原生解析 ANSI)上工作。 惯用辅助 —— **优先用标准库** —— `printstyled(io, xs...; color=:red, bold=true, italic=true, underline=true, blink=true, reverse=true, hidden=true)` 是 Julia 标准的样式化打印函数。`color` 可以是命名 `Symbol`(`:red` / `:light_blue` / `:cyan`)、`0–255` 的整数(256 色板)或 `(r, g, b)` 三元组(在能力允许时 truecolor)。`Base.text_colors` 与 `Base.disable_text_style` 字典暴露原始 SGR 码,便于手工输出。可组合的字符串级样式选 **`Crayons.jl`** —— 事实上的第三方 Julia ANSI 库 —— `Crayon(foreground = :red, bold = true)` 构造可复用样式,通过 `*` 组合(`bold_red = Crayon(foreground=:red, bold=true) * Crayon(underline=true)`),`Box(content; foreground = :green)` 在值前后包裹起止字节。完整 TUI 链接 **`Term.jl`**(FedeClaudi)—— 面板、语法高亮打印、进度条、树形渲染、`tprint("[red bold]error[/red bold] denied")` 标记解析器。方向键交互菜单使用标准库 **`REPL.TerminalMenus`** —— 内置 `RadioMenu` / `MultiSelectMenu`,Pkg.jl 本身就用它做交互式包选择。 能力门控:`Base.have_color` 是标准标志位 —— Julia 在启动时从 `--color=auto|yes|no` 推断(`auto` 在 stdout 是 TTY 且未设置 `$NO_COLOR` 时为 `true`)。将 Julia 输出管道到 `less -R` 也按预期工作。**关键注意**:在 Jupyter / IJulia / Pluto notebook 前端中 `Base.have_color` 为 `false`,ANSI 转义会原样透传为文本 —— 在那里需要切换到 HTML 或按 MIME 类型化的渲染路径。Documenter.jl 使用 **`ANSIColoredPrinters.jl`** 将捕获的 ANSI 输出转换为 HTML 用于文档构建 —— 是从终端着色 `@example` 输出到渲染 HTML 的标准桥梁。

推荐库

  • printstyled (Base)

    Julia 标准库的标准样式化打印函数 —— `printstyled([io], xs...; color=:red, bold=true, italic=true, underline=true, blink=true, reverse=true, hidden=true)`。`color` 接受命名 `Symbol`(`:red` / `:light_blue` / `:cyan`)、`0–255` 的整数(256 色板)或 `(r, g, b)` 三元组(truecolor)。`Base.text_colors` 与 `Base.disable_text_style` 字典暴露底层 SGR 码以供手工输出。无需额外依赖。

  • Crayons.jl

    事实上的第三方 Julia ANSI 库。`Crayon(foreground = :red, background = :light_blue, bold = true, underline = true)` 构造可复用样式;`print(c, "text")` 应用样式;`Crayon(reset = true)` 回到默认。Crayon 通过 `*` 组合(`bold_red = Crayon(foreground=:red, bold=true) * Crayon(underline=true)`)。`Box(content; foreground = :green)` 自动在值前后包裹起止字节。当 `Base.have_color` 为 false 时自动禁用。

  • Term.jl

    现代 Julia TUI 库 —— `Panel`、`Tree`、`ProgressBar`、语法高亮代码打印(`@green "text"` 风格标记宏)、表格布局、`tprint("[red bold]error[/red bold] denied")` 标记解析器,以及 ANSI 感知的多行布局原语。当 `printstyled` + `Crayons` 不够、需要完整面板时选它。

  • REPL.TerminalMenus (stdlib)

    Julia 标准库交互式选单模块 —— `RadioMenu(options; pagesize=N)` 单选、`MultiSelectMenu(options; pagesize=N)` 多选、方向键导航、Enter 确认、q 或 Ctrl-C 取消。Pkg.jl 本身就用它做交互式包选择。继承 `AbstractMenu` 实现自定义控件。无需额外依赖。

常用写法

直接 println 配合 \e 与惯用的 printstyled
# Julia supports \e (backslash-e), \x1b (hex), \033 (octal),
# and \u1b (Unicode) in string literals — all parse to byte 27.
# \e reads cleanest; pick whichever you prefer.

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

# Truecolor — 38;2;R;G;B
println("\e[38;2;255;128;0morange truecolor\e[0m")

# Idiomatic Julia: printstyled wraps the bytes for you.
printstyled("error: "; color = :red, bold = true)
println("permission denied")
printstyled("warn: "; color = :yellow);  println("deprecated flag")
printstyled("ok: ";   color = :green);   println("142 tests passed")

# 256-colour — integer 0-255:
printstyled("orange (256-palette)\n"; color = 208)

# Truecolor — (r, g, b) tuple:
printstyled("orange (truecolor)\n"; color = (255, 128, 0))

# Underline + bold + reverse all compose as keyword args:
printstyled("FATAL\n"; color = :red, bold = true, reverse = true)
Crayons.jl —— 可组合 Crayon、`*` 组合、truecolor
# import Pkg; Pkg.add("Crayons")
using Crayons

# A Crayon is a reusable style; print(c, "x") applies it,
# print(Crayon(reset = true), ...) returns to default.
red = Crayon(foreground = :red, bold = true)
print(red, "error: ")
println(Crayon(reset = true), "permission denied")

# Compose via * — combine attributes into a single Crayon:
heading = Crayon(foreground = :blue, bold = true) * Crayon(underline = true)
println(heading, "Section 1", Crayon(reset = true))

# Box wraps content with start + reset bytes automatically:
println(Box("warm orange"; foreground = (255, 128, 0)))

# 256-palette — pass an Int 0-255:
println(Crayon(foreground = 208), "orange256", Crayon(reset = true))

# Background colour (named symbol):
println(
  Crayon(background = :yellow, foreground = :black),
  " CAUTION ",
  Crayon(reset = true),
  " brakes wet",
)

# String-interpolation friendly — Crayons stringify cleanly:
status = string(Crayon(foreground = :green), "PASS", Crayon(reset = true))
println("build: $(status)")
能力门控 —— Base.have_color + NO_COLOR + Jupyter 注意事项
# Base.have_color is set at Julia startup from --color=:
#   --color=auto (default): true when stdout is a TTY AND
#                           $NO_COLOR is unset
#   --color=yes / true:      always true (force-enable)
#   --color=no  / false:     always false (force-disable)
#
# Julia handles automatically:
#   - Piping stdout to a file → have_color = false
#   - $NO_COLOR set           → have_color = false
#
# CRUCIAL Jupyter / Pluto caveat: have_color = false in
# notebook front-ends — ANSI passes through as raw text.
# Switch to HTML / MIME-typed display there:
#
#   display("text/html", "<span style='color:red'>err</span>")

function styled(text::AbstractString, sgr::AbstractString)
    Base.have_color || return text
    return "\e[$(sgr)m$(text)\e[0m"
end

# Roll your own check when you don't want to depend on the
# startup flag — useful in libraries that may run under
# IOContext wrappers that reset have_color:
function ansi_capable(io::IO = stdout)
    haskey(ENV, "NO_COLOR") && return false
    return get(io, :color, false) === true || isa(io, Base.TTY)
end

println(styled("OK",   "32"))
println(styled("FAIL", "1;31"))

# Force-enable in scripts where capture wrapper resets have_color
# (e.g. tests with IOBuffer):
let buf = IOBuffer()
    ctx = IOContext(buf, :color => true)
    printstyled(ctx, "captured red\n"; color = :red)
    println(String(take!(buf)))   # contains real \e[31m bytes
end

# To force colour on at startup:  julia --color=yes script.jl
REPL.TerminalMenus —— RadioMenu / MultiSelectMenu 交互式选择
# stdlib — no Pkg.add needed.
using REPL.TerminalMenus

# Single-select arrow-key menu — same API Pkg.jl uses.
choice = request(
    "Pick a build profile:",
    RadioMenu(
        ["debug", "release", "release-with-debug-info"];
        pagesize = 5,
    ),
)
println("selected: ", choice)   # 1-based index; -1 if cancelled

# Multi-select: SPACE toggles, ENTER confirms, q/Ctrl-C cancels.
picks = request(
    "Pick test groups (space to toggle, enter to confirm):",
    MultiSelectMenu(
        ["unit", "integration", "e2e", "perf"];
        pagesize = 5,
    ),
)
println("selected: ", picks)    # Set{Int}, 1-based indices

# Menus emit raw ANSI — they work in every TTY-attached
# Julia REPL but NOT under IJulia / Pluto (no raw-mode stdin).
# Guard with isinteractive() + isa(stdin, Base.TTY) before
# calling request() in library code.

相关序列

其他语言