std/tty
Terminal detection and ANSI coloring.
A small, terminal-aware coloring API. The color/style helpers return a string
that carries ANSI SGR codes only when coloring is enabled; otherwise they
return their input unchanged, so callers keep using println / eprintln
as-is.
Coloring is enabled when either standard stream is an interactive terminal
(so a build that redirects only one of stdout/stderr still colors what stays
on the terminal) and the NO_COLOR convention is not in effect. The
detection runs once per stream, in a @singleton.
Cross-platform: the codes used here (the 16-color palette + bold/dim/italic/
underline/reverse) are understood by POSIX terminals and, on Windows 10+, by
the console once virtual-terminal processing is enabled, which is_tty does
on its first probe. On a console too old for VT, is_tty reports false, so
output degrades to plain text rather than leaking raw escapes. 256-color and
24-bit "true color" are deliberately omitted: they are not reliably supported
across the three targets.
Functions
raw_mode_begin :: fn() -> bool Put stdin in raw mode: keys arrive as they are pressed, with no echo and no line editing.
It also turns OFF signal generation, so Ctrl-C arrives as the byte 0x03
instead of killing the process. That is what keeps the terminal usable (a
process killed mid-prompt would leave the shell with no echo) and it makes
handling the cancellation the caller's job.
⚠️ Always pair it with defer raw_mode_end(). defer covers the normal
return AND a panic (the runtime runs pending defers before aborting), which
is the whole reason the restore does not need a signal handler. What it does
not cover is a kill from outside the process; a shell's reset fixes that.
⚠️ On POSIX this goes through cfmakeraw, which also clears OPOST: a bare
\n no longer returns the carriage. Print through raw_print between
begin and end: println is only safe once the terminal is restored.
Returns
bool true when raw mode is now installed, false when stdin is not an interactive terminal: in which case NOTHING was changed and the caller must fall back rather than read keys.raw_mode_end :: fn() -> void Restore the terminal state saved by raw_mode_begin. A no-op when raw mode
is not installed, so an unpaired call is harmless.
columns :: fn() -> i32 Terminal width in columns, or 0 when it cannot be determined, not a terminal, or the query failed. A caller that needs a number anyway should pick its own default rather than treat 0 as a width.
Returns
i32 read_keys :: fn(max: i32) -> string | null Read the keys pressed so far, blocking until there is at least one.
Returns everything already queued, up to max bytes (capped at 64). Reading
a BURST rather than a byte is what makes a lone Escape distinguishable from
the start of an arrow sequence: the arrow's three bytes arrive in one call,
Escape alone arrives as one byte. No timing is involved, and it behaves the
same on POSIX and on a Windows console, whose keys arrive as the UTF-8 of
what was typed whatever its code page.
Parameters
| max | i32 | ceiling on the bytes returned; values above 64 are clamped. |
Returns
string | null the bytes read, never empty, since a successful read yields at least one byte, or null at end of input, on a read failure, and on a target with no terminal.raw_print :: fn(s: string) -> void Print s the way a raw-mode terminal needs, terminated.
raw_mode_begin clears OPOST on POSIX, so a bare \n advances a row
without returning the carriage. s may be several lines: terminating only
the last one leaves the rest climbing a staircase to the right.
Parameters
| s | string | one line, or a whole frame joined on \n. |
is_tty :: fn(stream: Stream = .Stdout) -> bool True when stream is connected to an interactive terminal whose ANSI codes
will render; defaults to stdout, so is_tty() checks stdout. On Windows this
also enables virtual-terminal processing on the stream (and reports false
when the console is too old to support it). The probe runs once per stream.
Parameters
| stream | Stream = .Stdout | the stream to probe. |
Returns
bool renders_unicode :: fn(stream: Stream = .Stdout) -> bool True when text beyond ASCII written to stream renders as such, so it may
draw with box and shape glyphs. A POSIX terminal is taken as UTF-8. On
Windows only a console renders it, print writing to one as text; a
redirected stream gets bytes whose reader may decode them otherwise.
Parameters
| stream | Stream = .Stdout | the stream that will be written to. |
Returns
bool should_colorize :: fn() -> bool True when ANSI color should be emitted: either standard stream is a terminal
and NO_COLOR is unset or empty. Per the NO_COLOR convention, a non-empty
NO_COLOR value disables color regardless of its content.
Returns
bool without_escapes :: fn(s: string) -> string s without the colour escapes the helpers here wrap text in, ESC [, a code
and m: the text a reader sees, to measure or to compare.
Parameters
| s | string | painted text. |
Returns
string Example
without_escapes("\u{1B}[31mred\u{1B}[0m") // "red"sgr :: fn(code: string, s: string) -> string Wrap s in an arbitrary SGR sequence code (e.g. "1;31") when coloring is
enabled, else return s unchanged. Escape hatch for combinations the named
helpers don't cover; prefer the named helpers for the common cases.
Parameters
| code | string | No description. |
| s | string | No description. |
Returns
string reset :: fn() -> string Reset all SGR attributes (SGR 0) when coloring is enabled, else "". Useful when composing sequences by hand.
Returns
string bold :: fn(s: string) -> string Bold (SGR 1): emphasis, e.g. diagnostic codes.
Parameters
| s | string | No description. |
Returns
string dim :: fn(s: string) -> string Dim / faint (SGR 2). Renders on modern terminals; a no-op on consoles that don't support it (never raw escapes).
Parameters
| s | string | No description. |
Returns
string italic :: fn(s: string) -> string Italic (SGR 3). As dim, degrades to a no-op where unsupported.
Parameters
| s | string | No description. |
Returns
string underline :: fn(s: string) -> string Underline (SGR 4).
Parameters
| s | string | No description. |
Returns
string reverse :: fn(s: string) -> string Reverse video: swap foreground and background (SGR 7).
Parameters
| s | string | No description. |
Returns
string black :: fn(s: string) -> string Black foreground (SGR 30).
Parameters
| s | string | No description. |
Returns
string red :: fn(s: string) -> string Red foreground (SGR 31): errors / failures.
Parameters
| s | string | No description. |
Returns
string green :: fn(s: string) -> string Green foreground (SGR 32): successes.
Parameters
| s | string | No description. |
Returns
string yellow :: fn(s: string) -> string Yellow foreground (SGR 33): warnings (the ANSI stand-in for orange).
Parameters
| s | string | No description. |
Returns
string blue :: fn(s: string) -> string Blue foreground (SGR 34).
Parameters
| s | string | No description. |
Returns
string magenta :: fn(s: string) -> string Magenta foreground (SGR 35).
Parameters
| s | string | No description. |
Returns
string cyan :: fn(s: string) -> string Cyan foreground (SGR 36).
Parameters
| s | string | No description. |
Returns
string white :: fn(s: string) -> string White foreground (SGR 37).
Parameters
| s | string | No description. |
Returns
string gray :: fn(s: string) -> string Bright black / gray foreground (SGR 90).
Parameters
| s | string | No description. |
Returns
string bright_red :: fn(s: string) -> string Bright red foreground (SGR 91).
Parameters
| s | string | No description. |
Returns
string bright_green :: fn(s: string) -> string Bright green foreground (SGR 92).
Parameters
| s | string | No description. |
Returns
string bright_yellow :: fn(s: string) -> string Bright yellow foreground (SGR 93).
Parameters
| s | string | No description. |
Returns
string bright_blue :: fn(s: string) -> string Bright blue foreground (SGR 94).
Parameters
| s | string | No description. |
Returns
string bright_magenta :: fn(s: string) -> string Bright magenta foreground (SGR 95).
Parameters
| s | string | No description. |
Returns
string bright_cyan :: fn(s: string) -> string Bright cyan foreground (SGR 96).
Parameters
| s | string | No description. |
Returns
string bright_white :: fn(s: string) -> string Bright white foreground (SGR 97).
Parameters
| s | string | No description. |
Returns
string