Skip to content

Commit

Permalink
Add 'lingering' feature.
Browse files Browse the repository at this point in the history
Allow a style to linger without clearing itself.
  • Loading branch information
SergioBenitez committed Jun 23, 2023
1 parent d9d4016 commit 1fd9832
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 24 deletions.
23 changes: 11 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,23 +27,20 @@ Why *y*et another *ANSI* terminal coloring library? Here are some reasons:
* This library makes simple things _simple_: `use` [`Paint`] and go!
* Zero dependencies by default. It really is simple.
* Zero allocations except as needed by opt-in [wrapping].
* [Automatic Windows support] for the vast majority (95%+) of Windows
users.
* Featureful `no_std`, no-`alloc`, support with `default-features =
false`.
* [Automatic Windows support] for the vast majority (95%+) of Windows users.
* [Featureful `no_std`], no-`alloc`, support with `default-features = false`.
* [`Style` constructors are `const`]: store styles statically, even with
dynamic conditions!
* _Any_ type implementing a formatting trait can be stylizd, not just
strings.
* Styling can be [enabled] and [disabled] globally and [dynamically], on
the fly.
* _Any_ type implementing a formatting trait can be styled, not just strings.
* Styling can be [enabled] and [disabled] globally and [dynamically], on the
fly.
* A `Style` can be predicated on arbitrary [conditions].
* Formatting specifiers like `{:x}` and `{:08b}` are supported and
preserved!
* [Built-in (optional) conditions] for [TTY detection] and [common
environment variables].
* Formatting specifiers like `{:x}` and `{:08b}` are supported and preserved!
* [Built-in (optional) conditions] for [TTY detection] and [common environment
variables].
* Arbitrary items can be [_masked_] for selective disabling.
* Styling can [_wrap_] to preserve styling across resets.
* Styling can [_linger_] beyond a single value.
* Experimental support for [hyperlinking] is included.
* The name `yansi` is pretty cool 😎.

Expand All @@ -54,6 +51,7 @@ Why *y*et another *ANSI* terminal coloring library? Here are some reasons:
[_masked_]: https://docs.rs/yansi/1.0.0-gamma/yansi/#masking
[wrapping]: https://docs.rs/yansi/1.0.0-gamma/yansi/#wrapping
[_wrap_]: https://docs.rs/yansi/1.0.0-gamma/yansi/#wrapping
[_linger_]: https://docs.rs/yansi/1.0.0-gamma/yansi/#lingering
[conditions]: https://docs.rs/yansi/1.0.0-gamma/yansi/#per-style
[enabled]: https://docs.rs/yansi/1.0.0-gamma/yansi/fn.enable.html
[disabled]: https://docs.rs/yansi/1.0.0-gamma/yansi/fn.disable.html
Expand All @@ -65,6 +63,7 @@ Why *y*et another *ANSI* terminal coloring library? Here are some reasons:
[Built-in (optional) conditions]: https://docs.rs/yansi/1.0.0-gamma/yansi/struct.Condition.html#built-in-conditions
[hyperlinking]: https://docs.rs/yansi/1.0.0-gamma/yansi/hyperlink/index.html
[`Style` constructors are `const`]: https://docs.rs/yansi/1.0.0-gamma/yansi/#uniform-const-builders
[Featureful `no_std`]: https://docs.rs/yansi/1.0.0-gamma/yansi/#crate-features

## License

