From ab6363e8755e4834f5117cc52c905e835e515738 Mon Sep 17 00:00:00 2001 From: Yuya Nishihara Date: Thu, 22 Feb 2024 18:57:29 +0900 Subject: [PATCH] templater: add special "self" variable to refer to top-level object This allows us to call alias function with the top-level object. For convenience, all self.()s are available as keywords. I don't think we'll want to deprecate them. It would be tedious if we had to specify -T'self.commit_id()' instead of -Tcommit_id. --- CHANGELOG.md | 3 +++ cli/src/template_builder.rs | 21 +++++++++++++++++++++ docs/templates.md | 9 ++++++--- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20d5ab4f22..988c7f55b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Templates now support logical operators: `||`, `&&`, `!` +* Templates now support the `self` keyword, which is the current commit in `jj + log`/`obslog` templates. + * `jj show` now accepts `-T`/`--template` option to render its output using template diff --git a/cli/src/template_builder.rs b/cli/src/template_builder.rs index 8bf8d46339..136c273ba1 100644 --- a/cli/src/template_builder.rs +++ b/cli/src/template_builder.rs @@ -866,6 +866,9 @@ pub fn build_expression<'a, L: TemplateLanguage<'a>>( if let Some(make) = build_ctx.local_variables.get(name) { // Don't label a local variable with its name Ok(Expression::unlabeled(make())) + } else if *name == "self" { + // "self" is a special variable, so don't label it + Ok(Expression::unlabeled(language.build_self())) } else { build_keyword(language, build_ctx, name, node.span) } @@ -1315,6 +1318,24 @@ mod tests { "###); } + #[test] + fn test_self_keyword() { + let mut env = TestTemplateEnv::default(); + env.add_keyword("say_hello", |language| { + language.wrap_string(Literal("Hello".to_owned())) + }); + + insta::assert_snapshot!(env.render_ok(r#"self.say_hello()"#), @"Hello"); + insta::assert_snapshot!(env.parse_err(r#"self"#), @r###" + --> 1:1 + | + 1 | self + | ^--^ + | + = Expected expression of type "Template" + "###); + } + #[test] fn test_boolean_cast() { let mut env = TestTemplateEnv::default(); diff --git a/docs/templates.md b/docs/templates.md index 003dfa66ce..0825fc03a0 100644 --- a/docs/templates.md +++ b/docs/templates.md @@ -9,17 +9,20 @@ A couple of `jj` commands accept a template via `-T`/`--template` option. ## Keywords Keywords represent objects of different types; the types are described in -a follow-up section. +a follow-up section. In addition to context-specific keywords, the top-level +object can be referenced as `self`. ### Commit keywords In `jj log`/`jj obslog` templates, all 0-argument methods of [the `Commit` -type](#commit-type) are available as keywords. +type](#commit-type) are available as keywords. For example, `commit_id` is +equivalent to `self.commit_id()`. ### Operation keywords In `jj op log` templates, all 0-argument methods of [the `Operation` -type](#operation-type) are available as keywords. +type](#operation-type) are available as keywords. For example, +`current_operation` is equivalent to `self.current_operation()`. ## Operators