1 use style::{Colour, Style}; 2 3 use std::fmt; 4 5 use write::AnyWrite; 6 7 8 // ---- generating ANSI codes ---- 9 10 impl Style { 11 12 /// Write any bytes that go *before* a piece of text to the given writer. write_prefix<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error>13 fn write_prefix<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> { 14 15 // If there are actually no styles here, then don’t write *any* codes 16 // as the prefix. An empty ANSI code may not affect the terminal 17 // output at all, but a user may just want a code-free string. 18 if self.is_plain() { 19 return Ok(()); 20 } 21 22 // Write the codes’ prefix, then write numbers, separated by 23 // semicolons, for each text style we want to apply. 24 write!(f, "\x1B[")?; 25 let mut written_anything = false; 26 27 { 28 let mut write_char = |c| { 29 if written_anything { write!(f, ";")?; } 30 written_anything = true; 31 write!(f, "{}", c)?; 32 Ok(()) 33 }; 34 35 if self.is_bold { write_char('1')? } 36 if self.is_dimmed { write_char('2')? } 37 if self.is_italic { write_char('3')? } 38 if self.is_underline { write_char('4')? } 39 if self.is_blink { write_char('5')? } 40 if self.is_reverse { write_char('7')? } 41 if self.is_hidden { write_char('8')? } 42 if self.is_strikethrough { write_char('9')? } 43 } 44 45 // The foreground and background colours, if specified, need to be 46 // handled specially because the number codes are more complicated. 47 // (see `write_background_code` and `write_foreground_code`) 48 if let Some(bg) = self.background { 49 if written_anything { write!(f, ";")?; } 50 written_anything = true; 51 bg.write_background_code(f)?; 52 } 53 54 if let Some(fg) = self.foreground { 55 if written_anything { write!(f, ";")?; } 56 fg.write_foreground_code(f)?; 57 } 58 59 // All the codes end with an `m`, because reasons. 60 write!(f, "m")?; 61 62 Ok(()) 63 } 64 65 /// Write any bytes that go *after* a piece of text to the given writer. write_suffix<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error>66 fn write_suffix<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> { 67 if self.is_plain() { 68 Ok(()) 69 } 70 else { 71 write!(f, "{}", RESET) 72 } 73 } 74 } 75 76 77 /// The code to send to reset all styles and return to `Style::default()`. 78 pub static RESET: &str = "\x1B[0m"; 79 80 81 82 impl Colour { write_foreground_code<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error>83 fn write_foreground_code<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> { 84 match *self { 85 Colour::Black => write!(f, "30"), 86 Colour::Red => write!(f, "31"), 87 Colour::Green => write!(f, "32"), 88 Colour::Yellow => write!(f, "33"), 89 Colour::Blue => write!(f, "34"), 90 Colour::Purple => write!(f, "35"), 91 Colour::Cyan => write!(f, "36"), 92 Colour::White => write!(f, "37"), 93 Colour::Fixed(num) => write!(f, "38;5;{}", &num), 94 Colour::RGB(r,g,b) => write!(f, "38;2;{};{};{}", &r, &g, &b), 95 } 96 } 97 write_background_code<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error>98 fn write_background_code<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> { 99 match *self { 100 Colour::Black => write!(f, "40"), 101 Colour::Red => write!(f, "41"), 102 Colour::Green => write!(f, "42"), 103 Colour::Yellow => write!(f, "43"), 104 Colour::Blue => write!(f, "44"), 105 Colour::Purple => write!(f, "45"), 106 Colour::Cyan => write!(f, "46"), 107 Colour::White => write!(f, "47"), 108 Colour::Fixed(num) => write!(f, "48;5;{}", &num), 109 Colour::RGB(r,g,b) => write!(f, "48;2;{};{};{}", &r, &g, &b), 110 } 111 } 112 } 113 114 115 /// Like `ANSIString`, but only displays the style prefix. 116 /// 117 /// This type implements the `Display` trait, meaning it can be written to a 118 /// `std::fmt` formatting without doing any extra allocation, and written to a 119 /// string with the `.to_string()` method. For examples, see 120 /// [`Style::prefix`](struct.Style.html#method.prefix). 121 #[derive(Clone, Copy, Debug)] 122 pub struct Prefix(Style); 123 124 /// Like `ANSIString`, but only displays the difference between two 125 /// styles. 126 /// 127 /// This type implements the `Display` trait, meaning it can be written to a 128 /// `std::fmt` formatting without doing any extra allocation, and written to a 129 /// string with the `.to_string()` method. For examples, see 130 /// [`Style::infix`](struct.Style.html#method.infix). 131 #[derive(Clone, Copy, Debug)] 132 pub struct Infix(Style, Style); 133 134 /// Like `ANSIString`, but only displays the style suffix. 135 /// 136 /// This type implements the `Display` trait, meaning it can be written to a 137 /// `std::fmt` formatting without doing any extra allocation, and written to a 138 /// string with the `.to_string()` method. For examples, see 139 /// [`Style::suffix`](struct.Style.html#method.suffix). 140 #[derive(Clone, Copy, Debug)] 141 pub struct Suffix(Style); 142 143 144 impl Style { 145 146 /// The prefix bytes for this style. These are the bytes that tell the 147 /// terminal to use a different colour or font style. 148 /// 149 /// # Examples 150 /// 151 /// ``` 152 /// use ansi_term::{Style, Colour::Blue}; 153 /// 154 /// let style = Style::default().bold(); 155 /// assert_eq!("\x1b[1m", 156 /// style.prefix().to_string()); 157 /// 158 /// let style = Blue.bold(); 159 /// assert_eq!("\x1b[1;34m", 160 /// style.prefix().to_string()); 161 /// 162 /// let style = Style::default(); 163 /// assert_eq!("", 164 /// style.prefix().to_string()); 165 /// ``` prefix(self) -> Prefix166 pub fn prefix(self) -> Prefix { 167 Prefix(self) 168 } 169 170 /// The infix bytes between this style and `next` style. These are the bytes 171 /// that tell the terminal to change the style to `next`. These may include 172 /// a reset followed by the next colour and style, depending on the two styles. 173 /// 174 /// # Examples 175 /// 176 /// ``` 177 /// use ansi_term::{Style, Colour::Green}; 178 /// 179 /// let style = Style::default().bold(); 180 /// assert_eq!("\x1b[32m", 181 /// style.infix(Green.bold()).to_string()); 182 /// 183 /// let style = Green.normal(); 184 /// assert_eq!("\x1b[1m", 185 /// style.infix(Green.bold()).to_string()); 186 /// 187 /// let style = Style::default(); 188 /// assert_eq!("", 189 /// style.infix(style).to_string()); 190 /// ``` infix(self, next: Style) -> Infix191 pub fn infix(self, next: Style) -> Infix { 192 Infix(self, next) 193 } 194 195 /// The suffix for this style. These are the bytes that tell the terminal 196 /// to reset back to its normal colour and font style. 197 /// 198 /// # Examples 199 /// 200 /// ``` 201 /// use ansi_term::{Style, Colour::Green}; 202 /// 203 /// let style = Style::default().bold(); 204 /// assert_eq!("\x1b[0m", 205 /// style.suffix().to_string()); 206 /// 207 /// let style = Green.normal().bold(); 208 /// assert_eq!("\x1b[0m", 209 /// style.suffix().to_string()); 210 /// 211 /// let style = Style::default(); 212 /// assert_eq!("", 213 /// style.suffix().to_string()); 214 /// ``` suffix(self) -> Suffix215 pub fn suffix(self) -> Suffix { 216 Suffix(self) 217 } 218 } 219 220 221 impl Colour { 222 223 /// The prefix bytes for this colour as a `Style`. These are the bytes 224 /// that tell the terminal to use a different colour or font style. 225 /// 226 /// See also [`Style::prefix`](struct.Style.html#method.prefix). 227 /// 228 /// # Examples 229 /// 230 /// ``` 231 /// use ansi_term::Colour::Green; 232 /// 233 /// assert_eq!("\x1b[0m", 234 /// Green.suffix().to_string()); 235 /// ``` prefix(self) -> Prefix236 pub fn prefix(self) -> Prefix { 237 Prefix(self.normal()) 238 } 239 240 /// The infix bytes between this colour and `next` colour. These are the bytes 241 /// that tell the terminal to use the `next` colour, or to do nothing if 242 /// the two colours are equal. 243 /// 244 /// See also [`Style::infix`](struct.Style.html#method.infix). 245 /// 246 /// # Examples 247 /// 248 /// ``` 249 /// use ansi_term::Colour::{Red, Yellow}; 250 /// 251 /// assert_eq!("\x1b[33m", 252 /// Red.infix(Yellow).to_string()); 253 /// ``` infix(self, next: Colour) -> Infix254 pub fn infix(self, next: Colour) -> Infix { 255 Infix(self.normal(), next.normal()) 256 } 257 258 /// The suffix for this colour as a `Style`. These are the bytes that 259 /// tell the terminal to reset back to its normal colour and font style. 260 /// 261 /// See also [`Style::suffix`](struct.Style.html#method.suffix). 262 /// 263 /// # Examples 264 /// 265 /// ``` 266 /// use ansi_term::Colour::Purple; 267 /// 268 /// assert_eq!("\x1b[0m", 269 /// Purple.suffix().to_string()); 270 /// ``` suffix(self) -> Suffix271 pub fn suffix(self) -> Suffix { 272 Suffix(self.normal()) 273 } 274 } 275 276 277 impl fmt::Display for Prefix { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result278 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 279 let f: &mut fmt::Write = f; 280 self.0.write_prefix(f) 281 } 282 } 283 284 285 impl fmt::Display for Infix { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result286 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 287 use difference::Difference; 288 289 match Difference::between(&self.0, &self.1) { 290 Difference::ExtraStyles(style) => { 291 let f: &mut fmt::Write = f; 292 style.write_prefix(f) 293 }, 294 Difference::Reset => { 295 let f: &mut fmt::Write = f; 296 write!(f, "{}{}", RESET, self.1.prefix()) 297 }, 298 Difference::NoDifference => { 299 Ok(()) // nothing to write 300 }, 301 } 302 } 303 } 304 305 306 impl fmt::Display for Suffix { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result307 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 308 let f: &mut fmt::Write = f; 309 self.0.write_suffix(f) 310 } 311 } 312 313 314 315 #[cfg(test)] 316 mod test { 317 use style::Style; 318 use style::Colour::*; 319 320 macro_rules! test { 321 ($name: ident: $style: expr; $input: expr => $result: expr) => { 322 #[test] 323 fn $name() { 324 assert_eq!($style.paint($input).to_string(), $result.to_string()); 325 326 let mut v = Vec::new(); 327 $style.paint($input.as_bytes()).write_to(&mut v).unwrap(); 328 assert_eq!(v.as_slice(), $result.as_bytes()); 329 } 330 }; 331 } 332 333 test!(plain: Style::default(); "text/plain" => "text/plain"); 334 test!(red: Red; "hi" => "\x1B[31mhi\x1B[0m"); 335 test!(black: Black.normal(); "hi" => "\x1B[30mhi\x1B[0m"); 336 test!(yellow_bold: Yellow.bold(); "hi" => "\x1B[1;33mhi\x1B[0m"); 337 test!(yellow_bold_2: Yellow.normal().bold(); "hi" => "\x1B[1;33mhi\x1B[0m"); 338 test!(blue_underline: Blue.underline(); "hi" => "\x1B[4;34mhi\x1B[0m"); 339 test!(green_bold_ul: Green.bold().underline(); "hi" => "\x1B[1;4;32mhi\x1B[0m"); 340 test!(green_bold_ul_2: Green.underline().bold(); "hi" => "\x1B[1;4;32mhi\x1B[0m"); 341 test!(purple_on_white: Purple.on(White); "hi" => "\x1B[47;35mhi\x1B[0m"); 342 test!(purple_on_white_2: Purple.normal().on(White); "hi" => "\x1B[47;35mhi\x1B[0m"); 343 test!(yellow_on_blue: Style::new().on(Blue).fg(Yellow); "hi" => "\x1B[44;33mhi\x1B[0m"); 344 test!(yellow_on_blue_2: Cyan.on(Blue).fg(Yellow); "hi" => "\x1B[44;33mhi\x1B[0m"); 345 test!(cyan_bold_on_white: Cyan.bold().on(White); "hi" => "\x1B[1;47;36mhi\x1B[0m"); 346 test!(cyan_ul_on_white: Cyan.underline().on(White); "hi" => "\x1B[4;47;36mhi\x1B[0m"); 347 test!(cyan_bold_ul_on_white: Cyan.bold().underline().on(White); "hi" => "\x1B[1;4;47;36mhi\x1B[0m"); 348 test!(cyan_ul_bold_on_white: Cyan.underline().bold().on(White); "hi" => "\x1B[1;4;47;36mhi\x1B[0m"); 349 test!(fixed: Fixed(100); "hi" => "\x1B[38;5;100mhi\x1B[0m"); 350 test!(fixed_on_purple: Fixed(100).on(Purple); "hi" => "\x1B[45;38;5;100mhi\x1B[0m"); 351 test!(fixed_on_fixed: Fixed(100).on(Fixed(200)); "hi" => "\x1B[48;5;200;38;5;100mhi\x1B[0m"); 352 test!(rgb: RGB(70,130,180); "hi" => "\x1B[38;2;70;130;180mhi\x1B[0m"); 353 test!(rgb_on_blue: RGB(70,130,180).on(Blue); "hi" => "\x1B[44;38;2;70;130;180mhi\x1B[0m"); 354 test!(blue_on_rgb: Blue.on(RGB(70,130,180)); "hi" => "\x1B[48;2;70;130;180;34mhi\x1B[0m"); 355 test!(rgb_on_rgb: RGB(70,130,180).on(RGB(5,10,15)); "hi" => "\x1B[48;2;5;10;15;38;2;70;130;180mhi\x1B[0m"); 356 test!(bold: Style::new().bold(); "hi" => "\x1B[1mhi\x1B[0m"); 357 test!(underline: Style::new().underline(); "hi" => "\x1B[4mhi\x1B[0m"); 358 test!(bunderline: Style::new().bold().underline(); "hi" => "\x1B[1;4mhi\x1B[0m"); 359 test!(dimmed: Style::new().dimmed(); "hi" => "\x1B[2mhi\x1B[0m"); 360 test!(italic: Style::new().italic(); "hi" => "\x1B[3mhi\x1B[0m"); 361 test!(blink: Style::new().blink(); "hi" => "\x1B[5mhi\x1B[0m"); 362 test!(reverse: Style::new().reverse(); "hi" => "\x1B[7mhi\x1B[0m"); 363 test!(hidden: Style::new().hidden(); "hi" => "\x1B[8mhi\x1B[0m"); 364 test!(stricken: Style::new().strikethrough(); "hi" => "\x1B[9mhi\x1B[0m"); 365 366 #[test] test_infix()367 fn test_infix() { 368 assert_eq!(Style::new().dimmed().infix(Style::new()).to_string(), "\x1B[0m"); 369 assert_eq!(White.dimmed().infix(White.normal()).to_string(), "\x1B[0m\x1B[37m"); 370 assert_eq!(White.normal().infix(White.bold()).to_string(), "\x1B[1m"); 371 assert_eq!(White.normal().infix(Blue.normal()).to_string(), "\x1B[34m"); 372 assert_eq!(Blue.bold().infix(Blue.bold()).to_string(), ""); 373 } 374 } 375