|
1 | 1 | (ns io.aviso.ansi
|
2 | 2 | "Help with generating textual output that includes ANSI escape codes for formatting."
|
3 |
| - (:import |
4 |
| - [java.util.regex Pattern]) |
5 |
| - (:require |
6 |
| - [clojure.string :as str])) |
| 3 | + (:require [clojure.string :as str] |
| 4 | + [clojure.walk :as walk])) |
| 5 | + |
| 6 | +(def ^:const ansi-output-enabled? |
| 7 | + "Determine if ANSI output is enabled. If the environment variable ENABLE_ANSI_COLORS is non-null, |
| 8 | + then it sets the value: the value `false` (matched caselessly) disables ANSI colors and fonts, |
| 9 | + otherwise they are enabled. |
| 10 | +
|
| 11 | + Next, there is an attempt to determine if execution is currently inside an REPL environment, |
| 12 | + possibly started from an IDE; a check is made to see if `nrepl.core` namespace is loaded; |
| 13 | + if so, then ANSI colors are enabled. |
| 14 | +
|
| 15 | + This has been verified to work with Cursive, with `lein repl`, and with `clojure` (or `clj`). |
| 16 | +
|
| 17 | + This check is necessary, because often in such cases, there is no console (the next check). |
| 18 | +
|
| 19 | + Otherwise, if the system has a console (via `(System/console)`) ANSI output will be enabled; |
| 20 | + when Clojure is running in a pipe, or as a background job without a terminal attached, the console |
| 21 | + will be nil and ANSI output will be disabled. |
| 22 | +
|
| 23 | + When this value is false, all the generated color and font constants return the empty string, and the |
| 24 | + color and font functions return the input string unchanged. This is decided during macro expansion when |
| 25 | + the ansi namespace is first loaded, so it can't be changed at runtime." |
| 26 | + (if-let [value (System/getenv "ENABLE_ANSI_COLORS")] |
| 27 | + (not (.equalsIgnoreCase value "false")) |
| 28 | + (some? |
| 29 | + (or |
| 30 | + (contains? (set (loaded-libs)) 'nrepl.core) |
| 31 | + (System/console))))) |
| 32 | + |
| 33 | +(defmacro ^:private if-enabled? |
| 34 | + [expr] |
| 35 | + (if ansi-output-enabled? |
| 36 | + expr |
| 37 | + "")) |
7 | 38 |
|
8 | 39 | (def ^:const csi
|
9 | 40 | "The control sequence initiator: `ESC [`"
|
|
14 | 45 | "The Select Graphic Rendition suffix: m"
|
15 | 46 | "m")
|
16 | 47 |
|
17 |
| -(def ^:const |
18 |
| - reset-font |
| 48 | +(def ^:const reset-font |
19 | 49 | "Resets the font, clearing bold, italic, color, and background color."
|
20 |
| - (str csi sgr)) |
| 50 | + (if-enabled? (str csi sgr))) |
21 | 51 |
|
22 | 52 | (defmacro ^:private def-sgr-const
|
23 | 53 | "Utility for defining a font-modifying constant."
|
24 | 54 | [symbol-name color-name & codes]
|
25 | 55 | `(def ~(vary-meta (symbol symbol-name) assoc :const true)
|
26 | 56 | ~(format "Constant for ANSI code to enable %s text." color-name)
|
27 |
| - (str csi ~(str/join ";" codes) sgr))) |
| 57 | + ~(if-enabled? (str csi (str/join ";" codes) sgr)))) |
28 | 58 |
|
29 | 59 | (defmacro ^:private def-sgr-fn
|
30 | 60 | "Utility for creating a function that enables some combination of SGR codes around some text, but resets
|
31 | 61 | the font after the text."
|
32 | 62 | [fn-name color-name & codes]
|
33 |
| - (let [arg 'text] |
| 63 | + (let [arg 'text |
| 64 | + prefix (str csi (str/join ";" codes) sgr)] |
34 | 65 | `(defn ~(symbol fn-name)
|
35 | 66 | ~(format "Wraps the provided text with ANSI codes to render as %s text." color-name)
|
36 | 67 | [~arg]
|
37 |
| - (str csi ~(str/join ";" codes) sgr ~arg reset-font)))) |
| 68 | + ~(if ansi-output-enabled? |
| 69 | + `(str ~prefix ~arg ~reset-font) |
| 70 | + arg)))) |
38 | 71 |
|
39 | 72 | ;;; Define functions and constants for each color. The functions accept a string
|
40 | 73 | ;;; and wrap it with the ANSI codes to set up a rendition before the text,
|
|
86 | 119 |
|
87 | 120 | (define-fonts)
|
88 | 121 |
|
89 |
| -(def ^:const ^:private ansi-pattern (Pattern/compile "\\e\\[.*?m")) |
| 122 | +(def ^:const ^:private ansi-pattern #"\e\[.*?m") |
90 | 123 |
|
91 | 124 | (defn ^String strip-ansi
|
92 | 125 | "Removes ANSI codes from a string, returning just the raw text."
|
|
0 commit comments