split_string_by_width: since we pad in the middle, we need to tell split_and_apply how many bytes we've padded

pull/301/head
lilydjwg 2022-07-04 10:55:38 +07:00
parent c72626808f
commit 612d45196a
1 changed files with 24 additions and 14 deletions

@ -55,31 +55,41 @@ fn substring_by_byte(s: &str, start: usize, end: usize) -> &str {
/// Split a string into equal length parts, padding if necessary.
///
/// Return splitted strings and how many spaces each has been padded with.
///
/// ```
/// split_string_by_width("fooba", 3, true) // vec!["foo", "ba "]
/// split_string_by_width("一个汉字两列宽", 8, false) // vec!["一个汉字", "两列宽"]
/// split_string_by_width("fooba", 3, true) // vec![("foo", 0), ("ba ", 1)]
/// split_string_by_width("一个汉字两列宽", 8, false) // vec![("一个汉字", 0), ("两列宽", 0)]
/// ```
fn split_string_by_width(s: &str, max_width: usize, pad: bool) -> Vec<String> {
fn split_string_by_width(s: &str, max_width: usize, pad: bool) -> Vec<(String, usize)> {
let mut res = vec![];
let mut s = s;
while s.width() > max_width {
let mut l = substring_by_width(s, 0, max_width).to_owned();
let used = l.width();
let padded;
if pad && used < max_width {
// a fullwidth char is followed
l.push(' ');
padded = 1;
} else {
padded = 0;
}
res.push(l);
res.push((l, padded));
s = substring_by_width(s, used, s.width());
}
if res.is_empty() || !s.is_empty() {
let mut string = s.to_string();
let padded;
if pad {
string.push_str(&" ".repeat(max_width - s.width()));
padded = max_width - s.width();
string.push_str(&" ".repeat(padded));
} else {
padded = 0;
}
res.push(string);
res.push((string, padded));
}
res
@ -103,7 +113,7 @@ pub fn split_and_apply(
// Missing styles is a bug, so highlight in purple to make this obvious.
return split_string_by_width(line, max_len, matches!(side, Side::Left))
.into_iter()
.map(|part| {
.map(|(part, _)| {
if use_color {
highlight_missing_style_bug(&part)
} else {
@ -116,7 +126,7 @@ pub fn split_and_apply(
let mut styled_parts = vec![];
let mut part_start = 0;
for part in split_string_by_width(line, max_len, matches!(side, Side::Left)) {
for (part, padded) in split_string_by_width(line, max_len, matches!(side, Side::Left)) {
let mut res = String::with_capacity(part.len());
let mut prev_style_end = 0;
for (span, style) in styles {
@ -164,7 +174,7 @@ pub fn split_and_apply(
}
styled_parts.push(res);
part_start += byte_len(&part);
part_start += byte_len(&part) - padded;
}
styled_parts
@ -403,7 +413,7 @@ mod tests {
fn split_string_simple() {
assert_eq!(
split_string_by_width("fooba", 3, true),
vec!["foo", "ba "]
vec![(String::from("foo"), 0), (String::from("ba "), 1)]
);
}
@ -411,7 +421,7 @@ mod tests {
fn split_string_simple_no_pad() {
assert_eq!(
split_string_by_width("fooba", 3, false),
vec!["foo", "ba"]
vec![(String::from("foo"), 0), (String::from("ba"), 0)]
);
}
@ -419,7 +429,7 @@ mod tests {
fn split_string_unicode() {
assert_eq!(
split_string_by_width("ab📦def", 4, true),
vec!["ab📦", "def "]
vec![(String::from("ab📦"), 0), (String::from("def "), 1)]
);
}
@ -427,7 +437,7 @@ mod tests {
fn split_string_cjk() {
assert_eq!(
split_string_by_width("一个汉字两列宽", 8, false),
vec!["一个汉字", "两列宽"]
vec![(String::from("一个汉字"), 0), (String::from("两列宽"), 0)]
);
}
@ -435,7 +445,7 @@ mod tests {
fn split_string_cjk2() {
assert_eq!(
split_string_by_width("你好啊", 5, true),
vec!["你好 ", ""]
vec![(String::from("你好 "), 1), (String::from(""), 3)]
);
}