Direct write command ansi_codes (#390)

This commit is contained in:
Riey 2020-03-10 21:38:38 +09:00 committed by GitHub
parent e620c6b8be
commit 9c9479deb2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 205 additions and 125 deletions

View File

@ -132,7 +132,7 @@ fn draw_cursor_box<W, F, T>(w: &mut W, description: &str, cursor_command: F) ->
where
W: Write,
F: Fn(u16, u16) -> T,
T: Command<AnsiType = String>,
T: Command,
{
execute!(
w,

4
src/ansi.rs Normal file
View File

@ -0,0 +1,4 @@
/// Wrapper type for write dynamic ansi string
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[doc(hidden)]
pub struct Ansi<T>(pub T);

View File

@ -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<MoveTo> {
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<Self>;
#[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<MoveToNextLine> {
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<Self>;
#[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<MoveToPreviousLine> {
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<Self>;
#[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<MoveToColumn> {
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<Self>;
#[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<MoveUp> {
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<Self>;
#[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<MoveRight> {
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<Self>;
#[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<MoveDown> {
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<Self>;
#[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<MoveLeft> {
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<Self>;
#[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
}

View File

@ -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";

View File

@ -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;

View File

@ -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<SetForegroundColor> {
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<Self>;
#[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<SetBackgroundColor> {
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<Self>;
#[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<SetAttribute> {
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<Self>;
#[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<SetAttributes> {
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<Self>;
fn ansi_code(&self) -> Self::AnsiType {
ansi::set_attrs_csi_sequence(self.0)
Ansi(*self)
}
#[cfg(windows)]

View File

@ -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::<String>::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::<String>::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<Colored> 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::<String>::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::<String>::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::<String>::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::<String>::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::<String>::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::<String>::into(colored), "38;5;255");
assert_eq!(colored.to_string(), "38;5;255");
}
}

View File

@ -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,
)
}
}
};
}