Skip to content

Commit

Permalink
Merge branch 'unicode'
Browse files Browse the repository at this point in the history
  • Loading branch information
mitsuhiko committed Jun 10, 2020
2 parents 9ac7c2b + 0596151 commit cbd0133
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 27 deletions.
5 changes: 5 additions & 0 deletions Cargo.toml
Expand Up @@ -16,6 +16,8 @@ regex = { version = "1.3.1", default-features = false, features = ["std"] }
lazy_static = "1.0"
number_prefix = "0.3"
console = ">=0.9.1, <1.0.0"
unicode-segmentation = { version = "1.6.0", optional = true }
unicode-width = { version = "0.1.7", optional = true }
rayon = { version = "1.0", optional = true }

[dev-dependencies]
Expand All @@ -25,4 +27,7 @@ tokio-core = "0.1"

[features]
default = []
improved_unicode = ["unicode-segmentation", "unicode-width", "console/unicode-width"]

# Legacy alias for `rayon`
with_rayon = ["rayon"]
2 changes: 1 addition & 1 deletion examples/fastbar.rs
Expand Up @@ -27,7 +27,7 @@ fn many_units_of_easy_work(n: u64, label: &str, draw_delta: Option<u64>) {
}

fn main() {
const N: u64 = (1 << 20);
const N: u64 = 1 << 20;

// Perform a long sequence of many simple computations monitored by a
// default progress bar.
Expand Down
2 changes: 1 addition & 1 deletion src/iter.rs
Expand Up @@ -52,7 +52,7 @@ impl<S, T: Iterator<Item = S>> ProgressIterator for T {
}
}

