Skip to content

Commit

Permalink
Merge branch 'main' into no-bar-multiprogress
Browse files Browse the repository at this point in the history
  • Loading branch information
chris-laplante committed Jun 2, 2023
2 parents 025a566 + 6cc3814 commit c79e336
Show file tree
Hide file tree
Showing 15 changed files with 303 additions and 37 deletions.
17 changes: 14 additions & 3 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@ jobs:
- os: ubuntu-latest
rust: beta
features: --all-features
- os: ubuntu-latest
rust: 1.58
features: --features improved_unicode
- os: ubuntu-latest
rust: stable
features: --all-features
Expand Down Expand Up @@ -66,6 +63,20 @@ jobs:
command: test
args: --workspace ${{ matrix.features }} ${{ matrix.target }}

msrv:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: "1.62.1"
override: true
- uses: actions-rs/cargo@v1
with:
command: check
args: --lib --all-features

lint:
runs-on: ubuntu-latest
steps:
Expand Down
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "indicatif"
description = "A progress bar and cli reporting library for Rust"
version = "0.17.3"
version = "0.17.4"
keywords = ["cli", "progress", "pb", "colors", "progressbar"]
categories = ["command-line-interface"]
license = "MIT"
Expand All @@ -10,7 +10,7 @@ documentation = "https://docs.rs/indicatif"
readme = "README.md"
edition = "2018"
exclude = ["screenshots/*"]
rust-version = "1.58"
rust-version = "1.62.1"

[dependencies]
console = { version = "0.15", default-features = false, features = ["ansi-parsing"] }
Expand All @@ -23,7 +23,7 @@ unicode-width = { version = "0.1", optional = true }
vt100 = { version = "0.15.1", optional = true }

