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();
+}