Skip to content

Commit

Permalink
commit_templater: make git_head return Option<RefName> instead of Vec<_>
Browse files Browse the repository at this point in the history
Since we've introduced Option type, it no longer makes sense that git_head
returns a Vec<RefName>.
  • Loading branch information
yuja committed Mar 25, 2024
1 parent bd3d930 commit b363e69
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 11 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Breaking changes

* The `git_head` template keyword now returns an optional value instead of a
list of 0 or 1 element.

### New features

* Config now supports rgb hex colors (in the form `#rrggbb`) wherever existing color names are supported.
Expand Down
35 changes: 25 additions & 10 deletions cli/src/commit_templater.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,14 @@ impl<'repo> TemplateLanguage<'repo> for CommitTemplateLanguage<'repo> {
let build = template_parser::lookup_method("RefName", table, function)?;
build(self, build_ctx, property, function)
}
CommitTemplatePropertyKind::RefNameOpt(property) => {
let table = &self.build_fn_table.ref_name_methods;
let build = template_parser::lookup_method("RefName", table, function)?;
let inner_property = property.and_then(|opt| {
opt.ok_or_else(|| TemplatePropertyError("No RefName available".into()))
});
build(self, build_ctx, Box::new(inner_property), function)
}
CommitTemplatePropertyKind::RefNameList(property) => {
// TODO: migrate to table?
template_builder::build_formattable_list_method(
Expand Down Expand Up @@ -216,6 +224,12 @@ impl<'repo> CommitTemplateLanguage<'repo> {
CommitTemplatePropertyKind::RefName(Box::new(property))
}

pub fn wrap_ref_name_opt(
property: impl TemplateProperty<Output = Option<RefName>> + 'repo,
) -> CommitTemplatePropertyKind<'repo> {
CommitTemplatePropertyKind::RefNameOpt(Box::new(property))
}

pub fn wrap_ref_name_list(
property: impl TemplateProperty<Output = Vec<RefName>> + 'repo,
) -> CommitTemplatePropertyKind<'repo> {
Expand All @@ -241,6 +255,7 @@ pub enum CommitTemplatePropertyKind<'repo> {
CommitOpt(Box<dyn TemplateProperty<Output = Option<Commit>> + 'repo>),
CommitList(Box<dyn TemplateProperty<Output = Vec<Commit>> + 'repo>),
RefName(Box<dyn TemplateProperty<Output = RefName> + 'repo>),
RefNameOpt(Box<dyn TemplateProperty<Output = Option<RefName>> + 'repo>),
RefNameList(Box<dyn TemplateProperty<Output = Vec<RefName>> + 'repo>),
CommitOrChangeId(Box<dyn TemplateProperty<Output = CommitOrChangeId> + 'repo>),
ShortestIdPrefix(Box<dyn TemplateProperty<Output = ShortestIdPrefix> + 'repo>),
Expand All @@ -258,6 +273,9 @@ impl<'repo> IntoTemplateProperty<'repo> for CommitTemplatePropertyKind<'repo> {
Some(Box::new(property.map(|l| !l.is_empty())))
}
CommitTemplatePropertyKind::RefName(_) => None,
CommitTemplatePropertyKind::RefNameOpt(property) => {
Some(Box::new(property.map(|opt| opt.is_some())))
}
CommitTemplatePropertyKind::RefNameList(property) => {
Some(Box::new(property.map(|l| !l.is_empty())))
}
Expand Down Expand Up @@ -290,6 +308,7 @@ impl<'repo> IntoTemplateProperty<'repo> for CommitTemplatePropertyKind<'repo> {
CommitTemplatePropertyKind::CommitOpt(_) => None,
CommitTemplatePropertyKind::CommitList(_) => None,
CommitTemplatePropertyKind::RefName(property) => Some(property.into_template()),
CommitTemplatePropertyKind::RefNameOpt(property) => Some(property.into_template()),
CommitTemplatePropertyKind::RefNameList(property) => Some(property.into_template()),
CommitTemplatePropertyKind::CommitOrChangeId(property) => {
Some(property.into_template())
Expand Down Expand Up @@ -530,7 +549,7 @@ fn builtin_commit_methods<'repo>() -> CommitTemplateBuildMethodFnMap<'repo, Comm
template_parser::expect_no_arguments(function)?;
let repo = language.repo;
let out_property = self_property.map(|commit| extract_git_head(repo, &commit));
Ok(L::wrap_ref_name_list(out_property))
Ok(L::wrap_ref_name_opt(out_property))
},
);
map.insert(
Expand Down Expand Up @@ -764,20 +783,16 @@ fn build_ref_names_index<'a>(
index
}

// TODO: maybe add option or nullable type?
fn extract_git_head(repo: &dyn Repo, commit: &Commit) -> Vec<RefName> {
fn extract_git_head(repo: &dyn Repo, commit: &Commit) -> Option<RefName> {
let target = repo.view().git_head();
if target.added_ids().contains(commit.id()) {
let ref_name = RefName {
target.added_ids().contains(commit.id()).then(|| {
RefName {
name: "HEAD".to_owned(),
remote: Some(git::REMOTE_NAME_FOR_LOCAL_GIT_REPO.to_owned()),
conflict: target.has_conflict(),
synced: false, // has no local counterpart
};
vec![ref_name]
} else {
vec![]
}
}
})
}

#[derive(Clone, Debug, Eq, PartialEq)]
Expand Down
8 changes: 8 additions & 0 deletions cli/src/templater.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@ impl<T: Template + ?Sized> Template for Box<T> {
}
}

// All optional printable types should be printable, and it's unlikely to
// implement different formatting per type.
impl<T: Template> Template for Option<T> {
fn format(&self, formatter: &mut dyn Formatter) -> io::Result<()> {
self.as_ref().map_or(Ok(()), |t| t.format(formatter))
}
}

impl Template for Signature {
fn format(&self, formatter: &mut dyn Formatter) -> io::Result<()> {
write!(formatter.labeled("name"), "{}", self.name)?;
Expand Down
14 changes: 14 additions & 0 deletions cli/tests/test_commit_template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,20 @@ fn test_log_git_head() {

test_env.jj_cmd_ok(&repo_path, &["new", "-m=initial"]);
std::fs::write(repo_path.join("file"), "foo\n").unwrap();

let template = r#"
separate(", ",
if(git_head, "name: " ++ git_head.name()),
"remote: " ++ git_head.remote(),
) ++ "\n"
"#;
let stdout = test_env.jj_cmd_success(&repo_path, &["log", "-T", template]);
insta::assert_snapshot!(stdout, @r###"
@ remote: <Error: No RefName available>
◉ name: HEAD, remote: git
◉ remote: <Error: No RefName available>
"###);

let stdout = test_env.jj_cmd_success(&repo_path, &["log", "--color=always"]);
insta::assert_snapshot!(stdout, @r###"
@ rlvkpnrz [38;5;[email protected] 2001-02-03 08:05:09 50aaf475
Expand Down
2 changes: 1 addition & 1 deletion docs/templates.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ This type cannot be printed. The following methods are defined.
* `remote_branches() -> List<RefName>`: All remote branches pointing to the commit.
* `tags() -> List<RefName>`
* `git_refs() -> List<RefName>`
* `git_head() -> List<RefName>`
* `git_head() -> Option<RefName>`
* `divergent() -> Boolean`: True if the commit's change id corresponds to multiple
visible commits.
* `hidden() -> Boolean`: True if the commit is not visible (a.k.a. abandoned).
Expand Down

0 comments on commit b363e69

Please sign in to comment.