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

Handle terminal line wrap to avoid new line #533

Merged
merged 3 commits into from
May 9, 2023
Merged
Show file tree
Hide file tree
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
8 changes: 7 additions & 1 deletion src/draw_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,13 @@ impl DrawState {
};

let len = self.lines.len();
let mut real_len = 0;
for (idx, line) in self.lines.iter().enumerate() {
// Calculate real length based on terminal width
// This take in account linewrap from terminal
real_len +=
(console::measure_text_width(line) as f64 / term.width() as f64).ceil() as usize;

if idx + 1 != len {
term.write_line(line)?;
} else {
Expand All @@ -509,7 +515,7 @@ impl DrawState {
}

term.flush()?;
*last_line_count = self.lines.len() - self.orphan_lines_count + shift;
*last_line_count = real_len - self.orphan_lines_count + shift;
Ok(())
}

Expand Down
15 changes: 11 additions & 4 deletions src/multi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,14 @@ impl MultiState {
if panicking() {
return Ok(());
}
let width = self.width() as f64;
// 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!(
Expand All @@ -279,9 +287,8 @@ impl MultiState {
let line_count = member
.draw_state
.as_ref()
.map(|d| d.lines.len())
.map(|d| real_len(&d.lines, width))
.unwrap_or_default();

// Track the total number of zombie lines on the screen.
self.zombie_lines_count += line_count;

Expand All @@ -300,7 +307,7 @@ impl MultiState {
self.zombie_lines_count = 0;
}

let orphan_lines_count = self.orphan_lines.len();
let orphan_lines_count = real_len(&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 @@ -313,7 +320,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 += extra_lines.len();
draw_state.orphan_lines_count += real_len(extra_lines, width);
}

// Add lines from `ProgressBar::println` call.
Expand Down
128 changes: 128 additions & 0 deletions tests/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -916,3 +916,131 @@ fn multi_progress_bottom_alignment() {
in_mem.write_line("anchor").unwrap();
assert_eq!(in_mem.contents(), "\n\nanchor");
}

#[test]
fn progress_bar_terminal_wrap() {
use std::cmp::min;
let in_mem = InMemoryTerm::new(10, 20);

let mut downloaded = 0;
let total_size = 231231231;

let pb = ProgressBar::with_draw_target(
None,
ProgressDrawTarget::term_like(Box::new(in_mem.clone())),
);
pb.set_style(ProgressStyle::default_bar()
.template("{msg:>12.cyan.bold} {spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {bytes}/{total_bytes}").unwrap()
.progress_chars("#>-"));

pb.set_message("Downloading");
assert_eq!(
in_mem.contents(),
r#" Downloading ⠁ [00:0
0:00] [-------------
--------------------
-------] 0B/0B"#
);

let new = min(downloaded + 223211, total_size);
downloaded = new;
pb.set_position(new);
assert_eq!(
in_mem.contents(),
r#" Downloading ⠁ [00:0
0:00] [-------------
--------------------
-------] 217.98 KiB/
217.98 KiB"#
);

let new = min(downloaded + 223211, total_size);
pb.set_position(new);
assert_eq!(
in_mem.contents(),
r#" Downloading ⠉ [00:0
0:00] [-------------
--------------------
-------] 435.96 KiB/
435.96 KiB"#
);

pb.set_style(
ProgressStyle::default_bar()
.template("{msg:>12.green.bold} downloading {total_bytes:.green} in {elapsed:.green}")
.unwrap(),
);
pb.finish_with_message("Finished");
assert_eq!(
in_mem.contents(),
r#" Finished downloa
ding 435.96 KiB in 0
s"#
);

println!("{:?}", in_mem.contents())
}

#[test]
fn multi_progress_println_terminal_wrap() {
let in_mem = InMemoryTerm::new(10, 48);
let mp =
MultiProgress::with_draw_target(ProgressDrawTarget::term_like(Box::new(in_mem.clone())));

let pb1 = mp.add(ProgressBar::new(10));
let pb2 = mp.add(ProgressBar::new(5));
let pb3 = mp.add(ProgressBar::new(100));

assert_eq!(in_mem.contents(), "");

pb1.inc(2);
mp.println("message printed that is longer than terminal width :)")
.unwrap();
assert_eq!(
in_mem.contents(),
r#"message printed that is longer than terminal wid
th :)
████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 2/10"#
);

mp.println("another great message!").unwrap();
assert_eq!(
in_mem.contents(),
r#"message printed that is longer than terminal wid
th :)
another great message!
████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 2/10"#
);

pb2.inc(1);
pb3.tick();
mp.println("one last message but this one is also longer than terminal width")
.unwrap();

assert_eq!(
in_mem.contents(),
r#"message printed that is longer than terminal wid
th :)
another great message!
one last message but this one is also longer tha
n terminal width
████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 2/10
████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 1/5
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0/100"#
.trim()
);

drop(pb1);
drop(pb2);
drop(pb3);

assert_eq!(
in_mem.contents(),
r#"message printed that is longer than terminal wid
th :)
another great message!
one last message but this one is also longer tha
n terminal width"#
.trim()
);
}