templater: require infix ++ operator to concatenate expressions

This eliminates ambiguous parsing between "func()" and "expr ()".

I chose "++" as template concatenation operator in case we want to add
bit-wise negate operator. It's also easier to find/replace than "~".
This commit is contained in:
Yuya Nishihara 2023-02-28 20:30:57 +09:00
parent fd27d228ed
commit 66458a097e
23 changed files with 104 additions and 100 deletions

View file

@ -13,6 +13,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
`format_time_range()` template alias instead. For details, see
[the documentation](docs/config.md).
* Implicit concatenation of template expressions has been disabled. Use
`++` operator, `concat()`, or `separate()` function instead.
Example: `description ++ "\n"`
### New features
* `jj git push --deleted` will remove all locally deleted branches from the remote.

View file

@ -147,7 +147,7 @@ Can be customized by the `format_short_id()` template alias.
# Just the shortest possible unique prefix
'format_short_id(id)' = 'id.shortest()'
# Show unique prefix and the rest surrounded by brackets
'format_short_id(id)' = 'id.shortest(12).prefix() "[" id.shortest(12).rest() "]"'
'format_short_id(id)' = 'id.shortest(12).prefix() ++ "[" ++ id.shortest(12).rest() ++ "]"'
# Always show 12 characters
'format_short_id(id)' = 'id.short(12)'
```
@ -178,7 +178,7 @@ will need to modify the `format_time_range()` template alias.
```toml
[template-aliases]
'format_time_range(time_range)' = 'time_range.start() " - " time_range.end()'
'format_time_range(time_range)' = 'time_range.start() ++ " - " ++ time_range.end()'
```
### Author format

View file

@ -11,7 +11,7 @@ label(if(current_working_copy, "working_copy"),
concat(
separate(" ",
if(divergent,
label("divergent", format_short_change_id(change_id) "??"),
label("divergent", format_short_change_id(change_id) ++ "??"),
format_short_change_id(change_id)),
format_short_signature(author),
format_timestamp(committer.timestamp()),
@ -21,11 +21,11 @@ label(if(current_working_copy, "working_copy"),
git_head,
format_short_commit_id(commit_id),
if(conflict, label("conflict", "conflict")),
) "\n",
) ++ "\n",
separate(" ",
if(empty, label("empty", "(empty)")),
if(description, description.first_line(), description_placeholder),
) "\n",
) ++ "\n",
),
)
'''
@ -37,8 +37,8 @@ label(if(current_operation, "current_operation"),
id.short(),
user,
format_time_range(time),
) "\n",
description.first_line() "\n",
) ++ "\n",
description.first_line() ++ "\n",
tags,
),
)
@ -56,19 +56,19 @@ show = 'show'
'format_short_commit_id(id)' = 'format_short_id(id)'
'format_short_signature(signature)' = 'signature.email()'
'format_time_range(time_range)' = '''
time_range.start().ago() label("time", ", lasted ") time_range.duration()'''
time_range.start().ago() ++ label("time", ", lasted ") ++ time_range.duration()'''
'format_timestamp(timestamp)' = 'timestamp'
# TODO: Add branches, tags, etc
# TODO: Indent the description like Git does
'show' = '''
concat(
"Commit ID: " commit_id "\n",
"Change ID: " change_id "\n",
"Author: " author " (" format_timestamp(author.timestamp()) ")\n",
"Committer: " committer " (" format_timestamp(committer.timestamp()) ")\n",
"Commit ID: " ++ commit_id ++ "\n",
"Change ID: " ++ change_id ++ "\n",
"Author: " ++ author ++ " (" ++ format_timestamp(author.timestamp()) ++ ")\n",
"Committer: " ++ committer ++ " (" ++ format_timestamp(committer.timestamp()) ++ ")\n",
"\n",
if(description, description, description_placeholder "\n"),
if(description, description, description_placeholder ++ "\n"),
"\n",
)
'''

View file

@ -13,9 +13,9 @@
// limitations under the License.
// Example:
// "commit: " short(commit_id) "\n"
// predecessors % ("predecessor: " commit_id)
// parents % (commit_id " is a parent of " super.commit_id)
// "commit: " ++ short(commit_id) ++ "\n"
// predecessors % ("predecessor: " ++ commit_id)
// parents % (commit_id ++ " is a parent of " ++ super.commit_id)
whitespace = _{ " " | "\n" }
@ -41,7 +41,6 @@ formal_parameters = {
| ""
}
// Note that "x(y)" is a function call but "x (y)" concatenates "x" and "y"
primary = _{
("(" ~ whitespace* ~ template ~ whitespace* ~ ")")
| function
@ -55,7 +54,7 @@ term = {
}
list = _{
term ~ (whitespace+ ~ term)+
term ~ (whitespace* ~ "++" ~ whitespace* ~ term)+
}
template = { list | term }

View file

@ -1254,16 +1254,16 @@ mod tests {
#[test]
fn test_parse_tree_eq() {
assert_eq!(
normalize_tree(parse_template(r#" commit_id.short(1 ) description"#).unwrap()),
normalize_tree(parse_template(r#"commit_id.short( 1 ) (description)"#).unwrap()),
normalize_tree(parse_template(r#" commit_id.short(1 ) ++ description"#).unwrap()),
normalize_tree(parse_template(r#"commit_id.short( 1 )++(description)"#).unwrap()),
);
assert_ne!(
normalize_tree(parse_template(r#" "ab" "#).unwrap()),
normalize_tree(parse_template(r#" "a" "b" "#).unwrap()),
normalize_tree(parse_template(r#" "a" ++ "b" "#).unwrap()),
);
assert_ne!(
normalize_tree(parse_template(r#" "foo" "0" "#).unwrap()),
normalize_tree(parse_template(r#" "foo" 0 "#).unwrap()),
normalize_tree(parse_template(r#" "foo" ++ "0" "#).unwrap()),
normalize_tree(parse_template(r#" "foo" ++ 0 "#).unwrap()),
);
}
@ -1335,24 +1335,24 @@ mod tests {
#[test]
fn test_expand_symbol_alias() {
assert_eq!(
with_aliases([("AB", "a b")])
.parse_normalized("AB c")
with_aliases([("AB", "a ++ b")])
.parse_normalized("AB ++ c")
.unwrap(),
parse_normalized("(a b) c").unwrap(),
parse_normalized("(a ++ b) ++ c").unwrap(),
);
assert_eq!(
with_aliases([("AB", "a b")])
with_aliases([("AB", "a ++ b")])
.parse_normalized("if(AB, label(c, AB))")
.unwrap(),
parse_normalized("if((a b), label(c, (a b)))").unwrap(),
parse_normalized("if((a ++ b), label(c, (a ++ b)))").unwrap(),
);
// Multi-level substitution.
assert_eq!(
with_aliases([("A", "BC"), ("BC", "b C"), ("C", "c")])
with_aliases([("A", "BC"), ("BC", "b ++ C"), ("C", "c")])
.parse_normalized("A")
.unwrap(),
parse_normalized("b c").unwrap(),
parse_normalized("b ++ c").unwrap(),
);
// Method receiver and arguments should be expanded.
@ -1375,7 +1375,7 @@ mod tests {
TemplateParseErrorKind::BadAliasExpansion("A".to_owned()),
);
assert_eq!(
with_aliases([("A", "B"), ("B", "b C"), ("C", "c B")])
with_aliases([("A", "B"), ("B", "b ++ C"), ("C", "c ++ B")])
.parse("A")
.unwrap_err()
.kind,
@ -1404,48 +1404,48 @@ mod tests {
parse_normalized("a").unwrap(),
);
assert_eq!(
with_aliases([("F( x, y )", "x y")])
with_aliases([("F( x, y )", "x ++ y")])
.parse_normalized("F(a, b)")
.unwrap(),
parse_normalized("a b").unwrap(),
parse_normalized("a ++ b").unwrap(),
);
// Arguments should be resolved in the current scope.
assert_eq!(
with_aliases([("F(x,y)", "if(x, y)")])
.parse_normalized("F(a y, b x)")
.parse_normalized("F(a ++ y, b ++ x)")
.unwrap(),
parse_normalized("if((a y), (b x))").unwrap(),
parse_normalized("if((a ++ y), (b ++ x))").unwrap(),
);
// F(a) -> if(G(a), y) -> if((x a), y)
// F(a) -> if(G(a), y) -> if((x ++ a), y)
assert_eq!(
with_aliases([("F(x)", "if(G(x), y)"), ("G(y)", "x y")])
with_aliases([("F(x)", "if(G(x), y)"), ("G(y)", "x ++ y")])
.parse_normalized("F(a)")
.unwrap(),
parse_normalized("if((x a), y)").unwrap(),
parse_normalized("if((x ++ a), y)").unwrap(),
);
// F(G(a)) -> F(x a) -> if(G(x a), y) -> if((x (x a)), y)
// F(G(a)) -> F(x ++ a) -> if(G(x ++ a), y) -> if((x ++ (x ++ a)), y)
assert_eq!(
with_aliases([("F(x)", "if(G(x), y)"), ("G(y)", "x y")])
with_aliases([("F(x)", "if(G(x), y)"), ("G(y)", "x ++ y")])
.parse_normalized("F(G(a))")
.unwrap(),
parse_normalized("if((x (x a)), y)").unwrap(),
parse_normalized("if((x ++ (x ++ a)), y)").unwrap(),
);
// Function parameter should precede the symbol alias.
assert_eq!(
with_aliases([("F(X)", "X"), ("X", "x")])
.parse_normalized("F(a) X")
.parse_normalized("F(a) ++ X")
.unwrap(),
parse_normalized("a x").unwrap(),
parse_normalized("a ++ x").unwrap(),
);
// Function parameter shouldn't be expanded in symbol alias.
assert_eq!(
with_aliases([("F(x)", "x A"), ("A", "x")])
with_aliases([("F(x)", "x ++ A"), ("A", "x")])
.parse_normalized("F(a)")
.unwrap(),
parse_normalized("a x").unwrap(),
parse_normalized("a ++ x").unwrap(),
);
// Function and symbol aliases reside in separate namespaces.
@ -1474,7 +1474,7 @@ mod tests {
TemplateParseErrorKind::InvalidArgumentCountExact(1),
);
assert_eq!(
with_aliases([("F(x,y)", "x y")])
with_aliases([("F(x,y)", "x ++ y")])
.parse("F(a,b,c)")
.unwrap_err()
.kind,

View file

@ -102,6 +102,6 @@ fn test_branch_forget_glob() {
}
fn get_log_output(test_env: &TestEnvironment, cwd: &Path) -> String {
let template = r#"branches " " commit_id.short()"#;
let template = r#"branches ++ " " ++ commit_id.short()"#;
test_env.jj_cmd_success(cwd, &["log", "-T", template])
}

View file

@ -100,6 +100,6 @@ fn test_checkout_not_single_rev() {
}
fn get_log_output(test_env: &TestEnvironment, cwd: &Path) -> String {
let template = r#"commit_id " " description"#;
let template = r#"commit_id ++ " " ++ description"#;
test_env.jj_cmd_success(cwd, &["log", "-T", template])
}

View file

@ -89,6 +89,6 @@ fn test_commit_without_working_copy() {
}
fn get_log_output(test_env: &TestEnvironment, cwd: &Path) -> String {
let template = r#"commit_id.short() " " description"#;
let template = r#"commit_id.short() ++ " " ++ description"#;
test_env.jj_cmd_success(cwd, &["log", "-T", template])
}

View file

@ -43,7 +43,7 @@ fn test_log_author_timestamp_ago() {
test_env.jj_cmd_success(&repo_path, &["describe", "-m", "first"]);
test_env.jj_cmd_success(&repo_path, &["new", "-m", "second"]);
let template = r#"author.timestamp().ago() "\n""#;
let template = r#"author.timestamp().ago() ++ "\n""#;
let stdout = test_env.jj_cmd_success(&repo_path, &["log", "--no-graph", "-T", template]);
let line_re = Regex::new(r"[0-9]+ years ago").unwrap();
assert!(
@ -177,7 +177,7 @@ fn test_log_customize_short_id() {
&[
"log",
"--config-toml",
&format!(r#"{decl}='id.shortest(5).prefix().upper() "_" id.shortest(5).rest()'"#),
&format!(r#"{decl}='id.shortest(5).prefix().upper() ++ "_" ++ id.shortest(5).rest()'"#),
],
);
insta::assert_snapshot!(stdout, @r###"

View file

@ -138,6 +138,6 @@ fn test_concurrent_operations_wc_modified() {
}
fn get_log_output(test_env: &TestEnvironment, cwd: &Path) -> String {
let template = r#"commit_id " " description"#;
let template = r#"commit_id ++ " " ++ description"#;
test_env.jj_cmd_success(cwd, &["log", "-T", template])
}

View file

@ -322,12 +322,13 @@ fn test_rebase_duplicates() {
}
fn get_log_output(test_env: &TestEnvironment, repo_path: &Path) -> String {
let template = r#"commit_id.short() " " description.first_line()"#;
let template = r#"commit_id.short() ++ " " ++ description.first_line()"#;
test_env.jj_cmd_success(repo_path, &["log", "-T", template])
}
fn get_log_output_with_ts(test_env: &TestEnvironment, repo_path: &Path) -> String {
let template =
r#"commit_id.short() " " description.first_line() " @ " committer.timestamp()"#;
let template = r###"
commit_id.short() ++ " " ++ description.first_line() ++ " @ " ++ committer.timestamp()
"###;
test_env.jj_cmd_success(repo_path, &["log", "-T", template])
}

View file

@ -109,7 +109,7 @@ fn read_file(path: &Path) -> String {
}
fn get_log_output(test_env: &TestEnvironment, cwd: &Path) -> String {
let template = r#"commit_id.short() " " description"#;
let template = r#"commit_id.short() ++ " " ++ description"#;
test_env.jj_cmd_success(cwd, &["log", "-T", template])
}

View file

@ -312,7 +312,7 @@ fn get_log_output_divergence(test_env: &TestEnvironment, repo_path: &Path) -> St
}
fn get_log_output(test_env: &TestEnvironment, workspace_root: &Path) -> String {
let template = r#"commit_id " " branches"#;
let template = r#"commit_id ++ " " ++ branches"#;
test_env.jj_cmd_success(workspace_root, &["log", "-T", template, "-r=all()"])
}

View file

@ -64,7 +64,7 @@ fn create_commit(test_env: &TestEnvironment, repo_path: &Path, name: &str, paren
}
fn get_log_output(test_env: &TestEnvironment, workspace_root: &Path) -> String {
let template = r#"commit_id.short() " " description.first_line() " " branches"#;
let template = r#"commit_id.short() ++ " " ++ description.first_line() ++ " " ++ branches"#;
test_env.jj_cmd_success(workspace_root, &["log", "-T", template, "-r", "all()"])
}

View file

@ -297,7 +297,7 @@ fn test_log_shortest_accessors() {
test_env.add_config(
r###"
[template-aliases]
'format_id(id)' = 'id.shortest(12).prefix() "[" id.shortest(12).rest() "]"'
'format_id(id)' = 'id.shortest(12).prefix() ++ "[" ++ id.shortest(12).rest() ++ "]"'
"###,
);
@ -305,7 +305,7 @@ fn test_log_shortest_accessors() {
test_env.jj_cmd_success(&repo_path, &["describe", "-m", "initial"]);
test_env.jj_cmd_success(&repo_path, &["branch", "c", "original"]);
insta::assert_snapshot!(
render("original", r#"format_id(change_id) " " format_id(commit_id)"#),
render("original", r#"format_id(change_id) ++ " " ++ format_id(commit_id)"#),
@"q[pvuntsmwlqt] b[a1a30916d29]");
// Create a chain of 10 commits
@ -319,11 +319,11 @@ fn test_log_shortest_accessors() {
}
insta::assert_snapshot!(
render("original", r#"format_id(change_id) " " format_id(commit_id)"#),
render("original", r#"format_id(change_id) ++ " " ++ format_id(commit_id)"#),
@"qpv[untsmwlqt] ba1[a30916d29]");
insta::assert_snapshot!(
render(":@", r#"change_id.shortest() " " commit_id.shortest() "\n""#),
render(":@", r#"change_id.shortest() ++ " " ++ commit_id.shortest() ++ "\n""#),
@r###"
wq 03
km f7
@ -339,7 +339,7 @@ fn test_log_shortest_accessors() {
"###);
insta::assert_snapshot!(
render(":@", r#"format_id(change_id) " " format_id(commit_id) "\n""#),
render(":@", r#"format_id(change_id) ++ " " ++ format_id(commit_id) ++ "\n""#),
@r###"
wq[nwkozpkust] 03[f51310b83e]
km[kuslswpqwq] f7[7fb1909080]
@ -494,7 +494,7 @@ fn test_log_prefix_highlight_counts_hidden_commits() {
test_env.add_config(
r###"
[template-aliases]
'format_id(id)' = 'id.shortest(12).prefix() "[" id.shortest(12).rest() "]"'
'format_id(id)' = 'id.shortest(12).prefix() ++ "[" ++ id.shortest(12).rest() ++ "]"'
"###,
);
@ -609,7 +609,7 @@ fn test_log_divergence() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_success(test_env.env_root(), &["init", "repo", "--git"]);
let repo_path = test_env.env_root().join("repo");
let template = r#"description.first_line() if(divergent, " !divergence!")"#;
let template = r#"description.first_line() ++ if(divergent, " !divergence!")"#;
std::fs::write(repo_path.join("file"), "foo\n").unwrap();
test_env.jj_cmd_success(&repo_path, &["describe", "-m", "description 1"]);

View file

@ -320,7 +320,7 @@ fn test_move_partial() {
}
fn get_log_output(test_env: &TestEnvironment, cwd: &Path) -> String {
let template = r#"commit_id.short() " " branches"#;
let template = r#"commit_id.short() ++ " " ++ branches"#;
test_env.jj_cmd_success(cwd, &["log", "-T", template])
}

View file

@ -299,7 +299,7 @@ fn test_new_insert_before_no_loop() {
test_env.jj_cmd_success(test_env.env_root(), &["init", "repo", "--git"]);
let repo_path = test_env.env_root().join("repo");
setup_before_insertion(&test_env, &repo_path);
let template = r#"commit_id.short() " " if(description, description, "root")"#;
let template = r#"commit_id.short() ++ " " ++ if(description, description, "root")"#;
let stdout = test_env.jj_cmd_success(&repo_path, &["log", "-T", template]);
insta::assert_snapshot!(stdout, @r###"
@ 7705d353bf5d F
@ -403,7 +403,7 @@ fn setup_before_insertion(test_env: &TestEnvironment, repo_path: &Path) {
}
fn get_log_output(test_env: &TestEnvironment, repo_path: &Path) -> String {
let template = r#"commit_id " " description"#;
let template = r#"commit_id ++ " " ++ description"#;
test_env.jj_cmd_success(repo_path, &["log", "-T", template])
}

View file

@ -135,13 +135,13 @@ fn test_op_log_template() {
let repo_path = test_env.env_root().join("repo");
let render = |template| test_env.jj_cmd_success(&repo_path, &["op", "log", "-T", template]);
insta::assert_snapshot!(render(r#"id "\n""#), @r###"
insta::assert_snapshot!(render(r#"id ++ "\n""#), @r###"
@ a99a3fd5c51e8f7ccb9ae2f9fb749612a23f0a7cf25d8c644f36c35c077449ce3c66f49d098a5a704ca5e47089a7f019563a5b8cbc7d451619e0f90c82241ceb
o 56b94dfc38e7d54340377f566e96ab97dc6163ea7841daf49fb2e1d1ceb27e26274db1245835a1a421fb9d06e6e0fe1e4f4aa1b0258c6e86df676ad9111d0dab
"###);
insta::assert_snapshot!(
render(r#"separate(" ", id.short(5), current_operation, user,
time.start(), time.end(), time.duration()) "\n""#), @r###"
time.start(), time.end(), time.duration()) ++ "\n""#), @r###"
@ a99a3 true test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 2001-02-03 04:05:07.000 +07:00 less than a microsecond
o 56b94 false test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 2001-02-03 04:05:07.000 +07:00 less than a microsecond
"###);

View file

@ -136,6 +136,6 @@ fn test_split_by_paths() {
}
fn get_log_output(test_env: &TestEnvironment, cwd: &Path) -> String {
let template = r#"change_id.short() " " empty"#;
let template = r#"change_id.short() ++ " " ++ empty"#;
test_env.jj_cmd_success(cwd, &["log", "-T", template])
}

View file

@ -233,7 +233,7 @@ fn test_squash_partial() {
}
fn get_log_output(test_env: &TestEnvironment, repo_path: &Path) -> String {
let template = r#"commit_id.short() " " branches"#;
let template = r#"commit_id.short() ++ " " ++ branches"#;
test_env.jj_cmd_success(repo_path, &["log", "-T", template])
}

View file

@ -63,7 +63,7 @@ fn test_templater_branches() {
test_env.jj_cmd_success(&origin_path, &["git", "export"]);
test_env.jj_cmd_success(&workspace_root, &["git", "fetch"]);
let template = r#"commit_id.short() " " branches"#;
let template = r#"commit_id.short() ++ " " ++ branches"#;
let output = test_env.jj_cmd_success(&workspace_root, &["log", "-T", template]);
insta::assert_snapshot!(output, @r###"
o b1bb3766d584 branch3??
@ -90,13 +90,13 @@ fn test_templater_parsed_tree() {
insta::assert_snapshot!(render(r#" commit_id.short() "#), @"000000000000");
// Multiple terms
insta::assert_snapshot!(render(r#" commit_id.short() empty "#), @"000000000000true");
insta::assert_snapshot!(render(r#" commit_id.short() ++ empty "#), @"000000000000true");
// Parenthesized single term
insta::assert_snapshot!(render(r#"(commit_id.short())"#), @"000000000000");
// Parenthesized multiple terms and concatenation
insta::assert_snapshot!(render(r#"(commit_id.short() " ") empty"#), @"000000000000 true");
insta::assert_snapshot!(render(r#"(commit_id.short() ++ " ") ++ empty"#), @"000000000000 true");
// Parenthesized "if" condition
insta::assert_snapshot!(render(r#"if((divergent), "t", "f")"#), @"f");
@ -113,12 +113,12 @@ fn test_templater_parse_error() {
let render_err = |template| test_env.jj_cmd_failure(&repo_path, &["log", "-T", template]);
insta::assert_snapshot!(render_err(r#"description ()"#), @r###"
Error: Failed to parse template: --> 1:14
Error: Failed to parse template: --> 1:13
|
1 | description ()
| ^---
| ^---
|
= expected template
= expected EOI
"###);
insta::assert_snapshot!(render_err(r#"foo"#), @r###"
@ -165,11 +165,11 @@ fn test_templater_parse_error() {
= Method "foo" doesn't exist for type "Integer"
"###);
insta::assert_snapshot!(render_err(r#"("foo" "bar").baz()"#), @r###"
Error: Failed to parse template: --> 1:15
insta::assert_snapshot!(render_err(r#"("foo" ++ "bar").baz()"#), @r###"
Error: Failed to parse template: --> 1:18
|
1 | ("foo" "bar").baz()
| ^-^
1 | ("foo" ++ "bar").baz()
| ^-^
|
= Method "baz" doesn't exist for type "Template"
"###);
@ -370,8 +370,8 @@ fn test_templater_separate_function() {
@"a b");
// List template
insta::assert_snapshot!(render(r#"separate(" ", "a", ("" ""))"#), @"a");
insta::assert_snapshot!(render(r#"separate(" ", "a", ("" "b"))"#), @"a b");
insta::assert_snapshot!(render(r#"separate(" ", "a", ("" ++ ""))"#), @"a");
insta::assert_snapshot!(render(r#"separate(" ", "a", ("" ++ "b"))"#), @"a b");
// Nested separate
insta::assert_snapshot!(
@ -408,10 +408,10 @@ fn test_templater_upper_lower() {
let render = |template| get_colored_template_output(&test_env, &repo_path, "@-", template);
insta::assert_snapshot!(
render(r#"change_id.shortest(4).upper() change_id.shortest(4).upper().lower()"#),
render(r#"change_id.shortest(4).upper() ++ change_id.shortest(4).upper().lower()"#),
@"ZZZZzzzz");
insta::assert_snapshot!(
render(r#""Hello".upper() "Hello".lower()"#), @"HELLOhello");
render(r#""Hello".upper() ++ "Hello".lower()"#), @"HELLOhello");
}
#[test]
@ -439,11 +439,11 @@ fn test_templater_alias() {
insta::assert_snapshot!(render("my_commit_id"), @"000000000000");
insta::assert_snapshot!(render("identity(my_commit_id)"), @"000000000000");
insta::assert_snapshot!(render_err("commit_id syntax_error"), @r###"
Error: Failed to parse template: --> 1:11
insta::assert_snapshot!(render_err("commit_id ++ syntax_error"), @r###"
Error: Failed to parse template: --> 1:14
|
1 | commit_id syntax_error
| ^----------^
1 | commit_id ++ syntax_error
| ^----------^
|
= Alias "syntax_error" cannot be expanded
--> 1:5
@ -454,11 +454,11 @@ fn test_templater_alias() {
= expected identifier
"###);
insta::assert_snapshot!(render_err("commit_id name_error"), @r###"
Error: Failed to parse template: --> 1:11
insta::assert_snapshot!(render_err("commit_id ++ name_error"), @r###"
Error: Failed to parse template: --> 1:14
|
1 | commit_id name_error
| ^--------^
1 | commit_id ++ name_error
| ^--------^
|
= Alias "name_error" cannot be expanded
--> 1:1
@ -490,11 +490,11 @@ fn test_templater_alias() {
= Expected argument of type "Integer"
"###);
insta::assert_snapshot!(render_err("commit_id recurse"), @r###"
Error: Failed to parse template: --> 1:11
insta::assert_snapshot!(render_err("commit_id ++ recurse"), @r###"
Error: Failed to parse template: --> 1:14
|
1 | commit_id recurse
| ^-----^
1 | commit_id ++ recurse
| ^-----^
|
= Alias "recurse" cannot be expanded
--> 1:1

View file

@ -199,7 +199,7 @@ fn test_unsquash_partial() {
}
fn get_log_output(test_env: &TestEnvironment, repo_path: &Path) -> String {
let template = r#"commit_id.short() " " branches"#;
let template = r#"commit_id.short() ++ " " ++ branches"#;
test_env.jj_cmd_success(repo_path, &["log", "-T", template])
}

View file

@ -342,7 +342,7 @@ fn test_list_workspaces_template() {
test_env.jj_cmd_success(test_env.env_root(), &["init", "--git", "main"]);
test_env.add_config(
r#"
templates.commit_summary = """commit_id.short() " " description.first_line()
templates.commit_summary = """commit_id.short() ++ " " ++ description.first_line() ++
if(current_working_copy, " (current)")"""
"#,
);