跳到主要内容
ansicode
Clojure

在 Clojure 中使用 ANSI 转义码 —— \u001b、clansi、io.aviso/pretty

Clojure 继承 Java 字符串字面量规则 —— 只支持 `\u####` Unicode 转义。`\e` 不支持、`\x1b` 不支持、`\033` 不支持。写 `"\u001b[31m"`,且 `\u` 后的十六进制必须小写(Clojure 读取器对 Unicode 转义形式区分大小写)。`(println "\u001b[1;31merror:\u001b[0m permission denied")` 在 JVM 宿主的 Clojure ≥ 1.0 以及 ClojureScript(编译到 JS,`"\u001b"` 同样有效)上工作。在 macOS、Linux、BSD、Windows 10+ 的 Conhost 1709+ / Windows Terminal 中,终端原生解析 ANSI。 惯用辅助选 **`clansi`** —— 经典的最小 Clojure ANSI 库 —— `(clansi.core/style "error" :red :bold)` 返回带 SGR 前缀与 reset 的样式化字符串。需要 Leiningen 级结构化输出与彩色栈追踪选 **`io.aviso/pretty`** —— Leiningen 自身彩色错误格式化所用的库;`io.aviso.ansi/red`、`io.aviso.ansi/bold-red`,以及 `io.aviso.exception/format-exception` 用于漂亮的栈追踪。需要数据结构的彩色漂亮打印选 **`mvxcvi/puget`** —— 标准 Clojure pretty-printer,被 `cider` 的检查器以及 deps.edn 工具链用作人类可读 EDN;`(puget.printer/cprint value)` 输出 ANSI 着色的缩进 EDN。需要 Windows VT 启用(Conhost 1709 之前)与 JVM 可移植终端能力时,通过 Java 互操作使用 **`jansi`** —— `(org.fusesource.jansi.AnsiConsole/systemInstall)` 在每个支持 JVM 的平台启用 VT。 能力门控:`(some? (System/console))` 是 JVM 可移植的检查(stdin/stdout 重定向时返回 `nil`)。搭配 `(System/getenv "NO_COLOR")` 处理 env 约定。**CIDER nREPL 注意(本页主 SERP 差异化)**:编辑器附着的 REPL(Emacs 的 CIDER、VSCode 的 Calva、IntelliJ 的 Cursive)使用 nREPL bencode 协议 —— 它们消费返回的 `:value` 字段并自行渲染,**不会**直接管道 stdout。`:value` 字符串里的 ANSI 转义会原样穿透但大多数编辑器 REPL 会按字面文本显示。终端附着的 REPL(`lein repl`、`clj`、`bb`)会解析 stdout 的 ANSI。在编辑器 REPL 中确实需要彩色输出时,向 `*err*` 打印(CIDER 会原样透传 stderr,让宿主终端渲染颜色),或安装 `cider-nrepl` 的 `print-color` 中间件 —— 它把 SGR 码映射为编辑器字体修饰。

推荐库

  • clansi

    经典的最小 Clojure ANSI 库。`(clansi.core/style "error" :red :bold)` 返回带 SGR 前缀与 reset 的样式化字符串。样式关键字:`:red`/`:green`/`:blue`/`:yellow`/`:magenta`/`:cyan`/`:white`、`:bg-red` 等(背景色)、`:bold`/`:underline`/`:reverse`/`:blink`/`:reset`。零魔法 —— 通过普通字符串拼接组合。不想要更重的库时的标准选择。

  • io.aviso/pretty

    Leiningen 级结构化输出 —— `lein` 自身用于彩色错误格式化的库。`io.aviso.ansi/red`、`io.aviso.ansi/bold-red`、`io.aviso.ansi/cyan-bg` 等命名 SGR 辅助。`(io.aviso.exception/format-exception e)` 渲染带颜色、感知源文件的栈追踪,远比 JVM 默认追踪可读。当你希望异常输出不再像 Java 企业级文本墙时,这是事实上的标准答案。

  • mvxcvi/puget

    标准 Clojure pretty-printer,支持 ANSI 着色输出。`(puget.printer/cprint value)` 输出 ANSI 着色的缩进 EDN —— 被 `cider` 检查器与 deps.edn 工具链使用。通过 `{:color-scheme {:keyword [:bold :cyan] :string [:green]}}` 可配置调色板。当 stdout 不是 TTY 时切到普通的 `puget.printer/pprint`。需要嵌套数据结构可读输出时选它。

  • jansi (Java interop)

    JVM 可移植的 ANSI 库 —— Clojure 栈的 Java 一侧。`(org.fusesource.jansi.AnsiConsole/systemInstall)` 在 Conhost 1709 之前的 Windows 上启用 VT 模式,并把 `System/out` 重新路由到 ANSI 感知的封装。`(org.fusesource.jansi.Ansi/ansi)` 提供流畅的构造器 API,可通过 Clojure 的 `.` 互操作访问。需要 Windows 10 1709 之前的支持,或包装已经使用 jansi 的 Java 库时引入它。

