crossterm/
style.rs

1//! # Style
2//!
3//! The `style` module provides a functionality to apply attributes and colors on your text.
4//!
5//! This documentation does not contain a lot of examples. The reason is that it's fairly
6//! obvious how to use this crate. Although, we do provide
7//! [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples) repository
8//! to demonstrate the capabilities.
9//!
10//! ## Platform-specific Notes
11//!
12//! Not all features are supported on all terminals/platforms. You should always consult
13//! platform-specific notes of the following types:
14//!
15//! * [Color](enum.Color.html#platform-specific-notes)
16//! * [Attribute](enum.Attribute.html#platform-specific-notes)
17//!
18//! ## Examples
19//!
20//! A few examples of how to use the style module.
21//!
22//! ### Colors
23//!
24//! How to change the terminal text color.
25//!
26//! Command API:
27//!
28//! Using the Command API to color text.
29//!
30//! ```no_run
31//! use std::io::{stdout, Write};
32//!
33//! use crossterm::{execute, Result};
34//! use crossterm::style::{Print, SetForegroundColor, SetBackgroundColor, ResetColor, Color, Attribute};
35//!
36//! fn main() -> Result<()> {
37//!     execute!(
38//!         stdout(),
39//!         // Blue foreground
40//!         SetForegroundColor(Color::Blue),
41//!         // Red background
42//!         SetBackgroundColor(Color::Red),
43//!         // Print text
44//!         Print("Blue text on Red.".to_string()),
45//!         // Reset to default colors
46//!         ResetColor
47//!     )
48//! }
49//! ```
50//!
51//! Functions:
52//!
53//! Using functions from [`Stylize`](crate::style::Stylize) on a `String` or `&'static str` to color
54//! it.
55//!
56//! ```no_run
57//! use crossterm::style::Stylize;
58//!
59//! println!("{}", "Red foreground color & blue background.".red().on_blue());
60//! ```
61//!
62//! ### Attributes
63//!
64//! How to apply terminal attributes to text.
65//!
66//! Command API:
67//!
68//! Using the Command API to set attributes.
69//!
70//! ```no_run
71//! use std::io::{stdout, Write};
72//!
73//! use crossterm::{execute, Result, style::Print};
74//! use crossterm::style::{SetAttribute, Attribute};
75//!
76//! fn main() -> Result<()> {
77//!     execute!(
78//!         stdout(),
79//!         // Set to bold
80//!         SetAttribute(Attribute::Bold),
81//!         Print("Bold text here.".to_string()),
82//!         // Reset all attributes
83//!         SetAttribute(Attribute::Reset)
84//!     )
85//! }
86//! ```
87//!
88//! Functions:
89//!
90//! Using [`Stylize`](crate::style::Stylize) functions on a `String` or `&'static str` to set
91//! attributes to it.
92//!
93//! ```no_run
94//! use crossterm::style::Stylize;
95//!
96//! println!("{}", "Bold".bold());
97//! println!("{}", "Underlined".underlined());
98//! println!("{}", "Negative".negative());
99//! ```
100//!
101//! Displayable:
102//!
103//! [`Attribute`](enum.Attribute.html) implements [Display](https://doc.rust-lang.org/beta/std/fmt/trait.Display.html) and therefore it can be formatted like:
104//!
105//! ```no_run
106//! use crossterm::style::Attribute;
107//!
108//! println!(
109//!     "{} Underlined {} No Underline",
110//!     Attribute::Underlined,
111//!     Attribute::NoUnderline
112//! );
113//! ```
114
115use std::{
116    env,
117    fmt::{self, Display},
118};
119
120use crate::command::execute_fmt;
121#[cfg(windows)]
122use crate::Result;
123use crate::{csi, impl_display, Command};
124
125pub use self::{
126    attributes::Attributes,
127    content_style::ContentStyle,
128    styled_content::StyledContent,
129    stylize::Stylize,
130    types::{Attribute, Color, Colored, Colors},
131};
132
133mod attributes;
134mod content_style;
135mod styled_content;
136mod stylize;
137mod sys;
138mod types;
139
140/// Creates a `StyledContent`.
141///
142/// This could be used to style any type that implements `Display` with colors and text attributes.
143///
144/// See [`StyledContent`](struct.StyledContent.html) for more info.
145///
146/// # Examples
147///
148/// ```no_run
149/// use crossterm::style::{style, Stylize, Color};
150///
151/// let styled_content = style("Blue colored text on yellow background")
152///     .with(Color::Blue)
153///     .on(Color::Yellow);
154///
155/// println!("{}", styled_content);
156/// ```
157pub fn style<D: Display>(val: D) -> StyledContent<D> {
158    ContentStyle::new().apply(val)
159}
160
161/// Returns available color count.
162///
163/// # Notes
164///
165/// This does not always provide a good result.
166pub fn available_color_count() -> u16 {
167    env::var("TERM")
168        .map(|x| if x.contains("256color") { 256 } else { 8 })
169        .unwrap_or(8)
170}
171
172/// A command that sets the the foreground color.
173///
174/// See [`Color`](enum.Color.html) for more info.
175///
176/// [`SetColors`](struct.SetColors.html) can also be used to set both the foreground and background
177/// color in one command.
178///
179/// # Notes
180///
181/// Commands must be executed/queued for execution otherwise they do nothing.
182#[derive(Debug, Clone, Copy, PartialEq, Eq)]
183pub struct SetForegroundColor(pub Color);
184
185impl Command for SetForegroundColor {
186    fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
187        write!(f, csi!("{}m"), Colored::ForegroundColor(self.0))
188    }
189
190    #[cfg(windows)]
191    fn execute_winapi(&self) -> Result<()> {
192        sys::windows::set_foreground_color(self.0)
193    }
194}
195
196/// A command that sets the the background color.
197///
198/// See [`Color`](enum.Color.html) for more info.
199///
200/// [`SetColors`](struct.SetColors.html) can also be used to set both the foreground and background
201/// color with one command.
202///
203/// # Notes
204///
205/// Commands must be executed/queued for execution otherwise they do nothing.
206#[derive(Debug, Clone, Copy, PartialEq, Eq)]
207pub struct SetBackgroundColor(pub Color);
208
209impl Command for SetBackgroundColor {
210    fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
211        write!(f, csi!("{}m"), Colored::BackgroundColor(self.0))
212    }
213
214    #[cfg(windows)]
215    fn execute_winapi(&self) -> Result<()> {
216        sys::windows::set_background_color(self.0)
217    }
218}
219
220/// A command that sets the the underline color.
221///
222/// See [`Color`](enum.Color.html) for more info.
223///
224/// [`SetColors`](struct.SetColors.html) can also be used to set both the foreground and background
225/// color with one command.
226///
227/// # Notes
228///
229/// Commands must be executed/queued for execution otherwise they do nothing.
230#[derive(Debug, Clone, Copy, PartialEq, Eq)]
231pub struct SetUnderlineColor(pub Color);
232
233impl Command for SetUnderlineColor {
234    fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
235        write!(f, csi!("{}m"), Colored::UnderlineColor(self.0))
236    }
237
238    #[cfg(windows)]
239    fn execute_winapi(&self) -> Result<()> {
240        Err(std::io::Error::new(
241            std::io::ErrorKind::Other,
242            "SetUnderlineColor not supported by winapi.",
243        ))
244    }
245}
246
247/// A command that optionally sets the foreground and/or background color.
248///
249/// For example:
250/// ```no_run
251/// use std::io::{stdout, Write};
252///
253/// use crossterm::execute;
254/// use crossterm::style::{Color::{Green, Black}, Colors, Print, SetColors};
255///
256/// execute!(
257///     stdout(),
258///     SetColors(Colors::new(Green, Black)),
259///     Print("Hello, world!".to_string()),
260/// ).unwrap();
261/// ```
262///
263/// See [`Colors`](struct.Colors.html) for more info.
264///
265/// # Notes
266///
267/// Commands must be executed/queued for execution otherwise they do nothing.
268#[derive(Debug, Clone, Copy, PartialEq, Eq)]
269pub struct SetColors(pub Colors);
270
271impl Command for SetColors {
272    fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
273        if let Some(color) = self.0.foreground {
274            SetForegroundColor(color).write_ansi(f)?;
275        }
276        if let Some(color) = self.0.background {
277            SetBackgroundColor(color).write_ansi(f)?;
278        }
279        Ok(())
280    }
281
282    #[cfg(windows)]
283    fn execute_winapi(&self) -> Result<()> {
284        if let Some(color) = self.0.foreground {
285            sys::windows::set_foreground_color(color)?;
286        }
287        if let Some(color) = self.0.background {
288            sys::windows::set_background_color(color)?;
289        }
290        Ok(())
291    }
292}
293
294/// A command that sets an attribute.
295///
296/// See [`Attribute`](enum.Attribute.html) for more info.
297///
298/// # Notes
299///
300/// Commands must be executed/queued for execution otherwise they do nothing.
301#[derive(Debug, Clone, Copy, PartialEq, Eq)]
302pub struct SetAttribute(pub Attribute);
303
304impl Command for SetAttribute {
305    fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
306        write!(f, csi!("{}m"), self.0.sgr())
307    }
308
309    #[cfg(windows)]
310    fn execute_winapi(&self) -> Result<()> {
311        // attributes are not supported by WinAPI.
312        Ok(())
313    }
314}
315
316/// A command that sets several attributes.
317///
318/// See [`Attributes`](struct.Attributes.html) for more info.
319///
320/// # Notes
321///
322/// Commands must be executed/queued for execution otherwise they do nothing.
323#[derive(Debug, Clone, Copy, PartialEq, Eq)]
324pub struct SetAttributes(pub Attributes);
325
326impl Command for SetAttributes {
327    fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
328        for attr in Attribute::iterator() {
329            if self.0.has(attr) {
330                SetAttribute(attr).write_ansi(f)?;
331            }
332        }
333        Ok(())
334    }
335
336    #[cfg(windows)]
337    fn execute_winapi(&self) -> Result<()> {
338        // attributes are not supported by WinAPI.
339        Ok(())
340    }
341}
342
343/// A command that sets a style (colors and attributes).
344///
345/// # Notes
346///
347/// Commands must be executed/queued for execution otherwise they do nothing.
348#[derive(Debug, Clone, Copy, PartialEq, Eq)]
349pub struct SetStyle(pub ContentStyle);
350
351impl Command for SetStyle {
352    fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
353        if let Some(bg) = self.0.background_color {
354            execute_fmt(f, SetBackgroundColor(bg)).map_err(|_| fmt::Error)?;
355        }
356        if let Some(fg) = self.0.foreground_color {
357            execute_fmt(f, SetForegroundColor(fg)).map_err(|_| fmt::Error)?;
358        }
359        if let Some(ul) = self.0.underline_color {
360            execute_fmt(f, SetUnderlineColor(ul)).map_err(|_| fmt::Error)?;
361        }
362        if !self.0.attributes.is_empty() {
363            execute_fmt(f, SetAttributes(self.0.attributes)).map_err(|_| fmt::Error)?;
364        }
365
366        Ok(())
367    }
368
369    #[cfg(windows)]
370    fn execute_winapi(&self) -> Result<()> {
371        panic!("tried to execute SetStyle command using WinAPI, use ANSI instead");
372    }
373
374    #[cfg(windows)]
375    fn is_ansi_code_supported(&self) -> bool {
376        true
377    }
378}
379
380/// A command that prints styled content.
381///
382/// See [`StyledContent`](struct.StyledContent.html) for more info.
383///
384/// # Notes
385///
386/// Commands must be executed/queued for execution otherwise they do nothing.
387#[derive(Debug, Copy, Clone)]
388pub struct PrintStyledContent<D: Display>(pub StyledContent<D>);
389
390impl<D: Display> Command for PrintStyledContent<D> {
391    fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
392        let style = self.0.style();
393
394        let mut reset_background = false;
395        let mut reset_foreground = false;
396        let mut reset = false;
397
398        if let Some(bg) = style.background_color {
399            execute_fmt(f, SetBackgroundColor(bg)).map_err(|_| fmt::Error)?;
400            reset_background = true;
401        }
402        if let Some(fg) = style.foreground_color {
403            execute_fmt(f, SetForegroundColor(fg)).map_err(|_| fmt::Error)?;
404            reset_foreground = true;
405        }
406        if let Some(ul) = style.underline_color {
407            execute_fmt(f, SetUnderlineColor(ul)).map_err(|_| fmt::Error)?;
408            reset_foreground = true;
409        }
410
411        if !style.attributes.is_empty() {
412            execute_fmt(f, SetAttributes(style.attributes)).map_err(|_| fmt::Error)?;
413            reset = true;
414        }
415
416        write!(f, "{}", self.0.content())?;
417
418        if reset {
419            // NOTE: This will reset colors even though self has no colors, hence produce unexpected
420            // resets.
421            // TODO: reset the set attributes only.
422            execute_fmt(f, ResetColor).map_err(|_| fmt::Error)?;
423        } else {
424            // NOTE: Since the above bug, we do not need to reset colors when we reset attributes.
425            if reset_background {
426                execute_fmt(f, SetBackgroundColor(Color::Reset)).map_err(|_| fmt::Error)?;
427            }
428            if reset_foreground {
429                execute_fmt(f, SetForegroundColor(Color::Reset)).map_err(|_| fmt::Error)?;
430            }
431        }
432
433        Ok(())
434    }
435
436    #[cfg(windows)]
437    fn execute_winapi(&self) -> Result<()> {
438        Ok(())
439    }
440}
441
442/// A command that resets the colors back to default.
443///
444/// # Notes
445///
446/// Commands must be executed/queued for execution otherwise they do nothing.
447#[derive(Debug, Clone, Copy, PartialEq, Eq)]
448pub struct ResetColor;
449
450impl Command for ResetColor {
451    fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
452        f.write_str(csi!("0m"))
453    }
454
455    #[cfg(windows)]
456    fn execute_winapi(&self) -> Result<()> {
457        sys::windows::reset()
458    }
459}
460
461/// A command that prints the given displayable type.
462///
463/// Commands must be executed/queued for execution otherwise they do nothing.
464#[derive(Debug, Clone, Copy, PartialEq, Eq)]
465pub struct Print<T: Display>(pub T);
466
467impl<T: Display> Command for Print<T> {
468    fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
469        write!(f, "{}", self.0)
470    }
471
472    #[cfg(windows)]
473    fn execute_winapi(&self) -> Result<()> {
474        panic!("tried to execute Print command using WinAPI, use ANSI instead");
475    }
476
477    #[cfg(windows)]
478    fn is_ansi_code_supported(&self) -> bool {
479        true
480    }
481}
482
483impl<T: Display> Display for Print<T> {
484    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
485        self.0.fmt(f)
486    }
487}
488
489impl_display!(for SetForegroundColor);
490impl_display!(for SetBackgroundColor);
491impl_display!(for SetColors);
492impl_display!(for SetAttribute);
493impl_display!(for PrintStyledContent<String>);
494impl_display!(for PrintStyledContent<&'static str>);
495impl_display!(for ResetColor);
496
497/// Utility function for ANSI parsing in Color and Colored.
498/// Gets the next element of `iter` and tries to parse it as a `u8`.
499fn parse_next_u8<'a>(iter: &mut impl Iterator<Item = &'a str>) -> Option<u8> {
500    iter.next().and_then(|s| s.parse().ok())
501}