// Color
//
// Failure messages will be in color by default. Color is turned off until
// `moon test` can report it (see `color_supported`). Matchers build plain
// text, and
// color is added only when a failure message is rendered. Code that reads
// failure messages strips the color first.
//
// - `NO_COLOR` (set and not empty) or `TERM=dumb` turns color off.
// - `EXPECT_COLOR=0` or `EXPECT_COLOR=1` forces color off or on, and wins
// over the other variables.
///|
/// Whether `moon test` can report failure messages with color. Its test
/// driver writes control characters as `\u{1b}`, which is not valid JSON, so
/// a colored failure stops the whole test run (moonbitlang/moon#2257). Set
/// this to `true` when that is fixed.
let color_supported = false
///|
/// Whether failure messages are in color.
fn color_enabled() -> Bool {
color_supported && color_from_env()
}
///|
/// Whether the environment asks for color.
fn color_from_env() -> Bool {
match @env.get_env_var("EXPECT_COLOR") {
Some("0") => return false
Some("1") => return true
_ => ()
}
if @env.get_env_var("NO_COLOR") is Some(value) && !value.is_empty() {
return false
}
if @env.get_env_var("TERM") is Some("dumb") {
return false
}
true
}
///|
priv enum Style {
Bold
Red
Green
Yellow
Cyan
}
///|
/// `text` in `style` when `color` is on.
fn paint(text : String, style : Style, color : Bool) -> String {
if !color || text.is_empty() {
return text
}
let code = match style {
Bold => "1"
Red => "31"
Green => "32"
Yellow => "33"
Cyan => "36"
}
"\u{1b}[\{code}m\{text}\u{1b}[0m"
}
///|
/// A line of a diff in color: removed lines in red, added lines in green,
/// change marks in yellow and hunk headers in cyan.
fn paint_diff_line(line : String, color : Bool) -> String {
match line.get_char(0) {
Some('-') => paint(line, Red, color)
Some('+') => paint(line, Green, color)
Some('?') => paint(line, Yellow, color)
Some('@') => paint(line, Cyan, color)
_ => line
}
}
///|
/// `text` without ANSI escape sequences.
fn strip_ansi(text : String) -> String {
if !text.contains("\u{1b}[") {
return text
}
let builder = StringBuilder()
let chars = text.to_array()
let mut i = 0
while i < chars.length() {
if chars[i] == '\u{1b}' && i + 1 < chars.length() && chars[i + 1] == '[' {
i += 2
// Skip the parameters and the final letter.
while i < chars.length() && !chars[i].is_ascii_alphabetic() {
i += 1
}
i += 1
} else {
builder.write_char(chars[i])
i += 1
}
}
builder.to_string()
}