console-rs / indicatif

A command line progress reporting library for Rust
MIT License
4.43k stars 243 forks source link

MultiProgress: should prune leading progress bars that have become `DoneVisible` or `DoneHidden` #426

Closed chris-laplante closed 2 years ago

chris-laplante commented 2 years ago

Consider this small example program:

use std::thread;
use std::time::Duration;

use indicatif::{MultiProgress, ProgressBar, ProgressFinish};

fn main() {
    let m = MultiProgress::new();
    let height = console::Term::stdout().size().0;

    for i in 0..(height + 1) {
        let p1 = m.add(
            ProgressBar::new_spinner()
                .with_message(format!("p{}", i))
                .with_finish(ProgressFinish::AndLeave),
        );
        p1.enable_steady_tick(Duration::from_millis(50));
        thread::sleep(Duration::from_millis(200));
    }
}

It creates one more spinner that there are lines in the terminal. Here is a video of it running:

https://user-images.githubusercontent.com/40474653/166507072-efdbaf25-82e5-4f25-91ac-e6ab51db2314.mp4

Since we never call MultiProgress::remove on ProgressBars that are dropped/finished (see #419), bars are always redrawn even when they don't need to be. When the number of bars (n) becomes greater than terminal lines (m), on each draw MultiProgress will clear the last m lines but write n lines, leaving m - n lines in the scrollback history.

I propose the following algorithm. On each MultiProgress:draw, iterate over the bars in order (according to MultiState::ordering) and take_while(|bar| bar.is_finished()). This set of leading progress bars is then pruned by calling MultiProgress:remove on each one. This ensures we are not re-drawing bars that don't need to be redrawn and that we are some reclaiming memory.

The wrinkle is that MultiState cannot currently get at the ProgressState to check is_finished(), so we will need to restructure some code.

djc commented 2 years ago

This sounds reasonable. I don't have a deep understanding of MultiProgress paged in right now and the video doesn't want to play for me ("Video can't be played because the file is corrupt" -- Firefox on macOS), but I'd be happy to review a PR for this.