Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
* The `CryptographicSignature.key()` template method now also works for SSH
signatures and returns the corresponding public key fingerprint.

* Templates now support an `is_terminal_output()` function that returns true if
stdout is connected to a terminal.

### Fixed bugs

* `jj metaedit --author-timestamp` twice with the same value no longer
Expand Down
17 changes: 17 additions & 0 deletions cli/src/template_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use std::cmp::Ordering;
use std::collections::HashMap;
use std::io;
use std::io::IsTerminal as _;
use std::iter;

use itertools::Itertools as _;
Expand Down Expand Up @@ -1939,6 +1940,14 @@ fn builtin_functions<'a, L: TemplateLanguage<'a> + ?Sized>() -> TemplateBuildFun
// .decorated("", "") to trim leading/trailing whitespace
Ok(Literal(value.decorated("", "")).into_dyn_wrapped())
});
map.insert(
"is_terminal_output",
|_language, _diagnostics, _build_ctx, function| {
function.expect_no_arguments()?;
let is_terminal = io::stdout().is_terminal();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since template output may be written into file (when rendering commit description template for example), we should test if the output stream/formatter is terminal (or colored.) However, the templater doesn't know about the output stream. Maybe we can inject is_terminal context variable by TemplateRenderer, but I don't know how many terminal-related flags should be provided. Currently, there's just colored or not.

Another option is to rename this function to something like is_stdout_terminal to clarify that it is useless when rendering description templates.

Ok(Literal(is_terminal).into_dyn_wrapped())
},
);
map
}

Expand Down Expand Up @@ -3874,4 +3883,12 @@ mod tests {
env.render_ok(r#"surround(lt, gt, if(empty_content, "not empty", ""))"#),
@"");
}

#[test]
fn test_is_terminal_output_function() {
let env = TestTemplateEnv::new();

// When running tests, stdout is typically not a terminal, so we expect false
insta::assert_snapshot!(env.render_ok(r#"is_terminal_output()"#), @"false");
}
}
4 changes: 4 additions & 0 deletions docs/templates.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ The following functions are defined.
* `surround(prefix: Template, suffix: Template, content: Template) -> Template`:
Surround **non-empty** content with texts such as parentheses.
* `config(name: String) -> ConfigValue`: Look up configuration value by `name`.
* `is_terminal_output() -> Boolean`: Returns true if stdout is connected to a
terminal, or false otherwise. This is mostly useful to conditionally output
escape codes or other terminal-specific formatting only when writing to
a terminal.

## Built-in Aliases

Expand Down