diff --git a/README.md b/README.md index ae57d71a..622f4170 100644 --- a/README.md +++ b/README.md @@ -1851,6 +1851,24 @@ for details. `requirement`, e.g., `">=0.1.0"`, returning `"true"` if so and `"false"` otherwise. +#### Style + +- `style(name)`master - Return a named terminal display attribute + escape sequence used by `just`. Unlike terminal display attribute escape + sequence constants, which contain standard colors and styles, `style(name)` + returns an escape sequence used by `just` itself, and can be used to make + recipe output match `just`'s own output. + + Recognized values for `name` are `'command'`, for echoed recipe lines, + `error`, and `warning`. + + For example, to style an error message: + + ```just + scary: + @echo '{{ style("error") }}OH NO{{ NORMAL }}' + ``` + ##### XDG Directories1.23.0 These functions return paths to user-specific directories for things like diff --git a/src/color.rs b/src/color.rs index 953b8ae9..7742597b 100644 --- a/src/color.rs +++ b/src/color.rs @@ -35,7 +35,6 @@ impl Color { Self::default() } - #[cfg(test)] pub(crate) fn always() -> Self { Self { use_color: UseColor::Always, diff --git a/src/function.rs b/src/function.rs index a714a8d0..abeae943 100644 --- a/src/function.rs +++ b/src/function.rs @@ -98,6 +98,7 @@ pub(crate) fn get(name: &str) -> Option { "snakecase" => Unary(snakecase), "source_directory" => Nullary(source_directory), "source_file" => Nullary(source_file), + "style" => Unary(style), "titlecase" => Unary(titlecase), "trim" => Unary(trim), "trim_end" => Unary(trim_end), @@ -623,6 +624,20 @@ fn source_file(context: Context) -> FunctionResult { }) } +fn style(context: Context, s: &str) -> FunctionResult { + match s { + "command" => Ok( + Color::always() + .command(context.evaluator.context.config.command_color) + .prefix() + .to_string(), + ), + "error" => Ok(Color::always().error().prefix().to_string()), + "warning" => Ok(Color::always().warning().prefix().to_string()), + _ => Err(format!("unknown style: `{s}`")), + } +} + fn titlecase(_context: Context, s: &str) -> FunctionResult { Ok(s.to_title_case()) } diff --git a/tests/functions.rs b/tests/functions.rs index 76964b74..d68b3946 100644 --- a/tests/functions.rs +++ b/tests/functions.rs @@ -1183,3 +1183,78 @@ bar: .args(["foo", "bar"]) .run(); } + +#[test] +fn style_command_default() { + Test::new() + .justfile( + r#" + foo: + @echo '{{ style("command") }}foo{{NORMAL}}' + "#, + ) + .stdout("\x1b[1mfoo\x1b[0m\n") + .run(); +} + +#[test] +fn style_command_non_default() { + Test::new() + .justfile( + r#" + foo: + @echo '{{ style("command") }}foo{{NORMAL}}' + "#, + ) + .args(["--command-color", "red"]) + .stdout("\x1b[1;31mfoo\x1b[0m\n") + .run(); +} + +#[test] +fn style_error() { + Test::new() + .justfile( + r#" + foo: + @echo '{{ style("error") }}foo{{NORMAL}}' + "#, + ) + .stdout("\x1b[1;31mfoo\x1b[0m\n") + .run(); +} + +#[test] +fn style_warning() { + Test::new() + .justfile( + r#" + foo: + @echo '{{ style("warning") }}foo{{NORMAL}}' + "#, + ) + .stdout("\x1b[1;33mfoo\x1b[0m\n") + .run(); +} + +#[test] +fn style_unknown() { + Test::new() + .justfile( + r#" + foo: + @echo '{{ style("hippo") }}foo{{NORMAL}}' + "#, + ) + .stderr( + r#" + error: Call to function `style` failed: unknown style: `hippo` + ——▶ justfile:2:13 + │ + 2 │ @echo '{{ style("hippo") }}foo{{NORMAL}}' + │ ^^^^^ + "#, + ) + .status(EXIT_FAILURE) + .run(); +}