Vendor dependencies
Let's see how I like this workflow.
This commit is contained in:
parent
34d1830413
commit
9c435dc440
7500 changed files with 1665121 additions and 99 deletions
120
vendor/crossterm/src/style/attributes.rs
vendored
Normal file
120
vendor/crossterm/src/style/attributes.rs
vendored
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
use std::ops::{BitAnd, BitOr, BitXor};
|
||||
|
||||
use crate::style::Attribute;
|
||||
|
||||
/// a bitset for all possible attributes
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Attributes(u32);
|
||||
|
||||
impl From<Attribute> for Attributes {
|
||||
fn from(attribute: Attribute) -> Self {
|
||||
Self(attribute.bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[Attribute]> for Attributes {
|
||||
fn from(arr: &[Attribute]) -> Self {
|
||||
let mut attributes = Attributes::default();
|
||||
for &attr in arr {
|
||||
attributes.set(attr);
|
||||
}
|
||||
attributes
|
||||
}
|
||||
}
|
||||
|
||||
impl BitAnd<Attribute> for Attributes {
|
||||
type Output = Self;
|
||||
fn bitand(self, rhs: Attribute) -> Self {
|
||||
Self(self.0 & rhs.bytes())
|
||||
}
|
||||
}
|
||||
impl BitAnd for Attributes {
|
||||
type Output = Self;
|
||||
fn bitand(self, rhs: Self) -> Self {
|
||||
Self(self.0 & rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl BitOr<Attribute> for Attributes {
|
||||
type Output = Self;
|
||||
fn bitor(self, rhs: Attribute) -> Self {
|
||||
Self(self.0 | rhs.bytes())
|
||||
}
|
||||
}
|
||||
impl BitOr for Attributes {
|
||||
type Output = Self;
|
||||
fn bitor(self, rhs: Self) -> Self {
|
||||
Self(self.0 | rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl BitXor<Attribute> for Attributes {
|
||||
type Output = Self;
|
||||
fn bitxor(self, rhs: Attribute) -> Self {
|
||||
Self(self.0 ^ rhs.bytes())
|
||||
}
|
||||
}
|
||||
impl BitXor for Attributes {
|
||||
type Output = Self;
|
||||
fn bitxor(self, rhs: Self) -> Self {
|
||||
Self(self.0 ^ rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Attributes {
|
||||
/// Sets the attribute.
|
||||
/// If it's already set, this does nothing.
|
||||
#[inline(always)]
|
||||
pub fn set(&mut self, attribute: Attribute) {
|
||||
self.0 |= attribute.bytes();
|
||||
}
|
||||
|
||||
/// Unsets the attribute.
|
||||
/// If it's not set, this changes nothing.
|
||||
#[inline(always)]
|
||||
pub fn unset(&mut self, attribute: Attribute) {
|
||||
self.0 &= !attribute.bytes();
|
||||
}
|
||||
|
||||
/// Sets the attribute if it's unset, unset it
|
||||
/// if it is set.
|
||||
#[inline(always)]
|
||||
pub fn toggle(&mut self, attribute: Attribute) {
|
||||
self.0 ^= attribute.bytes();
|
||||
}
|
||||
|
||||
/// Returns whether the attribute is set.
|
||||
#[inline(always)]
|
||||
pub const fn has(self, attribute: Attribute) -> bool {
|
||||
self.0 & attribute.bytes() != 0
|
||||
}
|
||||
|
||||
/// Sets all the passed attributes. Removes none.
|
||||
#[inline(always)]
|
||||
pub fn extend(&mut self, attributes: Attributes) {
|
||||
self.0 |= attributes.0;
|
||||
}
|
||||
|
||||
/// Returns whether there is no attribute set.
|
||||
#[inline(always)]
|
||||
pub const fn is_empty(self) -> bool {
|
||||
self.0 == 0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Attribute, Attributes};
|
||||
|
||||
#[test]
|
||||
fn test_attributes() {
|
||||
let mut attributes: Attributes = Attribute::Bold.into();
|
||||
assert!(attributes.has(Attribute::Bold));
|
||||
attributes.set(Attribute::Italic);
|
||||
assert!(attributes.has(Attribute::Italic));
|
||||
attributes.unset(Attribute::Italic);
|
||||
assert!(!attributes.has(Attribute::Italic));
|
||||
attributes.toggle(Attribute::Bold);
|
||||
assert!(attributes.is_empty());
|
||||
}
|
||||
}
|
||||
43
vendor/crossterm/src/style/content_style.rs
vendored
Normal file
43
vendor/crossterm/src/style/content_style.rs
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
//! This module contains the `content style` that can be applied to an `styled content`.
|
||||
|
||||
use std::fmt::Display;
|
||||
|
||||
use crate::style::{Attributes, Color, StyledContent};
|
||||
|
||||
/// The style that can be put on content.
|
||||
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
|
||||
pub struct ContentStyle {
|
||||
/// The foreground color.
|
||||
pub foreground_color: Option<Color>,
|
||||
/// The background color.
|
||||
pub background_color: Option<Color>,
|
||||
/// The underline color.
|
||||
pub underline_color: Option<Color>,
|
||||
/// List of attributes.
|
||||
pub attributes: Attributes,
|
||||
}
|
||||
|
||||
impl ContentStyle {
|
||||
/// Creates a `StyledContent` by applying the style to the given `val`.
|
||||
#[inline]
|
||||
pub fn apply<D: Display>(self, val: D) -> StyledContent<D> {
|
||||
StyledContent::new(self, val)
|
||||
}
|
||||
|
||||
/// Creates a new `ContentStyle`.
|
||||
#[inline]
|
||||
pub fn new() -> ContentStyle {
|
||||
ContentStyle::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<ContentStyle> for ContentStyle {
|
||||
fn as_ref(&self) -> &Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
impl AsMut<ContentStyle> for ContentStyle {
|
||||
fn as_mut(&mut self) -> &mut Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
77
vendor/crossterm/src/style/styled_content.rs
vendored
Normal file
77
vendor/crossterm/src/style/styled_content.rs
vendored
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
//! This module contains the logic to style some content.
|
||||
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use super::{ContentStyle, PrintStyledContent};
|
||||
|
||||
/// The style with the content to be styled.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use crossterm::style::{style, Color, Attribute, Stylize};
|
||||
///
|
||||
/// let styled = "Hello there"
|
||||
/// .with(Color::Yellow)
|
||||
/// .on(Color::Blue)
|
||||
/// .attribute(Attribute::Bold);
|
||||
///
|
||||
/// println!("{}", styled);
|
||||
/// ```
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct StyledContent<D: Display> {
|
||||
/// The style (colors, content attributes).
|
||||
style: ContentStyle,
|
||||
/// A content to apply the style on.
|
||||
content: D,
|
||||
}
|
||||
|
||||
impl<D: Display> StyledContent<D> {
|
||||
/// Creates a new `StyledContent`.
|
||||
#[inline]
|
||||
pub fn new(style: ContentStyle, content: D) -> StyledContent<D> {
|
||||
StyledContent { style, content }
|
||||
}
|
||||
|
||||
/// Returns the content.
|
||||
#[inline]
|
||||
pub fn content(&self) -> &D {
|
||||
&self.content
|
||||
}
|
||||
|
||||
/// Returns the style.
|
||||
#[inline]
|
||||
pub fn style(&self) -> &ContentStyle {
|
||||
&self.style
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the style, so that it can be further
|
||||
/// manipulated
|
||||
#[inline]
|
||||
pub fn style_mut(&mut self) -> &mut ContentStyle {
|
||||
&mut self.style
|
||||
}
|
||||
}
|
||||
|
||||
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> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
crate::command::execute_fmt(
|
||||
f,
|
||||
PrintStyledContent(StyledContent {
|
||||
style: self.style,
|
||||
content: &self.content,
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
200
vendor/crossterm/src/style/stylize.rs
vendored
Normal file
200
vendor/crossterm/src/style/stylize.rs
vendored
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
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, $method_name_ul: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)
|
||||
}
|
||||
|
||||
#[doc = concat!(
|
||||
"Sets the underline color to [`",
|
||||
stringify!($color),
|
||||
"`](Color::",
|
||||
stringify!($color),
|
||||
")."
|
||||
)]
|
||||
fn $method_name_ul(self) -> Self::Styled {
|
||||
self.underline(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
|
||||
}
|
||||
|
||||
/// Sets the underline color.
|
||||
fn underline(self, color: Color) -> Self::Styled {
|
||||
let mut styled = self.stylize();
|
||||
styled.as_mut().underline_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, underline_black Color::Black);
|
||||
stylize_method!(dark_grey, on_dark_grey, underline_dark_grey Color::DarkGrey);
|
||||
stylize_method!(red, on_red, underline_red Color::Red);
|
||||
stylize_method!(dark_red, on_dark_red, underline_dark_red Color::DarkRed);
|
||||
stylize_method!(green, on_green, underline_green Color::Green);
|
||||
stylize_method!(dark_green, on_dark_green, underline_dark_green Color::DarkGreen);
|
||||
stylize_method!(yellow, on_yellow, underline_yellow Color::Yellow);
|
||||
stylize_method!(dark_yellow, on_dark_yellow, underline_dark_yellow Color::DarkYellow);
|
||||
stylize_method!(blue, on_blue, underline_blue Color::Blue);
|
||||
stylize_method!(dark_blue, on_dark_blue, underline_dark_blue Color::DarkBlue);
|
||||
stylize_method!(magenta, on_magenta, underline_magenta Color::Magenta);
|
||||
stylize_method!(dark_magenta, on_dark_magenta, underline_dark_magenta Color::DarkMagenta);
|
||||
stylize_method!(cyan, on_cyan, underline_cyan Color::Cyan);
|
||||
stylize_method!(dark_cyan, on_dark_cyan, underline_dark_cyan Color::DarkCyan);
|
||||
stylize_method!(white, on_white, underline_white Color::White);
|
||||
stylize_method!(grey, on_grey, underline_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));
|
||||
}
|
||||
}
|
||||
2
vendor/crossterm/src/style/sys.rs
vendored
Normal file
2
vendor/crossterm/src/style/sys.rs
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
#[cfg(windows)]
|
||||
pub(crate) mod windows;
|
||||
206
vendor/crossterm/src/style/sys/windows.rs
vendored
Normal file
206
vendor/crossterm/src/style/sys/windows.rs
vendored
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
use std::convert::TryFrom;
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
use crossterm_winapi::{Console, Handle, HandleType, ScreenBuffer};
|
||||
use winapi::um::wincon;
|
||||
|
||||
use crate::Result;
|
||||
|
||||
use super::super::{Color, Colored};
|
||||
|
||||
const FG_GREEN: u16 = wincon::FOREGROUND_GREEN;
|
||||
const FG_RED: u16 = wincon::FOREGROUND_RED;
|
||||
const FG_BLUE: u16 = wincon::FOREGROUND_BLUE;
|
||||
const FG_INTENSITY: u16 = wincon::FOREGROUND_INTENSITY;
|
||||
|
||||
const BG_GREEN: u16 = wincon::BACKGROUND_GREEN;
|
||||
const BG_RED: u16 = wincon::BACKGROUND_RED;
|
||||
const BG_BLUE: u16 = wincon::BACKGROUND_BLUE;
|
||||
const BG_INTENSITY: u16 = wincon::BACKGROUND_INTENSITY;
|
||||
|
||||
pub(crate) fn set_foreground_color(fg_color: Color) -> Result<()> {
|
||||
init_console_color()?;
|
||||
|
||||
let color_value: u16 = Colored::ForegroundColor(fg_color).into();
|
||||
|
||||
let screen_buffer = ScreenBuffer::current()?;
|
||||
let csbi = screen_buffer.info()?;
|
||||
|
||||
// Notice that the color values are stored in wAttribute.
|
||||
// So we need to use bitwise operators to check if the values exists or to get current console colors.
|
||||
let attrs = csbi.attributes();
|
||||
let bg_color = attrs & 0x0070;
|
||||
let mut color = color_value | bg_color;
|
||||
|
||||
// background intensity is a separate value in attrs,
|
||||
// we need to check if this was applied to the current bg color.
|
||||
if (attrs & wincon::BACKGROUND_INTENSITY as u16) != 0 {
|
||||
color |= wincon::BACKGROUND_INTENSITY as u16;
|
||||
}
|
||||
|
||||
Console::from(screen_buffer.handle().clone()).set_text_attribute(color)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn set_background_color(bg_color: Color) -> Result<()> {
|
||||
init_console_color()?;
|
||||
|
||||
let color_value: u16 = Colored::BackgroundColor(bg_color).into();
|
||||
|
||||
let screen_buffer = ScreenBuffer::current()?;
|
||||
let csbi = screen_buffer.info()?;
|
||||
|
||||
// Notice that the color values are stored in wAttribute.
|
||||
// So we need to use bitwise operators to check if the values exists or to get current console colors.
|
||||
let attrs = csbi.attributes();
|
||||
let fg_color = attrs & 0x0007;
|
||||
let mut color = fg_color | color_value;
|
||||
|
||||
// Foreground intensity is a separate value in attrs,
|
||||
// So we need to check if this was applied to the current fg color.
|
||||
if (attrs & wincon::FOREGROUND_INTENSITY as u16) != 0 {
|
||||
color |= wincon::FOREGROUND_INTENSITY as u16;
|
||||
}
|
||||
|
||||
Console::from(screen_buffer.handle().clone()).set_text_attribute(color)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn reset() -> Result<()> {
|
||||
if let Ok(original_color) = u16::try_from(ORIGINAL_CONSOLE_COLOR.load(Ordering::Relaxed)) {
|
||||
Console::from(Handle::new(HandleType::CurrentOutputHandle)?)
|
||||
.set_text_attribute(original_color)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Initializes the default console color. It will will be skipped if it has already been initialized.
|
||||
pub(crate) fn init_console_color() -> Result<()> {
|
||||
if ORIGINAL_CONSOLE_COLOR.load(Ordering::Relaxed) == u32::MAX {
|
||||
let screen_buffer = ScreenBuffer::current()?;
|
||||
let attr = screen_buffer.info()?.attributes();
|
||||
ORIGINAL_CONSOLE_COLOR.store(u32::from(attr), Ordering::Relaxed);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the original console color, make sure to call `init_console_color` before calling this function. Otherwise this function will panic.
|
||||
pub(crate) fn original_console_color() -> u16 {
|
||||
u16::try_from(ORIGINAL_CONSOLE_COLOR.load(Ordering::Relaxed))
|
||||
// safe unwrap, initial console color was set with `init_console_color` in `WinApiColor::new()`
|
||||
.expect("Initial console color not set")
|
||||
}
|
||||
|
||||
// This is either a valid u16 in which case it stores the original console color or it is u32::MAX
|
||||
// in which case it is uninitialized.
|
||||
static ORIGINAL_CONSOLE_COLOR: AtomicU32 = AtomicU32::new(u32::MAX);
|
||||
|
||||
impl From<Colored> for u16 {
|
||||
/// Returns the WinAPI color value (u16) from the `Colored` struct.
|
||||
fn from(colored: Colored) -> Self {
|
||||
match colored {
|
||||
Colored::ForegroundColor(color) => {
|
||||
match color {
|
||||
Color::Black => 0,
|
||||
Color::DarkGrey => FG_INTENSITY,
|
||||
Color::Red => FG_INTENSITY | FG_RED,
|
||||
Color::DarkRed => FG_RED,
|
||||
Color::Green => FG_INTENSITY | FG_GREEN,
|
||||
Color::DarkGreen => FG_GREEN,
|
||||
Color::Yellow => FG_INTENSITY | FG_GREEN | FG_RED,
|
||||
Color::DarkYellow => FG_GREEN | FG_RED,
|
||||
Color::Blue => FG_INTENSITY | FG_BLUE,
|
||||
Color::DarkBlue => FG_BLUE,
|
||||
Color::Magenta => FG_INTENSITY | FG_RED | FG_BLUE,
|
||||
Color::DarkMagenta => FG_RED | FG_BLUE,
|
||||
Color::Cyan => FG_INTENSITY | FG_GREEN | FG_BLUE,
|
||||
Color::DarkCyan => FG_GREEN | FG_BLUE,
|
||||
Color::White => FG_INTENSITY | FG_RED | FG_GREEN | FG_BLUE,
|
||||
Color::Grey => FG_RED | FG_GREEN | FG_BLUE,
|
||||
|
||||
Color::Reset => {
|
||||
// safe unwrap, initial console color was set with `init_console_color`.
|
||||
let original_color = original_console_color();
|
||||
|
||||
const REMOVE_BG_MASK: u16 = BG_INTENSITY | BG_RED | BG_GREEN | BG_BLUE;
|
||||
// remove all background values from the original color, we don't want to reset those.
|
||||
|
||||
original_color & !REMOVE_BG_MASK
|
||||
}
|
||||
|
||||
/* WinAPI will be used for systems that do not support ANSI, those are windows version less then 10. RGB and 255 (AnsiBValue) colors are not supported in that case.*/
|
||||
Color::Rgb { .. } => 0,
|
||||
Color::AnsiValue(_val) => 0,
|
||||
}
|
||||
}
|
||||
Colored::BackgroundColor(color) => {
|
||||
match color {
|
||||
Color::Black => 0,
|
||||
Color::DarkGrey => BG_INTENSITY,
|
||||
Color::Red => BG_INTENSITY | BG_RED,
|
||||
Color::DarkRed => BG_RED,
|
||||
Color::Green => BG_INTENSITY | BG_GREEN,
|
||||
Color::DarkGreen => BG_GREEN,
|
||||
Color::Yellow => BG_INTENSITY | BG_GREEN | BG_RED,
|
||||
Color::DarkYellow => BG_GREEN | BG_RED,
|
||||
Color::Blue => BG_INTENSITY | BG_BLUE,
|
||||
Color::DarkBlue => BG_BLUE,
|
||||
Color::Magenta => BG_INTENSITY | BG_RED | BG_BLUE,
|
||||
Color::DarkMagenta => BG_RED | BG_BLUE,
|
||||
Color::Cyan => BG_INTENSITY | BG_GREEN | BG_BLUE,
|
||||
Color::DarkCyan => BG_GREEN | BG_BLUE,
|
||||
Color::White => BG_INTENSITY | BG_RED | BG_GREEN | BG_BLUE,
|
||||
Color::Grey => BG_RED | BG_GREEN | BG_BLUE,
|
||||
|
||||
Color::Reset => {
|
||||
let original_color = original_console_color();
|
||||
|
||||
const REMOVE_FG_MASK: u16 = FG_INTENSITY | FG_RED | FG_GREEN | FG_BLUE;
|
||||
// remove all foreground values from the original color, we don't want to reset those.
|
||||
|
||||
original_color & !REMOVE_FG_MASK
|
||||
}
|
||||
/* WinAPI will be used for systems that do not support ANSI, those are windows version less then 10. RGB and 255 (AnsiBValue) colors are not supported in that case.*/
|
||||
Color::Rgb { .. } => 0,
|
||||
Color::AnsiValue(_val) => 0,
|
||||
}
|
||||
}
|
||||
Colored::UnderlineColor(_) => 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use crate::style::sys::windows::set_foreground_color;
|
||||
|
||||
use super::{
|
||||
Color, Colored, BG_INTENSITY, BG_RED, FG_INTENSITY, FG_RED, ORIGINAL_CONSOLE_COLOR,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_parse_fg_color() {
|
||||
let colored = Colored::ForegroundColor(Color::Red);
|
||||
assert_eq!(Into::<u16>::into(colored), FG_INTENSITY | FG_RED);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_bg_color() {
|
||||
let colored = Colored::BackgroundColor(Color::Red);
|
||||
assert_eq!(Into::<u16>::into(colored), BG_INTENSITY | BG_RED);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_original_console_color_is_set() {
|
||||
assert_eq!(ORIGINAL_CONSOLE_COLOR.load(Ordering::Relaxed), u32::MAX);
|
||||
|
||||
// will call `init_console_color`
|
||||
set_foreground_color(Color::Blue).unwrap();
|
||||
|
||||
assert_ne!(ORIGINAL_CONSOLE_COLOR.load(Ordering::Relaxed), u32::MAX);
|
||||
}
|
||||
}
|
||||
6
vendor/crossterm/src/style/types.rs
vendored
Normal file
6
vendor/crossterm/src/style/types.rs
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
pub use self::{attribute::Attribute, color::Color, colored::Colored, colors::Colors};
|
||||
|
||||
mod attribute;
|
||||
mod color;
|
||||
mod colored;
|
||||
mod colors;
|
||||
183
vendor/crossterm/src/style/types/attribute.rs
vendored
Normal file
183
vendor/crossterm/src/style/types/attribute.rs
vendored
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
use std::fmt::Display;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::super::SetAttribute;
|
||||
|
||||
// This macro generates the Attribute enum, its iterator
|
||||
// function, and the static array containing the sgr code
|
||||
// of each attribute
|
||||
macro_rules! Attribute {
|
||||
(
|
||||
$(
|
||||
$(#[$inner:ident $($args:tt)*])*
|
||||
$name:ident = $sgr:expr,
|
||||
)*
|
||||
) => {
|
||||
/// Represents an attribute.
|
||||
///
|
||||
/// # Platform-specific Notes
|
||||
///
|
||||
/// * Only UNIX and Windows 10 terminals do support text attributes.
|
||||
/// * Keep in mind that not all terminals support all attributes.
|
||||
/// * Crossterm implements almost all attributes listed in the
|
||||
/// [SGR parameters](https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters).
|
||||
///
|
||||
/// | Attribute | Windows | UNIX | Notes |
|
||||
/// | :-- | :--: | :--: | :-- |
|
||||
/// | `Reset` | ✓ | ✓ | |
|
||||
/// | `Bold` | ✓ | ✓ | |
|
||||
/// | `Dim` | ✓ | ✓ | |
|
||||
/// | `Italic` | ? | ? | Not widely supported, sometimes treated as inverse. |
|
||||
/// | `Underlined` | ✓ | ✓ | |
|
||||
/// | `SlowBlink` | ? | ? | Not widely supported, sometimes treated as inverse. |
|
||||
/// | `RapidBlink` | ? | ? | Not widely supported. MS-DOS ANSI.SYS; 150+ per minute. |
|
||||
/// | `Reverse` | ✓ | ✓ | |
|
||||
/// | `Hidden` | ✓ | ✓ | Also known as Conceal. |
|
||||
/// | `Fraktur` | ✗ | ✓ | Legible characters, but marked for deletion. |
|
||||
/// | `DefaultForegroundColor` | ? | ? | Implementation specific (according to standard). |
|
||||
/// | `DefaultBackgroundColor` | ? | ? | Implementation specific (according to standard). |
|
||||
/// | `Framed` | ? | ? | Not widely supported. |
|
||||
/// | `Encircled` | ? | ? | This should turn on the encircled attribute. |
|
||||
/// | `OverLined` | ? | ? | This should draw a line at the top of the text. |
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Basic usage:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use crossterm::style::Attribute;
|
||||
///
|
||||
/// println!(
|
||||
/// "{} Underlined {} No Underline",
|
||||
/// Attribute::Underlined,
|
||||
/// Attribute::NoUnderline
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// Style existing text:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use crossterm::style::Stylize;
|
||||
///
|
||||
/// println!("{}", "Bold text".bold());
|
||||
/// println!("{}", "Underlined text".underlined());
|
||||
/// println!("{}", "Negative text".negative());
|
||||
/// ```
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
|
||||
#[non_exhaustive]
|
||||
pub enum Attribute {
|
||||
$(
|
||||
$(#[$inner $($args)*])*
|
||||
$name,
|
||||
)*
|
||||
}
|
||||
|
||||
pub static SGR: &'static[i16] = &[
|
||||
$($sgr,)*
|
||||
];
|
||||
|
||||
impl Attribute {
|
||||
/// Iterates over all the variants of the Attribute enum.
|
||||
pub fn iterator() -> impl Iterator<Item = Attribute> {
|
||||
use self::Attribute::*;
|
||||
[ $($name,)* ].iter().copied()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Attribute! {
|
||||
/// Resets all the attributes.
|
||||
Reset = 0,
|
||||
/// Increases the text intensity.
|
||||
Bold = 1,
|
||||
/// Decreases the text intensity.
|
||||
Dim = 2,
|
||||
/// Emphasises the text.
|
||||
Italic = 3,
|
||||
/// Underlines the text.
|
||||
Underlined = 4,
|
||||
|
||||
// Other types of underlining
|
||||
/// Double underlines the text.
|
||||
DoubleUnderlined = 2,
|
||||
/// Undercurls the text.
|
||||
Undercurled = 3,
|
||||
/// Underdots the text.
|
||||
Underdotted = 4,
|
||||
/// Underdashes the text.
|
||||
Underdashed = 5,
|
||||
|
||||
/// Makes the text blinking (< 150 per minute).
|
||||
SlowBlink = 5,
|
||||
/// Makes the text blinking (>= 150 per minute).
|
||||
RapidBlink = 6,
|
||||
/// Swaps foreground and background colors.
|
||||
Reverse = 7,
|
||||
/// Hides the text (also known as Conceal).
|
||||
Hidden = 8,
|
||||
/// Crosses the text.
|
||||
CrossedOut = 9,
|
||||
/// Sets the [Fraktur](https://en.wikipedia.org/wiki/Fraktur) typeface.
|
||||
///
|
||||
/// Mostly used for [mathematical alphanumeric symbols](https://en.wikipedia.org/wiki/Mathematical_Alphanumeric_Symbols).
|
||||
Fraktur = 20,
|
||||
/// Turns off the `Bold` attribute. - Inconsistent - Prefer to use NormalIntensity
|
||||
NoBold = 21,
|
||||
/// Switches the text back to normal intensity (no bold, italic).
|
||||
NormalIntensity = 22,
|
||||
/// Turns off the `Italic` attribute.
|
||||
NoItalic = 23,
|
||||
/// Turns off the `Underlined` attribute.
|
||||
NoUnderline = 24,
|
||||
/// Turns off the text blinking (`SlowBlink` or `RapidBlink`).
|
||||
NoBlink = 25,
|
||||
/// Turns off the `Reverse` attribute.
|
||||
NoReverse = 27,
|
||||
/// Turns off the `Hidden` attribute.
|
||||
NoHidden = 28,
|
||||
/// Turns off the `CrossedOut` attribute.
|
||||
NotCrossedOut = 29,
|
||||
/// Makes the text framed.
|
||||
Framed = 51,
|
||||
/// Makes the text encircled.
|
||||
Encircled = 52,
|
||||
/// Draws a line at the top of the text.
|
||||
OverLined = 53,
|
||||
/// Turns off the `Frame` and `Encircled` attributes.
|
||||
NotFramedOrEncircled = 54,
|
||||
/// Turns off the `OverLined` attribute.
|
||||
NotOverLined = 55,
|
||||
}
|
||||
|
||||
impl Display for Attribute {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
||||
write!(f, "{}", SetAttribute(*self))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Attribute {
|
||||
/// Returns a u32 with one bit set, which is the
|
||||
/// signature of this attribute in the Attributes
|
||||
/// bitset.
|
||||
///
|
||||
/// The +1 enables storing Reset (whose index is 0)
|
||||
/// in the bitset Attributes.
|
||||
#[inline(always)]
|
||||
pub const fn bytes(self) -> u32 {
|
||||
1 << ((self as u32) + 1)
|
||||
}
|
||||
/// Returns the SGR attribute value.
|
||||
///
|
||||
/// See <https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters>
|
||||
pub fn sgr(self) -> String {
|
||||
if (self as usize) > 4 && (self as usize) < 9 {
|
||||
return "4:".to_string() + SGR[self as usize].to_string().as_str();
|
||||
}
|
||||
SGR[self as usize].to_string()
|
||||
}
|
||||
}
|
||||
476
vendor/crossterm/src/style/types/color.rs
vendored
Normal file
476
vendor/crossterm/src/style/types/color.rs
vendored
Normal file
|
|
@ -0,0 +1,476 @@
|
|||
use std::{convert::AsRef, convert::TryFrom, result::Result, str::FromStr};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use std::fmt;
|
||||
|
||||
use crate::style::parse_next_u8;
|
||||
|
||||
/// Represents a color.
|
||||
///
|
||||
/// # Platform-specific Notes
|
||||
///
|
||||
/// The following list of 16 base colors are available for almost all terminals (Windows 7 and 8 included).
|
||||
///
|
||||
/// | Light | Dark |
|
||||
/// | :--| :-- |
|
||||
/// | `DarkGrey` | `Black` |
|
||||
/// | `Red` | `DarkRed` |
|
||||
/// | `Green` | `DarkGreen` |
|
||||
/// | `Yellow` | `DarkYellow` |
|
||||
/// | `Blue` | `DarkBlue` |
|
||||
/// | `Magenta` | `DarkMagenta` |
|
||||
/// | `Cyan` | `DarkCyan` |
|
||||
/// | `White` | `Grey` |
|
||||
///
|
||||
/// Most UNIX terminals and Windows 10 consoles support additional colors.
|
||||
/// See [`Color::Rgb`] or [`Color::AnsiValue`] for more info.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
|
||||
pub enum Color {
|
||||
/// Resets the terminal color.
|
||||
Reset,
|
||||
|
||||
/// Black color.
|
||||
Black,
|
||||
|
||||
/// Dark grey color.
|
||||
DarkGrey,
|
||||
|
||||
/// Light red color.
|
||||
Red,
|
||||
|
||||
/// Dark red color.
|
||||
DarkRed,
|
||||
|
||||
/// Light green color.
|
||||
Green,
|
||||
|
||||
/// Dark green color.
|
||||
DarkGreen,
|
||||
|
||||
/// Light yellow color.
|
||||
Yellow,
|
||||
|
||||
/// Dark yellow color.
|
||||
DarkYellow,
|
||||
|
||||
/// Light blue color.
|
||||
Blue,
|
||||
|
||||
/// Dark blue color.
|
||||
DarkBlue,
|
||||
|
||||
/// Light magenta color.
|
||||
Magenta,
|
||||
|
||||
/// Dark magenta color.
|
||||
DarkMagenta,
|
||||
|
||||
/// Light cyan color.
|
||||
Cyan,
|
||||
|
||||
/// Dark cyan color.
|
||||
DarkCyan,
|
||||
|
||||
/// White color.
|
||||
White,
|
||||
|
||||
/// Grey color.
|
||||
Grey,
|
||||
|
||||
/// An RGB color. See [RGB color model](https://en.wikipedia.org/wiki/RGB_color_model) for more info.
|
||||
///
|
||||
/// Most UNIX terminals and Windows 10 supported only.
|
||||
/// See [Platform-specific notes](enum.Color.html#platform-specific-notes) for more info.
|
||||
Rgb { r: u8, g: u8, b: u8 },
|
||||
|
||||
/// An ANSI color. See [256 colors - cheat sheet](https://jonasjacek.github.io/colors/) for more info.
|
||||
///
|
||||
/// Most UNIX terminals and Windows 10 supported only.
|
||||
/// See [Platform-specific notes](enum.Color.html#platform-specific-notes) for more info.
|
||||
AnsiValue(u8),
|
||||
}
|
||||
|
||||
impl Color {
|
||||
/// Parses an ANSI color sequence.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use crossterm::style::Color;
|
||||
///
|
||||
/// assert_eq!(Color::parse_ansi("5;0"), Some(Color::Black));
|
||||
/// assert_eq!(Color::parse_ansi("5;26"), Some(Color::AnsiValue(26)));
|
||||
/// assert_eq!(Color::parse_ansi("2;50;60;70"), Some(Color::Rgb { r: 50, g: 60, b: 70 }));
|
||||
/// assert_eq!(Color::parse_ansi("invalid color"), None);
|
||||
/// ```
|
||||
///
|
||||
/// Currently, 3/4 bit color values aren't supported so return `None`.
|
||||
///
|
||||
/// See also: [`Colored::parse_ansi`](crate::style::Colored::parse_ansi).
|
||||
pub fn parse_ansi(ansi: &str) -> Option<Self> {
|
||||
Self::parse_ansi_iter(&mut ansi.split(';'))
|
||||
}
|
||||
|
||||
/// The logic for parse_ansi, takes an iterator of the sequences terms (the numbers between the
|
||||
/// ';'). It's a separate function so it can be used by both Color::parse_ansi and
|
||||
/// colored::parse_ansi.
|
||||
/// Tested in Colored tests.
|
||||
pub(crate) fn parse_ansi_iter<'a>(values: &mut impl Iterator<Item = &'a str>) -> Option<Self> {
|
||||
let color = match parse_next_u8(values)? {
|
||||
// 8 bit colors: `5;<n>`
|
||||
5 => {
|
||||
let n = parse_next_u8(values)?;
|
||||
|
||||
use Color::*;
|
||||
[
|
||||
Black, // 0
|
||||
DarkRed, // 1
|
||||
DarkGreen, // 2
|
||||
DarkYellow, // 3
|
||||
DarkBlue, // 4
|
||||
DarkMagenta, // 5
|
||||
DarkCyan, // 6
|
||||
Grey, // 7
|
||||
DarkGrey, // 8
|
||||
Red, // 9
|
||||
Green, // 10
|
||||
Yellow, // 11
|
||||
Blue, // 12
|
||||
Magenta, // 13
|
||||
Cyan, // 14
|
||||
White, // 15
|
||||
]
|
||||
.get(n as usize)
|
||||
.copied()
|
||||
.unwrap_or(Color::AnsiValue(n))
|
||||
}
|
||||
|
||||
// 24 bit colors: `2;<r>;<g>;<b>`
|
||||
2 => Color::Rgb {
|
||||
r: parse_next_u8(values)?,
|
||||
g: parse_next_u8(values)?,
|
||||
b: parse_next_u8(values)?,
|
||||
},
|
||||
|
||||
_ => return None,
|
||||
};
|
||||
// If there's another value, it's unexpected so return None.
|
||||
if values.next().is_some() {
|
||||
return None;
|
||||
}
|
||||
Some(color)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for Color {
|
||||
type Error = ();
|
||||
|
||||
/// Try to create a `Color` from the string representation. This returns an error if the string does not match.
|
||||
fn try_from(src: &str) -> Result<Self, Self::Error> {
|
||||
let src = src.to_lowercase();
|
||||
|
||||
match src.as_ref() {
|
||||
"black" => Ok(Color::Black),
|
||||
"dark_grey" => Ok(Color::DarkGrey),
|
||||
"red" => Ok(Color::Red),
|
||||
"dark_red" => Ok(Color::DarkRed),
|
||||
"green" => Ok(Color::Green),
|
||||
"dark_green" => Ok(Color::DarkGreen),
|
||||
"yellow" => Ok(Color::Yellow),
|
||||
"dark_yellow" => Ok(Color::DarkYellow),
|
||||
"blue" => Ok(Color::Blue),
|
||||
"dark_blue" => Ok(Color::DarkBlue),
|
||||
"magenta" => Ok(Color::Magenta),
|
||||
"dark_magenta" => Ok(Color::DarkMagenta),
|
||||
"cyan" => Ok(Color::Cyan),
|
||||
"dark_cyan" => Ok(Color::DarkCyan),
|
||||
"white" => Ok(Color::White),
|
||||
"grey" => Ok(Color::Grey),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Color {
|
||||
type Err = ();
|
||||
|
||||
/// Creates a `Color` from the string representation.
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// * Returns `Color::White` in case of an unknown color.
|
||||
/// * Does not return `Err` and you can safely unwrap.
|
||||
fn from_str(src: &str) -> Result<Self, Self::Err> {
|
||||
Ok(Color::try_from(src).unwrap_or(Color::White))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(u8, u8, u8)> for Color {
|
||||
/// Creates a 'Color' from the tuple representation.
|
||||
fn from(val: (u8, u8, u8)) -> Self {
|
||||
let (r, g, b) = val;
|
||||
Self::Rgb { r, g, b }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl serde::ser::Serialize for Color {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::ser::Serializer,
|
||||
{
|
||||
let str = match *self {
|
||||
Color::Black => "black",
|
||||
Color::DarkGrey => "dark_grey",
|
||||
Color::Red => "red",
|
||||
Color::DarkRed => "dark_red",
|
||||
Color::Green => "green",
|
||||
Color::DarkGreen => "dark_green",
|
||||
Color::Yellow => "yellow",
|
||||
Color::DarkYellow => "dark_yellow",
|
||||
Color::Blue => "blue",
|
||||
Color::DarkBlue => "dark_blue",
|
||||
Color::Magenta => "magenta",
|
||||
Color::DarkMagenta => "dark_magenta",
|
||||
Color::Cyan => "cyan",
|
||||
Color::DarkCyan => "dark_cyan",
|
||||
Color::White => "white",
|
||||
Color::Grey => "grey",
|
||||
_ => "",
|
||||
};
|
||||
|
||||
if str == "" {
|
||||
match *self {
|
||||
Color::AnsiValue(value) => {
|
||||
return serializer.serialize_str(&format!("ansi_({})", value));
|
||||
}
|
||||
Color::Rgb { r, g, b } => {
|
||||
return serializer.serialize_str(&format!("rgb_({},{},{})", r, g, b));
|
||||
}
|
||||
_ => {
|
||||
return Err(serde::ser::Error::custom("Could not serialize enum type"));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return serializer.serialize_str(str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de> serde::de::Deserialize<'de> for Color {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Color, D::Error>
|
||||
where
|
||||
D: serde::de::Deserializer<'de>,
|
||||
{
|
||||
struct ColorVisitor;
|
||||
impl<'de> serde::de::Visitor<'de> for ColorVisitor {
|
||||
type Value = Color;
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str(
|
||||
"`black`, `blue`, `dark_blue`, `cyan`, `dark_cyan`, `green`, `dark_green`, `grey`, `dark_grey`, `magenta`, `dark_magenta`, `red`, `dark_red`, `white`, `yellow`, `dark_yellow`, `ansi_(value)`, or `rgb_(r,g,b)`",
|
||||
)
|
||||
}
|
||||
fn visit_str<E>(self, value: &str) -> Result<Color, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
if let Ok(c) = Color::try_from(value) {
|
||||
Ok(c)
|
||||
} else {
|
||||
if value.contains("ansi") {
|
||||
// strip away `ansi_(..)' and get the inner value between parenthesis.
|
||||
let results = value.replace("ansi_(", "").replace(")", "");
|
||||
|
||||
let ansi_val = results.parse::<u8>();
|
||||
|
||||
if let Ok(ansi) = ansi_val {
|
||||
return Ok(Color::AnsiValue(ansi));
|
||||
}
|
||||
} else if value.contains("rgb") {
|
||||
// strip away `rgb_(..)' and get the inner values between parenthesis.
|
||||
let results = value
|
||||
.replace("rgb_(", "")
|
||||
.replace(")", "")
|
||||
.split(',')
|
||||
.map(|x| x.to_string())
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
if results.len() == 3 {
|
||||
let r = results[0].parse::<u8>();
|
||||
let g = results[1].parse::<u8>();
|
||||
let b = results[2].parse::<u8>();
|
||||
|
||||
if r.is_ok() && g.is_ok() && b.is_ok() {
|
||||
return Ok(Color::Rgb {
|
||||
r: r.unwrap(),
|
||||
g: g.unwrap(),
|
||||
b: b.unwrap(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(E::invalid_value(serde::de::Unexpected::Str(value), &self))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_str(ColorVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Color;
|
||||
|
||||
#[test]
|
||||
fn test_known_color_conversion() {
|
||||
assert_eq!("grey".parse(), Ok(Color::Grey));
|
||||
assert_eq!("dark_grey".parse(), Ok(Color::DarkGrey));
|
||||
assert_eq!("red".parse(), Ok(Color::Red));
|
||||
assert_eq!("dark_red".parse(), Ok(Color::DarkRed));
|
||||
assert_eq!("green".parse(), Ok(Color::Green));
|
||||
assert_eq!("dark_green".parse(), Ok(Color::DarkGreen));
|
||||
assert_eq!("yellow".parse(), Ok(Color::Yellow));
|
||||
assert_eq!("dark_yellow".parse(), Ok(Color::DarkYellow));
|
||||
assert_eq!("blue".parse(), Ok(Color::Blue));
|
||||
assert_eq!("dark_blue".parse(), Ok(Color::DarkBlue));
|
||||
assert_eq!("magenta".parse(), Ok(Color::Magenta));
|
||||
assert_eq!("dark_magenta".parse(), Ok(Color::DarkMagenta));
|
||||
assert_eq!("cyan".parse(), Ok(Color::Cyan));
|
||||
assert_eq!("dark_cyan".parse(), Ok(Color::DarkCyan));
|
||||
assert_eq!("white".parse(), Ok(Color::White));
|
||||
assert_eq!("black".parse(), Ok(Color::Black));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unknown_color_conversion_yields_white() {
|
||||
assert_eq!("foo".parse(), Ok(Color::White));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_know_rgb_color_conversion() {
|
||||
assert_eq!(Color::from((0, 0, 0)), Color::Rgb { r: 0, g: 0, b: 0 });
|
||||
assert_eq!(
|
||||
Color::from((255, 255, 255)),
|
||||
Color::Rgb {
|
||||
r: 255,
|
||||
g: 255,
|
||||
b: 255
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(feature = "serde")]
|
||||
mod serde_tests {
|
||||
use super::Color;
|
||||
use serde_json;
|
||||
|
||||
#[test]
|
||||
fn test_deserial_known_color_conversion() {
|
||||
assert_eq!(
|
||||
serde_json::from_str::<Color>("\"Red\"").unwrap(),
|
||||
Color::Red
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::from_str::<Color>("\"red\"").unwrap(),
|
||||
Color::Red
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::from_str::<Color>("\"dark_red\"").unwrap(),
|
||||
Color::DarkRed
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::from_str::<Color>("\"grey\"").unwrap(),
|
||||
Color::Grey
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::from_str::<Color>("\"dark_grey\"").unwrap(),
|
||||
Color::DarkGrey
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::from_str::<Color>("\"green\"").unwrap(),
|
||||
Color::Green
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::from_str::<Color>("\"dark_green\"").unwrap(),
|
||||
Color::DarkGreen
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::from_str::<Color>("\"yellow\"").unwrap(),
|
||||
Color::Yellow
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::from_str::<Color>("\"dark_yellow\"").unwrap(),
|
||||
Color::DarkYellow
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::from_str::<Color>("\"blue\"").unwrap(),
|
||||
Color::Blue
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::from_str::<Color>("\"dark_blue\"").unwrap(),
|
||||
Color::DarkBlue
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::from_str::<Color>("\"magenta\"").unwrap(),
|
||||
Color::Magenta
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::from_str::<Color>("\"dark_magenta\"").unwrap(),
|
||||
Color::DarkMagenta
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::from_str::<Color>("\"cyan\"").unwrap(),
|
||||
Color::Cyan
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::from_str::<Color>("\"dark_cyan\"").unwrap(),
|
||||
Color::DarkCyan
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::from_str::<Color>("\"white\"").unwrap(),
|
||||
Color::White
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::from_str::<Color>("\"black\"").unwrap(),
|
||||
Color::Black
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deserial_unknown_color_conversion() {
|
||||
assert!(serde_json::from_str::<Color>("\"unknown\"").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deserial_ansi_value() {
|
||||
assert_eq!(
|
||||
serde_json::from_str::<Color>("\"ansi_(255)\"").unwrap(),
|
||||
Color::AnsiValue(255)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deserial_unvalid_ansi_value() {
|
||||
assert!(serde_json::from_str::<Color>("\"ansi_(256)\"").is_err());
|
||||
assert!(serde_json::from_str::<Color>("\"ansi_(-1)\"").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deserial_rgb() {
|
||||
assert_eq!(
|
||||
serde_json::from_str::<Color>("\"rgb_(255,255,255)\"").unwrap(),
|
||||
Color::from((255, 255, 255))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deserial_unvalid_rgb() {
|
||||
assert!(serde_json::from_str::<Color>("\"rgb_(255,255,255,255)\"").is_err());
|
||||
assert!(serde_json::from_str::<Color>("\"rgb_(256,255,255)\"").is_err());
|
||||
}
|
||||
}
|
||||
270
vendor/crossterm/src/style/types/colored.rs
vendored
Normal file
270
vendor/crossterm/src/style/types/colored.rs
vendored
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
use std::fmt::{self, Formatter};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::style::{parse_next_u8, Color};
|
||||
|
||||
/// Represents a foreground or background color.
|
||||
///
|
||||
/// This can be converted to a [Colors](struct.Colors.html) by calling `into()` and applied
|
||||
/// using the [SetColors](struct.SetColors.html) command.
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
|
||||
pub enum Colored {
|
||||
/// A foreground color.
|
||||
ForegroundColor(Color),
|
||||
/// A background color.
|
||||
BackgroundColor(Color),
|
||||
/// An underline color.
|
||||
/// Imporant: doesnt work on windows 10 or lower.
|
||||
UnderlineColor(Color),
|
||||
}
|
||||
|
||||
impl Colored {
|
||||
/// Parse an ANSI foreground or background color.
|
||||
/// This is the string that would appear within an `ESC [ <str> m` escape sequence, as found in
|
||||
/// various configuration files.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use crossterm::style::{Colored::{self, ForegroundColor, BackgroundColor}, Color};
|
||||
///
|
||||
/// assert_eq!(Colored::parse_ansi("38;5;0"), Some(ForegroundColor(Color::Black)));
|
||||
/// assert_eq!(Colored::parse_ansi("38;5;26"), Some(ForegroundColor(Color::AnsiValue(26))));
|
||||
/// assert_eq!(Colored::parse_ansi("48;2;50;60;70"), Some(BackgroundColor(Color::Rgb { r: 50, g: 60, b: 70 })));
|
||||
/// assert_eq!(Colored::parse_ansi("49"), Some(BackgroundColor(Color::Reset)));
|
||||
/// assert_eq!(Colored::parse_ansi("invalid color"), None);
|
||||
/// ```
|
||||
///
|
||||
/// Currently, 3/4 bit color values aren't supported so return `None`.
|
||||
///
|
||||
/// See also: [`Color::parse_ansi`].
|
||||
pub fn parse_ansi(ansi: &str) -> Option<Self> {
|
||||
use Colored::{BackgroundColor, ForegroundColor, UnderlineColor};
|
||||
|
||||
let values = &mut ansi.split(';');
|
||||
|
||||
let output = match parse_next_u8(values)? {
|
||||
38 => return Color::parse_ansi_iter(values).map(ForegroundColor),
|
||||
48 => return Color::parse_ansi_iter(values).map(BackgroundColor),
|
||||
58 => return Color::parse_ansi_iter(values).map(UnderlineColor),
|
||||
|
||||
39 => ForegroundColor(Color::Reset),
|
||||
49 => BackgroundColor(Color::Reset),
|
||||
59 => UnderlineColor(Color::Reset),
|
||||
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
if values.next().is_some() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(output)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Colored {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
let color;
|
||||
|
||||
match *self {
|
||||
Colored::ForegroundColor(new_color) => {
|
||||
if new_color == Color::Reset {
|
||||
return f.write_str("39");
|
||||
} else {
|
||||
f.write_str("38;")?;
|
||||
color = new_color;
|
||||
}
|
||||
}
|
||||
Colored::BackgroundColor(new_color) => {
|
||||
if new_color == Color::Reset {
|
||||
return f.write_str("49");
|
||||
} else {
|
||||
f.write_str("48;")?;
|
||||
color = new_color;
|
||||
}
|
||||
}
|
||||
Colored::UnderlineColor(new_color) => {
|
||||
if new_color == Color::Reset {
|
||||
return f.write_str("59");
|
||||
} else {
|
||||
f.write_str("58;")?;
|
||||
color = new_color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match color {
|
||||
Color::Black => f.write_str("5;0"),
|
||||
Color::DarkGrey => f.write_str("5;8"),
|
||||
Color::Red => f.write_str("5;9"),
|
||||
Color::DarkRed => f.write_str("5;1"),
|
||||
Color::Green => f.write_str("5;10"),
|
||||
Color::DarkGreen => f.write_str("5;2"),
|
||||
Color::Yellow => f.write_str("5;11"),
|
||||
Color::DarkYellow => f.write_str("5;3"),
|
||||
Color::Blue => f.write_str("5;12"),
|
||||
Color::DarkBlue => f.write_str("5;4"),
|
||||
Color::Magenta => f.write_str("5;13"),
|
||||
Color::DarkMagenta => f.write_str("5;5"),
|
||||
Color::Cyan => f.write_str("5;14"),
|
||||
Color::DarkCyan => f.write_str("5;6"),
|
||||
Color::White => f.write_str("5;15"),
|
||||
Color::Grey => f.write_str("5;7"),
|
||||
Color::Rgb { r, g, b } => write!(f, "2;{};{};{}", r, g, b),
|
||||
Color::AnsiValue(val) => write!(f, "5;{}", val),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::style::{Color, Colored};
|
||||
|
||||
#[test]
|
||||
fn test_format_fg_color() {
|
||||
let colored = Colored::ForegroundColor(Color::Red);
|
||||
assert_eq!(colored.to_string(), "38;5;9");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_bg_color() {
|
||||
let colored = Colored::BackgroundColor(Color::Red);
|
||||
assert_eq!(colored.to_string(), "48;5;9");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_reset_fg_color() {
|
||||
let colored = Colored::ForegroundColor(Color::Reset);
|
||||
assert_eq!(colored.to_string(), "39");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_reset_bg_color() {
|
||||
let colored = Colored::BackgroundColor(Color::Reset);
|
||||
assert_eq!(colored.to_string(), "49");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_fg_rgb_color() {
|
||||
let colored = Colored::BackgroundColor(Color::Rgb { r: 1, g: 2, b: 3 });
|
||||
assert_eq!(colored.to_string(), "48;2;1;2;3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_fg_ansi_color() {
|
||||
let colored = Colored::ForegroundColor(Color::AnsiValue(255));
|
||||
assert_eq!(colored.to_string(), "38;5;255");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_ansi_fg() {
|
||||
test_parse_ansi(Colored::ForegroundColor)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_ansi_bg() {
|
||||
test_parse_ansi(Colored::ForegroundColor)
|
||||
}
|
||||
|
||||
/// Used for test_parse_ansi_fg and test_parse_ansi_bg
|
||||
fn test_parse_ansi(bg_or_fg: impl Fn(Color) -> Colored) {
|
||||
/// Formats a re-parses `color` to check the result.
|
||||
macro_rules! test {
|
||||
($color:expr) => {
|
||||
let colored = bg_or_fg($color);
|
||||
assert_eq!(Colored::parse_ansi(&format!("{}", colored)), Some(colored));
|
||||
};
|
||||
}
|
||||
|
||||
use Color::*;
|
||||
|
||||
test!(Reset);
|
||||
test!(Black);
|
||||
test!(DarkGrey);
|
||||
test!(Red);
|
||||
test!(DarkRed);
|
||||
test!(Green);
|
||||
test!(DarkGreen);
|
||||
test!(Yellow);
|
||||
test!(DarkYellow);
|
||||
test!(Blue);
|
||||
test!(DarkBlue);
|
||||
test!(Magenta);
|
||||
test!(DarkMagenta);
|
||||
test!(Cyan);
|
||||
test!(DarkCyan);
|
||||
test!(White);
|
||||
test!(Grey);
|
||||
|
||||
// n in 0..=15 will give us the color values above back.
|
||||
for n in 16..=255 {
|
||||
test!(AnsiValue(n));
|
||||
}
|
||||
|
||||
for r in 0..=255 {
|
||||
for g in [0, 2, 18, 19, 60, 100, 200, 250, 254, 255].iter().copied() {
|
||||
for b in [0, 12, 16, 99, 100, 161, 200, 255].iter().copied() {
|
||||
test!(Rgb { r, g, b });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_invalid_ansi_color() {
|
||||
/// Checks that trying to parse `s` yields None.
|
||||
fn test(s: &str) {
|
||||
assert_eq!(Colored::parse_ansi(s), None);
|
||||
}
|
||||
test("");
|
||||
test(";");
|
||||
test(";;");
|
||||
test(";;");
|
||||
test("0");
|
||||
test("1");
|
||||
test("12");
|
||||
test("100");
|
||||
test("100048949345");
|
||||
test("39;");
|
||||
test("49;");
|
||||
test("39;2");
|
||||
test("49;2");
|
||||
test("38");
|
||||
test("38;");
|
||||
test("38;0");
|
||||
test("38;5");
|
||||
test("38;5;0;");
|
||||
test("38;5;0;2");
|
||||
test("38;5;80;");
|
||||
test("38;5;80;2");
|
||||
test("38;5;257");
|
||||
test("38;2");
|
||||
test("38;2;");
|
||||
test("38;2;0");
|
||||
test("38;2;0;2");
|
||||
test("38;2;0;2;257");
|
||||
test("38;2;0;2;25;");
|
||||
test("38;2;0;2;25;3");
|
||||
test("48");
|
||||
test("48;");
|
||||
test("48;0");
|
||||
test("48;5");
|
||||
test("48;5;0;");
|
||||
test("48;5;0;2");
|
||||
test("48;5;80;");
|
||||
test("48;5;80;2");
|
||||
test("48;5;257");
|
||||
test("48;2");
|
||||
test("48;2;");
|
||||
test("48;2;0");
|
||||
test("48;2;0;2");
|
||||
test("48;2;0;2;257");
|
||||
test("48;2;0;2;25;");
|
||||
test("48;2;0;2;25;3");
|
||||
}
|
||||
}
|
||||
234
vendor/crossterm/src/style/types/colors.rs
vendored
Normal file
234
vendor/crossterm/src/style/types/colors.rs
vendored
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
use crate::style::{Color, Colored};
|
||||
|
||||
/// Represents, optionally, a foreground and/or a background color.
|
||||
///
|
||||
/// It can be applied using the `SetColors` command.
|
||||
///
|
||||
/// It can also be created from a [Colored](enum.Colored.html) value or a tuple of
|
||||
/// `(Color, Color)` in the order `(foreground, background)`.
|
||||
///
|
||||
/// The [then](#method.then) method can be used to combine `Colors` values.
|
||||
///
|
||||
/// For example:
|
||||
/// ```no_run
|
||||
/// use crossterm::style::{Color, Colors, Colored};
|
||||
///
|
||||
/// // An example color, loaded from a config, file in ANSI format.
|
||||
/// let config_color = "38;2;23;147;209";
|
||||
///
|
||||
/// // Default to green text on a black background.
|
||||
/// let default_colors = Colors::new(Color::Green, Color::Black);
|
||||
/// // Load a colored value from a config and override the default colors
|
||||
/// let colors = match Colored::parse_ansi(config_color) {
|
||||
/// Some(colored) => default_colors.then(&colored.into()),
|
||||
/// None => default_colors,
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// See [Color](enum.Color.html).
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Colors {
|
||||
pub foreground: Option<Color>,
|
||||
pub background: Option<Color>,
|
||||
}
|
||||
|
||||
impl Colors {
|
||||
/// Returns a new `Color` which, when applied, has the same effect as applying `self` and *then*
|
||||
/// `other`.
|
||||
pub fn then(&self, other: &Colors) -> Colors {
|
||||
Colors {
|
||||
foreground: other.foreground.or(self.foreground),
|
||||
background: other.background.or(self.background),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Colors {
|
||||
pub fn new(foreground: Color, background: Color) -> Colors {
|
||||
Colors {
|
||||
foreground: Some(foreground),
|
||||
background: Some(background),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Colored> for Colors {
|
||||
fn from(colored: Colored) -> Colors {
|
||||
match colored {
|
||||
Colored::ForegroundColor(color) => Colors {
|
||||
foreground: Some(color),
|
||||
background: None,
|
||||
},
|
||||
Colored::BackgroundColor(color) => Colors {
|
||||
foreground: None,
|
||||
background: Some(color),
|
||||
},
|
||||
Colored::UnderlineColor(color) => Colors {
|
||||
foreground: None,
|
||||
background: Some(color),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::style::{Color, Colors};
|
||||
|
||||
#[test]
|
||||
fn test_colors_then() {
|
||||
use Color::*;
|
||||
|
||||
assert_eq!(
|
||||
Colors {
|
||||
foreground: None,
|
||||
background: None,
|
||||
}
|
||||
.then(&Colors {
|
||||
foreground: None,
|
||||
background: None,
|
||||
}),
|
||||
Colors {
|
||||
foreground: None,
|
||||
background: None,
|
||||
}
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Colors {
|
||||
foreground: None,
|
||||
background: None,
|
||||
}
|
||||
.then(&Colors {
|
||||
foreground: Some(Black),
|
||||
background: None,
|
||||
}),
|
||||
Colors {
|
||||
foreground: Some(Black),
|
||||
background: None,
|
||||
}
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Colors {
|
||||
foreground: None,
|
||||
background: None,
|
||||
}
|
||||
.then(&Colors {
|
||||
foreground: None,
|
||||
background: Some(Grey),
|
||||
}),
|
||||
Colors {
|
||||
foreground: None,
|
||||
background: Some(Grey),
|
||||
}
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Colors {
|
||||
foreground: None,
|
||||
background: None,
|
||||
}
|
||||
.then(&Colors::new(White, Grey)),
|
||||
Colors::new(White, Grey),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Colors {
|
||||
foreground: None,
|
||||
background: Some(Blue),
|
||||
}
|
||||
.then(&Colors::new(White, Grey)),
|
||||
Colors::new(White, Grey),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Colors {
|
||||
foreground: Some(Blue),
|
||||
background: None,
|
||||
}
|
||||
.then(&Colors::new(White, Grey)),
|
||||
Colors::new(White, Grey),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Colors::new(Blue, Green).then(&Colors::new(White, Grey)),
|
||||
Colors::new(White, Grey),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Colors {
|
||||
foreground: Some(Blue),
|
||||
background: Some(Green),
|
||||
}
|
||||
.then(&Colors {
|
||||
foreground: None,
|
||||
background: Some(Grey),
|
||||
}),
|
||||
Colors {
|
||||
foreground: Some(Blue),
|
||||
background: Some(Grey),
|
||||
}
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Colors {
|
||||
foreground: Some(Blue),
|
||||
background: Some(Green),
|
||||
}
|
||||
.then(&Colors {
|
||||
foreground: Some(White),
|
||||
background: None,
|
||||
}),
|
||||
Colors {
|
||||
foreground: Some(White),
|
||||
background: Some(Green),
|
||||
}
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Colors {
|
||||
foreground: Some(Blue),
|
||||
background: Some(Green),
|
||||
}
|
||||
.then(&Colors {
|
||||
foreground: None,
|
||||
background: None,
|
||||
}),
|
||||
Colors {
|
||||
foreground: Some(Blue),
|
||||
background: Some(Green),
|
||||
}
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Colors {
|
||||
foreground: None,
|
||||
background: Some(Green),
|
||||
}
|
||||
.then(&Colors {
|
||||
foreground: None,
|
||||
background: None,
|
||||
}),
|
||||
Colors {
|
||||
foreground: None,
|
||||
background: Some(Green),
|
||||
}
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Colors {
|
||||
foreground: Some(Blue),
|
||||
background: None,
|
||||
}
|
||||
.then(&Colors {
|
||||
foreground: None,
|
||||
background: None,
|
||||
}),
|
||||
Colors {
|
||||
foreground: Some(Blue),
|
||||
background: None,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue