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