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

Implement multi-line progress message support #443

Merged
merged 3 commits into from Sep 22, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
82 changes: 70 additions & 12 deletions src/style.rs
Expand Up @@ -359,22 +359,42 @@ impl ProgressStyle {
}
}
TemplatePart::Literal(s) => cur.push_str(s.expanded()),
TemplatePart::NewLine => lines.push(match wide {
Some(inner) => {
inner.expand(mem::take(&mut cur), self, state, &mut buf, target_width)
}
None => mem::take(&mut cur),
}),
TemplatePart::NewLine => {
self.push_line(lines, &mut cur, state, &mut buf, target_width, &wide)
}
}
}

if !cur.is_empty() {
lines.push(match wide {
Some(inner) => {
inner.expand(mem::take(&mut cur), self, state, &mut buf, target_width)
}
None => mem::take(&mut cur),
})
self.push_line(lines, &mut cur, state, &mut buf, target_width, &wide);
}
}

fn push_line(
&self,
lines: &mut Vec<String>,
cur: &mut String,
state: &ProgressState,
buf: &mut String,
target_width: u16,
wide: &Option<WideElement>,
) {
let expanded = match wide {
Some(inner) => inner.expand(mem::take(cur), self, state, buf, target_width),
None => mem::take(cur),
};

// If there are newlines, we need to split them up
// and add the lines separately so that they're counted
// correctly on re-render.
for (i, line) in expanded.split('\n').enumerate() {
// No newlines found in this case
if i == 0 && line.len() == expanded.len() {
lines.push(expanded);
break;
}

lines.push(line.to_string());
}
}
}
Expand Down Expand Up @@ -931,4 +951,42 @@ mod tests {
style.format_state(&state, &mut buf, WIDTH);
assert_eq!(&buf[0], "\u{1b}[31m\u{1b}[44m foobar \u{1b}[0m");
}

#[test]
fn multiline_handling() {
const WIDTH: u16 = 80;
let pos = Arc::new(AtomicPosition::new());
let mut state = ProgressState::new(Some(10), pos);
let mut buf = Vec::new();

let mut style = ProgressStyle::default_bar();
state.message = TabExpandedString::new("foo\nbar\nbaz".into(), 2);
style.template = Template::from_str("{msg}").unwrap();
style.format_state(&state, &mut buf, WIDTH);

assert_eq!(buf.len(), 3);
assert_eq!(&buf[0], "foo");
assert_eq!(&buf[1], "bar");
assert_eq!(&buf[2], "baz");

buf.clear();
style.template = Template::from_str("{wide_msg}").unwrap();
style.format_state(&state, &mut buf, WIDTH);

assert_eq!(buf.len(), 3);
assert_eq!(&buf[0], "foo");
assert_eq!(&buf[1], "bar");
assert_eq!(&buf[2], "baz");

buf.clear();
state.prefix = TabExpandedString::new("prefix\nprefix".into(), 2);
style.template = Template::from_str("{prefix} {wide_msg}").unwrap();
style.format_state(&state, &mut buf, WIDTH);

assert_eq!(buf.len(), 4);
assert_eq!(&buf[0], "prefix");
assert_eq!(&buf[1], "prefix foo");
assert_eq!(&buf[2], "bar");
assert_eq!(&buf[3], "baz");
}
}
86 changes: 86 additions & 0 deletions tests/render.rs
Expand Up @@ -774,3 +774,89 @@ fn multi_zombie_handling() {

assert_eq!(in_mem.contents(), "pb1 done!\npb3 done!\npb2 done!");
}

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

let pb1 = mp.add(ProgressBar::new_spinner().with_message("test1"));
let pb2 = mp.add(ProgressBar::new_spinner().with_message("test2"));

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

pb1.inc(1);
pb2.inc(1);

assert_eq!(
in_mem.contents(),
r#"
⠁ test1
⠁ test2
"#
.trim()
);

pb1.set_message("test1\n test1 line2\n test1 line3");

assert_eq!(
in_mem.contents(),
r#"
⠁ test1
test1 line2
test1 line3
⠁ test2
"#
.trim()
);

pb1.inc(1);
pb2.inc(1);

assert_eq!(
in_mem.contents(),
r#"
⠉ test1
test1 line2
test1 line3
⠉ test2
"#
.trim()
);

pb2.set_message("test2\n test2 line2");

assert_eq!(
in_mem.contents(),
r#"
⠉ test1
test1 line2
test1 line3
⠉ test2
test2 line2
"#
.trim()
);

pb1.set_message("single line again");

assert_eq!(
in_mem.contents(),
r#"
⠉ single line again
⠉ test2
test2 line2
"#
.trim()
);

pb1.finish_with_message("test1 done!");
pb2.finish_with_message("test2 done!");

assert_eq!(
in_mem.contents(),
r#" test1 done!
test2 done!"#
);
}