Skip to content

Commit

Permalink
templater: add "parent_commit_ids" keyword
Browse files Browse the repository at this point in the history
A list type isn't so useful without a map operation, but List<CommitId>
is at least printable. Maybe we can experiment with it to craft a map
operation.

If a map operation is introduced, this keyword might be replaced with
"parents.map(|commit| commit.commit_id)", where parents is of List<Commit>
type, and the .map() method will probably return List<Template>.
  • Loading branch information
yuja committed Mar 7, 2023
1 parent 0b27f83 commit 4984e61
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 1 deletion.
5 changes: 5 additions & 0 deletions docs/templates.md
Expand Up @@ -18,6 +18,7 @@ The following keywords can be used in `jj log`/`jj obslog` templates.
* `description: String`
* `change_id: ChangeId`
* `commit_id: CommitId`
* `parent_commit_ids: List<CommitId>`
* `author: Signature`
* `committer: Signature`
* `working_copies: String`: For multi-workspace repository, indicate
Expand Down Expand Up @@ -83,6 +84,10 @@ The following methods are defined.

No methods are defined.

### List type

No methods are defined.

### OperationId type

The following methods are defined.
Expand Down
35 changes: 34 additions & 1 deletion src/commit_templater.rs
Expand Up @@ -30,7 +30,7 @@ use crate::template_parser::{
TemplateLanguage, TemplateParseError, TemplateParseResult,
};
use crate::templater::{
IntoTemplate, PlainTextFormattedProperty, Template, TemplateProperty, TemplatePropertyFn,
self, IntoTemplate, PlainTextFormattedProperty, Template, TemplateProperty, TemplatePropertyFn,
};

struct CommitTemplateLanguage<'repo, 'b> {
Expand Down Expand Up @@ -60,6 +60,9 @@ impl<'repo> TemplateLanguage<'repo> for CommitTemplateLanguage<'repo, '_> {
CommitTemplatePropertyKind::CommitOrChangeId(property) => {
build_commit_or_change_id_method(self, property, function)
}
CommitTemplatePropertyKind::CommitOrChangeIdList(property) => {
template_parser::build_list_method(self, property, function)
}
CommitTemplatePropertyKind::ShortestIdPrefix(property) => {
build_shortest_id_prefix_method(self, property, function)
}
Expand All @@ -77,6 +80,13 @@ impl<'repo> CommitTemplateLanguage<'repo, '_> {
CommitTemplatePropertyKind::CommitOrChangeId(property)
}

fn wrap_commit_or_change_id_list(
&self,
property: Box<dyn TemplateProperty<Commit, Output = Vec<CommitOrChangeId<'repo>>> + 'repo>,
) -> CommitTemplatePropertyKind<'repo> {
CommitTemplatePropertyKind::CommitOrChangeIdList(property)
}

fn wrap_shortest_id_prefix(
&self,
property: Box<dyn TemplateProperty<Commit, Output = ShortestIdPrefix> + 'repo>,
Expand All @@ -88,13 +98,17 @@ impl<'repo> CommitTemplateLanguage<'repo, '_> {
enum CommitTemplatePropertyKind<'repo> {
Core(CoreTemplatePropertyKind<'repo, Commit>),
CommitOrChangeId(Box<dyn TemplateProperty<Commit, Output = CommitOrChangeId<'repo>> + 'repo>),
CommitOrChangeIdList(
Box<dyn TemplateProperty<Commit, Output = Vec<CommitOrChangeId<'repo>>> + 'repo>,
),
ShortestIdPrefix(Box<dyn TemplateProperty<Commit, Output = ShortestIdPrefix> + 'repo>),
}

impl<'repo> IntoTemplateProperty<'repo, Commit> for CommitTemplatePropertyKind<'repo> {
fn try_into_boolean(self) -> Option<Box<dyn TemplateProperty<Commit, Output = bool> + 'repo>> {
match self {
CommitTemplatePropertyKind::Core(property) => property.try_into_boolean(),
// TODO: should we allow implicit cast of List type?
_ => None,
}
}
Expand All @@ -119,6 +133,7 @@ impl<'repo> IntoTemplate<'repo, Commit> for CommitTemplatePropertyKind<'repo> {
match self {
CommitTemplatePropertyKind::Core(property) => property.into_template(),
CommitTemplatePropertyKind::CommitOrChangeId(property) => property.into_template(),
CommitTemplatePropertyKind::CommitOrChangeIdList(property) => property.into_template(),
CommitTemplatePropertyKind::ShortestIdPrefix(property) => property.into_template(),
}
}
Expand Down Expand Up @@ -152,6 +167,13 @@ fn build_commit_keyword<'repo>(
"commit_id" => language.wrap_commit_or_change_id(wrap_fn(move |commit| {
CommitOrChangeId::new(repo, IdKind::Commit(commit.id().to_owned()))
})),
"parent_commit_ids" => language.wrap_commit_or_change_id_list(wrap_fn(move |commit| {
commit
.parent_ids()
.iter()
.map(|id| CommitOrChangeId::new(repo, IdKind::Commit(id.to_owned())))
.collect()
})),
"author" => language.wrap_signature(wrap_fn(|commit| commit.author().clone())),
"committer" => language.wrap_signature(wrap_fn(|commit| commit.committer().clone())),
"working_copies" => language.wrap_string(wrap_repo_fn(repo, extract_working_copies)),
Expand Down Expand Up @@ -179,6 +201,7 @@ fn build_commit_keyword<'repo>(
Ok(property)
}

