Skip to content

Commit

Permalink
Implement customizable tab expansion (console-rs#150)
Browse files Browse the repository at this point in the history
  • Loading branch information
chris-laplante committed Jun 23, 2022
1 parent 72c25c1 commit 8bbf5a1
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 35 deletions.
25 changes: 18 additions & 7 deletions src/progress_bar.rs
Expand Up @@ -70,15 +70,21 @@ impl ProgressBar {
self
}

/// A convenience builder-like function for a progress bar with a given tab width
pub fn with_tab_width(self, tab_width: usize) -> ProgressBar {
self.state().set_tab_width_without_draw(tab_width);
self
}

/// A convenience builder-like function for a progress bar with a given prefix
pub fn with_prefix(self, prefix: impl Into<Cow<'static, str>>) -> ProgressBar {
self.state().state.prefix = prefix.into();
self.state().set_prefix_without_draw(prefix.into());
self
}

/// A convenience builder-like function for a progress bar with a given message
pub fn with_message(self, message: impl Into<Cow<'static, str>>) -> ProgressBar {
self.state().state.message = message.into();
self.state().set_message_without_draw(message.into());
self
}

Expand Down Expand Up @@ -123,7 +129,12 @@ impl ProgressBar {
///
/// This does not redraw the bar. Call [`ProgressBar::tick()`] to force it.
pub fn set_style(&self, style: ProgressStyle) {
self.state().style = style;
self.state().set_style(style);
}

/// Sets the tab width (default: 8). All tabs will be expanded to this many spaces.
pub fn set_tab_width(&mut self, tab_width: usize) {
self.state().set_tab_width(Instant::now(), tab_width);
}

/// Spawns a background thread to tick the progress bar
Expand Down Expand Up @@ -516,8 +527,8 @@ impl ProgressBar {
}

/// Current message
pub fn message(&self) -> Cow<'static, str> {
self.state().state.message.clone()
pub fn message(&self) -> String {
self.state().state.message().to_string()
}

/// Current message (with ANSI escape codes stripped)
Expand All @@ -526,8 +537,8 @@ impl ProgressBar {
}

/// Current prefix
pub fn prefix(&self) -> Cow<'static, str> {
self.state().state.prefix.clone()
pub fn prefix(&self) -> String {
self.state().state.prefix().to_string()
}

/// Current prefix (with ANSI escape codes stripped)
Expand Down
114 changes: 106 additions & 8 deletions src/state.rs
Expand Up @@ -7,11 +7,14 @@ use std::{fmt, io};
use crate::draw_target::ProgressDrawTarget;
use crate::style::ProgressStyle;

pub(crate) const DEFAULT_TAB_WIDTH: usize = 8;

pub(crate) struct BarState {
pub(crate) draw_target: ProgressDrawTarget,
pub(crate) on_finish: ProgressFinish,
pub(crate) style: ProgressStyle,
pub(crate) state: ProgressState,
tab_width: usize,
}

impl BarState {
Expand All @@ -25,6 +28,7 @@ impl BarState {
on_finish: ProgressFinish::default(),
style: ProgressStyle::default_bar(),
state: ProgressState::new(len, pos),
tab_width: DEFAULT_TAB_WIDTH,
}
}

Expand All @@ -42,7 +46,7 @@ impl BarState {
if let Some(len) = self.state.len {
self.state.pos.set(len);
}
self.state.message = msg;
self.state.message = TabExpandedString::new(msg, self.tab_width);
}
ProgressFinish::AndClear => {
if let Some(len) = self.state.len {
Expand All @@ -51,7 +55,9 @@ impl BarState {
self.state.status = Status::DoneHidden;
}
ProgressFinish::Abandon => {}
ProgressFinish::AbandonWithMessage(msg) => self.state.message = msg,
ProgressFinish::AbandonWithMessage(msg) => {
self.state.message = TabExpandedString::new(msg, self.tab_width)
}
}

// There's no need to update the estimate here; once the `status` is no longer
Expand Down Expand Up @@ -93,15 +99,42 @@ impl BarState {
}

pub(crate) fn set_message(&mut self, now: Instant, msg: Cow<'static, str>) {
self.state.message = msg;
self.state.message = TabExpandedString::new(msg, self.tab_width);
self.update_estimate_and_draw(now);
}

// Called in builder context
pub(crate) fn set_message_without_draw(&mut self, msg: Cow<'static, str>) {
self.state.message = TabExpandedString::new(msg, self.tab_width);
}

pub(crate) fn set_prefix(&mut self, now: Instant, prefix: Cow<'static, str>) {
self.state.prefix = prefix;
self.state.prefix = TabExpandedString::new(prefix, self.tab_width);
self.update_estimate_and_draw(now);
}

// Called in builder context
pub(crate) fn set_prefix_without_draw(&mut self, prefix: Cow<'static, str>) {
self.state.prefix = TabExpandedString::new(prefix, self.tab_width);
}

pub(crate) fn set_tab_width(&mut self, now: Instant, tab_width: usize) {
self.set_tab_width_without_draw(tab_width);
self.update_estimate_and_draw(now);
}

pub(crate) fn set_tab_width_without_draw(&mut self, tab_width: usize) {
self.tab_width = tab_width;
self.state.message.change_tab_width(tab_width);
self.state.prefix.change_tab_width(tab_width);
self.style.change_tab_width(tab_width);
}

pub(crate) fn set_style(&mut self, style: ProgressStyle) {
self.style = style;
self.style.change_tab_width(self.tab_width);
}

pub(crate) fn tick(&mut self, now: Instant) {
self.state.tick = self.state.tick.saturating_add(1);
self.update_estimate_and_draw(now);
Expand Down Expand Up @@ -190,8 +223,8 @@ pub struct ProgressState {
pub(crate) started: Instant,
status: Status,
est: Estimator,
pub(crate) message: Cow<'static, str>,
pub(crate) prefix: Cow<'static, str>,
message: TabExpandedString,
prefix: TabExpandedString,
}

impl ProgressState {
Expand All @@ -203,8 +236,8 @@ impl ProgressState {
status: Status::InProgress,
started: Instant::now(),
est: Estimator::new(Instant::now()),
message: "".into(),
prefix: "".into(),
message: TabExpandedString::NoTabs("".into()),
prefix: TabExpandedString::NoTabs("".into()),
}
}

Expand Down Expand Up @@ -287,6 +320,71 @@ impl ProgressState {
pub fn set_len(&mut self, len: u64) {
self.len = Some(len);
}

pub fn set_message(&mut self, msg: TabExpandedString) {
self.message = msg;
}

pub fn message(&self) -> &str {
self.message.expanded()
}

pub fn set_prefix(&mut self, prefix: TabExpandedString) {
self.prefix = prefix;
}

pub fn prefix(&self) -> &str {
self.prefix.expanded()
}
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub enum TabExpandedString {
NoTabs(Cow<'static, str>),
WithTabs {
original: Cow<'static, str>,
expanded: String,
tab_width: usize,
},
}

impl TabExpandedString {
pub(crate) fn new(s: Cow<'static, str>, tab_width: usize) -> Self {
let expanded = s.replace('\t', &" ".repeat(tab_width));
if s == expanded {
Self::NoTabs(s)
} else {
Self::WithTabs {
original: s,
expanded,
tab_width,
}
}
}

pub(crate) fn expanded(&self) -> &str {
match &self {
Self::NoTabs(s) => {
debug_assert!(!s.contains('\t'));
s
}
Self::WithTabs { expanded, .. } => expanded,
}
}

pub(crate) fn change_tab_width(&mut self, new_tab_width: usize) {
if let TabExpandedString::WithTabs {
original,
expanded,
tab_width,
} = self
{
if *tab_width != new_tab_width {
*tab_width = new_tab_width;
*expanded = original.replace('\t', &" ".repeat(new_tab_width));
}
}
}
}

/// Estimate the number of seconds per step
Expand Down

0 comments on commit 8bbf5a1

Please sign in to comment.