nu_ansi_term/
ansi.rs

1#![allow(missing_docs)]
2use crate::style::{Color, Style};
3use crate::write::AnyWrite;
4use std::fmt;
5
6impl Style {
7    /// Write any bytes that go *before* a piece of text to the given writer.
8    fn write_prefix<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
9        // If there are actually no styles here, then don’t write *any* codes
10        // as the prefix. An empty ANSI code may not affect the terminal
11        // output at all, but a user may just want a code-free string.
12        if self.is_plain() {
13            return Ok(());
14        }
15
16        // Prefix everything with reset characters if needed
17        if self.prefix_with_reset {
18            write!(f, "\x1B[0m")?
19        }
20
21        // Write the codes’ prefix, then write numbers, separated by
22        // semicolons, for each text style we want to apply.
23        write!(f, "\x1B[")?;
24        let mut written_anything = false;
25
26        {
27            let mut write_char = |c| {
28                if written_anything {
29                    write!(f, ";")?;
30                }
31                written_anything = true;
32                #[cfg(feature = "gnu_legacy")]
33                write!(f, "0")?;
34                write!(f, "{}", c)?;
35                Ok(())
36            };
37
38            if self.is_bold {
39                write_char('1')?
40            }
41            if self.is_dimmed {
42                write_char('2')?
43            }
44            if self.is_italic {
45                write_char('3')?
46            }
47            if self.is_underline {
48                write_char('4')?
49            }
50            if self.is_blink {
51                write_char('5')?
52            }
53            if self.is_reverse {
54                write_char('7')?
55            }
56            if self.is_hidden {
57                write_char('8')?
58            }
59            if self.is_strikethrough {
60                write_char('9')?
61            }
62        }
63
64        // The foreground and background colors, if specified, need to be
65        // handled specially because the number codes are more complicated.
66        // (see `write_background_code` and `write_foreground_code`)
67
68        #[cfg(feature = "gnu_legacy")]
69        // with GNU, write foreground first, else background first.
70        {
71            if let Some(fg) = self.foreground {
72                if written_anything {
73                    write!(f, ";")?;
74                }
75                written_anything = true;
76                fg.write_foreground_code(f)?;
77            }
78
79            if let Some(bg) = self.background {
80                if written_anything {
81                    write!(f, ";")?;
82                }
83                bg.write_background_code(f)?;
84            }
85        }
86        #[cfg(not(feature = "gnu_legacy"))]
87        {
88            if let Some(bg) = self.background {
89                if written_anything {
90                    write!(f, ";")?;
91                }
92                written_anything = true;
93                bg.write_background_code(f)?;
94            }
95
96            if let Some(fg) = self.foreground {
97                if written_anything {
98                    write!(f, ";")?;
99                }
100                fg.write_foreground_code(f)?;
101            }
102        }
103        // All the codes end with an `m`, because reasons.
104        write!(f, "m")?;
105
106        Ok(())
107    }
108
109    /// Write any bytes that go *after* a piece of text to the given writer.
110    fn write_suffix<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
111        if self.is_plain() {
112            Ok(())
113        } else {
114            write!(f, "{}", RESET)
115        }
116    }
117}
118
119/// The code to send to reset all styles and return to `Style::default()`.
120pub static RESET: &str = "\x1B[0m";
121
122impl Color {
123    fn write_foreground_code<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
124        match self {
125            Color::Black => write!(f, "30"),
126            Color::Red => write!(f, "31"),
127            Color::Green => write!(f, "32"),
128            Color::Yellow => write!(f, "33"),
129            Color::Blue => write!(f, "34"),
130            Color::Purple => write!(f, "35"),
131            Color::Magenta => write!(f, "35"),
132            Color::Cyan => write!(f, "36"),
133            Color::White => write!(f, "37"),
134            Color::Fixed(num) => write!(f, "38;5;{}", num),
135            Color::Rgb(r, g, b) => write!(f, "38;2;{};{};{}", r, g, b),
136            Color::Default => write!(f, "39"),
137            Color::DarkGray => write!(f, "90"),
138            Color::LightRed => write!(f, "91"),
139            Color::LightGreen => write!(f, "92"),
140            Color::LightYellow => write!(f, "93"),
141            Color::LightBlue => write!(f, "94"),
142            Color::LightPurple => write!(f, "95"),
143            Color::LightMagenta => write!(f, "95"),
144            Color::LightCyan => write!(f, "96"),
145            Color::LightGray => write!(f, "97"),
146        }
147    }
148
149    fn write_background_code<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
150        match self {
151            Color::Black => write!(f, "40"),
152            Color::Red => write!(f, "41"),
153            Color::Green => write!(f, "42"),
154            Color::Yellow => write!(f, "43"),
155            Color::Blue => write!(f, "44"),
156            Color::Purple => write!(f, "45"),
157            Color::Magenta => write!(f, "45"),
158            Color::Cyan => write!(f, "46"),
159            Color::White => write!(f, "47"),
160            Color::Fixed(num) => write!(f, "48;5;{}", num),
161            Color::Rgb(r, g, b) => write!(f, "48;2;{};{};{}", r, g, b),
162            Color::Default => write!(f, "49"),
163            Color::DarkGray => write!(f, "100"),
164            Color::LightRed => write!(f, "101"),
165            Color::LightGreen => write!(f, "102"),
166            Color::LightYellow => write!(f, "103"),
167            Color::LightBlue => write!(f, "104"),
168            Color::LightPurple => write!(f, "105"),
169            Color::LightMagenta => write!(f, "105"),
170            Color::LightCyan => write!(f, "106"),
171            Color::LightGray => write!(f, "107"),
172        }
173    }
174}
175
176/// Like `AnsiString`, but only displays the style prefix.
177///
178/// This type implements the `Display` trait, meaning it can be written to a
179/// `std::fmt` formatting without doing any extra allocation, and written to a
180/// string with the `.to_string()` method. For examples, see
181/// [`Style::prefix`](struct.Style.html#method.prefix).
182#[derive(Clone, Copy, Debug)]
183pub struct Prefix(Style);
184
185/// Like `AnsiString`, but only displays the difference between two
186/// styles.
187///
188/// This type implements the `Display` trait, meaning it can be written to a
189/// `std::fmt` formatting without doing any extra allocation, and written to a
190/// string with the `.to_string()` method. For examples, see
191/// [`Style::infix`](struct.Style.html#method.infix).
192#[derive(Clone, Copy, Debug)]
193pub struct Infix(Style, Style);
194
195/// Like `AnsiString`, but only displays the style suffix.
196///
197/// This type implements the `Display` trait, meaning it can be written to a
198/// `std::fmt` formatting without doing any extra allocation, and written to a
199/// string with the `.to_string()` method. For examples, see
200/// [`Style::suffix`](struct.Style.html#method.suffix).
201#[derive(Clone, Copy, Debug)]
202pub struct Suffix(Style);
203
204impl Style {
205    /// The prefix bytes for this style. These are the bytes that tell the
206    /// terminal to use a different color or font style.
207    ///
208    /// # Examples
209    ///
210    /// ```
211    /// # #[cfg(not(feature = "gnu_legacy"))]
212    /// # {
213    /// use nu_ansi_term::{Style, Color::Blue};
214    ///
215    /// let style = Style::default().bold();
216    /// assert_eq!("\x1b[1m",
217    ///            style.prefix().to_string());
218    ///
219    /// let style = Blue.bold();
220    /// assert_eq!("\x1b[1;34m",
221    ///            style.prefix().to_string());
222    ///
223    /// let style = Style::default();
224    /// assert_eq!("",
225    ///            style.prefix().to_string());
226    /// # }
227    /// ```
228    ///     
229    /// # Examples with gnu_legacy feature enabled
230    /// Styles like bold, underlined, etc. are two-digit now
231    ///
232    /// ```
233    /// # #[cfg(feature = "gnu_legacy")]
234    /// # {
235    /// use nu_ansi_term::{Style, Color::Blue};
236    ///
237    /// let style = Style::default().bold();
238    /// assert_eq!("\x1b[01m",
239    ///            style.prefix().to_string());
240    ///
241    /// let style = Blue.bold();
242    /// assert_eq!("\x1b[01;34m",
243    ///            style.prefix().to_string());
244    /// # }
245    /// ```
246    pub const fn prefix(self) -> Prefix {
247        Prefix(self)
248    }
249
250    /// The infix bytes between this style and `next` style. These are the bytes
251    /// that tell the terminal to change the style to `next`. These may include
252    /// a reset followed by the next color and style, depending on the two styles.
253    ///
254    /// # Examples
255    /// ```
256    /// # #[cfg(not(feature = "gnu_legacy"))]
257    /// # {
258    /// use nu_ansi_term::{Style, Color::Green};
259    ///
260    /// let style = Style::default().bold();
261    /// assert_eq!("\x1b[32m",
262    ///            style.infix(Green.bold()).to_string());
263    ///
264    /// let style = Green.normal();
265    /// assert_eq!("\x1b[1m",
266    ///            style.infix(Green.bold()).to_string());
267    ///
268    /// let style = Style::default();
269    /// assert_eq!("",
270    ///            style.infix(style).to_string());
271    /// # }
272    /// ```
273    /// # Examples with gnu_legacy feature enabled
274    /// Styles like bold, underlined, etc. are two-digit now
275    /// ```
276    /// # #[cfg(feature = "gnu_legacy")]
277    /// # {
278    /// use nu_ansi_term::Color::Green;
279    ///
280    /// let style = Green.normal();
281    /// assert_eq!("\x1b[01m",
282    ///            style.infix(Green.bold()).to_string());
283    /// # }
284    /// ```
285    pub const fn infix(self, next: Style) -> Infix {
286        Infix(self, next)
287    }
288
289    /// The suffix for this style. These are the bytes that tell the terminal
290    /// to reset back to its normal color and font style.
291    ///
292    /// # Examples
293    ///
294    /// ```
295    /// use nu_ansi_term::{Style, Color::Green};
296    ///
297    /// let style = Style::default().bold();
298    /// assert_eq!("\x1b[0m",
299    ///            style.suffix().to_string());
300    ///
301    /// let style = Green.normal().bold();
302    /// assert_eq!("\x1b[0m",
303    ///            style.suffix().to_string());
304    ///
305    /// let style = Style::default();
306    /// assert_eq!("",
307    ///            style.suffix().to_string());
308    /// ```
309    pub const fn suffix(self) -> Suffix {
310        Suffix(self)
311    }
312}
313
314impl Color {
315    /// The prefix bytes for this color as a `Style`. These are the bytes
316    /// that tell the terminal to use a different color or font style.
317    ///
318    /// See also [`Style::prefix`](struct.Style.html#method.prefix).
319    ///
320    /// # Examples
321    ///
322    /// ```
323    /// use nu_ansi_term::Color::Green;
324    ///
325    /// assert_eq!("\x1b[32m",
326    ///            Green.prefix().to_string());
327    /// ```
328    pub fn prefix(self) -> Prefix {
329        Prefix(self.normal())
330    }
331
332    /// The infix bytes between this color and `next` color. These are the bytes
333    /// that tell the terminal to use the `next` color, or to do nothing if
334    /// the two colors are equal.
335    ///
336    /// See also [`Style::infix`](struct.Style.html#method.infix).
337    ///
338    /// # Examples
339    ///
340    /// ```
341    /// use nu_ansi_term::Color::{Red, Yellow};
342    ///
343    /// assert_eq!("\x1b[33m",
344    ///            Red.infix(Yellow).to_string());
345    /// ```
346    pub fn infix(self, next: Color) -> Infix {
347        Infix(self.normal(), next.normal())
348    }
349
350    /// The suffix for this color as a `Style`. These are the bytes that
351    /// tell the terminal to reset back to its normal color and font style.
352    ///
353    /// See also [`Style::suffix`](struct.Style.html#method.suffix).
354    ///
355    /// # Examples
356    ///
357    /// ```
358    /// use nu_ansi_term::Color::Purple;
359    ///
360    /// assert_eq!("\x1b[0m",
361    ///            Purple.suffix().to_string());
362    /// ```
363    pub fn suffix(self) -> Suffix {
364        Suffix(self.normal())
365    }
366}
367
368impl fmt::Display for Prefix {
369    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
370        let f: &mut dyn fmt::Write = f;
371        self.0.write_prefix(f)
372    }
373}
374
375impl fmt::Display for Infix {
376    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
377        use crate::difference::Difference;
378
379        match Difference::between(&self.0, &self.1) {
380            Difference::ExtraStyles(style) => {
381                let f: &mut dyn fmt::Write = f;
382                style.write_prefix(f)
383            }
384            Difference::Reset => {
385                let f: &mut dyn fmt::Write = f;
386                write!(f, "{}{}", RESET, self.1.prefix())
387            }
388            Difference::Empty => {
389                Ok(()) // nothing to write
390            }
391        }
392    }
393}
394
395impl fmt::Display for Suffix {
396    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
397        let f: &mut dyn fmt::Write = f;
398        self.0.write_suffix(f)
399    }
400}
401
402#[cfg(test)]
403macro_rules! test {
404    ($name: ident: $style: expr; $input: expr => $result: expr) => {
405        #[test]
406        fn $name() {
407            assert_eq!($style.paint($input).to_string(), $result.to_string());
408
409            let mut v = Vec::new();
410            $style.paint($input.as_bytes()).write_to(&mut v).unwrap();
411            assert_eq!(v.as_slice(), $result.as_bytes());
412        }
413    };
414}
415
416#[cfg(test)]
417#[cfg(not(feature = "gnu_legacy"))]
418mod test {
419    use crate::style::Color::*;
420    use crate::style::Style;
421    use crate::Color;
422    use std::default::Default;
423
424    test!(plain:                 Style::default();                  "text/plain" => "text/plain");
425    test!(red:                   Red;                               "hi" => "\x1B[31mhi\x1B[0m");
426    test!(black:                 Black.normal();                    "hi" => "\x1B[30mhi\x1B[0m");
427    test!(yellow_bold:           Yellow.bold();                     "hi" => "\x1B[1;33mhi\x1B[0m");
428    test!(yellow_bold_2:         Yellow.normal().bold();            "hi" => "\x1B[1;33mhi\x1B[0m");
429    test!(blue_underline:        Blue.underline();                  "hi" => "\x1B[4;34mhi\x1B[0m");
430    test!(green_bold_ul:         Green.bold().underline();          "hi" => "\x1B[1;4;32mhi\x1B[0m");
431    test!(green_bold_ul_2:       Green.underline().bold();          "hi" => "\x1B[1;4;32mhi\x1B[0m");
432    test!(purple_on_white:       Purple.on(White);                  "hi" => "\x1B[47;35mhi\x1B[0m");
433    test!(purple_on_white_2:     Purple.normal().on(White);         "hi" => "\x1B[47;35mhi\x1B[0m");
434    test!(yellow_on_blue:        Style::new().on(Blue).fg(Yellow);  "hi" => "\x1B[44;33mhi\x1B[0m");
435    test!(magenta_on_white:      Magenta.on(White);                  "hi" => "\x1B[47;35mhi\x1B[0m");
436    test!(magenta_on_white_2:    Magenta.normal().on(White);         "hi" => "\x1B[47;35mhi\x1B[0m");
437    test!(yellow_on_blue_2:      Cyan.on(Blue).fg(Yellow);          "hi" => "\x1B[44;33mhi\x1B[0m");
438    test!(yellow_on_blue_reset:  Cyan.on(Blue).reset_before_style().fg(Yellow); "hi" => "\x1B[0m\x1B[44;33mhi\x1B[0m");
439    test!(yellow_on_blue_reset_2: Cyan.on(Blue).fg(Yellow).reset_before_style(); "hi" => "\x1B[0m\x1B[44;33mhi\x1B[0m");
440    test!(cyan_bold_on_white:    Cyan.bold().on(White);             "hi" => "\x1B[1;47;36mhi\x1B[0m");
441    test!(cyan_ul_on_white:      Cyan.underline().on(White);        "hi" => "\x1B[4;47;36mhi\x1B[0m");
442    test!(cyan_bold_ul_on_white: Cyan.bold().underline().on(White); "hi" => "\x1B[1;4;47;36mhi\x1B[0m");
443    test!(cyan_ul_bold_on_white: Cyan.underline().bold().on(White); "hi" => "\x1B[1;4;47;36mhi\x1B[0m");
444    test!(fixed:                 Fixed(100);                        "hi" => "\x1B[38;5;100mhi\x1B[0m");
445    test!(fixed_on_purple:       Fixed(100).on(Purple);             "hi" => "\x1B[45;38;5;100mhi\x1B[0m");
446    test!(fixed_on_fixed:        Fixed(100).on(Fixed(200));         "hi" => "\x1B[48;5;200;38;5;100mhi\x1B[0m");
447    test!(rgb:                   Rgb(70,130,180);                   "hi" => "\x1B[38;2;70;130;180mhi\x1B[0m");
448    test!(rgb_on_blue:           Rgb(70,130,180).on(Blue);          "hi" => "\x1B[44;38;2;70;130;180mhi\x1B[0m");
449    test!(blue_on_rgb:           Blue.on(Rgb(70,130,180));          "hi" => "\x1B[48;2;70;130;180;34mhi\x1B[0m");
450    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");
451    test!(bold:                  Style::new().bold();               "hi" => "\x1B[1mhi\x1B[0m");
452    test!(bold_with_reset:       Style::new().reset_before_style().bold(); "hi" => "\x1B[0m\x1B[1mhi\x1B[0m");
453    test!(bold_with_reset_2:     Style::new().bold().reset_before_style(); "hi" => "\x1B[0m\x1B[1mhi\x1B[0m");
454    test!(underline:             Style::new().underline();          "hi" => "\x1B[4mhi\x1B[0m");
455    test!(bunderline:            Style::new().bold().underline();   "hi" => "\x1B[1;4mhi\x1B[0m");
456    test!(dimmed:                Style::new().dimmed();             "hi" => "\x1B[2mhi\x1B[0m");
457    test!(italic:                Style::new().italic();             "hi" => "\x1B[3mhi\x1B[0m");
458    test!(blink:                 Style::new().blink();              "hi" => "\x1B[5mhi\x1B[0m");
459    test!(reverse:               Style::new().reverse();            "hi" => "\x1B[7mhi\x1B[0m");
460    test!(hidden:                Style::new().hidden();             "hi" => "\x1B[8mhi\x1B[0m");
461    test!(stricken:              Style::new().strikethrough();      "hi" => "\x1B[9mhi\x1B[0m");
462    test!(lr_on_lr:              LightRed.on(LightRed);             "hi" => "\x1B[101;91mhi\x1B[0m");
463
464    #[test]
465    fn test_infix() {
466        assert_eq!(
467            Style::new().dimmed().infix(Style::new()).to_string(),
468            "\x1B[0m"
469        );
470        assert_eq!(
471            White.dimmed().infix(White.normal()).to_string(),
472            "\x1B[0m\x1B[37m"
473        );
474        assert_eq!(White.normal().infix(White.bold()).to_string(), "\x1B[1m");
475        assert_eq!(White.normal().infix(Blue.normal()).to_string(), "\x1B[34m");
476        assert_eq!(Blue.bold().infix(Blue.bold()).to_string(), "");
477    }
478
479    #[test]
480    fn test_write_prefix_no_gnu_compat_order() {
481        let style = Style {
482            foreground: Some(Color::Red),
483            background: Some(Color::Blue),
484            ..Default::default()
485        };
486        assert_eq!(
487            style.paint("file").to_string(),
488            "\u{1b}[44;31mfile\u{1b}[0m".to_string()
489        );
490    }
491}
492
493#[cfg(test)]
494#[cfg(feature = "gnu_legacy")]
495mod gnu_legacy_test {
496    use crate::style::Color::*;
497    use crate::style::Style;
498    use crate::Color;
499    use std::default::Default;
500
501    test!(plain:                 Style::default();                  "text/plain" => "text/plain");
502    test!(red:                   Red;                               "hi" => "\x1B[31mhi\x1B[0m");
503    test!(black:                 Black.normal();                    "hi" => "\x1B[30mhi\x1B[0m");
504    test!(yellow_bold:           Yellow.bold();                     "hi" => "\x1B[01;33mhi\x1B[0m");
505    test!(yellow_bold_2:         Yellow.normal().bold();            "hi" => "\x1B[01;33mhi\x1B[0m");
506    test!(blue_underline:        Blue.underline();                  "hi" => "\x1B[04;34mhi\x1B[0m");
507    test!(green_bold_ul:         Green.bold().underline();          "hi" => "\x1B[01;04;32mhi\x1B[0m");
508    test!(green_bold_ul_2:       Green.underline().bold();          "hi" => "\x1B[01;04;32mhi\x1B[0m");
509    test!(purple_on_white:       Purple.on(White);                  "hi" => "\x1B[35;47mhi\x1B[0m");
510    test!(purple_on_white_2:     Purple.normal().on(White);         "hi" => "\x1B[35;47mhi\x1B[0m");
511    test!(yellow_on_blue:        Style::new().on(Blue).fg(Yellow);  "hi" => "\x1B[33;44mhi\x1B[0m");
512    test!(yellow_on_blue_reset_2: Cyan.on(Blue).fg(Yellow).reset_before_style(); "hi" => "\x1B[0m\x1B[33;44mhi\x1B[0m");
513    test!(magenta_on_white:      Magenta.on(White);                  "hi" => "\x1B[35;47mhi\x1B[0m");
514    test!(magenta_on_white_2:    Magenta.normal().on(White);         "hi" => "\x1B[35;47mhi\x1B[0m");
515    test!(yellow_on_blue_2:      Cyan.on(Blue).fg(Yellow);          "hi" => "\x1B[33;44mhi\x1B[0m");
516    test!(cyan_bold_on_white:    Cyan.bold().on(White);             "hi" => "\x1B[01;36;47mhi\x1B[0m");
517    test!(cyan_ul_on_white:      Cyan.underline().on(White);        "hi" => "\x1B[04;36;47mhi\x1B[0m");
518    test!(cyan_bold_ul_on_white: Cyan.bold().underline().on(White); "hi" => "\x1B[01;04;36;47mhi\x1B[0m");
519    test!(cyan_ul_bold_on_white: Cyan.underline().bold().on(White); "hi" => "\x1B[01;04;36;47mhi\x1B[0m");
520    test!(fixed:                 Fixed(100);                        "hi" => "\x1B[38;5;100mhi\x1B[0m");
521    test!(fixed_on_purple:       Fixed(100).on(Purple);             "hi" => "\x1B[38;5;100;45mhi\x1B[0m");
522    test!(fixed_on_fixed:        Fixed(100).on(Fixed(200));         "hi" => "\x1B[38;5;100;48;5;200mhi\x1B[0m");
523    test!(rgb:                   Rgb(70,130,180);                   "hi" => "\x1B[38;2;70;130;180mhi\x1B[0m");
524    test!(rgb_on_blue:           Rgb(70,130,180).on(Blue);          "hi" => "\x1B[38;2;70;130;180;44mhi\x1B[0m");
525    test!(blue_on_rgb:           Blue.on(Rgb(70,130,180));          "hi" => "\x1B[34;48;2;70;130;180mhi\x1B[0m");
526    test!(rgb_on_rgb:            Rgb(70,130,180).on(Rgb(5,10,15));  "hi" => "\x1B[38;2;70;130;180;48;2;5;10;15mhi\x1B[0m");
527    test!(bold:                  Style::new().bold();               "hi" => "\x1B[01mhi\x1B[0m");
528    test!(bold_with_reset:       Style::new().reset_before_style().bold(); "hi" => "\x1B[0m\x1B[01mhi\x1B[0m");
529    test!(bold_with_reset_2:     Style::new().bold().reset_before_style(); "hi" => "\x1B[0m\x1B[01mhi\x1B[0m");
530    test!(underline:             Style::new().underline();          "hi" => "\x1B[04mhi\x1B[0m");
531    test!(bunderline:            Style::new().bold().underline();   "hi" => "\x1B[01;04mhi\x1B[0m");
532    test!(dimmed:                Style::new().dimmed();             "hi" => "\x1B[02mhi\x1B[0m");
533    test!(italic:                Style::new().italic();             "hi" => "\x1B[03mhi\x1B[0m");
534    test!(blink:                 Style::new().blink();              "hi" => "\x1B[05mhi\x1B[0m");
535    test!(reverse:               Style::new().reverse();            "hi" => "\x1B[07mhi\x1B[0m");
536    test!(hidden:                Style::new().hidden();             "hi" => "\x1B[08mhi\x1B[0m");
537    test!(stricken:              Style::new().strikethrough();      "hi" => "\x1B[09mhi\x1B[0m");
538    test!(lr_on_lr:              LightRed.on(LightRed);             "hi" => "\x1B[91;101mhi\x1B[0m");
539
540    #[test]
541    fn test_write_prefix_gnu_compat_order() {
542        let style = Style {
543            foreground: Some(Color::Red),
544            background: Some(Color::Blue),
545            ..Default::default()
546        };
547        assert_eq!(
548            style.paint("file").to_string(),
549            "\u{1b}[31;44mfile\u{1b}[0m".to_string()
550        );
551    }
552}