常用写法

直接 println 配合 \u001b —— Java 规则,ClojureScript 兼容
;; Clojure inherits Java string-literal rules — only \u####
;; Unicode escapes work. \e / \x1b / \033 are NOT supported.
;; Hex digits after \u must be lowercase: \u001b not \u001B.
;; The same syntax compiles unchanged on ClojureScript.

(ns example.ansi)

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

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

;; Reusable helper — wraps SGR prefix + reset around text:
(defn esc [sgr text]
  (str "\u001b[" sgr "m" text "\u001b[0m"))

(println (esc "1;31" "FATAL") "server crashed")
(println (esc "32"   "ok:")    "142 tests passed")
(println (esc "38;2;255;128;0" "truecolor orange"))
clansi + io.aviso/pretty —— 样式关键字 + 彩色异常栈追踪
;; deps.edn:
;;   {:deps {clansi/clansi      {:mvn/version "1.0.0"}
;;           io.aviso/pretty    {:mvn/version "1.4.4"}}}
;;
;; project.clj:
;;   :dependencies [[clansi "1.0.0"]
;;                  [io.aviso/pretty "1.4.4"]]

(require '[clansi.core :as ansi]
         '[io.aviso.ansi :as aansi]
         '[io.aviso.exception :as aex])

;; clansi/style wraps text with named SGR + reset.
;; Keywords compose left-to-right inside one call.
(println (ansi/style "error: " :red :bold) "permission denied")
(println (ansi/style "ok: " :green)        "142 tests passed")
(println (ansi/style " CAUTION " :black :bg-yellow) " brakes wet")

;; io.aviso.ansi — named helpers (same vocabulary Leiningen
;; uses internally for its own coloured output):
(println (aansi/red "error:")        "permission denied")
(println (aansi/bold-red "FATAL")    "server crashed")
(println (aansi/bold-underlined "Section 1"))

;; Pretty-print an exception with coloured stack frames —
;; THE Clojure-idiomatic way to render exceptions to a TTY:
(try
  (/ 1 0)
  (catch ArithmeticException e
    (println (aex/format-exception e))))
mvxcvi/puget —— 嵌套数据结构的彩色漂亮打印
;; deps.edn: {:deps {mvxcvi/puget {:mvn/version "1.3.4"}}}

(require '[puget.printer :as puget])

(def data
  {:users [{:name "alice" :email "[email protected]" :age 30}
           {:name "bob"   :email "[email protected]" :age 25}]
   :counts {:active 42 :inactive 7}
   :timestamp #inst "2026-05-19T10:00:00Z"})

;; cprint = "coloured print" — ANSI-coloured indented EDN
;; (default palette matches CIDER's inspector colours):
(puget/cprint data)

;; cprint-str returns the coloured string instead of printing:
(let [out (puget/cprint-str data)]
  (spit "snapshot.txt" out))

;; Switch to plain pprint when stdout isn't a TTY:
(if (some? (System/console))
  (puget/cprint data)
  (puget/pprint data))

;; Custom palette — override individual SGR codes per type:
(puget/cprint data
              {:print-color true
               :color-scheme {:keyword [:bold :cyan]
                              :string  [:green]
                              :number  [:yellow]
                              :nil     [:bold :black]}})
能力门控 + CIDER nREPL vs lein repl ANSI 透传注意事项
;; Capability gating in Clojure:
;;   (some? (System/console))      — JVM-portable, returns nil
;;                                    when stdin/stdout redirected
;;   (System/getenv "NO_COLOR")    — universal env-var convention
;;   (System/getenv "FORCE_COLOR") — universal opt-in
;;
;; CIDER nREPL CAVEAT (the #1 "my colours don't show up in CIDER"
;; friction point):
;;
;; Terminal-attached REPLs (lein repl, clj, bb) speak raw stdout,
;; so ANSI passes through and the host terminal parses it.
;;
;; Editor-attached REPLs (CIDER in Emacs, Calva in VSCode,
;; Cursive in IntelliJ) speak the nREPL bencode protocol — they
;; consume the result :value field and render it themselves.
;; ANSI bytes inside :value pass through unchanged but most
;; editor REPLs display them as raw text.
;;
;; Fix when you actually need coloured output in an editor REPL:
;;   1) Print to *err* — CIDER passes stderr through verbatim,
;;      so the host terminal (not the editor buffer) renders it.
;;   2) Install cider-nrepl's print-color middleware, which
;;      maps SGR codes to editor face overlays.

(defn ansi-capable? []
  (cond
    (System/getenv "NO_COLOR")    false
    (System/getenv "FORCE_COLOR") true
    :else (some? (System/console))))

(defn styled [text sgr]
  (if (ansi-capable?)
    (str "\u001b[" sgr "m" text "\u001b[0m")
    text))

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

;; Force-render to the host terminal even when CIDER is the
;; attached REPL — *err* is passed through verbatim:
(binding [*out* *err*]
  (println (styled "DEBUG" "36") "checkpoint hit"))

相关序列

其他语言