Expand Down
13 changes: 10 additions & 3 deletions src/attribute.rs → src/attr_quirk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ pub enum Attribute {
/// See the [crate level docs](crate#quirks) for details.
#[derive(Debug, PartialEq, Eq, Copy, Clone, PartialOrd, Ord, Hash)]
pub enum Quirk {
/// Mask the value: don't display it when painting is disabled.
/// Mask: omit when painting is disabled.
///
/// Typically applied via the [`mask()`](crate::Painted::mask()) builder
/// method.
Expand All @@ -104,6 +104,13 @@ pub enum Quirk {
///
/// See the [crate level docs](crate#wrapping) for details.
Wrap,
/// Linger: do not clear the style after it is applied.
///
/// Typically applied via the [`linger()`](crate::Painted::linger()) builder
/// method.
///
/// See the [crate level docs](crate#lingering) for details.
Linger,
/// Brighten the foreground color if it is not already bright.
///
/// Typically applied via the [`bright()`](crate::Painted::bright()) builder
Expand All @@ -118,15 +125,15 @@ pub enum Quirk {
/// method.
///
/// See the [crate level docs](crate#brightening) for details.
OnBright
OnBright,
}

set_enum! {
Attribute { Bold, Dim, Italic, Underline, Blink, RapidBlink, Invert, Conceal, Strike }
}

set_enum! {
Quirk { Mask, Wrap, Bright, OnBright }
Quirk { Mask, Wrap, Linger, Bright, OnBright }
}

impl Attribute {
Expand Down
58 changes: 53 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
//!
//! # Features
//!
//! Several terminal coloring libraries exist ([`ansi_term`], [`colored`],
//! [`term_painter`], to name a few), begging the question: why yet another?
//! Here are a few reasons:
//! Why *y*et another *ANSI* terminal coloring library? Here are some reasons:
//!
//! * This library makes simple things _simple_: `use` [`Paint`] and go!
//! * Zero dependencies by default. It really is simple.
Expand All @@ -26,6 +24,7 @@
//! environment variables].
//! * Arbitrary items can be [_masked_] for selective disabling.
//! * Styling can [_wrap_] to preserve styling across resets.
//! * Styling can [_linger_] beyond a single value.
//! * Experimental support for [hyperlinking] is included.
//! * The name `yansi` is pretty cool 😎.
//!
Expand All @@ -37,6 +36,7 @@
//! [`term_painter`]: https://crates.io/crates/term-painter
//! [_masked_]: #masking
//! [_wrap_]: #wrapping
//! [_linger_]: #lingering
//! [enabled]: crate::enable
//! [disabled]: crate::disable
//! [dynamically]: crate::whenever
Expand Down Expand Up @@ -199,6 +199,54 @@
//! if the wrapped item has styling applied to it. Otherwise, it does not
//! allocate nor incur a meaningful performance cost.
//!
//! ## Lingering
//!
//! Styling can _linger_ beyond a single value via [`Quirk::Linger`] or the
//! equivalent [`linger()`](Painted::linger()) constructor. A lingering style
//! does not clear itself after being applied. In other words, the style lingers
//! on beyond the value it's applied to and until something else clears the
//! respective styling.
//!
//! Lingering is useful in situations where a given style is to be repeated
//! across multiple values, or when style is intended to persist even across
//! values that are not styled with `yansi`. The examples below illustrate these
//! scenarios:
//!
//! ```rust
//! use yansi::Paint;
//!
//! println!("Hello! {} {} things with {} {}?",
//! "How".magenta().underline().linger(),
//! "are".italic().linger(),
//! "you".on_yellow(), // doesn't linger, so all styling is cleared here
//! "today".blue());
//! ```
//!
//! `>` Hello!
//! <span style="color: magenta;">
//! <u>How <i>are things with <span style="background: yellow;">you</span></i></u>
//! </span>
//! <span style="color: blue;">today</span>?
//!
//! ```rust
//! use yansi::Paint;
//!
//! println!("Hello! {} {} things with {} {}?",
//! "How".magenta().underline().linger(),
//! "are".italic(), // doesn't linger, so all styling is cleared here
//! "you".on_yellow().linger(),
//! "today".blue()); // doesn't linger; styling is cleared
//! ```
//!
//! `>` Hello!
//! <span style="color: magenta;">
//! <u>How <i>are</i></u>
//! </span>
//! things with
//! <span style="background: yellow;">
//! you
//! <span style="color: blue;">today</span></span>?
//!
//! ## Brightening
//!
//! Most pimrary colors are available in regular and _bright_ variants, e.g.,
Expand Down Expand Up @@ -277,7 +325,7 @@ extern crate alloc;
#[macro_use]
mod macros;
mod windows;
mod attribute;
mod attr_quirk;
mod style;
mod color;
mod paint;
Expand All @@ -290,7 +338,7 @@ mod set;
pub mod hyperlink;

pub use paint::{Painted, Paint};
pub use attribute::{Attribute, Quirk};
pub use attr_quirk::{Attribute, Quirk};
pub use style::Style;
pub use color::Color;
pub use condition::Condition;
Expand Down
1 change: 1 addition & 0 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ define_properties! {
quirk(Quirk) {
mask => Quirk::Mask,
wrap => Quirk::Wrap,
linger => Quirk::Linger,
bright => Quirk::Bright,
on_bright => Quirk::OnBright,
},
Expand Down
8 changes: 4 additions & 4 deletions src/style.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use core::fmt::{self, Write};

use crate::color::{Color, Variant};
use crate::attribute::{Attribute, Quirk};
use crate::attr_quirk::{Attribute, Quirk};
use crate::condition::Condition;
use crate::set::Set;

Expand Down Expand Up @@ -112,7 +112,7 @@ impl Style {
}

/// Returns `true` if this style is enabled, based on
/// [`condition`](Paint:condition).
/// [`condition`](Paint.condition).
///
/// **Note:** _For a style to be effected, both this method **and**
/// [`yansi::is_enabled()`](crate::is_enabled) must return `true`._
Expand Down Expand Up @@ -250,7 +250,7 @@ impl Style {
/// }
/// ```
pub fn fmt_suffix(&self, f: &mut dyn fmt::Write) -> fmt::Result {
if self == &Style::DEFAULT {
if self.quirks.contains(Quirk::Linger) || self == &Style::DEFAULT {
return Ok(());
}

Expand All @@ -264,7 +264,7 @@ impl Style {
#[cfg(feature = "alloc")]
#[cfg_attr(feature = "_nightly", doc(cfg(feature = "alloc")))]
pub fn suffix_seq(&self) -> Cow<'static, str> {
if self == &Style::DEFAULT {
if self.quirks.contains(Quirk::Linger) || self == &Style::DEFAULT {
return Cow::from("");
}

Expand Down
28 changes: 28 additions & 0 deletions tests/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,34 @@ fn wrapping() {
}
}

#[test]
fn lingering() {
let _lock = LOCK.lock().expect("FAIL FAST - LOCK POISONED");
yansi::enable();

assert_eq! {
format!("Hello! {} {} things with {} {}?",
"How".magenta().underline().linger(),
"are".italic(),
"you".on_yellow().linger(),
"today".blue()),
"Hello! \u{1b}[4;35mHow \u{1b}[3mare\u{1b}[0m things with \
\u{1b}[43myou \u{1b}[34mtoday\u{1b}[0m?",
};

assert_eq! {
format!("Hi! {} {} things with {} {}?",
"How".magenta().underline().linger(),
"are".italic().linger(),
"you".on_yellow().linger(),
"today".blue()),
"Hi! \u{1b}[4;35mHow \u{1b}[3mare things with \u{1b}[43myou \
\u{1b}[34mtoday\u{1b}[0m?"
};

yansi::whenever(Condition::DEFAULT);
}

#[test]
fn hash_eq() {
use std::collections::hash_map::DefaultHasher;
Expand Down

0 comments on commit 1fd9832

Please sign in to comment.