Changed vec of Attributes to bitfield (#380)
This commit is contained in:
parent
2ba0ac07bc
commit
f58aca9354
@ -5,7 +5,7 @@ use crossterm::{cursor, queue, style};
|
||||
use std::io::Write;
|
||||
|
||||
const ATTRIBUTES: [(style::Attribute, style::Attribute); 6] = [
|
||||
(style::Attribute::Bold, style::Attribute::NoBold),
|
||||
(style::Attribute::Bold, style::Attribute::NormalIntensity),
|
||||
(style::Attribute::Italic, style::Attribute::NoItalic),
|
||||
(style::Attribute::Underlined, style::Attribute::NoUnderline),
|
||||
(style::Attribute::Reverse, style::Attribute::NoReverse),
|
||||
|
26
src/style.rs
26
src/style.rs
@ -118,6 +118,7 @@ use crate::{impl_display, Command};
|
||||
|
||||
pub(crate) use self::enums::Colored;
|
||||
pub use self::{
|
||||
attributes::Attributes,
|
||||
content_style::ContentStyle,
|
||||
enums::{Attribute, Color},
|
||||
styled_content::StyledContent,
|
||||
@ -127,6 +128,7 @@ pub use self::{
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
mod ansi;
|
||||
mod attributes;
|
||||
mod content_style;
|
||||
mod enums;
|
||||
mod styled_content;
|
||||
@ -290,6 +292,30 @@ impl Command for SetAttribute {
|
||||
}
|
||||
}
|
||||
|
||||
/// A command that sets several attributes.
|
||||
///
|
||||
/// See [`Attributes`](struct.Attributes.html) for more info.
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// Commands must be executed/queued for execution otherwise they do nothing.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct SetAttributes(pub Attributes);
|
||||
|
||||
impl Command for SetAttributes {
|
||||
type AnsiType = String;
|
||||
|
||||
fn ansi_code(&self) -> Self::AnsiType {
|
||||
ansi::set_attrs_csi_sequence(self.0)
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn execute_winapi(&self) -> Result<()> {
|
||||
// attributes are not supported by WinAPI.
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// A command that prints styled content.
|
||||
///
|
||||
/// See [`StyledContent`](struct.StyledContent.html) for more info.
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
use crate::{
|
||||
csi,
|
||||
style::{Attribute, Color, Colored},
|
||||
style::{Attribute, Attributes, Color, Colored},
|
||||
};
|
||||
|
||||
pub(crate) fn set_fg_csi_sequence(fg_color: Color) -> String {
|
||||
@ -21,7 +21,17 @@ pub(crate) fn set_bg_csi_sequence(bg_color: Color) -> String {
|
||||
}
|
||||
|
||||
pub(crate) fn set_attr_csi_sequence(attribute: Attribute) -> String {
|
||||
format!(csi!("{}m"), attribute as i16)
|
||||
format!(csi!("{}m"), attribute.sgr())
|
||||
}
|
||||
|
||||
pub(crate) fn set_attrs_csi_sequence(attributes: Attributes) -> String {
|
||||
let mut ansi = String::new();
|
||||
for attr in Attribute::iterator() {
|
||||
if attributes.has(attr) {
|
||||
ansi.push_str(&format!(csi!("{}m"), attr.sgr()));
|
||||
}
|
||||
}
|
||||
ansi
|
||||
}
|
||||
|
||||
pub(crate) const RESET_CSI_SEQUENCE: &str = csi!("0m");
|
||||
|
119
src/style/attributes.rs
Normal file
119
src/style/attributes.rs
Normal file
@ -0,0 +1,119 @@
|
||||
use crate::style::Attribute;
|
||||
use std::ops::{BitAnd, BitOr, BitXor};
|
||||
|
||||
/// 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 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 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());
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
|
||||
use std::fmt::Display;
|
||||
|
||||
use crate::style::{Attribute, Color, StyledContent};
|
||||
use crate::style::{Attribute, Attributes, Color, StyledContent};
|
||||
|
||||
/// The style that can be put on content.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
@ -12,7 +12,7 @@ pub struct ContentStyle {
|
||||
/// The background color.
|
||||
pub background_color: Option<Color>,
|
||||
/// List of attributes.
|
||||
pub attributes: Vec<Attribute>,
|
||||
pub attributes: Attributes,
|
||||
}
|
||||
|
||||
impl ContentStyle {
|
||||
@ -51,7 +51,7 @@ impl ContentStyle {
|
||||
/// You can add more attributes by calling this method multiple times.
|
||||
#[inline]
|
||||
pub fn attribute(mut self, attr: Attribute) -> ContentStyle {
|
||||
self.attributes.push(attr);
|
||||
self.attributes.set(attr);
|
||||
self
|
||||
}
|
||||
}
|
||||
@ -65,11 +65,11 @@ mod tests {
|
||||
let content_style = ContentStyle::new()
|
||||
.foreground(Color::Blue)
|
||||
.background(Color::Red)
|
||||
.attribute(Attribute::Reset);
|
||||
.attribute(Attribute::Bold);
|
||||
|
||||
assert_eq!(content_style.foreground_color, Some(Color::Blue));
|
||||
assert_eq!(content_style.background_color, Some(Color::Red));
|
||||
assert_eq!(content_style.attributes[0], Attribute::Reset);
|
||||
assert!(content_style.attributes.has(Attribute::Bold));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -83,6 +83,6 @@ mod tests {
|
||||
|
||||
assert_eq!(styled_content.style().foreground_color, Some(Color::Blue));
|
||||
assert_eq!(styled_content.style().background_color, Some(Color::Red));
|
||||
assert_eq!(styled_content.style().attributes[0], Attribute::Reset);
|
||||
assert!(styled_content.style().attributes.has(Attribute::Reset));
|
||||
}
|
||||
}
|
||||
|
@ -5,59 +5,89 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::super::SetAttribute;
|
||||
|
||||
/// 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::Styler;
|
||||
///
|
||||
/// 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)]
|
||||
pub enum Attribute {
|
||||
// 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::Styler;
|
||||
///
|
||||
/// println!("{}", "Bold text".bold());
|
||||
/// println!("{}", "Underlined text".underlined());
|
||||
/// println!("{}", "Negative text".negative());
|
||||
/// ```
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[non_exhaustive]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
|
||||
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.
|
||||
@ -82,7 +112,7 @@ pub enum Attribute {
|
||||
///
|
||||
/// Mostly used for [mathematical alphanumeric symbols](https://en.wikipedia.org/wiki/Mathematical_Alphanumeric_Symbols).
|
||||
Fraktur = 20,
|
||||
/// Turns off the `Bold` attribute.
|
||||
/// Turns off the `Bold` attribute. - Inconsistent - Prefer to use NormalIntensity
|
||||
NoBold = 21,
|
||||
/// Switches the text back to normal intensity (no bold, italic).
|
||||
NormalIntensity = 22,
|
||||
@ -108,8 +138,6 @@ pub enum Attribute {
|
||||
NotFramedOrEncircled = 54,
|
||||
/// Turns off the `OverLined` attribute.
|
||||
NotOverLined = 55,
|
||||
#[doc(hidden)]
|
||||
__Nonexhaustive,
|
||||
}
|
||||
|
||||
impl Display for Attribute {
|
||||
@ -118,3 +146,22 @@ impl Display for Attribute {
|
||||
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) -> i16 {
|
||||
SGR[self as usize]
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ macro_rules! def_str_attr {
|
||||
fn $name(self) -> StyledContent<&'static str> {
|
||||
StyledContent::new(
|
||||
ContentStyle {
|
||||
attributes: vec![ $attr ],
|
||||
attributes: $attr.into(),
|
||||
..Default::default()
|
||||
},
|
||||
self
|
||||
|
@ -8,7 +8,7 @@ use std::{
|
||||
use crate::{
|
||||
queue,
|
||||
style::{
|
||||
Attribute, Color, Colorize, ContentStyle, ResetColor, SetAttribute, SetBackgroundColor,
|
||||
Attribute, Color, Colorize, ContentStyle, ResetColor, SetAttributes, SetBackgroundColor,
|
||||
SetForegroundColor, Styler,
|
||||
},
|
||||
};
|
||||
@ -104,8 +104,8 @@ impl<D: Display> Display for StyledContent<D> {
|
||||
reset = true;
|
||||
}
|
||||
|
||||
for attr in &self.style.attributes {
|
||||
queue!(f, SetAttribute(*attr)).map_err(|_| fmt::Error)?;
|
||||
if !self.style.attributes.is_empty() {
|
||||
queue!(f, SetAttributes(self.style.attributes)).map_err(|_| fmt::Error)?;
|
||||
reset = true;
|
||||
}
|
||||
|
||||
@ -183,7 +183,7 @@ mod tests {
|
||||
let style = ContentStyle::new()
|
||||
.foreground(Color::Blue)
|
||||
.background(Color::Red)
|
||||
.attribute(Attribute::Reset);
|
||||
.attribute(Attribute::Bold);
|
||||
|
||||
let mut styled_content = style.apply("test");
|
||||
|
||||
@ -194,8 +194,7 @@ mod tests {
|
||||
|
||||
assert_eq!(styled_content.style.foreground_color, Some(Color::Green));
|
||||
assert_eq!(styled_content.style.background_color, Some(Color::Magenta));
|
||||
assert_eq!(styled_content.style.attributes.len(), 2);
|
||||
assert_eq!(styled_content.style.attributes[0], Attribute::Reset);
|
||||
assert_eq!(styled_content.style.attributes[1], Attribute::NoItalic);
|
||||
assert!(styled_content.style.attributes.has(Attribute::Bold));
|
||||
assert!(styled_content.style.attributes.has(Attribute::NoItalic));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user