diff --git a/src/template_parser.rs b/src/template_parser.rs index 976309d2a..1f893f8a9 100644 --- a/src/template_parser.rs +++ b/src/template_parser.rs @@ -201,7 +201,6 @@ impl<'a, I: 'a> Property<'a, I> { } } - #[cfg(test)] // TODO: remove after adding call site in production code fn try_into_integer(self) -> Option + 'a>> { match self { Property::Integer(property) => Some(property), @@ -263,7 +262,6 @@ impl<'a, C: 'a> Expression<'a, C> { } } - #[cfg(test)] // TODO: remove after adding call site in production code fn try_into_integer(self) -> Option + 'a>> { match self { Expression::Property(PropertyAndLabels(property, _)) => property.try_into_integer(), @@ -471,8 +469,20 @@ fn parse_commit_or_change_id_method<'a, I: 'a>( self_property: impl TemplateProperty> + 'a, name: Pair, args_pair: Pair, - _parse_keyword: &impl Fn(Pair) -> TemplateParseResult>, + parse_keyword: &impl Fn(Pair) -> TemplateParseResult>, ) -> TemplateParseResult> { + let parse_optional_integer = |args_pair: Pair| -> Result, TemplateParseError> { + let ([], [len_pair]) = expect_arguments(args_pair)?; + len_pair + .map(|len_pair| { + let span = len_pair.as_span(); + parse_template_rule(len_pair, parse_keyword).and_then(|p| { + p.try_into_integer() + .ok_or_else(|| TemplateParseError::invalid_argument_type("Integer", span)) + }) + }) + .transpose() + }; let property = match name.as_str() { "short" => { expect_no_arguments(args_pair)?; @@ -482,17 +492,21 @@ fn parse_commit_or_change_id_method<'a, I: 'a>( )) } "shortest_prefix_and_brackets" => { - expect_no_arguments(args_pair)?; + let len_property = parse_optional_integer(args_pair)?; Property::String(chain_properties( - self_property, - TemplatePropertyFn(|id: &CommitOrChangeId| id.shortest_prefix_and_brackets()), + (self_property, len_property), + TemplatePropertyFn(|(id, len): &(CommitOrChangeId, Option)| { + id.shortest_prefix_and_brackets(len.unwrap_or(12)) + }), )) } "shortest_styled_prefix" => { - expect_no_arguments(args_pair)?; + let len_property = parse_optional_integer(args_pair)?; Property::IdWithHighlightedPrefix(chain_properties( - self_property, - TemplatePropertyFn(|id: &CommitOrChangeId| id.shortest_styled_prefix()), + (self_property, len_property), + TemplatePropertyFn(|(id, len): &(CommitOrChangeId, Option)| { + id.shortest_styled_prefix(len.unwrap_or(12)) + }), )) } _ => { diff --git a/src/templater.rs b/src/templater.rs index a83aa66ce..7c56c7b09 100644 --- a/src/templater.rs +++ b/src/templater.rs @@ -567,12 +567,14 @@ impl<'a> CommitOrChangeId<'a> { hex } - pub fn shortest_prefix_and_brackets(&self) -> String { + /// The length of the id printed (not counting the brackets) will be the + /// maximum of `total_len` and the length of the shortest unique prefix + pub fn shortest_prefix_and_brackets(&self, total_len: i64) -> String { let hex = self.hex(); let (prefix, rest) = extract_entire_prefix_and_trimmed_tail( &hex, self.repo.shortest_unique_id_prefix_len(self.as_bytes()), - 12 - 2, + max(total_len, 0) as usize, ); if rest.is_empty() { prefix.to_string() @@ -581,12 +583,14 @@ impl<'a> CommitOrChangeId<'a> { } } - pub fn shortest_styled_prefix(&self) -> IdWithHighlightedPrefix { + /// The length of the id printed will be the maximum of `total_len` and the + /// length of the shortest unique prefix + pub fn shortest_styled_prefix(&self, total_len: i64) -> IdWithHighlightedPrefix { let hex = self.hex(); let (prefix, rest) = extract_entire_prefix_and_trimmed_tail( &hex, self.repo.shortest_unique_id_prefix_len(self.as_bytes()), - 12, + max(total_len, 0) as usize, ); IdWithHighlightedPrefix { prefix: prefix.to_string(), diff --git a/tests/test_log_command.rs b/tests/test_log_command.rs index 5cae92895..db617a690 100644 --- a/tests/test_log_command.rs +++ b/tests/test_log_command.rs @@ -284,23 +284,28 @@ fn test_log_with_or_without_diff() { } #[test] -fn test_log_prefix_highlight() { +fn test_log_prefix_highlight_brackets() { 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 prefix_format = r#" - "Change " change_id.shortest_prefix_and_brackets() " " description.first_line() - " " commit_id.shortest_prefix_and_brackets() " " branches - "#; + fn prefix_format(len: Option) -> String { + format!( + r#" + "Change " change_id.shortest_prefix_and_brackets({0}) " " description.first_line() + " " commit_id.shortest_prefix_and_brackets({0}) " " branches + "#, + len.map(|l| l.to_string()).unwrap_or(String::default()) + ) + } std::fs::write(repo_path.join("file"), "original file\n").unwrap(); test_env.jj_cmd_success(&repo_path, &["describe", "-m", "initial"]); test_env.jj_cmd_success(&repo_path, &["branch", "c", "original"]); insta::assert_snapshot!( - test_env.jj_cmd_success(&repo_path, &["log", "-r", "original", "-T", prefix_format]), + test_env.jj_cmd_success(&repo_path, &["log", "-r", "original", "-T", &prefix_format(None)]), @r###" - @ Change 9[a45c67d3e] initial b[a1a30916d] original + @ Change 9[a45c67d3e96] initial b[a1a30916d29] original ~ "### ); @@ -309,26 +314,180 @@ fn test_log_prefix_highlight() { std::fs::write(repo_path.join("file"), format!("file {i}\n")).unwrap(); } insta::assert_snapshot!( - test_env.jj_cmd_success(&repo_path, &["log", "-r", "original", "-T", prefix_format]), + test_env.jj_cmd_success(&repo_path, &["log", "-r", "original", "-T", &prefix_format(None)]), @r###" - o Change 9a4[5c67d3e] initial ba1[a30916d] original + o Change 9a4[5c67d3e96] initial ba1[a30916d29] original ~ "### ); insta::assert_snapshot!( - test_env.jj_cmd_success(&repo_path, &["log", "-r", "@-----------..@", "-T", prefix_format]), + test_env.jj_cmd_success(&repo_path, &["log", "-r", "@-----------..@", "-T", &prefix_format(None)]), @r###" - @ Change 4c9[32da801] commit49 d8[3437a2ce] - o Change 0d[58f15eab] commit48 f3[abb4ea0a] - o Change fc[e6c2c591] commit47 38e[891bea2] - o Change d5[1defcac3] commit46 1c[04d94770] - o Change 4f[13b1391d] commit45 747[24ae22b] - o Change 6a[de2950a0] commit44 c7a[a67cf7b] - o Change 06c[482e452] commit43 8e[c99dfcb6] - o Change 392[beeb018] commit42 8f0[e60411b] - o Change a1[b73d3ff9] commit41 71[d6937a66] - o Change 708[8f46129] commit40 db[57204902] - o Change c49[f7f006c] commit39 d94[54fec8a] + @ Change 4c9[32da80131] commit49 d8[3437a2ceff] + o Change 0d[58f15eaba6] commit48 f3[abb4ea0ac3] + o Change fc[e6c2c59123] commit47 38e[891bea27b] + o Change d5[1defcac305] commit46 1c[04d947707a] + o Change 4f[13b1391d68] commit45 747[24ae22b1e] + o Change 6a[de2950a042] commit44 c7a[a67cf7bbd] + o Change 06c[482e452d3] commit43 8e[c99dfcb6c7] + o Change 392[beeb018eb] commit42 8f0[e60411b78] + o Change a1[b73d3ff916] commit41 71[d6937a66c3] + o Change 708[8f461291f] commit40 db[5720490266] + o Change c49[f7f006c77] commit39 d94[54fec8a69] + ~ + "### + ); + insta::assert_snapshot!( + test_env.jj_cmd_success(&repo_path, &["log", "-r", "@-----------..@", "-T", &prefix_format(Some(3))]), + @r###" + @ Change 4c9 commit49 d8[3] + o Change 0d[5] commit48 f3[a] + o Change fc[e] commit47 38e + o Change d5[1] commit46 1c[0] + o Change 4f[1] commit45 747 + o Change 6a[d] commit44 c7a + o Change 06c commit43 8e[c] + o Change 392 commit42 8f0 + o Change a1[b] commit41 71[d] + o Change 708 commit40 db[5] + o Change c49 commit39 d94 + ~ + "### + ); + insta::assert_snapshot!( + test_env.jj_cmd_success(&repo_path, &["log", "-r", "@-----------..@", "-T", &prefix_format(Some(0))]), + @r###" + @ Change 4c9 commit49 d8 + o Change 0d commit48 f3 + o Change fc commit47 38e + o Change d5 commit46 1c + o Change 4f commit45 747 + o Change 6a commit44 c7a + o Change 06c commit43 8e + o Change 392 commit42 8f0 + o Change a1 commit41 71 + o Change 708 commit40 db + o Change c49 commit39 d94 + ~ + "### + ); +} + +#[test] +fn test_log_prefix_highlight_styled() { + 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"); + + fn prefix_format(len: Option) -> String { + format!( + r#" + "Change " change_id.shortest_styled_prefix({0}) " " description.first_line() + " " commit_id.shortest_styled_prefix({0}) " " branches + "#, + len.map(|l| l.to_string()).unwrap_or(String::default()) + ) + } + + std::fs::write(repo_path.join("file"), "original file\n").unwrap(); + test_env.jj_cmd_success(&repo_path, &["describe", "-m", "initial"]); + test_env.jj_cmd_success(&repo_path, &["branch", "c", "original"]); + insta::assert_snapshot!( + test_env.jj_cmd_success(&repo_path, &["log", "-r", "original", "-T", &prefix_format(None)]), + @r###" + @ Change 9a45c67d3e96 initial ba1a30916d29 original + ~ + "### + ); + for i in 1..50 { + test_env.jj_cmd_success(&repo_path, &["new", "-m", &format!("commit{i}")]); + std::fs::write(repo_path.join("file"), format!("file {i}\n")).unwrap(); + } + insta::assert_snapshot!( + test_env.jj_cmd_success(&repo_path, &["log", "-r", "original", "-T", &prefix_format(None)]), + @r###" + o Change 9a45c67d3e96 initial ba1a30916d29 original + ~ + "### + ); + let stdout = test_env.jj_cmd_success( + &repo_path, + &[ + "--color=always", + "log", + "-r", + "@-----------..@", + "-T", + &prefix_format(None), + ], + ); + insta::assert_snapshot!(stdout, + @r###" + @ Change 4c932da80131 commit49 d83437a2ceff + o Change 0d58f15eaba6 commit48 f3abb4ea0ac3 + o Change fce6c2c59123 commit47 38e891bea27b + o Change d51defcac305 commit46 1c04d947707a + o Change 4f13b1391d68 commit45 74724ae22b1e + o Change 6ade2950a042 commit44 c7aa67cf7bbd + o Change 06c482e452d3 commit43 8ec99dfcb6c7 + o Change 392beeb018eb commit42 8f0e60411b78 + o Change a1b73d3ff916 commit41 71d6937a66c3 + o Change 7088f461291f commit40 db5720490266 + o Change c49f7f006c77 commit39 d9454fec8a69 + ~ + "### + ); + let stdout = test_env.jj_cmd_success( + &repo_path, + &[ + "--color=always", + "log", + "-r", + "@-----------..@", + "-T", + &prefix_format(Some(3)), + ], + ); + insta::assert_snapshot!(stdout, + @r###" + @ Change 4c9 commit49 d83 + o Change 0d5 commit48 f3a + o Change fce commit47 38e + o Change d51 commit46 1c0 + o Change 4f1 commit45 747 + o Change 6ad commit44 c7a + o Change 06c commit43 8ec + o Change 392 commit42 8f0 + o Change a1b commit41 71d + o Change 708 commit40 db5 + o Change c49 commit39 d94 + ~ + "### + ); + let stdout = test_env.jj_cmd_success( + &repo_path, + &[ + "--color=always", + "log", + "-r", + "@-----------..@", + "-T", + &prefix_format(Some(0)), + ], + ); + insta::assert_snapshot!(stdout, + @r###" + @ Change 4c9 commit49 d8 + o Change 0d commit48 f3 + o Change fc commit47 38e + o Change d5 commit46 1c + o Change 4f commit45 747 + o Change 6a commit44 c7a + o Change 06c commit43 8e + o Change 392 commit42 8f0 + o Change a1 commit41 71 + o Change 708 commit40 db + o Change c49 commit39 d94 ~ "### ); @@ -351,8 +510,8 @@ fn test_log_prefix_highlight_counts_hidden_commits() { insta::assert_snapshot!( test_env.jj_cmd_success(&repo_path, &["log", "-r", "all()", "-T", prefix_format]), @r###" - @ Change 9[a45c67d3e] initial b[a1a30916d] original - o Change 0[000000000] 0[000000000] + @ Change 9[a45c67d3e96] initial b[a1a30916d29] original + o Change 0[00000000000] 0[00000000000] "### ); for i in 1..100 { @@ -363,7 +522,7 @@ fn test_log_prefix_highlight_counts_hidden_commits() { insta::assert_snapshot!( test_env.jj_cmd_success(&repo_path, &["log", "-r", "ba1", "-T", prefix_format]), @r###" - o Change 9a4[5c67d3e] initial ba1[a30916d] + o Change 9a4[5c67d3e96] initial ba1[a30916d29] ~ "### ); @@ -371,8 +530,8 @@ fn test_log_prefix_highlight_counts_hidden_commits() { insta::assert_snapshot!( test_env.jj_cmd_success(&repo_path, &["log", "-r", "all()", "-T", prefix_format]), @r###" - @ Change 9a4[5c67d3e] commit99 de[3177d2ac] original - o Change 000[0000000] 000[0000000] + @ Change 9a4[5c67d3e96] commit99 de[3177d2acf2] original + o Change 000[000000000] 000[000000000] "### ); insta::assert_snapshot!( @@ -384,7 +543,7 @@ fn test_log_prefix_highlight_counts_hidden_commits() { insta::assert_snapshot!( test_env.jj_cmd_success(&repo_path, &["log", "-r", "de", "-T", prefix_format]), @r###" - @ Change 9a4[5c67d3e] commit99 de[3177d2ac] original + @ Change 9a4[5c67d3e96] commit99 de[3177d2acf2] original ~ "### );