From f88ec3ba072a3d4b6651ce6b6408d952e9c095b3 Mon Sep 17 00:00:00 2001 From: Martijn Gribnau Date: Fri, 2 Jun 2023 07:27:04 +0200 Subject: [PATCH] Fix subtract with overflow when measuring terminal line length If measure_text_width returns 0, the line is effectively empty, although line.is_empty() may still be false. This can for example happen when there is a line which just consists of ANSI color escape sequences. --- src/draw_target.rs | 10 ++++++++-- tests/render.rs | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/draw_target.rs b/src/draw_target.rs index 27c31012..57847bf4 100644 --- a/src/draw_target.rs +++ b/src/draw_target.rs @@ -502,8 +502,14 @@ impl DrawState { } else { // 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; + let terminal_len = (console::measure_text_width(line) as f64 / term.width() as f64) + .ceil() as usize; + + // If the line is effectively empty (for example when it consists + // solely of ANSI color code sequences, count it the same as a + // new line. If the line is measured to be len = 0, we will + // subtract with overflow later. + real_len += usize::max(terminal_len, 1); } if idx + 1 != len { term.write_line(line)?; diff --git a/tests/render.rs b/tests/render.rs index ef81c896..f3b2fa36 100644 --- a/tests/render.rs +++ b/tests/render.rs @@ -981,6 +981,25 @@ s"# println!("{:?}", in_mem.contents()) } +#[test] +fn spinner_terminal_cleared_log_line_with_ansi_codes() { + let in_mem = InMemoryTerm::new(10, 100); + + let pb = ProgressBar::with_draw_target( + Some(10), + ProgressDrawTarget::term_like(Box::new(in_mem.clone())), + ); + pb.set_style(ProgressStyle::default_spinner()); + assert_eq!(in_mem.contents(), String::new()); + + pb.finish_and_clear(); + // Visually empty, but consists of an ANSII code + pb.println("\u{1b}[1m"); + + pb.println("text\u{1b}[0m"); + assert_eq!(in_mem.contents(), "\ntext"); +} + #[test] fn multi_progress_println_terminal_wrap() { let in_mem = InMemoryTerm::new(10, 48);