ok/jj
1
0
Fork 0
forked from mirrors/jj

templater: add surround(prefix, suffix, content) function

I'll probably add infix logical operators later, but the surround() function
is still useful because we don't have to repeat the condition:

    if(x || y, "<" ++ separate(" ", x, y) ++ ">")
    surround("<", ">", separate(" ", x, y))

It can't be used if we want to add placeholder text, though:

    if(x || y, "<" ++ separate(" ", x, y) ++ ">", "(none)")

Closes #2924
This commit is contained in:
Yuya Nishihara 2024-02-06 19:28:51 +09:00
parent 1adf6b5d6e
commit 026a72dfe7
3 changed files with 65 additions and 1 deletions

View file

@ -90,7 +90,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* `jj diff` no longer shows the contents of binary files.
* `jj git` now has an `init` command that initializes a git backed repo.
* New template function `surround(prefix, suffix, content)` is added.
### Fixed bugs

View file

@ -753,6 +753,23 @@ fn build_global_function<'a, L: TemplateLanguage<'a>>(
.try_collect()?;
language.wrap_template(Box::new(SeparateTemplate::new(separator, contents)))
}
"surround" => {
let [prefix_node, suffix_node, content_node] =
template_parser::expect_exact_arguments(function)?;
let prefix = expect_template_expression(language, build_ctx, prefix_node)?;
let suffix = expect_template_expression(language, build_ctx, suffix_node)?;
let content = expect_template_expression(language, build_ctx, content_node)?;
let template = ReformatTemplate::new(content, move |context, formatter, recorded| {
if recorded.data().is_empty() {
return Ok(());
}
prefix.format(context, formatter)?;
recorded.replay(formatter)?;
suffix.format(context, formatter)?;
Ok(())
});
language.wrap_template(Box::new(template))
}
_ => return Err(TemplateParseError::no_such_function(function)),
};
Ok(Expression::unlabeled(property))
@ -1745,4 +1762,48 @@ mod tests {
env.render_ok(r#"separate(hidden, "X", "Y", "Z")"#),
@"XfalseYfalseZ");
}
#[test]
fn test_surround_function() {
let mut env = TestTemplateEnv::default();
env.add_keyword("lt", |language| {
language.wrap_string(Literal("<".to_owned()))
});
env.add_keyword("gt", |language| {
language.wrap_string(Literal(">".to_owned()))
});
env.add_keyword("content", |language| {
language.wrap_string(Literal("content".to_owned()))
});
env.add_keyword("empty_content", |language| {
language.wrap_string(Literal("".to_owned()))
});
env.add_color("error", crossterm::style::Color::DarkRed);
env.add_color("paren", crossterm::style::Color::Cyan);
insta::assert_snapshot!(env.render_ok(r#"surround("{", "}", "")"#), @"");
insta::assert_snapshot!(env.render_ok(r#"surround("{", "}", "a")"#), @"{a}");
// Labeled
insta::assert_snapshot!(
env.render_ok(
r#"surround(label("paren", "("), label("paren", ")"), label("error", "a"))"#),
@"(a)");
// Keyword
insta::assert_snapshot!(
env.render_ok(r#"surround(lt, gt, content)"#),
@"<content>");
insta::assert_snapshot!(
env.render_ok(r#"surround(lt, gt, empty_content)"#),
@"");
// Conditional template as content
insta::assert_snapshot!(
env.render_ok(r#"surround(lt, gt, if(empty_content, "", "empty"))"#),
@"<empty>");
insta::assert_snapshot!(
env.render_ok(r#"surround(lt, gt, if(empty_content, "not empty", ""))"#),
@"");
}
}

View file

@ -75,6 +75,8 @@ The following functions are defined.
Same as `content_1 ++ ... ++ content_n`.
* `separate(separator: Template, content: Template...) -> Template`:
Insert separator between **non-empty** contents.
* `surround(prefix: Template, suffix: Template, content: Template) -> Template`:
Surround **non-empty** content with texts such as parentheses.
## Types