Unify Colorize
and Styler
as Stylize
(#571)
This commit is contained in:
parent
e1260446e9
commit
0e8be6a891
@ -175,7 +175,7 @@
|
|||||||
//! use std::io::{stdout, Write};
|
//! use std::io::{stdout, Write};
|
||||||
//! use crossterm::{
|
//! use crossterm::{
|
||||||
//! ExecutableCommand, QueueableCommand,
|
//! ExecutableCommand, QueueableCommand,
|
||||||
//! terminal, cursor, style::{self, Colorize}, Result
|
//! terminal, cursor, style::{self, Stylize}, Result
|
||||||
//! };
|
//! };
|
||||||
//!
|
//!
|
||||||
//! fn main() -> Result<()> {
|
//! fn main() -> Result<()> {
|
||||||
@ -204,7 +204,7 @@
|
|||||||
//! use std::io::{stdout, Write};
|
//! use std::io::{stdout, Write};
|
||||||
//! use crossterm::{
|
//! use crossterm::{
|
||||||
//! execute, queue,
|
//! execute, queue,
|
||||||
//! style::{self, Colorize}, cursor, terminal, Result
|
//! style::{self, Stylize}, cursor, terminal, Result
|
||||||
//! };
|
//! };
|
||||||
//!
|
//!
|
||||||
//! fn main() -> Result<()> {
|
//! fn main() -> Result<()> {
|
||||||
|
76
src/style.rs
76
src/style.rs
@ -50,10 +50,11 @@
|
|||||||
//!
|
//!
|
||||||
//! Functions:
|
//! Functions:
|
||||||
//!
|
//!
|
||||||
//! Using functions from [`Colorize`](trait.Colorize.html) on a `String` or `&'static str` to color it.
|
//! Using functions from [`Stylize`](crate::style::Stylize) on a `String` or `&'static str` to color
|
||||||
|
//! it.
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! use crossterm::style::Colorize;
|
//! use crossterm::style::Stylize;
|
||||||
//!
|
//!
|
||||||
//! println!("{}", "Red foreground color & blue background.".red().on_blue());
|
//! println!("{}", "Red foreground color & blue background.".red().on_blue());
|
||||||
//! ```
|
//! ```
|
||||||
@ -86,10 +87,11 @@
|
|||||||
//!
|
//!
|
||||||
//! Functions:
|
//! Functions:
|
||||||
//!
|
//!
|
||||||
//! Using [`Styler`](trait.Styler.html) functions on a `String` or `&'static str` to set attributes to it.
|
//! Using [`Stylize`](crate::style::Stylize) functions on a `String` or `&'static str` to set
|
||||||
|
//! attributes to it.
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! use crossterm::style::Styler;
|
//! use crossterm::style::Stylize;
|
||||||
//!
|
//!
|
||||||
//! println!("{}", "Bold".bold());
|
//! println!("{}", "Bold".bold());
|
||||||
//! println!("{}", "Underlined".underlined());
|
//! println!("{}", "Underlined".underlined());
|
||||||
@ -115,6 +117,7 @@ use std::{
|
|||||||
fmt::{self, Display},
|
fmt::{self, Display},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::command::execute_fmt;
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use crate::Result;
|
use crate::Result;
|
||||||
use crate::{csi, impl_display, Command};
|
use crate::{csi, impl_display, Command};
|
||||||
@ -123,17 +126,15 @@ pub use self::{
|
|||||||
attributes::Attributes,
|
attributes::Attributes,
|
||||||
content_style::ContentStyle,
|
content_style::ContentStyle,
|
||||||
styled_content::StyledContent,
|
styled_content::StyledContent,
|
||||||
traits::{Colorize, Styler},
|
stylize::Stylize,
|
||||||
types::{Attribute, Color, Colored, Colors},
|
types::{Attribute, Color, Colored, Colors},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
mod macros;
|
|
||||||
mod attributes;
|
mod attributes;
|
||||||
mod content_style;
|
mod content_style;
|
||||||
mod styled_content;
|
mod styled_content;
|
||||||
|
mod stylize;
|
||||||
mod sys;
|
mod sys;
|
||||||
mod traits;
|
|
||||||
mod types;
|
mod types;
|
||||||
|
|
||||||
/// Creates a `StyledContent`.
|
/// Creates a `StyledContent`.
|
||||||
@ -145,7 +146,7 @@ mod types;
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// use crossterm::style::{style, Color};
|
/// use crossterm::style::{style, Stylize, Color};
|
||||||
///
|
///
|
||||||
/// let styled_content = style("Blue colored text on yellow background")
|
/// let styled_content = style("Blue colored text on yellow background")
|
||||||
/// .with(Color::Blue)
|
/// .with(Color::Blue)
|
||||||
@ -157,24 +158,6 @@ pub fn style<D: Display>(val: D) -> StyledContent<D> {
|
|||||||
ContentStyle::new().apply(val)
|
ContentStyle::new().apply(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_colorize!(String);
|
|
||||||
impl_colorize!(char);
|
|
||||||
|
|
||||||
// We do actually need the parentheses here because the macro doesn't work without them otherwise
|
|
||||||
// This is probably a bug somewhere in the compiler, but it isn't that big a deal.
|
|
||||||
#[allow(unused_parens)]
|
|
||||||
impl<'a> Colorize<&'a str> for &'a str {
|
|
||||||
impl_colorize_callback!(def_color_base!((&'a str)));
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_styler!(String);
|
|
||||||
impl_styler!(char);
|
|
||||||
|
|
||||||
#[allow(unused_parens)]
|
|
||||||
impl<'a> Styler<&'a str> for &'a str {
|
|
||||||
impl_styler_callback!(def_attr_base!((&'a str)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns available color count.
|
/// Returns available color count.
|
||||||
///
|
///
|
||||||
/// # Notes
|
/// # Notes
|
||||||
@ -342,7 +325,44 @@ pub struct PrintStyledContent<D: Display>(pub StyledContent<D>);
|
|||||||
|
|
||||||
impl<D: Display> Command for PrintStyledContent<D> {
|
impl<D: Display> Command for PrintStyledContent<D> {
|
||||||
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
|
fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
|
||||||
write!(f, "{}", self.0)
|
let style = self.0.style();
|
||||||
|
|
||||||
|
let mut reset_background = false;
|
||||||
|
let mut reset_foreground = false;
|
||||||
|
let mut reset = false;
|
||||||
|
|
||||||
|
if let Some(bg) = style.background_color {
|
||||||
|
execute_fmt(f, SetBackgroundColor(bg)).map_err(|_| fmt::Error)?;
|
||||||
|
reset_background = true;
|
||||||
|
}
|
||||||
|
if let Some(fg) = style.foreground_color {
|
||||||
|
execute_fmt(f, SetForegroundColor(fg)).map_err(|_| fmt::Error)?;
|
||||||
|
reset_foreground = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !style.attributes.is_empty() {
|
||||||
|
execute_fmt(f, SetAttributes(style.attributes)).map_err(|_| fmt::Error)?;
|
||||||
|
reset = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, "{}", self.0.content())?;
|
||||||
|
|
||||||
|
if reset {
|
||||||
|
// NOTE: This will reset colors even though self has no colors, hence produce unexpected
|
||||||
|
// resets.
|
||||||
|
// TODO: reset the set attributes only.
|
||||||
|
execute_fmt(f, ResetColor).map_err(|_| fmt::Error)?;
|
||||||
|
} else {
|
||||||
|
// NOTE: Since the above bug, we do not need to reset colors when we reset attributes.
|
||||||
|
if reset_background {
|
||||||
|
execute_fmt(f, SetBackgroundColor(Color::Reset)).map_err(|_| fmt::Error)?;
|
||||||
|
}
|
||||||
|
if reset_foreground {
|
||||||
|
execute_fmt(f, SetForegroundColor(Color::Reset)).map_err(|_| fmt::Error)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
use crate::style::{Attribute, Attributes, Color, StyledContent};
|
use crate::style::{Attributes, Color, StyledContent};
|
||||||
|
|
||||||
/// The style that can be put on content.
|
/// The style that can be put on content.
|
||||||
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
|
||||||
@ -27,62 +27,15 @@ impl ContentStyle {
|
|||||||
pub fn new() -> ContentStyle {
|
pub fn new() -> ContentStyle {
|
||||||
ContentStyle::default()
|
ContentStyle::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the background color.
|
|
||||||
#[inline]
|
|
||||||
pub fn background(self, color: Color) -> ContentStyle {
|
|
||||||
Self {
|
|
||||||
background_color: Some(color),
|
|
||||||
..self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the foreground color.
|
impl AsRef<ContentStyle> for ContentStyle {
|
||||||
#[inline]
|
fn as_ref(&self) -> &Self {
|
||||||
pub fn foreground(self, color: Color) -> ContentStyle {
|
|
||||||
Self {
|
|
||||||
foreground_color: Some(color),
|
|
||||||
..self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds the attribute.
|
|
||||||
///
|
|
||||||
/// You can add more attributes by calling this method multiple times.
|
|
||||||
#[inline]
|
|
||||||
pub fn attribute(mut self, attr: Attribute) -> ContentStyle {
|
|
||||||
self.attributes.set(attr);
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl AsMut<ContentStyle> for ContentStyle {
|
||||||
#[cfg(test)]
|
fn as_mut(&mut self) -> &mut Self {
|
||||||
mod tests {
|
self
|
||||||
use crate::style::{Attribute, Color, ContentStyle};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_set_fg_bg_add_attr() {
|
|
||||||
let content_style = ContentStyle::new()
|
|
||||||
.foreground(Color::Blue)
|
|
||||||
.background(Color::Red)
|
|
||||||
.attribute(Attribute::Bold);
|
|
||||||
|
|
||||||
assert_eq!(content_style.foreground_color, Some(Color::Blue));
|
|
||||||
assert_eq!(content_style.background_color, Some(Color::Red));
|
|
||||||
assert!(content_style.attributes.has(Attribute::Bold));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_apply_content_style_to_text() {
|
|
||||||
let content_style = ContentStyle::new()
|
|
||||||
.foreground(Color::Blue)
|
|
||||||
.background(Color::Red)
|
|
||||||
.attribute(Attribute::Reset);
|
|
||||||
|
|
||||||
let styled_content = content_style.apply("test");
|
|
||||||
|
|
||||||
assert_eq!(styled_content.style().foreground_color, Some(Color::Blue));
|
|
||||||
assert_eq!(styled_content.style().background_color, Some(Color::Red));
|
|
||||||
assert!(styled_content.style().attributes.has(Attribute::Reset));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,179 +0,0 @@
|
|||||||
//! Defines the macros for repetitive styling implementations
|
|
||||||
|
|
||||||
// There's a single core set of macros structure here that's essentially repeated twice; once for
|
|
||||||
// implementing `Styler` and once for `Colorize`. We'll go through `Styler` as the example, knowing
|
|
||||||
// that `Colorize` works in precisely the same manner.
|
|
||||||
//
|
|
||||||
// There are four macros in each group. For `Styler`, they are:
|
|
||||||
// * def_attr_base,
|
|
||||||
// * def_attr_generic,
|
|
||||||
// * impl_styler_callback,
|
|
||||||
// * impl_styler
|
|
||||||
//
|
|
||||||
// Fundamentally, any implementation works in a similar fashion; many methods with near-identical
|
|
||||||
// bodies are grouped together. There are additionally two types of implementors: so-called "base"
|
|
||||||
// implementors (`char`, `String`, etc.) and a single "generic" implementor - 'StyledContent<D>'.
|
|
||||||
//
|
|
||||||
// We can visualize the macro expansion with a sort of pipeline:
|
|
||||||
//
|
|
||||||
// /--------> def_attr_base
|
|
||||||
// [impl_styler ->] impl_styler_callback
|
|
||||||
// \--------> def_attr_generic
|
|
||||||
//
|
|
||||||
// The code-gen starts at 'impl_styler' for "base" implementors, and at 'impl_styler_callback' for
|
|
||||||
// `StyledContent<D>`. From there, 'impl_styler_callback' either repeatedly calls 'def_attr_base'
|
|
||||||
// or 'def_attr_generic' - this is determined by the 'callback' argument.
|
|
||||||
//
|
|
||||||
// 'def_attr_base' is used to provide the method bodies for base types, and 'def_attr_generic'
|
|
||||||
// provides the method bodies for 'StyledContent<D>'.
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// `Styler` macros //
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// Produces a single method for a "base" Styler implementation
|
|
||||||
//
|
|
||||||
// The first argument is the type for which Styler is being implemented. Because this is the same
|
|
||||||
// for all "base" types, we can collapse them into a single macro.
|
|
||||||
macro_rules! def_attr_base {
|
|
||||||
($impl_ty:ty, $name:ident => $attr:path) => {
|
|
||||||
fn $name(self) -> StyledContent<$impl_ty> {
|
|
||||||
StyledContent::new(
|
|
||||||
ContentStyle {
|
|
||||||
attributes: $attr.into(),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
self,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Produces a single method within an implementation of Styler for 'StyledContent<D>'
|
|
||||||
//
|
|
||||||
// We give it an empty argument at the start so that it has the same "signature" as
|
|
||||||
// 'def_attr_base', which takes a type as its first argument
|
|
||||||
macro_rules! def_attr_generic {
|
|
||||||
((), $name:ident => $attr:path) => {
|
|
||||||
fn $name(self) -> StyledContent<D> {
|
|
||||||
self.attribute($attr)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Produces the set of methods inside the implementation, but not the outer block itself
|
|
||||||
//
|
|
||||||
// 'callback' should be either one of 'def_attr_base' or 'def_attr_generic'. Each expansion of
|
|
||||||
// 'callback' produces a single method with the name given by the second argument.
|
|
||||||
macro_rules! impl_styler_callback {
|
|
||||||
($callback:ident!($args:tt)) => {
|
|
||||||
$callback!($args, reset => Attribute::Reset);
|
|
||||||
$callback!($args, bold => Attribute::Bold);
|
|
||||||
$callback!($args, underlined => Attribute::Underlined);
|
|
||||||
$callback!($args, reverse => Attribute::Reverse);
|
|
||||||
$callback!($args, dim => Attribute::Dim);
|
|
||||||
$callback!($args, italic => Attribute::Italic);
|
|
||||||
$callback!($args, negative => Attribute::Reverse);
|
|
||||||
$callback!($args, slow_blink => Attribute::SlowBlink);
|
|
||||||
$callback!($args, rapid_blink => Attribute::RapidBlink);
|
|
||||||
$callback!($args, hidden => Attribute::Hidden);
|
|
||||||
$callback!($args, crossed_out => Attribute::CrossedOut);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Produces the full implementation of Styler for "base" types
|
|
||||||
//
|
|
||||||
// This macro is mostly here for convenience; it's nice to not require writing out the
|
|
||||||
// `impl Styler<..> for ..` for each base type.
|
|
||||||
macro_rules! impl_styler {
|
|
||||||
($impl_ty:ty) => {
|
|
||||||
impl Styler<$impl_ty> for $impl_ty {
|
|
||||||
impl_styler_callback!(def_attr_base!($impl_ty));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// `Colorize` macros //
|
|
||||||
// //
|
|
||||||
// These are effectively the same as the `Styler` macros described above, so //
|
|
||||||
// not much detail is repeated here. Where above we have 'def_attr_*', there //
|
|
||||||
// is 'def_color_*' here, and 'impl_colorize' takes the place of //
|
|
||||||
// 'impl_styler'. //
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
macro_rules! def_color_base {
|
|
||||||
($color_ty:ty, $side:ident: $name:ident => $color:path) => {
|
|
||||||
fn $name(self) -> StyledContent<$color_ty> {
|
|
||||||
StyledContent::new(
|
|
||||||
ContentStyle {
|
|
||||||
$side: Some($color),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
self,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! def_color_generic {
|
|
||||||
((), $side:ident: $name:ident => $color:path) => {
|
|
||||||
fn $name(self) -> StyledContent<D> {
|
|
||||||
StyledContent::new(
|
|
||||||
ContentStyle {
|
|
||||||
$side: Some($color),
|
|
||||||
..self.style
|
|
||||||
},
|
|
||||||
self.content,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! impl_colorize_callback {
|
|
||||||
($callback:ident!($args:tt)) => {
|
|
||||||
// foreground colors
|
|
||||||
$callback!($args, foreground_color: black => Color::Black);
|
|
||||||
$callback!($args, foreground_color: dark_grey => Color::DarkGrey);
|
|
||||||
$callback!($args, foreground_color: red => Color::Red);
|
|
||||||
$callback!($args, foreground_color: dark_red => Color::DarkRed);
|
|
||||||
$callback!($args, foreground_color: green => Color::Green);
|
|
||||||
$callback!($args, foreground_color: dark_green => Color::DarkGreen);
|
|
||||||
$callback!($args, foreground_color: yellow => Color::Yellow);
|
|
||||||
$callback!($args, foreground_color: dark_yellow => Color::DarkYellow);
|
|
||||||
$callback!($args, foreground_color: blue => Color::Blue);
|
|
||||||
$callback!($args, foreground_color: dark_blue => Color::DarkBlue);
|
|
||||||
$callback!($args, foreground_color: magenta => Color::Magenta);
|
|
||||||
$callback!($args, foreground_color: dark_magenta => Color::DarkMagenta);
|
|
||||||
$callback!($args, foreground_color: cyan => Color::Cyan);
|
|
||||||
$callback!($args, foreground_color: dark_cyan => Color::DarkCyan);
|
|
||||||
$callback!($args, foreground_color: white => Color::White);
|
|
||||||
$callback!($args, foreground_color: grey => Color::Grey);
|
|
||||||
|
|
||||||
// background colors
|
|
||||||
$callback!($args, background_color: on_black => Color::Black);
|
|
||||||
$callback!($args, background_color: on_dark_grey => Color::DarkGrey);
|
|
||||||
$callback!($args, background_color: on_red => Color::Red);
|
|
||||||
$callback!($args, background_color: on_dark_red => Color::DarkRed);
|
|
||||||
$callback!($args, background_color: on_green => Color::Green);
|
|
||||||
$callback!($args, background_color: on_dark_green => Color::DarkGreen);
|
|
||||||
$callback!($args, background_color: on_yellow => Color::Yellow);
|
|
||||||
$callback!($args, background_color: on_dark_yellow => Color::DarkYellow);
|
|
||||||
$callback!($args, background_color: on_blue => Color::Blue);
|
|
||||||
$callback!($args, background_color: on_dark_blue => Color::DarkBlue);
|
|
||||||
$callback!($args, background_color: on_magenta => Color::Magenta);
|
|
||||||
$callback!($args, background_color: on_dark_magenta => Color::DarkMagenta);
|
|
||||||
$callback!($args, background_color: on_cyan => Color::Cyan);
|
|
||||||
$callback!($args, background_color: on_dark_cyan => Color::DarkCyan);
|
|
||||||
$callback!($args, background_color: on_white => Color::White);
|
|
||||||
$callback!($args, background_color: on_grey => Color::Grey);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! impl_colorize {
|
|
||||||
($impl_ty:ty) => {
|
|
||||||
impl Colorize<$impl_ty> for $impl_ty {
|
|
||||||
impl_colorize_callback!(def_color_base!($impl_ty));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,26 +1,17 @@
|
|||||||
//! This module contains the logic to style some content.
|
//! This module contains the logic to style some content.
|
||||||
|
|
||||||
use std::{
|
use std::fmt::{self, Display, Formatter};
|
||||||
fmt::{self, Display, Formatter},
|
|
||||||
result,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
use super::{ContentStyle, PrintStyledContent};
|
||||||
command::execute_fmt,
|
|
||||||
style::{
|
|
||||||
Attribute, Color, Colorize, ContentStyle, ResetColor, SetAttributes, SetBackgroundColor,
|
|
||||||
SetForegroundColor, Styler,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// The style with the content to be styled.
|
/// The style with the content to be styled.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use crossterm::style::{style, Color, Attribute};
|
/// use crossterm::style::{style, Color, Attribute, Stylize};
|
||||||
///
|
///
|
||||||
/// let styled = style("Hello there")
|
/// let styled = "Hello there"
|
||||||
/// .with(Color::Yellow)
|
/// .with(Color::Yellow)
|
||||||
/// .on(Color::Blue)
|
/// .on(Color::Blue)
|
||||||
/// .attribute(Attribute::Bold);
|
/// .attribute(Attribute::Bold);
|
||||||
@ -42,35 +33,6 @@ impl<D: Display> StyledContent<D> {
|
|||||||
StyledContent { style, content }
|
StyledContent { style, content }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the foreground color.
|
|
||||||
#[inline]
|
|
||||||
pub fn with(self, foreground_color: Color) -> StyledContent<D> {
|
|
||||||
Self {
|
|
||||||
style: self.style.foreground(foreground_color),
|
|
||||||
..self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the background color.
|
|
||||||
#[inline]
|
|
||||||
pub fn on(self, background_color: Color) -> StyledContent<D> {
|
|
||||||
Self {
|
|
||||||
style: self.style.background(background_color),
|
|
||||||
..self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds the attribute.
|
|
||||||
///
|
|
||||||
/// You can add more attributes by calling this method multiple times.
|
|
||||||
#[inline]
|
|
||||||
pub fn attribute(self, attr: Attribute) -> StyledContent<D> {
|
|
||||||
Self {
|
|
||||||
style: self.style.attribute(attr),
|
|
||||||
..self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the content.
|
/// Returns the content.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn content(&self) -> &D {
|
pub fn content(&self) -> &D {
|
||||||
@ -91,76 +53,25 @@ impl<D: Display> StyledContent<D> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<D: Display> AsRef<ContentStyle> for StyledContent<D> {
|
||||||
|
fn as_ref(&self) -> &ContentStyle {
|
||||||
|
&self.style
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<D: Display> AsMut<ContentStyle> for StyledContent<D> {
|
||||||
|
fn as_mut(&mut self) -> &mut ContentStyle {
|
||||||
|
&mut self.style
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<D: Display> Display for StyledContent<D> {
|
impl<D: Display> Display for StyledContent<D> {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> result::Result<(), fmt::Error> {
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
let mut reset_background = false;
|
crate::command::execute_fmt(
|
||||||
let mut reset_foreground = false;
|
f,
|
||||||
let mut reset = false;
|
PrintStyledContent(StyledContent {
|
||||||
|
style: self.style,
|
||||||
if let Some(bg) = self.style.background_color {
|
content: &self.content,
|
||||||
execute_fmt(f, SetBackgroundColor(bg)).map_err(|_| fmt::Error)?;
|
}),
|
||||||
reset_background = true;
|
)
|
||||||
}
|
|
||||||
if let Some(fg) = self.style.foreground_color {
|
|
||||||
execute_fmt(f, SetForegroundColor(fg)).map_err(|_| fmt::Error)?;
|
|
||||||
reset_foreground = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !self.style.attributes.is_empty() {
|
|
||||||
execute_fmt(f, SetAttributes(self.style.attributes)).map_err(|_| fmt::Error)?;
|
|
||||||
reset = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.content.fmt(f)?;
|
|
||||||
|
|
||||||
if reset {
|
|
||||||
// NOTE: This will reset colors even though self has no colors, hence produce unexpected
|
|
||||||
// resets.
|
|
||||||
// TODO: reset the set attributes only.
|
|
||||||
execute_fmt(f, ResetColor).map_err(|_| fmt::Error)?;
|
|
||||||
} else {
|
|
||||||
// NOTE: Since the above bug, we do not need to reset colors when we reset attributes.
|
|
||||||
if reset_background {
|
|
||||||
execute_fmt(f, SetBackgroundColor(Color::Reset)).map_err(|_| fmt::Error)?;
|
|
||||||
}
|
|
||||||
if reset_foreground {
|
|
||||||
execute_fmt(f, SetForegroundColor(Color::Reset)).map_err(|_| fmt::Error)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<D: Display> Colorize<D> for StyledContent<D> {
|
|
||||||
impl_colorize_callback!(def_color_generic!(()));
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<D: Display> Styler<D> for StyledContent<D> {
|
|
||||||
impl_styler_callback!(def_attr_generic!(()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::{Attribute, Color, ContentStyle};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_set_fg_bg_add_attr() {
|
|
||||||
let style = ContentStyle::new()
|
|
||||||
.foreground(Color::Blue)
|
|
||||||
.background(Color::Red)
|
|
||||||
.attribute(Attribute::Bold);
|
|
||||||
|
|
||||||
let mut styled_content = style.apply("test");
|
|
||||||
|
|
||||||
styled_content = styled_content
|
|
||||||
.with(Color::Green)
|
|
||||||
.on(Color::Magenta)
|
|
||||||
.attribute(Attribute::NoItalic);
|
|
||||||
|
|
||||||
assert_eq!(styled_content.style.foreground_color, Some(Color::Green));
|
|
||||||
assert_eq!(styled_content.style.background_color, Some(Color::Magenta));
|
|
||||||
assert!(styled_content.style.attributes.has(Attribute::Bold));
|
|
||||||
assert!(styled_content.style.attributes.has(Attribute::NoItalic));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
182
src/style/stylize.rs
Normal file
182
src/style/stylize.rs
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use super::{style, Attribute, Color, ContentStyle, StyledContent};
|
||||||
|
|
||||||
|
macro_rules! stylize_method {
|
||||||
|
($method_name:ident Attribute::$attribute:ident) => {
|
||||||
|
calculated_docs! {
|
||||||
|
#[doc = concat!(
|
||||||
|
"Applies the [`",
|
||||||
|
stringify!($attribute),
|
||||||
|
"`](Attribute::",
|
||||||
|
stringify!($attribute),
|
||||||
|
") attribute to the text.",
|
||||||
|
)]
|
||||||
|
fn $method_name(self) -> Self::Styled {
|
||||||
|
self.attribute(Attribute::$attribute)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
($method_name_fg:ident, $method_name_bg:ident Color::$color:ident) => {
|
||||||
|
calculated_docs! {
|
||||||
|
#[doc = concat!(
|
||||||
|
"Sets the foreground color to [`",
|
||||||
|
stringify!($color),
|
||||||
|
"`](Color::",
|
||||||
|
stringify!($color),
|
||||||
|
")."
|
||||||
|
)]
|
||||||
|
fn $method_name_fg(self) -> Self::Styled {
|
||||||
|
self.with(Color::$color)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = concat!(
|
||||||
|
"Sets the background color to [`",
|
||||||
|
stringify!($color),
|
||||||
|
"`](Color::",
|
||||||
|
stringify!($color),
|
||||||
|
")."
|
||||||
|
)]
|
||||||
|
fn $method_name_bg(self) -> Self::Styled {
|
||||||
|
self.on(Color::$color)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provides a set of methods to set attributes and colors.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use crossterm::style::Stylize;
|
||||||
|
///
|
||||||
|
/// println!("{}", "Bold text".bold());
|
||||||
|
/// println!("{}", "Underlined text".underlined());
|
||||||
|
/// println!("{}", "Negative text".negative());
|
||||||
|
/// println!("{}", "Red on blue".red().on_blue());
|
||||||
|
/// ```
|
||||||
|
pub trait Stylize: Sized {
|
||||||
|
/// This type with styles applied.
|
||||||
|
type Styled: AsRef<ContentStyle> + AsMut<ContentStyle>;
|
||||||
|
|
||||||
|
/// Styles this type.
|
||||||
|
fn stylize(self) -> Self::Styled;
|
||||||
|
|
||||||
|
/// Sets the foreground color.
|
||||||
|
fn with(self, color: Color) -> Self::Styled {
|
||||||
|
let mut styled = self.stylize();
|
||||||
|
styled.as_mut().foreground_color = Some(color);
|
||||||
|
styled
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the background color.
|
||||||
|
fn on(self, color: Color) -> Self::Styled {
|
||||||
|
let mut styled = self.stylize();
|
||||||
|
styled.as_mut().background_color = Some(color);
|
||||||
|
styled
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Styles the content with the attribute.
|
||||||
|
fn attribute(self, attr: Attribute) -> Self::Styled {
|
||||||
|
let mut styled = self.stylize();
|
||||||
|
styled.as_mut().attributes.set(attr);
|
||||||
|
styled
|
||||||
|
}
|
||||||
|
|
||||||
|
stylize_method!(reset Attribute::Reset);
|
||||||
|
stylize_method!(bold Attribute::Bold);
|
||||||
|
stylize_method!(underlined Attribute::Underlined);
|
||||||
|
stylize_method!(reverse Attribute::Reverse);
|
||||||
|
stylize_method!(dim Attribute::Dim);
|
||||||
|
stylize_method!(italic Attribute::Italic);
|
||||||
|
stylize_method!(negative Attribute::Reverse);
|
||||||
|
stylize_method!(slow_blink Attribute::SlowBlink);
|
||||||
|
stylize_method!(rapid_blink Attribute::RapidBlink);
|
||||||
|
stylize_method!(hidden Attribute::Hidden);
|
||||||
|
stylize_method!(crossed_out Attribute::CrossedOut);
|
||||||
|
|
||||||
|
stylize_method!(black, on_black Color::Black);
|
||||||
|
stylize_method!(dark_grey, on_dark_grey Color::DarkGrey);
|
||||||
|
stylize_method!(red, on_red Color::Red);
|
||||||
|
stylize_method!(dark_red, on_dark_red Color::DarkRed);
|
||||||
|
stylize_method!(green, on_green Color::Green);
|
||||||
|
stylize_method!(dark_green, on_dark_green Color::DarkGreen);
|
||||||
|
stylize_method!(yellow, on_yellow Color::Yellow);
|
||||||
|
stylize_method!(dark_yellow, on_dark_yellow Color::DarkYellow);
|
||||||
|
stylize_method!(blue, on_blue Color::Blue);
|
||||||
|
stylize_method!(dark_blue, on_dark_blue Color::DarkBlue);
|
||||||
|
stylize_method!(magenta, on_magenta Color::Magenta);
|
||||||
|
stylize_method!(dark_magenta, on_dark_magenta Color::DarkMagenta);
|
||||||
|
stylize_method!(cyan, on_cyan Color::Cyan);
|
||||||
|
stylize_method!(dark_cyan, on_dark_cyan Color::DarkCyan);
|
||||||
|
stylize_method!(white, on_white Color::White);
|
||||||
|
stylize_method!(grey, on_grey Color::Grey);
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_stylize_for_display {
|
||||||
|
($($t:ty),*) => { $(
|
||||||
|
impl Stylize for $t {
|
||||||
|
type Styled = StyledContent<Self>;
|
||||||
|
#[inline]
|
||||||
|
fn stylize(self) -> Self::Styled {
|
||||||
|
style(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)* }
|
||||||
|
}
|
||||||
|
impl_stylize_for_display!(String, char, &str);
|
||||||
|
|
||||||
|
impl Stylize for ContentStyle {
|
||||||
|
type Styled = Self;
|
||||||
|
#[inline]
|
||||||
|
fn stylize(self) -> Self::Styled {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<D: Display> Stylize for StyledContent<D> {
|
||||||
|
type Styled = StyledContent<D>;
|
||||||
|
fn stylize(self) -> Self::Styled {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Workaround for https://github.com/rust-lang/rust/issues/78835
|
||||||
|
macro_rules! calculated_docs {
|
||||||
|
($(#[doc = $doc:expr] $item:item)*) => { $(#[doc = $doc] $item)* };
|
||||||
|
}
|
||||||
|
// Remove once https://github.com/rust-lang/rust-clippy/issues/7106 stabilizes.
|
||||||
|
#[allow(clippy::single_component_path_imports)]
|
||||||
|
#[allow(clippy::useless_attribute)]
|
||||||
|
use calculated_docs;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::super::{Attribute, Color, ContentStyle, Stylize};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn set_fg_bg_add_attr() {
|
||||||
|
let style = ContentStyle::new()
|
||||||
|
.with(Color::Blue)
|
||||||
|
.on(Color::Red)
|
||||||
|
.attribute(Attribute::Bold);
|
||||||
|
|
||||||
|
assert_eq!(style.foreground_color, Some(Color::Blue));
|
||||||
|
assert_eq!(style.background_color, Some(Color::Red));
|
||||||
|
assert!(style.attributes.has(Attribute::Bold));
|
||||||
|
|
||||||
|
let mut styled_content = style.apply("test");
|
||||||
|
|
||||||
|
styled_content = styled_content
|
||||||
|
.with(Color::Green)
|
||||||
|
.on(Color::Magenta)
|
||||||
|
.attribute(Attribute::NoItalic);
|
||||||
|
|
||||||
|
let style = styled_content.style();
|
||||||
|
|
||||||
|
assert_eq!(style.foreground_color, Some(Color::Green));
|
||||||
|
assert_eq!(style.background_color, Some(Color::Magenta));
|
||||||
|
assert!(style.attributes.has(Attribute::Bold));
|
||||||
|
assert!(style.attributes.has(Attribute::NoItalic));
|
||||||
|
}
|
||||||
|
}
|
@ -1,81 +0,0 @@
|
|||||||
use std::fmt::Display;
|
|
||||||
|
|
||||||
use super::StyledContent;
|
|
||||||
|
|
||||||
/// Provides a set of methods to set the colors.
|
|
||||||
///
|
|
||||||
/// Every method with the `on_` prefix sets the background color. All other methods set
|
|
||||||
/// the foreground color.
|
|
||||||
///
|
|
||||||
/// Method names correspond to the [`Color`](enum.Color.html) enum variants.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```no_run
|
|
||||||
/// use crossterm::style::Colorize;
|
|
||||||
///
|
|
||||||
/// let styled_text = "Red foreground color on blue background.".red().on_blue();
|
|
||||||
/// println!("{}", styled_text);
|
|
||||||
/// ```
|
|
||||||
pub trait Colorize<D: Display> {
|
|
||||||
fn black(self) -> StyledContent<D>;
|
|
||||||
fn dark_grey(self) -> StyledContent<D>;
|
|
||||||
fn red(self) -> StyledContent<D>;
|
|
||||||
fn dark_red(self) -> StyledContent<D>;
|
|
||||||
fn green(self) -> StyledContent<D>;
|
|
||||||
fn dark_green(self) -> StyledContent<D>;
|
|
||||||
fn yellow(self) -> StyledContent<D>;
|
|
||||||
fn dark_yellow(self) -> StyledContent<D>;
|
|
||||||
fn blue(self) -> StyledContent<D>;
|
|
||||||
fn dark_blue(self) -> StyledContent<D>;
|
|
||||||
fn magenta(self) -> StyledContent<D>;
|
|
||||||
fn dark_magenta(self) -> StyledContent<D>;
|
|
||||||
fn cyan(self) -> StyledContent<D>;
|
|
||||||
fn dark_cyan(self) -> StyledContent<D>;
|
|
||||||
fn white(self) -> StyledContent<D>;
|
|
||||||
fn grey(self) -> StyledContent<D>;
|
|
||||||
|
|
||||||
fn on_black(self) -> StyledContent<D>;
|
|
||||||
fn on_dark_grey(self) -> StyledContent<D>;
|
|
||||||
fn on_red(self) -> StyledContent<D>;
|
|
||||||
fn on_dark_red(self) -> StyledContent<D>;
|
|
||||||
fn on_green(self) -> StyledContent<D>;
|
|
||||||
fn on_dark_green(self) -> StyledContent<D>;
|
|
||||||
fn on_yellow(self) -> StyledContent<D>;
|
|
||||||
fn on_dark_yellow(self) -> StyledContent<D>;
|
|
||||||
fn on_blue(self) -> StyledContent<D>;
|
|
||||||
fn on_dark_blue(self) -> StyledContent<D>;
|
|
||||||
fn on_magenta(self) -> StyledContent<D>;
|
|
||||||
fn on_dark_magenta(self) -> StyledContent<D>;
|
|
||||||
fn on_cyan(self) -> StyledContent<D>;
|
|
||||||
fn on_dark_cyan(self) -> StyledContent<D>;
|
|
||||||
fn on_white(self) -> StyledContent<D>;
|
|
||||||
fn on_grey(self) -> StyledContent<D>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Provides a set of methods to set the text attributes.
|
|
||||||
///
|
|
||||||
/// Method names correspond to the [`Attribute`](enum.Attribute.html) enum variants.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```no_run
|
|
||||||
/// use crossterm::style::Styler;
|
|
||||||
///
|
|
||||||
/// println!("{}", "Bold text".bold());
|
|
||||||
/// println!("{}", "Underlined text".underlined());
|
|
||||||
/// println!("{}", "Negative text".negative());
|
|
||||||
/// ```
|
|
||||||
pub trait Styler<D: Display> {
|
|
||||||
fn reset(self) -> StyledContent<D>;
|
|
||||||
fn bold(self) -> StyledContent<D>;
|
|
||||||
fn underlined(self) -> StyledContent<D>;
|
|
||||||
fn reverse(self) -> StyledContent<D>;
|
|
||||||
fn dim(self) -> StyledContent<D>;
|
|
||||||
fn italic(self) -> StyledContent<D>;
|
|
||||||
fn negative(self) -> StyledContent<D>;
|
|
||||||
fn slow_blink(self) -> StyledContent<D>;
|
|
||||||
fn rapid_blink(self) -> StyledContent<D>;
|
|
||||||
fn hidden(self) -> StyledContent<D>;
|
|
||||||
fn crossed_out(self) -> StyledContent<D>;
|
|
||||||
}
|
|
@ -59,7 +59,7 @@ macro_rules! Attribute {
|
|||||||
/// Style existing text:
|
/// Style existing text:
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// use crossterm::style::Styler;
|
/// use crossterm::style::Stylize;
|
||||||
///
|
///
|
||||||
/// println!("{}", "Bold text".bold());
|
/// println!("{}", "Bold text".bold());
|
||||||
/// println!("{}", "Underlined text".underlined());
|
/// println!("{}", "Underlined text".underlined());
|
||||||
|
Loading…
Reference in New Issue
Block a user