[dev-dependencies]
clap = { version = "3", features = ["color", "derive"] }
clap = { version = "4", features = ["color", "derive"] }
once_cell = "1"
rand = "0.8"
tokio = { version = "1", features = ["fs", "time", "rt"] }
Expand Down
2 changes: 1 addition & 1 deletion examples/finebars.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ fn main() {
let wait = Duration::from_millis(thread_rng().gen_range(10..30));
thread::spawn(move || {
for i in 0..512 {
thread::sleep(wait);
pb.inc(1);
pb.set_message(format!("{:3}%", 100 * i / 512));
thread::sleep(wait);
}
pb.finish_with_message("100%");
})
Expand Down
2 changes: 1 addition & 1 deletion examples/morebars.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ fn main() {
let pb2 = m.add(ProgressBar::new(128));
pb2.set_style(sty.clone());
for _ in 0..128 {
pb2.inc(1);
thread::sleep(Duration::from_millis(5));
pb2.inc(1);
}
pb2.finish();
pb.inc(1);
Expand Down
2 changes: 1 addition & 1 deletion examples/multi-tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ fn main() {
let mut rng = ThreadRng::default();
pb_main.tick();
loop {
thread::sleep(Duration::from_millis(15));
match get_action(&mut rng, &tree) {
None => {
// all elements were exhausted
Expand Down Expand Up @@ -137,7 +138,6 @@ fn main() {
pb_main.inc(1);
}
}
thread::sleep(Duration::from_millis(15));
}
})
.join();
Expand Down
6 changes: 3 additions & 3 deletions examples/multi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ fn main() {
let m_clone = m.clone();
let h1 = thread::spawn(move || {
for i in 0..128 {
thread::sleep(Duration::from_millis(15));
pb.set_message(format!("item #{}", i + 1));
pb.inc(1);
thread::sleep(Duration::from_millis(15));
}
m_clone.println("pb1 is done!").unwrap();
pb.finish_with_message("done");
Expand All @@ -38,9 +38,9 @@ fn main() {
for _ in 0..3 {
pb2.set_position(0);
for i in 0..128 {
thread::sleep(Duration::from_millis(8));
pb2.set_message(format!("item #{}", i + 1));
pb2.inc(1);
thread::sleep(Duration::from_millis(8));
}
}
m_clone.println("pb2 is done!").unwrap();
Expand All @@ -50,9 +50,9 @@ fn main() {
let m_clone = m.clone();
let h3 = thread::spawn(move || {
for i in 0..1024 {
thread::sleep(Duration::from_millis(2));
pb3.set_message(format!("item #{}", i + 1));
pb3.inc(1);
thread::sleep(Duration::from_millis(2));
}
m_clone.println("pb3 is done!").unwrap();
pb3.finish_with_message("done");
Expand Down
2 changes: 1 addition & 1 deletion examples/single.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use indicatif::ProgressBar;
fn main() {
let pb = ProgressBar::new(1024);
for _ in 0..1024 {
pb.inc(1);
thread::sleep(Duration::from_millis(5));
pb.inc(1);
}
pb.finish_with_message("done");
}
4 changes: 2 additions & 2 deletions examples/yarnish.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ pub fn main() {
let deps = 1232;
let pb = ProgressBar::new(deps);
for _ in 0..deps {
pb.inc(1);
thread::sleep(Duration::from_millis(3));
pb.inc(1);
}
pb.finish_and_clear();

Expand All @@ -80,9 +80,9 @@ pub fn main() {
let pkg = PACKAGES.choose(&mut rng).unwrap();
for _ in 0..count {
let cmd = COMMANDS.choose(&mut rng).unwrap();
thread::sleep(Duration::from_millis(rng.gen_range(25..200)));
pb.set_message(format!("{pkg}: {cmd}"));
pb.inc(1);
thread::sleep(Duration::from_millis(rng.gen_range(25..200)));
}
pb.finish_with_message("waiting...");
})
Expand Down
54 changes: 42 additions & 12 deletions src/draw_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,29 +26,29 @@ pub struct ProgressDrawTarget {
impl ProgressDrawTarget {
/// Draw to a buffered stdout terminal at a max of 20 times a second.
///
/// For more information see `ProgressDrawTarget::to_term`.
/// For more information see [`ProgressDrawTarget::term`].
pub fn stdout() -> Self {
Self::term(Term::buffered_stdout(), 20)
}

/// Draw to a buffered stderr terminal at a max of 20 times a second.
///
/// This is the default draw target for progress bars. For more
/// information see `ProgressDrawTarget::to_term`.
/// information see [`ProgressDrawTarget::term`].
pub fn stderr() -> Self {
Self::term(Term::buffered_stderr(), 20)
}

/// Draw to a buffered stdout terminal at a max of `refresh_rate` times a second.
///
/// For more information see `ProgressDrawTarget::to_term`.
/// For more information see [`ProgressDrawTarget::term`].
pub fn stdout_with_hz(refresh_rate: u8) -> Self {
Self::term(Term::buffered_stdout(), refresh_rate)
}

/// Draw to a buffered stderr terminal at a max of `refresh_rate` times a second.
///
/// For more information see `ProgressDrawTarget::to_term`.
/// For more information see [`ProgressDrawTarget::term`].
pub fn stderr_with_hz(refresh_rate: u8) -> Self {
Self::term(Term::buffered_stderr(), refresh_rate)
}
Expand All @@ -59,14 +59,14 @@ impl ProgressDrawTarget {
}
}

/// Draw to a terminal, optionally with a specific refresh rate.
/// Draw to a terminal, with a specific refresh rate.
///
/// Progress bars are by default drawn to terminals however if the
/// terminal is not user attended the entire progress bar will be
/// hidden. This is done so that piping to a file will not produce
/// useless escape codes in that file.
///
/// Will panic if refresh_rate is `Some(0)`. To disable rate limiting use `None` instead.
/// Will panic if refresh_rate is `0`.
pub fn term(term: Term, refresh_rate: u8) -> Self {
Self {
kind: TargetKind::Term {
Expand All @@ -84,6 +84,20 @@ impl ProgressDrawTarget {
kind: TargetKind::TermLike {
inner: term_like,
last_line_count: 0,
rate_limiter: None,
draw_state: DrawState::default(),
},
}
}

/// Draw to a boxed object that implements the [`TermLike`] trait,
/// with a specific refresh rate.
pub fn term_like_with_hz(term_like: Box<dyn TermLike>, refresh_rate: u8) -> Self {
Self {
kind: TargetKind::TermLike {
inner: term_like,
last_line_count: 0,
rate_limiter: Option::from(RateLimiter::new(refresh_rate)),
draw_state: DrawState::default(),
},
}
Expand Down Expand Up @@ -163,12 +177,16 @@ impl ProgressDrawTarget {
TargetKind::TermLike {
inner,
last_line_count,
rate_limiter,
draw_state,
} => Some(Drawable::TermLike {
term_like: &**inner,
last_line_count,
draw_state,
}),
} => match force_draw || rate_limiter.as_mut().map_or(true, |r| r.allow(now)) {
true => Some(Drawable::TermLike {
term_like: &**inner,
last_line_count,
draw_state,
}),
false => None, // rate limited
},
// Hidden, finished, or no need to refresh yet
_ => None,
}
Expand Down Expand Up @@ -221,6 +239,7 @@ enum TargetKind {
TermLike {
inner: Box<dyn TermLike>,
last_line_count: usize,
rate_limiter: Option<RateLimiter>,
draw_state: DrawState,
},
}
Expand Down Expand Up @@ -475,7 +494,18 @@ impl DrawState {
};

let len = self.lines.len();
let mut real_len = 0;
for (idx, line) in self.lines.iter().enumerate() {
if line.is_empty() {
// Empty line are new line
real_len += 1;
} 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;
}

if idx + 1 != len || self.lines.len() == self.orphan_lines_count {
term.write_line(line)?;
} else {
Expand All @@ -491,7 +521,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
39 changes: 39 additions & 0 deletions src/in_memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,45 @@ impl InMemoryTerm {
rows.reverse();
rows.join("\n")
}

pub fn contents_formatted(&self) -> Vec<u8> {
let state = self.state.lock().unwrap();

// For some reason, the `Screen::contents` method doesn't include newlines in what it
// returns, making it useless for our purposes. So we need to manually reconstruct the
// contents by iterating over the rows in the terminal buffer.
let mut rows = state
.parser
.screen()
.rows_formatted(0, state.width)
.collect::<Vec<_>>();

// Reverse the rows and trim empty lines from the end
rows = rows
.into_iter()
.rev()
.skip_while(|line| line.is_empty())
.collect();

// Un-reverse the rows
rows.reverse();

// Calculate buffer size
let reset = b"";
let len = rows.iter().map(|line| line.len() + reset.len() + 1).sum();

// Join rows up with reset codes and newlines
let mut contents = rows.iter().fold(Vec::with_capacity(len), |mut acc, cur| {
acc.extend_from_slice(cur);
acc.extend_from_slice(reset);
acc.push(b'\n');
acc
});

// Remove last newline again, but leave the reset code
contents.truncate(len.saturating_sub(1));
contents
}
}

impl TermLike for InMemoryTerm {
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
//! Platform support:
//!
//! * Linux
//! * OS X
//! * macOS
//! * Windows (colors require Windows 10)
//!
//! Best paired with other libraries in the family:
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
Loading

0 comments on commit c79e336

Please sign in to comment.