Skip to content

Commit

Permalink
templater: add pad_centered template function
Browse files Browse the repository at this point in the history
Add a new pad_center function that centers content within a minimum
width. If an odd number of fill characters is required, the trailing
fill will be one character longer than the leading fill.

Fixes #5066.
  • Loading branch information
steadmon committed Feb 4, 2025
1 parent e6dfe13 commit 227ecce
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
* New template function `config(name)` to access to configuration variable from
template.

* New template function `pad_centered()` to center content within a minimum width.

* Templater now supports `list.filter(|x| ..)` method.

* The `diff` commit template keyword now supports custom formatting via
Expand Down
25 changes: 25 additions & 0 deletions cli/src/template_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1475,6 +1475,22 @@ fn builtin_functions<'a, L: TemplateLanguage<'a> + ?Sized>() -> TemplateBuildFun
let template = new_pad_template(content, fill_char, width, text_util::write_padded_end);
Ok(L::wrap_template(template))
});
map.insert(
"pad_centered",
|language, diagnostics, build_ctx, function| {
let ([width_node, content_node], [fill_char_node]) =
function.expect_named_arguments(&["", "", "fill_char"])?;
let width = expect_usize_expression(language, diagnostics, build_ctx, width_node)?;
let content =
expect_template_expression(language, diagnostics, build_ctx, content_node)?;
let fill_char = fill_char_node
.map(|node| expect_template_expression(language, diagnostics, build_ctx, node))
.transpose()?;
let template =
new_pad_template(content, fill_char, width, text_util::write_padded_centered);
Ok(L::wrap_template(template))
},
);
map.insert(
"truncate_start",
|language, diagnostics, build_ctx, function| {
Expand Down Expand Up @@ -2980,6 +2996,9 @@ mod tests {
insta::assert_snapshot!(
env.render_ok(r"'{' ++ pad_end(5, label('red', 'foo')) ++ '}'"),
@"{foo }");
insta::assert_snapshot!(
env.render_ok(r"'{' ++ pad_centered(5, label('red', 'foo')) ++ '}'"),
@"{ foo }");

// Labeled fill char
insta::assert_snapshot!(
Expand All @@ -2988,6 +3007,9 @@ mod tests {
insta::assert_snapshot!(
env.render_ok(r"pad_end(5, label('red', 'foo'), fill_char=label('cyan', '='))"),
@"foo==");
insta::assert_snapshot!(
env.render_ok(r"pad_centered(5, label('red', 'foo'), fill_char=label('cyan', '='))"),
@"=foo=");

// Error in fill char: the output looks odd (because the error message
// isn't 1-width character), but is still readable.
Expand All @@ -2997,6 +3019,9 @@ mod tests {
insta::assert_snapshot!(
env.render_ok(r"pad_end(5, 'foo', fill_char=bad_string)"),
@"foo<<Error: Error: Bad>Bad>");
insta::assert_snapshot!(
env.render_ok(r"pad_centered(5, 'foo', fill_char=bad_string)"),
@"<Error: Bad>foo<Error: Bad>");
}

#[test]
Expand Down
70 changes: 70 additions & 0 deletions cli/src/text_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,28 @@ pub fn write_padded_end(
Ok(())
}

/// Writes text padded to `min_width` by adding leading and trailing fill
/// characters.
///
/// The input `recorded_content` should be a single-line text. The
/// `recorded_fill_char` should be bytes of a 1-width character.
pub fn write_padded_centered(
formatter: &mut dyn Formatter,
recorded_content: &FormatRecorder,
recorded_fill_char: &FormatRecorder,
min_width: usize,
) -> io::Result<()> {
// We don't care about the width of non-UTF-8 bytes, but should not panic.
let width = String::from_utf8_lossy(recorded_content.data()).width();
let fill_width = min_width.saturating_sub(width);
let fill_left = fill_width / 2;
let fill_right = fill_width - fill_left;
write_padding(formatter, recorded_fill_char, fill_left)?;
recorded_content.replay(formatter)?;
write_padding(formatter, recorded_fill_char, fill_right)?;
Ok(())
}

fn write_padding(
formatter: &mut dyn Formatter,
recorded_fill_char: &FormatRecorder,
Expand Down Expand Up @@ -843,6 +865,24 @@ mod tests {
format_colored(|formatter| write_padded_end(formatter, &recorder, &fill, 8)),
@"foobar=="
);

// Pad centered
insta::assert_snapshot!(
format_colored(|formatter| write_padded_centered(formatter, &recorder, &fill, 6)),
@"foobar"
);
insta::assert_snapshot!(
format_colored(|formatter| write_padded_centered(formatter, &recorder, &fill, 7)),
@"foobar="
);
insta::assert_snapshot!(
format_colored(|formatter| write_padded_centered(formatter, &recorder, &fill, 8)),
@"=foobar="
);
insta::assert_snapshot!(
format_colored(|formatter| write_padded_centered(formatter, &recorder, &fill, 13)),
@"===foobar===="
);
}

#[test]
Expand All @@ -864,6 +904,12 @@ mod tests {
format_colored(|formatter| write_padded_end(formatter, &recorder, &fill, 6)),
@"foo==="
);

// Pad centered
insta::assert_snapshot!(
format_colored(|formatter| write_padded_centered(formatter, &recorder, &fill, 6)),
@"=foo=="
);
}

#[test]
Expand All @@ -890,6 +936,20 @@ mod tests {
format_colored(|formatter| write_padded_end(formatter, &recorder, &fill, 10)),
@"àbc̀一二三="
);

// Pad centered
insta::assert_snapshot!(
format_colored(|formatter| write_padded_centered(formatter, &recorder, &fill, 9)),
@"àbc̀一二三"
);
insta::assert_snapshot!(
format_colored(|formatter| write_padded_centered(formatter, &recorder, &fill, 10)),
@"àbc̀一二三="
);
insta::assert_snapshot!(
format_colored(|formatter| write_padded_centered(formatter, &recorder, &fill, 13)),
@"==àbc̀一二三=="
);
}

#[test]
Expand All @@ -916,6 +976,16 @@ mod tests {
format_colored(|formatter| write_padded_end(formatter, &recorder, &fill, 1)),
@"="
);

// Pad centered
insta::assert_snapshot!(
format_colored(|formatter| write_padded_centered(formatter, &recorder, &fill, 0)),
@""
);
insta::assert_snapshot!(
format_colored(|formatter| write_padded_centered(formatter, &recorder, &fill, 1)),
@"="
);
}

#[test]
Expand Down
4 changes: 4 additions & 0 deletions docs/templates.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ The following functions are defined.
* `pad_end(width: Integer, content: Template[, fill_char: Template])`: Pad (or
left-justify) content by adding trailing fill characters. The `content`
shouldn't have newline character.
* `pad_centered(width: Integer, content: Template[, fill_char: Template])`: Pad
content by adding both leading and trailing fill characters. If an odd number
of fill characters are needed, the trailing fill will be one longer than the
leading fill. The `content` shouldn't have newline characters.
* `truncate_start(width: Integer, content: Template)`: Truncate `content` by
removing leading characters. The `content` shouldn't have newline character.
* `truncate_end(width: Integer, content: Template)`: Truncate `content` by
Expand Down

0 comments on commit 227ecce

Please sign in to comment.