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. |
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. |
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 { |
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 | |
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 | /// ``` |
166 | 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 | /// ``` |
191 | 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 | /// ``` |
215 | 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 | /// ``` |
236 | 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 | /// ``` |
254 | 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 | /// ``` |
271 | pub fn suffix(self) -> Suffix { |
272 | Suffix(self.normal()) |
273 | } |
274 | } |
275 | |
276 | |
277 | impl fmt::Display for Prefix { |
278 | 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 { |
286 | 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: 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 { |
307 | 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 ] |
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 | |