From 9c9479deb21a9dcabd9a13453d422c6c6e713f2b Mon Sep 17 00:00:00 2001 From: Riey Date: Tue, 10 Mar 2020 21:38:38 +0900 Subject: [PATCH] Direct write command ansi_codes (#390) --- examples/interactive-demo/src/test/cursor.rs | 2 +- src/ansi.rs | 4 + src/cursor.rs | 113 +++++++++++++++---- src/cursor/ansi.rs | 33 +++--- src/lib.rs | 2 + src/style.rs | 52 +++++++-- src/style/ansi.rs | 108 ++++++++---------- src/style/macros.rs | 16 +-- 8 files changed, 205 insertions(+), 125 deletions(-) create mode 100644 src/ansi.rs diff --git a/examples/interactive-demo/src/test/cursor.rs b/examples/interactive-demo/src/test/cursor.rs index b5ef3fd..028b703 100644 --- a/examples/interactive-demo/src/test/cursor.rs +++ b/examples/interactive-demo/src/test/cursor.rs @@ -132,7 +132,7 @@ fn draw_cursor_box(w: &mut W, description: &str, cursor_command: F) -> where W: Write, F: Fn(u16, u16) -> T, - T: Command, + T: Command, { execute!( w, diff --git a/src/ansi.rs b/src/ansi.rs new file mode 100644 index 0000000..9dfa1d6 --- /dev/null +++ b/src/ansi.rs @@ -0,0 +1,4 @@ +/// Wrapper type for write dynamic ansi string +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[doc(hidden)] +pub struct Ansi(pub T); diff --git a/src/cursor.rs b/src/cursor.rs index e4bcda5..7c0b49f 100644 --- a/src/cursor.rs +++ b/src/cursor.rs @@ -46,7 +46,8 @@ pub use sys::position; #[cfg(windows)] use crate::Result; -use crate::{impl_display, Command}; +use crate::{impl_display, Ansi, Command}; +use std::fmt; mod ansi; pub(crate) mod sys; @@ -60,11 +61,18 @@ pub(crate) mod sys; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct MoveTo(pub u16, pub u16); -impl Command for MoveTo { - type AnsiType = String; +impl fmt::Display for Ansi { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + ansi::move_to_csi_sequence(f, (self.0).0, (self.0).1) + } +} +impl Command for MoveTo { + type AnsiType = Ansi; + + #[inline] fn ansi_code(&self) -> Self::AnsiType { - ansi::move_to_csi_sequence(self.0, self.1) + Ansi(*self) } #[cfg(windows)] @@ -82,11 +90,18 @@ impl Command for MoveTo { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct MoveToNextLine(pub u16); -impl Command for MoveToNextLine { - type AnsiType = String; +impl fmt::Display for Ansi { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + ansi::move_to_next_line_csi_sequence(f, (self.0).0) + } +} +impl Command for MoveToNextLine { + type AnsiType = Ansi; + + #[inline] fn ansi_code(&self) -> Self::AnsiType { - ansi::move_to_next_line_csi_sequence(self.0) + Ansi(*self) } #[cfg(windows)] @@ -104,11 +119,18 @@ impl Command for MoveToNextLine { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct MoveToPreviousLine(pub u16); -impl Command for MoveToPreviousLine { - type AnsiType = String; +impl fmt::Display for Ansi { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + ansi::move_to_previous_line_csi_sequence(f, (self.0).0) + } +} +impl Command for MoveToPreviousLine { + type AnsiType = Ansi; + + #[inline] fn ansi_code(&self) -> Self::AnsiType { - ansi::move_to_previous_line_csi_sequence(self.0) + Ansi(*self) } #[cfg(windows)] @@ -125,11 +147,18 @@ impl Command for MoveToPreviousLine { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct MoveToColumn(pub u16); -impl Command for MoveToColumn { - type AnsiType = String; +impl fmt::Display for Ansi { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + ansi::move_to_column_csi_sequence(f, (self.0).0) + } +} +impl Command for MoveToColumn { + type AnsiType = Ansi; + + #[inline] fn ansi_code(&self) -> Self::AnsiType { - ansi::move_to_column_csi_sequence(self.0) + Ansi(*self) } #[cfg(windows)] @@ -146,11 +175,18 @@ impl Command for MoveToColumn { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct MoveUp(pub u16); -impl Command for MoveUp { - type AnsiType = String; +impl fmt::Display for Ansi { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + ansi::move_up_csi_sequence(f, (self.0).0) + } +} +impl Command for MoveUp { + type AnsiType = Ansi; + + #[inline] fn ansi_code(&self) -> Self::AnsiType { - ansi::move_up_csi_sequence(self.0) + Ansi(*self) } #[cfg(windows)] @@ -167,11 +203,18 @@ impl Command for MoveUp { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct MoveRight(pub u16); -impl Command for MoveRight { - type AnsiType = String; +impl fmt::Display for Ansi { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + ansi::move_right_csi_sequence(f, (self.0).0) + } +} +impl Command for MoveRight { + type AnsiType = Ansi; + + #[inline] fn ansi_code(&self) -> Self::AnsiType { - ansi::move_right_csi_sequence(self.0) + Ansi(*self) } #[cfg(windows)] @@ -188,11 +231,18 @@ impl Command for MoveRight { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct MoveDown(pub u16); -impl Command for MoveDown { - type AnsiType = String; +impl fmt::Display for Ansi { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + ansi::move_down_csi_sequence(f, (self.0).0) + } +} +impl Command for MoveDown { + type AnsiType = Ansi; + + #[inline] fn ansi_code(&self) -> Self::AnsiType { - ansi::move_down_csi_sequence(self.0) + Ansi(*self) } #[cfg(windows)] @@ -209,11 +259,18 @@ impl Command for MoveDown { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct MoveLeft(pub u16); -impl Command for MoveLeft { - type AnsiType = String; +impl fmt::Display for Ansi { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + ansi::move_left_csi_sequence(f, (self.0).0) + } +} +impl Command for MoveLeft { + type AnsiType = Ansi; + + #[inline] fn ansi_code(&self) -> Self::AnsiType { - ansi::move_left_csi_sequence(self.0) + Ansi(*self) } #[cfg(windows)] @@ -236,6 +293,7 @@ pub struct SavePosition; impl Command for SavePosition { type AnsiType = &'static str; + #[inline] fn ansi_code(&self) -> Self::AnsiType { ansi::SAVE_POSITION_CSI_SEQUENCE } @@ -260,6 +318,7 @@ pub struct RestorePosition; impl Command for RestorePosition { type AnsiType = &'static str; + #[inline] fn ansi_code(&self) -> Self::AnsiType { ansi::RESTORE_POSITION_CSI_SEQUENCE } @@ -281,6 +340,7 @@ pub struct Hide; impl Command for Hide { type AnsiType = &'static str; + #[inline] fn ansi_code(&self) -> Self::AnsiType { ansi::HIDE_CSI_SEQUENCE } @@ -302,6 +362,7 @@ pub struct Show; impl Command for Show { type AnsiType = &'static str; + #[inline] fn ansi_code(&self) -> Self::AnsiType { ansi::SHOW_CSI_SEQUENCE } @@ -324,6 +385,7 @@ pub struct EnableBlinking; impl Command for EnableBlinking { type AnsiType = &'static str; + #[inline] fn ansi_code(&self) -> Self::AnsiType { ansi::ENABLE_BLINKING_CSI_SEQUENCE } @@ -346,6 +408,7 @@ pub struct DisableBlinking; impl Command for DisableBlinking { type AnsiType = &'static str; + #[inline] fn ansi_code(&self) -> Self::AnsiType { ansi::DISABLE_BLINKING_CSI_SEQUENCE } diff --git a/src/cursor/ansi.rs b/src/cursor/ansi.rs index d256569..556a864 100644 --- a/src/cursor/ansi.rs +++ b/src/cursor/ansi.rs @@ -1,37 +1,38 @@ //! This module provides cursor related ANSI escape codes. use crate::csi; +use std::fmt::{self, Formatter}; -pub(crate) fn move_to_csi_sequence(x: u16, y: u16) -> String { - format!(csi!("{};{}H"), y + 1, x + 1) +pub(crate) fn move_to_csi_sequence(f: &mut Formatter, x: u16, y: u16) -> fmt::Result { + write!(f, csi!("{};{}H"), y + 1, x + 1) } -pub(crate) fn move_up_csi_sequence(count: u16) -> String { - format!(csi!("{}A"), count) +pub(crate) fn move_up_csi_sequence(f: &mut Formatter, count: u16) -> fmt::Result { + write!(f, csi!("{}A"), count) } -pub(crate) fn move_right_csi_sequence(count: u16) -> String { - format!(csi!("{}C"), count) +pub(crate) fn move_right_csi_sequence(f: &mut Formatter, count: u16) -> fmt::Result { + write!(f, csi!("{}C"), count) } -pub(crate) fn move_down_csi_sequence(count: u16) -> String { - format!(csi!("{}B"), count) +pub(crate) fn move_down_csi_sequence(f: &mut Formatter, count: u16) -> fmt::Result { + write!(f, csi!("{}B"), count) } -pub(crate) fn move_left_csi_sequence(count: u16) -> String { - format!(csi!("{}D"), count) +pub(crate) fn move_left_csi_sequence(f: &mut Formatter, count: u16) -> fmt::Result { + write!(f, csi!("{}D"), count) } -pub(crate) fn move_to_column_csi_sequence(count: u16) -> String { - format!(csi!("{}G"), count) +pub(crate) fn move_to_column_csi_sequence(f: &mut Formatter, count: u16) -> fmt::Result { + write!(f, csi!("{}G"), count) } -pub(crate) fn move_to_previous_line_csi_sequence(count: u16) -> String { - format!(csi!("{}F"), count) +pub(crate) fn move_to_previous_line_csi_sequence(f: &mut Formatter, count: u16) -> fmt::Result { + write!(f, csi!("{}F"), count) } -pub(crate) fn move_to_next_line_csi_sequence(count: u16) -> String { - format!(csi!("{}E"), count) +pub(crate) fn move_to_next_line_csi_sequence(f: &mut Formatter, count: u16) -> fmt::Result { + write!(f, csi!("{}E"), count) } pub(crate) const SAVE_POSITION_CSI_SEQUENCE: &str = "\x1B7"; diff --git a/src/lib.rs b/src/lib.rs index 796c093..1a3ebef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -228,6 +228,7 @@ //! [flush]: https://doc.rust-lang.org/std/io/trait.Write.html#tymethod.flush pub use crate::{ + ansi::Ansi, command::{Command, ExecutableCommand, QueueableCommand}, error::{ErrorKind, Result}, }; @@ -241,6 +242,7 @@ pub mod style; /// A module to work with the terminal. pub mod terminal; +mod ansi; #[cfg(windows)] pub(crate) mod ansi_support; mod command; diff --git a/src/style.rs b/src/style.rs index deecff4..91a413e 100644 --- a/src/style.rs +++ b/src/style.rs @@ -114,7 +114,8 @@ use std::{env, fmt::Display}; #[cfg(windows)] use crate::Result; -use crate::{impl_display, Command}; +use crate::{impl_display, Ansi, Command}; +use std::fmt; pub(crate) use self::enums::Colored; pub use self::{ @@ -284,11 +285,18 @@ pub fn available_color_count() -> u16 { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct SetForegroundColor(pub Color); -impl Command for SetForegroundColor { - type AnsiType = String; +impl fmt::Display for Ansi { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + ansi::set_fg_csi_sequence(f, (self.0).0) + } +} +impl Command for SetForegroundColor { + type AnsiType = Ansi; + + #[inline] fn ansi_code(&self) -> Self::AnsiType { - ansi::set_fg_csi_sequence(self.0) + Ansi(*self) } #[cfg(windows)] @@ -307,11 +315,18 @@ impl Command for SetForegroundColor { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct SetBackgroundColor(pub Color); -impl Command for SetBackgroundColor { - type AnsiType = String; +impl fmt::Display for Ansi { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + ansi::set_bg_csi_sequence(f, (self.0).0) + } +} +impl Command for SetBackgroundColor { + type AnsiType = Ansi; + + #[inline] fn ansi_code(&self) -> Self::AnsiType { - ansi::set_bg_csi_sequence(self.0) + Ansi(*self) } #[cfg(windows)] @@ -330,11 +345,18 @@ impl Command for SetBackgroundColor { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct SetAttribute(pub Attribute); -impl Command for SetAttribute { - type AnsiType = String; +impl fmt::Display for Ansi { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + ansi::set_attr_csi_sequence(f, (self.0).0) + } +} +impl Command for SetAttribute { + type AnsiType = Ansi; + + #[inline] fn ansi_code(&self) -> Self::AnsiType { - ansi::set_attr_csi_sequence(self.0) + Ansi(*self) } #[cfg(windows)] @@ -354,11 +376,17 @@ impl Command for SetAttribute { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct SetAttributes(pub Attributes); +impl fmt::Display for Ansi { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + ansi::set_attrs_csi_sequence(f, (self.0).0) + } +} + impl Command for SetAttributes { - type AnsiType = String; + type AnsiType = Ansi; fn ansi_code(&self) -> Self::AnsiType { - ansi::set_attrs_csi_sequence(self.0) + Ansi(*self) } #[cfg(windows)] diff --git a/src/style/ansi.rs b/src/style/ansi.rs index cd03f79..03f2b00 100644 --- a/src/style/ansi.rs +++ b/src/style/ansi.rs @@ -6,93 +6,75 @@ use crate::{ style::{Attribute, Attributes, Color, Colored}, }; -pub(crate) fn set_fg_csi_sequence(fg_color: Color) -> String { - format!( - csi!("{}m"), - Into::::into(Colored::ForegroundColor(fg_color)) - ) +use std::fmt::{self, Formatter}; + +pub(crate) fn set_fg_csi_sequence(f: &mut Formatter, fg_color: Color) -> fmt::Result { + write!(f, csi!("{}m"), Colored::ForegroundColor(fg_color)) } -pub(crate) fn set_bg_csi_sequence(bg_color: Color) -> String { - format!( - csi!("{}m"), - Into::::into(Colored::BackgroundColor(bg_color)) - ) +pub(crate) fn set_bg_csi_sequence(f: &mut Formatter, bg_color: Color) -> fmt::Result { + write!(f, csi!("{}m"), Colored::BackgroundColor(bg_color)) } -pub(crate) fn set_attr_csi_sequence(attribute: Attribute) -> String { - format!(csi!("{}m"), attribute.sgr()) +pub(crate) fn set_attr_csi_sequence(f: &mut Formatter, attribute: Attribute) -> fmt::Result { + write!(f, csi!("{}m"), attribute.sgr()) } -pub(crate) fn set_attrs_csi_sequence(attributes: Attributes) -> String { - let mut ansi = String::new(); +pub(crate) fn set_attrs_csi_sequence(f: &mut Formatter, attributes: Attributes) -> fmt::Result { for attr in Attribute::iterator() { if attributes.has(attr) { - ansi.push_str(&format!(csi!("{}m"), attr.sgr())); + write!(f, csi!("{}m"), attr.sgr())?; } } - ansi + Ok(()) } pub(crate) const RESET_CSI_SEQUENCE: &str = csi!("0m"); -impl From for String { - fn from(colored: Colored) -> Self { - let mut ansi_value = String::new(); - +impl fmt::Display for Colored { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { let color; - match colored { + match *self { Colored::ForegroundColor(new_color) => { if new_color == Color::Reset { - ansi_value.push_str("39"); - return ansi_value; + return f.write_str("39"); } else { - ansi_value.push_str("38;"); + f.write_str("38;")?; color = new_color; } } Colored::BackgroundColor(new_color) => { if new_color == Color::Reset { - ansi_value.push_str("49"); - return ansi_value; + return f.write_str("49"); } else { - ansi_value.push_str("48;"); + f.write_str("48;")?; color = new_color; } } } - let color_val = match color { - Color::Black => "5;0", - Color::DarkGrey => "5;8", - Color::Red => "5;9", - Color::DarkRed => "5;1", - Color::Green => "5;10", - Color::DarkGreen => "5;2", - Color::Yellow => "5;11", - Color::DarkYellow => "5;3", - Color::Blue => "5;12", - Color::DarkBlue => "5;4", - Color::Magenta => "5;13", - Color::DarkMagenta => "5;5", - Color::Cyan => "5;14", - Color::DarkCyan => "5;6", - Color::White => "5;15", - Color::Grey => "5;7", - Color::Rgb { r, g, b } => { - ansi_value.push_str(format!("2;{};{};{}", r, g, b).as_str()); - "" - } - Color::AnsiValue(val) => { - ansi_value.push_str(format!("5;{}", val).as_str()); - "" - } - _ => "", - }; - - ansi_value.push_str(color_val); - ansi_value + 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(()), + } } } @@ -103,36 +85,36 @@ mod tests { #[test] fn test_parse_fg_color() { let colored = Colored::ForegroundColor(Color::Red); - assert_eq!(Into::::into(colored), "38;5;9"); + assert_eq!(colored.to_string(), "38;5;9"); } #[test] fn test_parse_bg_color() { let colored = Colored::BackgroundColor(Color::Red); - assert_eq!(Into::::into(colored), "48;5;9"); + assert_eq!(colored.to_string(), "48;5;9"); } #[test] fn test_parse_reset_fg_color() { let colored = Colored::ForegroundColor(Color::Reset); - assert_eq!(Into::::into(colored), "39"); + assert_eq!(colored.to_string(), "39"); } #[test] fn test_parse_reset_bg_color() { let colored = Colored::BackgroundColor(Color::Reset); - assert_eq!(Into::::into(colored), "49"); + assert_eq!(colored.to_string(), "49"); } #[test] fn test_parse_fg_rgb_color() { let colored = Colored::BackgroundColor(Color::Rgb { r: 1, g: 2, b: 3 }); - assert_eq!(Into::::into(colored), "48;2;1;2;3"); + assert_eq!(colored.to_string(), "48;2;1;2;3"); } #[test] fn test_parse_fg_ansi_color() { let colored = Colored::ForegroundColor(Color::AnsiValue(255)); - assert_eq!(Into::::into(colored), "38;5;255"); + assert_eq!(colored.to_string(), "38;5;255"); } } diff --git a/src/style/macros.rs b/src/style/macros.rs index e1e2f23..052d999 100644 --- a/src/style/macros.rs +++ b/src/style/macros.rs @@ -14,7 +14,7 @@ macro_rules! def_color { $side: Some($color), ..self.style }, - self.content + self.content, ) } }; @@ -22,13 +22,13 @@ macro_rules! def_color { macro_rules! def_str_color { ($side:ident: $name:ident => $color:path) => { - fn $name(self) -> StyledContent< &'static str> { + fn $name(self) -> StyledContent<&'static str> { StyledContent::new( ContentStyle { $side: Some($color), ..Default::default() }, - self + self, ) } }; @@ -42,7 +42,7 @@ macro_rules! def_char_color { $side: Some($color), ..Default::default() }, - self + self, ) } }; @@ -56,10 +56,10 @@ macro_rules! def_str_attr { attributes: $attr.into(), ..Default::default() }, - self + self, ) } - } + }; } macro_rules! def_char_attr { @@ -70,8 +70,8 @@ macro_rules! def_char_attr { attributes: $attr.into(), ..Default::default() }, - self + self, ) } - } + }; }