Add multiline progress string support in Stderr.

This commit is contained in:
jcamiel 2024-03-22 16:11:45 +01:00
parent 750f7120ea
commit e1cdbc0ab2
No known key found for this signature in database
GPG Key ID: 07FF11CFD55356CC

View File

@ -119,15 +119,12 @@ impl Stderr {
pub fn eprintln(&mut self, message: &str) { pub fn eprintln(&mut self, message: &str) {
match self.mode { match self.mode {
WriteMode::Immediate => { WriteMode::Immediate => {
let has_status = !self.progress.is_empty(); let has_progress = !self.progress.is_empty();
if has_status { if has_progress {
// This is the "EL - Erase in Line" sequence. It clears from the cursor self.rewind_cursor();
// to the end of line.
// https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_sequences
eprint!("\x1B[K");
} }
eprintln!("{message}"); eprintln!("{message}");
if has_status { if has_progress {
eprint!("{}", self.progress); eprint!("{}", self.progress);
} }
} }
@ -153,6 +150,32 @@ impl Stderr {
pub fn buffer(&self) -> &str { pub fn buffer(&self) -> &str {
&self.buffer &self.buffer
} }
/// Clears any progress and reset cursor terminal to the position of the last "real" message.
fn rewind_cursor(&self) {
if self.progress.is_empty() {
return;
}
match self.mode {
WriteMode::Immediate => {
// We count the number of new lines \n. We can't use the `String::lines()` because
// it counts a line for a single carriage return. We don't want to go up for a
// single carriage return.
let lines = self.progress.chars().filter(|c| *c == '\n').count();
// We used the following ANSI codes:
// - K: "EL - Erase in Line" sequence. It clears from the cursor to the end of line.
// - 1A: "Cursor Up". Up to one line
// <https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_sequences>
if lines > 0 {
(0..lines).for_each(|_| eprint!("\x1B[1A\x1B[K"));
} else {
eprint!("\x1B[K");
}
}
WriteMode::Buffered => {}
}
}
} }
#[cfg(test)] #[cfg(test)]