Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix and rename real_len #608

Merged
merged 3 commits into from
Dec 12, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 102 additions & 11 deletions src/multi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,14 +273,6 @@ impl MultiState {
None => return Ok(()),
};

// Calculate real length based on terminal width
// This take in account linewrap from terminal
fn real_len(lines: &[String], width: f64) -> usize {
lines.iter().fold(0, |sum, val| {
sum + (console::measure_text_width(val) as f64 / width).ceil() as usize
})
}

// Assumption: if extra_lines is not None, then it has at least one line
debug_assert_eq!(
extra_lines.is_some(),
Expand All @@ -300,7 +292,7 @@ impl MultiState {
let line_count = member
.draw_state
.as_ref()
.map(|d| real_len(&d.lines, width))
.map(|d| visual_line_count(&d.lines, width))
.unwrap_or_default();
// Track the total number of zombie lines on the screen.
self.zombie_lines_count += line_count;
Expand All @@ -320,7 +312,7 @@ impl MultiState {
self.zombie_lines_count = 0;
}

let orphan_lines_count = real_len(&self.orphan_lines, width);
let orphan_lines_count = visual_line_count(&self.orphan_lines, width);
force_draw |= orphan_lines_count > 0;
let mut drawable = match self.draw_target.drawable(force_draw, now) {
Some(drawable) => drawable,
Expand All @@ -333,7 +325,7 @@ impl MultiState {

if let Some(extra_lines) = &extra_lines {
draw_state.lines.extend_from_slice(extra_lines.as_slice());
draw_state.orphan_lines_count += real_len(extra_lines, width);
draw_state.orphan_lines_count += visual_line_count(extra_lines, width);
}

// Add lines from `ProgressBar::println` call.
Expand Down Expand Up @@ -527,6 +519,18 @@ enum InsertLocation {
Before(usize),
}

/// Calculate the number of visual lines in the given lines, after
/// accounting for line wrapping and non-printable characters.
fn visual_line_count(lines: &[impl AsRef<str>], width: f64) -> usize {
let mut real_lines = 0;
for line in lines {
let effective_line_length = console::measure_text_width(line.as_ref()) as f64;
real_lines += usize::max((effective_line_length / width).ceil() as usize, 1);
}

real_lines
}

#[cfg(test)]
mod tests {
use crate::{MultiProgress, ProgressBar, ProgressDrawTarget};
Expand Down Expand Up @@ -698,4 +702,91 @@ mod tests {
let pb = mp.add(ProgressBar::new(10));
mp.add(pb);
}

#[test]
fn real_line_count_test() {
#[derive(Debug)]
struct Case {
lines: &'static [&'static str],
expectation: usize,
width: f64,
}

let lines_and_expectations = [
Case {
lines: &["1234567890"],
expectation: 1,
width: 10.0,
},
Case {
lines: &["1234567890"],
expectation: 2,
width: 5.0,
},
Case {
lines: &["1234567890"],
expectation: 3,
width: 4.0,
},
Case {
lines: &["1234567890"],
expectation: 4,
width: 3.0,
},
Case {
djc marked this conversation as resolved.
Show resolved Hide resolved
lines: &["1234567890", "", "1234567890"],
expectation: 3,
width: 10.0,
},
Case {
lines: &["1234567890", "", "1234567890"],
expectation: 5,
width: 5.0,
},
Case {
lines: &["1234567890", "", "1234567890"],
expectation: 7,
width: 4.0,
},
Case {
lines: &["aaaaaaaaaaaaa", "", "bbbbbbbbbbbbbbbbb", "", "ccccccc"],
expectation: 8,
width: 7.0,
},
Case {
lines: &["", "", "", "", ""],
expectation: 5,
width: 6.0,
},
Case {
// These lines contain only ANSI escape sequences, so they should only count as 1 line
lines: &["\u{1b}[1m\u{1b}[1m\u{1b}[1m", "\u{1b}[1m\u{1b}[1m\u{1b}[1m"],
expectation: 2,
width: 5.0,
},
Case {
// These lines contain ANSI escape sequences and two effective chars, so they should only count as 1 line still
lines: &[
"a\u{1b}[1m\u{1b}[1m\u{1b}[1ma",
"a\u{1b}[1m\u{1b}[1m\u{1b}[1ma",
],
expectation: 2,
width: 5.0,
},
Case {
// These lines contain ANSI escape sequences and six effective chars, so they should count as 2 lines each
lines: &[
"aa\u{1b}[1m\u{1b}[1m\u{1b}[1mabcd",
"aa\u{1b}[1m\u{1b}[1m\u{1b}[1mabcd",
],
expectation: 4,
width: 5.0,
},
];

for case in lines_and_expectations.iter() {
let result = super::visual_line_count(case.lines, case.width);
assert_eq!(result, case.expectation, "case: {:?}", case);
}
}
}