#[cfg(feature = "with_rayon")]
#[cfg(feature = "rayon")]
pub mod rayon_support {
use super::*;
use rayon::iter::{
Expand Down
11 changes: 8 additions & 3 deletions src/lib.rs
Expand Up @@ -84,11 +84,11 @@
//! methods to configure the number of elements in the iterator or change
//! the progress bar style. Indicatif also has optional support for parallel
//! iterators with [Rayon](https://github.com/rayon-rs/rayon). In your
//! `cargo.toml`, use the "with_rayon" feature:
//! `cargo.toml`, use the "rayon" feature:
//!
//! ```toml
//! [dependencies]
//! indicatif = {version = "*", features = ["with_rayon"]}
//! indicatif = {version = "*", features = ["rayon"]}
//! ```
//!
//! And then use it like this:
Expand Down Expand Up @@ -178,6 +178,11 @@
//! println!("The file is {} large", HumanBytes(file.size));
//! println!("The script took {}", HumanDuration(started.elapsed()));
//! ```
//!
//! # Feature Flags
//!
//! * `rayon`: adds rayon support
//! * `improved_unicode`: adds improved unicode support (graphemes, better width calculation)

mod format;
mod iter;
Expand All @@ -190,5 +195,5 @@ pub use crate::iter::{ProgressBarIter, ProgressIterator};
pub use crate::progress::{MultiProgress, ProgressBar, ProgressBarWrap, ProgressDrawTarget};
pub use crate::style::ProgressStyle;

#[cfg(feature = "with_rayon")]
#[cfg(feature = "rayon")]
pub use iter::rayon_support::{ParProgressBarIter, ParallelProgressIterator};
97 changes: 75 additions & 22 deletions src/style.rs
Expand Up @@ -8,54 +8,105 @@ use crate::format::{BinaryBytes, DecimalBytes, FormattedDuration, HumanBytes, Hu
use crate::progress::ProgressState;
use crate::utils::{expand_template, pad_str};

#[cfg(feature = "improved_unicode")]
use unicode_segmentation::UnicodeSegmentation;

/// Controls the rendering style of progress bars.
#[derive(Clone, Debug)]
pub struct ProgressStyle {
tick_strings: Vec<String>,
progress_chars: Vec<char>,
tick_strings: Vec<Box<str>>,
progress_chars: Vec<Box<str>>,
template: Cow<'static, str>,
// how unicode-big each char in progress_chars is
char_width: usize,
}

#[cfg(feature = "improved_unicode")]
fn segment(s: &str) -> Vec<Box<str>> {
UnicodeSegmentation::graphemes(s, true)
.map(|s| s.into())
.collect()
}

#[cfg(not(feature = "improved_unicode"))]
fn segment(s: &str) -> Vec<Box<str>> {
s.chars().map(|x| x.to_string().into()).collect()
}

#[cfg(feature = "improved_unicode")]
fn measure(s: &str) -> usize {
unicode_width::UnicodeWidthStr::width(s)
}

#[cfg(not(feature = "improved_unicode"))]
fn measure(s: &str) -> usize {
s.chars().count()
}

/// finds the unicode-aware width of the passed grapheme cluters
/// panics on an empty parameter, or if the characters are not equal-width
fn width(c: &[Box<str>]) -> usize {
c.iter()
.map(|s| measure(s.as_ref()))
.fold(None, |acc, new| {
match acc {
None => return Some(new),
Some(old) => assert_eq!(old, new, "got passed un-equal width progress characters"),
}
acc
})
.unwrap()
}

impl ProgressStyle {
/// Returns the default progress bar style for bars.
pub fn default_bar() -> ProgressStyle {
let progress_chars = segment("█░");
let char_width = width(&progress_chars);
ProgressStyle {
tick_strings: "⠁⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈ "
.chars()
.map(|c| c.to_string())
.map(|c| c.to_string().into())
.collect(),
progress_chars: "█░".chars().collect(),
progress_chars,
char_width,
template: Cow::Borrowed("{wide_bar} {pos}/{len}"),
}
}

/// Returns the default progress bar style for spinners.
pub fn default_spinner() -> ProgressStyle {
let progress_chars = segment("█░");
let char_width = width(&progress_chars);
ProgressStyle {
tick_strings: "⠁⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈ "
.chars()
.map(|c| c.to_string())
.map(|c| c.to_string().into())
.collect(),
progress_chars: "█░".chars().collect(),
progress_chars,
char_width,
template: Cow::Borrowed("{spinner} {msg}"),
}
}

/// Sets the tick character sequence for spinners.
pub fn tick_chars(mut self, s: &str) -> ProgressStyle {
self.tick_strings = s.chars().map(|c| c.to_string()).collect();
self.tick_strings = s.chars().map(|c| c.to_string().into()).collect();
self
}

/// Sets the tick string sequence for spinners.
pub fn tick_strings(mut self, s: &[&str]) -> ProgressStyle {
self.tick_strings = s.iter().map(|s| s.to_string()).collect();
self.tick_strings = s.iter().map(|s| s.to_string().into()).collect();
self
}

/// Sets the three progress characters `(filled, current, to do)`.
/// Sets the progress characters `(filled, current, to do)`.
/// You can pass more then three for a more detailed display.
/// All passed grapheme clusters need to be of equal width.
pub fn progress_chars(mut self, s: &str) -> ProgressStyle {
self.progress_chars = s.chars().collect();
self.progress_chars = segment(s);
self.char_width = width(&self.progress_chars);
self
}

Expand Down Expand Up @@ -93,32 +144,34 @@ impl ProgressStyle {
width: usize,
alt_style: Option<&Style>,
) -> String {
// todo: this code could really use some comments
let width = width / state.style.char_width;
let pct = state.fraction();
let fill = pct * width as f32;
let head = if pct > 0.0 && (fill as usize) < width {
1
} else {
0
};
let fill = fill as usize;
let head = if pct > 0.0 && fill < width { 1 } else { 0 };

let pb: String = repeat(&state.style.progress_chars[0])
.take(fill)
.map(|b| b.as_ref())
.collect();

let pb = repeat(state.style.progress_chars[0])
.take(fill as usize)
.collect::<String>();
let cur = if head == 1 {
let n = state.style.progress_chars.len().saturating_sub(2);
let cur_char = if n == 0 {
1
} else {
n.saturating_sub((fill * n as f32) as usize % n)
n.saturating_sub((fill * n) % n)
};
state.style.progress_chars[cur_char].to_string()
} else {
"".into()
};
let bg = width.saturating_sub(fill as usize).saturating_sub(head);
let rest = repeat(state.style.progress_chars.last().unwrap())
let bg = width.saturating_sub(fill).saturating_sub(head);
let rest: String = repeat(state.style.progress_chars.last().unwrap())
.take(bg)
.collect::<String>();
.map(|b| b.as_ref())
.collect();
format!(
"{}{}{}",
pb,
Expand Down

0 comments on commit cbd0133

Please sign in to comment.