Skip to content

Commit

Permalink
Support precision specifier in run-time format strings
Browse files Browse the repository at this point in the history
  • Loading branch information
dandavison committed Nov 12, 2021
1 parent 781f0c9 commit 0e68efc
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 9 deletions.
31 changes: 27 additions & 4 deletions src/features/line_numbers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,14 +266,22 @@ fn format_and_paint_line_number_field<'a>(
.as_ref()
.unwrap_or(&Align::Center);
match placeholder.placeholder {
Some(Placeholder::NumberMinus) => ansi_strings.push(styles[Minus].paint(
format_line_number(line_numbers[Minus], alignment_spec, width, None, config),
)),
Some(Placeholder::NumberMinus) => {
ansi_strings.push(styles[Minus].paint(format_line_number(
line_numbers[Minus],
alignment_spec,
width,
placeholder.precision,
None,
config,
)))
}
Some(Placeholder::NumberPlus) => {
ansi_strings.push(styles[Plus].paint(format_line_number(
line_numbers[Plus],
alignment_spec,
width,
placeholder.precision,
Some(plus_file),
config,
)))
Expand All @@ -292,10 +300,11 @@ fn format_line_number(
line_number: Option<usize>,
alignment: &Align,
width: usize,
precision: Option<usize>,
plus_file: Option<&str>,
config: &config::Config,
) -> String {
let pad = |n| format::pad(n, width, alignment);
let pad = |n| format::pad(n, width, alignment, precision);
match (line_number, config.hyperlinks, plus_file) {
(None, _, _) => pad(""),
(Some(n), true, Some(file)) => {
Expand Down Expand Up @@ -332,6 +341,7 @@ pub mod tests {
placeholder: Some(Placeholder::NumberMinus),
alignment_spec: None,
width: None,
precision: None,
suffix: "".into(),
prefix_len: 0,
suffix_len: 0,
Expand All @@ -348,6 +358,7 @@ pub mod tests {
placeholder: Some(Placeholder::NumberPlus),
alignment_spec: None,
width: Some(4),
precision: None,
suffix: "".into(),
prefix_len: 0,
suffix_len: 0,
Expand All @@ -364,6 +375,7 @@ pub mod tests {
placeholder: Some(Placeholder::NumberPlus),
alignment_spec: Some(Align::Right),
width: Some(4),
precision: None,
suffix: "".into(),
prefix_len: 0,
suffix_len: 0,
Expand All @@ -380,6 +392,7 @@ pub mod tests {
placeholder: Some(Placeholder::NumberPlus),
alignment_spec: Some(Align::Right),
width: Some(4),
precision: None,
suffix: "".into(),
prefix_len: 0,
suffix_len: 0,
Expand All @@ -396,6 +409,7 @@ pub mod tests {
placeholder: Some(Placeholder::NumberPlus),
alignment_spec: Some(Align::Right),
width: Some(4),
precision: None,
suffix: "@@".into(),
prefix_len: 2,
suffix_len: 2,
Expand All @@ -413,6 +427,7 @@ pub mod tests {
placeholder: Some(Placeholder::NumberMinus),
alignment_spec: Some(Align::Left),
width: Some(3),
precision: None,
suffix: "@@---{np:_>4}**".into(),
prefix_len: 2,
suffix_len: 15,
Expand All @@ -422,6 +437,7 @@ pub mod tests {
placeholder: Some(Placeholder::NumberPlus),
alignment_spec: Some(Align::Right),
width: Some(4),
precision: None,
suffix: "**".into(),
prefix_len: 5,
suffix_len: 2,
Expand All @@ -439,6 +455,7 @@ pub mod tests {
placeholder: None,
alignment_spec: None,
width: None,
precision: None,
suffix: "__@@---**".into(),
prefix_len: 0,
suffix_len: 9,
Expand All @@ -455,12 +472,14 @@ pub mod tests {
placeholder: Some(Placeholder::NumberMinus),
alignment_spec: Some(Align::Left),
width: Some(4),
precision: None,
suffix: "|".into(),
prefix_len: 2,
suffix_len: 1,
}]
);
}

#[test]
fn test_line_number_format_odd_width_two() {
assert_eq!(
Expand All @@ -475,6 +494,7 @@ pub mod tests {
placeholder: Some(Placeholder::NumberMinus),
alignment_spec: Some(Align::Left),
width: Some(4),
precision: None,
suffix: "+{np:<4}|".into(),
prefix_len: 2,
suffix_len: 9,
Expand All @@ -484,6 +504,7 @@ pub mod tests {
placeholder: Some(Placeholder::NumberPlus),
alignment_spec: Some(Align::Left),
width: Some(4),
precision: None,
suffix: "|".into(),
prefix_len: 1,
suffix_len: 1,
Expand All @@ -500,6 +521,7 @@ pub mod tests {
placeholder: None,
alignment_spec: None,
width: None,
precision: None,
suffix: "|++|".into(),
prefix_len: 1,
suffix_len: 4,
Expand All @@ -522,6 +544,7 @@ pub mod tests {
placeholder: Some(Placeholder::NumberMinus),
alignment_spec: None,
width: None,
precision: None,
suffix: long.into(),
suffix_len: long.len(),
},]
Expand Down
51 changes: 46 additions & 5 deletions src/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ pub struct FormatStringPlaceholderData<'a> {
pub placeholder: Option<Placeholder<'a>>,
pub alignment_spec: Option<Align>,
pub width: Option<usize>,
pub precision: Option<usize>,
pub suffix: SmolStr,
pub suffix_len: usize,
}
Expand Down Expand Up @@ -90,6 +91,9 @@ pub fn make_placeholder_regex(labels: &[&str]) -> Regex {
([<^>]) # 3: Alignment spec
)? #
(\d+) # 4: Width
(?: # Start optional precision (non-capturing)
\.(\d+) # 5: Precision
)? #
)? #
\}}
",
Expand Down Expand Up @@ -134,6 +138,11 @@ pub fn parse_line_number_format<'a>(
.parse()
.unwrap_or_else(|_| panic!("Invalid width in format string: {}", format_string))
}),
precision: captures.get(5).map(|m| {
m.as_str().parse().unwrap_or_else(|_| {
panic!("Invalid precision in format string: {}", format_string)
})
}),
suffix,
suffix_len,
});
Expand All @@ -156,10 +165,42 @@ pub fn parse_line_number_format<'a>(
format_data
}

pub fn pad(s: &str, width: usize, alignment: &Align) -> String {
match alignment {
Align::Left => format!("{0:<1$}", s, width),
Align::Center => format!("{0:^1$}", s, width),
Align::Right => format!("{0:>1$}", s, width),
// Note that in this case of a string `s`, `precision` means "max width".
// See https://doc.rust-lang.org/std/fmt/index.html
pub fn pad(s: &str, width: usize, alignment: &Align, precision: Option<usize>) -> String {
match precision {
None => match alignment {
Align::Left => format!("{0:<1$}", s, width),
Align::Center => format!("{0:^1$}", s, width),
Align::Right => format!("{0:>1$}", s, width),
},
Some(precision) => match alignment {
Align::Left => format!("{0:<1$.2$}", s, width, precision),
Align::Center => format!("{0:^1$.2$}", s, width, precision),
Align::Right => format!("{0:>1$.2$}", s, width, precision),
},
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_placeholder_regex() {
let regex = make_placeholder_regex(&["placeholder"]);
assert_eq!(
parse_line_number_format("prefix {placeholder:<15.14} suffix", &regex, false),
vec![FormatStringPlaceholderData {
prefix: "prefix ".into(),
placeholder: Some(Placeholder::Str("placeholder")),
alignment_spec: Some(Align::Left),
width: Some(15),
precision: Some(14),
suffix: " suffix".into(),
prefix_len: 7,
suffix_len: 7,
}]
);
}
}

0 comments on commit 0e68efc

Please sign in to comment.