// TODO: return Vec<String>
fn extract_working_copies(repo: &dyn Repo, commit: &Commit) -> String {
let wc_commit_ids = repo.view().wc_commit_ids();
if wc_commit_ids.len() <= 1 {
Expand All @@ -193,6 +216,7 @@ fn extract_working_copies(repo: &dyn Repo, commit: &Commit) -> String {
names.join(" ")
}

// TODO: return Vec<Branch>?
fn extract_branches(repo: &dyn Repo, commit: &Commit) -> String {
let mut names = vec![];
for (branch_name, branch_target) in repo.view().branches() {
Expand Down Expand Up @@ -225,6 +249,7 @@ fn extract_branches(repo: &dyn Repo, commit: &Commit) -> String {
names.join(" ")
}

// TODO: return Vec<NameRef>?
fn extract_tags(repo: &dyn Repo, commit: &Commit) -> String {
let mut names = vec![];
for (tag_name, target) in repo.view().tags() {
Expand All @@ -239,6 +264,7 @@ fn extract_tags(repo: &dyn Repo, commit: &Commit) -> String {
names.join(" ")
}

// TODO: return Vec<NameRef>?
fn extract_git_refs(repo: &dyn Repo, commit: &Commit) -> String {
// TODO: We should keep a map from commit to ref names so we don't have to walk
// all refs here.
Expand All @@ -255,6 +281,7 @@ fn extract_git_refs(repo: &dyn Repo, commit: &Commit) -> String {
names.join(" ")
}

// TODO: return NameRef?
fn extract_git_head(repo: &dyn Repo, commit: &Commit) -> String {
match repo.view().git_head() {
Some(ref_target) if ref_target.has_add(commit.id()) => {
Expand Down Expand Up @@ -322,6 +349,12 @@ impl Template<()> for CommitOrChangeId<'_> {
}
}

impl Template<()> for Vec<CommitOrChangeId<'_>> {
fn format(&self, _: &(), formatter: &mut dyn Formatter) -> io::Result<()> {
templater::format_joined(&(), formatter, self, " ")
}
}

fn build_commit_or_change_id_method<'repo>(
language: &CommitTemplateLanguage<'repo, '_>,
self_property: impl TemplateProperty<Commit, Output = CommitOrChangeId<'repo>> + 'repo,
Expand Down
9 changes: 9 additions & 0 deletions src/template_parser.rs
Expand Up @@ -1040,6 +1040,15 @@ fn build_timestamp_range_method<'a, L: TemplateLanguage<'a>>(
Ok(property)
}

pub fn build_list_method<'a, L: TemplateLanguage<'a>, P>(
_language: &L,
_self_property: impl TemplateProperty<L::Context, Output = Vec<P>> + 'a,
function: &FunctionCallNode,
) -> TemplateParseResult<L::Property> {
// TODO: .join(separator), .map(), ...
Err(TemplateParseError::no_such_method("List", function))
}

fn build_global_function<'a, L: TemplateLanguage<'a>>(
language: &L,
function: &FunctionCallNode,
Expand Down
24 changes: 24 additions & 0 deletions tests/test_commit_template.rs
Expand Up @@ -17,6 +17,30 @@ use regex::Regex;

pub mod common;

#[test]
fn test_log_parent_commit_ids() {
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");

test_env.jj_cmd_success(&repo_path, &["new"]);
test_env.jj_cmd_success(&repo_path, &["new", "@-"]);
test_env.jj_cmd_success(&repo_path, &["new", "@", "@-"]);

let template = r#"commit_id ++ "\nP: " ++ parent_commit_ids ++ "\n""#;
let stdout = test_env.jj_cmd_success(&repo_path, &["log", "-T", template]);
insta::assert_snapshot!(stdout, @r###"
@ c067170d4ca1bc6162b64f7550617ec809647f84
├─╮ P: 4db490c88528133d579540b6900b8098f0c17701 230dd059e1b059aefc0da06a2e5a7dbf22362f22
o │ 4db490c88528133d579540b6900b8098f0c17701
├─╯ P: 230dd059e1b059aefc0da06a2e5a7dbf22362f22
o 230dd059e1b059aefc0da06a2e5a7dbf22362f22
│ P: 0000000000000000000000000000000000000000
o 0000000000000000000000000000000000000000
P:
"###);
}

#[test]
fn test_log_author_timestamp() {
let test_env = TestEnvironment::default();
Expand Down

0 comments on commit 4984e61

Please sign in to comment.