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 #[cfg(windows)]
165 {
166 // Check if we're running in a pseudo TTY, which supports true color.
167 // Fall back to env vars otherwise for other terminals on Windows.
168 if crate::ansi_support::supports_ansi() {
169 return u16::MAX;
170 }
171 }
172
173 const DEFAULT: u16 = 8;
174 env::var("COLORTERM")
175 .or_else(|_| env::var("TERM"))
176 .map_or(DEFAULT, |x| match x {
177 _ if x.contains("24bit") || x.contains("truecolor") => u16::MAX,
178 _ if x.contains("256") => 256,
179 _ => DEFAULT,
180 })
181}
182
183/// Forces colored output on or off globally, overriding NO_COLOR.
184///
185/// # Notes
186///
187/// crossterm supports NO_COLOR (https://no-color.org/) to disabled colored output.
188///
189/// This API allows applications to override that behavior and force colorized output
190/// even if NO_COLOR is set.
191pub fn force_color_output(enabled: bool) {
192 Colored::set_ansi_color_disabled(!enabled)
193}
194
195/// A command that sets the the foreground color.
196///
197/// See [`Color`](enum.Color.html) for more info.
198///
199/// [`SetColors`](struct.SetColors.html) can also be used to set both the foreground and background
200/// color in one command.
201///
202/// # Notes
203///
204/// Commands must be executed/queued for execution otherwise they do nothing.
205#[derive(Debug, Clone, Copy, PartialEq, Eq)]
206pub struct SetForegroundColor(pub Color);
207
208impl Command for SetForegroundColor {
209 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
210 write!(f, csi!("{}m"), Colored::ForegroundColor(self.0))
211 }
212
213 #[cfg(windows)]
214 fn execute_winapi(&self) -> std::io::Result<()> {
215 sys::windows::set_foreground_color(self.0)
216 }
217}
218
219/// A command that sets the the background color.
220///
221/// See [`Color`](enum.Color.html) for more info.
222///
223/// [`SetColors`](struct.SetColors.html) can also be used to set both the foreground and background
224/// color with one command.
225///
226/// # Notes
227///
228/// Commands must be executed/queued for execution otherwise they do nothing.
229#[derive(Debug, Clone, Copy, PartialEq, Eq)]
230pub struct SetBackgroundColor(pub Color);
231
232impl Command for SetBackgroundColor {
233 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
234 write!(f, csi!("{}m"), Colored::BackgroundColor(self.0))
235 }
236
237 #[cfg(windows)]
238 fn execute_winapi(&self) -> std::io::Result<()> {
239 sys::windows::set_background_color(self.0)
240 }
241}
242
243/// A command that sets the the underline color.
244///
245/// See [`Color`](enum.Color.html) for more info.
246///
247/// [`SetColors`](struct.SetColors.html) can also be used to set both the foreground and background
248/// color with one command.
249///
250/// # Notes
251///
252/// Commands must be executed/queued for execution otherwise they do nothing.
253#[derive(Debug, Clone, Copy, PartialEq, Eq)]
254pub struct SetUnderlineColor(pub Color);
255
256impl Command for SetUnderlineColor {
257 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
258 write!(f, csi!("{}m"), Colored::UnderlineColor(self.0))
259 }
260
261 #[cfg(windows)]
262 fn execute_winapi(&self) -> std::io::Result<()> {
263 Err(std::io::Error::new(
264 std::io::ErrorKind::Other,
265 "SetUnderlineColor not supported by winapi.",
266 ))
267 }
268}
269
270/// A command that optionally sets the foreground and/or background color.
271///
272/// For example:
273/// ```no_run
274/// use std::io::{stdout, Write};
275///
276/// use crossterm::execute;
277/// use crossterm::style::{Color::{Green, Black}, Colors, Print, SetColors};
278///
279/// execute!(
280/// stdout(),
281/// SetColors(Colors::new(Green, Black)),
282/// Print("Hello, world!".to_string()),
283/// ).unwrap();
284/// ```
285///
286/// See [`Colors`](struct.Colors.html) for more info.
287///
288/// # Notes
289///
290/// Commands must be executed/queued for execution otherwise they do nothing.
291#[derive(Debug, Clone, Copy, PartialEq, Eq)]
292pub struct SetColors(pub Colors);
293
294impl Command for SetColors {
295 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
296 // Writing both foreground and background colors in one command resulted in about 20% more
297 // FPS (20 to 24 fps) on a fullscreen (171x51) app that writes every cell with a different
298 // foreground and background color, compared to separately using the SetForegroundColor and
299 // SetBackgroundColor commands (iTerm2, M2 Macbook Pro). `Esc[38;5;<fg>mEsc[48;5;<bg>m` (16
300 // chars) vs `Esc[38;5;<fg>;48;5;<bg>m` (14 chars)
301 match (self.0.foreground, self.0.background) {
302 (Some(fg), Some(bg)) => {
303 write!(
304 f,
305 csi!("{};{}m"),
306 Colored::ForegroundColor(fg),
307 Colored::BackgroundColor(bg)
308 )
309 }
310 (Some(fg), None) => write!(f, csi!("{}m"), Colored::ForegroundColor(fg)),
311 (None, Some(bg)) => write!(f, csi!("{}m"), Colored::BackgroundColor(bg)),
312 (None, None) => Ok(()),
313 }
314 }
315
316 #[cfg(windows)]
317 fn execute_winapi(&self) -> std::io::Result<()> {
318 if let Some(color) = self.0.foreground {
319 sys::windows::set_foreground_color(color)?;
320 }
321 if let Some(color) = self.0.background {
322 sys::windows::set_background_color(color)?;
323 }
324 Ok(())
325 }
326}
327
328/// A command that sets an attribute.
329///
330/// See [`Attribute`](enum.Attribute.html) for more info.
331///
332/// # Notes
333///
334/// Commands must be executed/queued for execution otherwise they do nothing.
335#[derive(Debug, Clone, Copy, PartialEq, Eq)]
336pub struct SetAttribute(pub Attribute);
337
338impl Command for SetAttribute {
339 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
340 write!(f, csi!("{}m"), self.0.sgr())
341 }
342
343 #[cfg(windows)]
344 fn execute_winapi(&self) -> std::io::Result<()> {
345 // attributes are not supported by WinAPI.
346 Ok(())
347 }
348}
349
350/// A command that sets several attributes.
351///
352/// See [`Attributes`](struct.Attributes.html) for more info.
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 SetAttributes(pub Attributes);
359
360impl Command for SetAttributes {
361 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
362 for attr in Attribute::iterator() {
363 if self.0.has(attr) {
364 SetAttribute(attr).write_ansi(f)?;
365 }
366 }
367 Ok(())
368 }
369
370 #[cfg(windows)]
371 fn execute_winapi(&self) -> std::io::Result<()> {
372 // attributes are not supported by WinAPI.
373 Ok(())
374 }
375}
376
377/// A command that sets a style (colors and attributes).
378///
379/// # Notes
380///
381/// Commands must be executed/queued for execution otherwise they do nothing.
382#[derive(Debug, Clone, Copy, PartialEq, Eq)]
383pub struct SetStyle(pub ContentStyle);
384
385impl Command for SetStyle {
386 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
387 if let Some(bg) = self.0.background_color {
388 execute_fmt(f, SetBackgroundColor(bg)).map_err(|_| fmt::Error)?;
389 }
390 if let Some(fg) = self.0.foreground_color {
391 execute_fmt(f, SetForegroundColor(fg)).map_err(|_| fmt::Error)?;
392 }
393 if let Some(ul) = self.0.underline_color {
394 execute_fmt(f, SetUnderlineColor(ul)).map_err(|_| fmt::Error)?;
395 }
396 if !self.0.attributes.is_empty() {
397 execute_fmt(f, SetAttributes(self.0.attributes)).map_err(|_| fmt::Error)?;
398 }
399
400 Ok(())
401 }
402
403 #[cfg(windows)]
404 fn execute_winapi(&self) -> std::io::Result<()> {
405 panic!("tried to execute SetStyle command using WinAPI, use ANSI instead");
406 }
407
408 #[cfg(windows)]
409 fn is_ansi_code_supported(&self) -> bool {
410 true
411 }
412}
413
414/// A command that prints styled content.
415///
416/// See [`StyledContent`](struct.StyledContent.html) for more info.
417///
418/// # Notes
419///
420/// Commands must be executed/queued for execution otherwise they do nothing.
421#[derive(Debug, Copy, Clone)]
422pub struct PrintStyledContent<D: Display>(pub StyledContent<D>);
423
424impl<D: Display> Command for PrintStyledContent<D> {
425 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
426 let style = self.0.style();
427
428 let mut reset_background = false;
429 let mut reset_foreground = false;
430 let mut reset = false;
431
432 if let Some(bg) = style.background_color {
433 execute_fmt(f, SetBackgroundColor(bg)).map_err(|_| fmt::Error)?;
434 reset_background = true;
435 }
436 if let Some(fg) = style.foreground_color {
437 execute_fmt(f, SetForegroundColor(fg)).map_err(|_| fmt::Error)?;
438 reset_foreground = true;
439 }
440 if let Some(ul) = style.underline_color {
441 execute_fmt(f, SetUnderlineColor(ul)).map_err(|_| fmt::Error)?;
442 reset_foreground = true;
443 }
444
445 if !style.attributes.is_empty() {
446 execute_fmt(f, SetAttributes(style.attributes)).map_err(|_| fmt::Error)?;
447 reset = true;
448 }
449
450 write!(f, "{}", self.0.content())?;
451
452 if reset {
453 // NOTE: This will reset colors even though self has no colors, hence produce unexpected
454 // resets.
455 // TODO: reset the set attributes only.
456 execute_fmt(f, ResetColor).map_err(|_| fmt::Error)?;
457 } else {
458 // NOTE: Since the above bug, we do not need to reset colors when we reset attributes.
459 if reset_background {
460 execute_fmt(f, SetBackgroundColor(Color::Reset)).map_err(|_| fmt::Error)?;
461 }
462 if reset_foreground {
463 execute_fmt(f, SetForegroundColor(Color::Reset)).map_err(|_| fmt::Error)?;
464 }
465 }
466
467 Ok(())
468 }
469
470 #[cfg(windows)]
471 fn execute_winapi(&self) -> std::io::Result<()> {
472 Ok(())
473 }
474}
475
476/// A command that resets the colors back to default.
477///
478/// # Notes
479///
480/// Commands must be executed/queued for execution otherwise they do nothing.
481#[derive(Debug, Clone, Copy, PartialEq, Eq)]
482pub struct ResetColor;
483
484impl Command for ResetColor {
485 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
486 f.write_str(csi!("0m"))
487 }
488
489 #[cfg(windows)]
490 fn execute_winapi(&self) -> std::io::Result<()> {
491 sys::windows::reset()
492 }
493}
494
495/// A command that prints the given displayable type.
496///
497/// Commands must be executed/queued for execution otherwise they do nothing.
498#[derive(Debug, Clone, Copy, PartialEq, Eq)]
499pub struct Print<T: Display>(pub T);
500
501impl<T: Display> Command for Print<T> {
502 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
503 write!(f, "{}", self.0)
504 }
505
506 #[cfg(windows)]
507 fn execute_winapi(&self) -> std::io::Result<()> {
508 panic!("tried to execute Print command using WinAPI, use ANSI instead");
509 }
510
511 #[cfg(windows)]
512 fn is_ansi_code_supported(&self) -> bool {
513 true
514 }
515}
516
517impl<T: Display> Display for Print<T> {
518 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
519 self.0.fmt(f)
520 }
521}
522
523impl_display!(for SetForegroundColor);
524impl_display!(for SetBackgroundColor);
525impl_display!(for SetColors);
526impl_display!(for SetAttribute);
527impl_display!(for PrintStyledContent<String>);
528impl_display!(for PrintStyledContent<&'static str>);
529impl_display!(for ResetColor);
530
531/// Utility function for ANSI parsing in Color and Colored.
532/// Gets the next element of `iter` and tries to parse it as a `u8`.
533fn parse_next_u8<'a>(iter: &mut impl Iterator<Item = &'a str>) -> Option<u8> {
534 iter.next().and_then(|s| s.parse().ok())
535}
536
537#[cfg(test)]
538mod tests {
539 use super::*;
540
541 // On Windows many env var tests will fail so we need to conditionally check for ANSI support.
542 // This allows other terminals on Windows to still assert env var support.
543 macro_rules! skip_windows_ansi_supported {
544 () => {
545 #[cfg(windows)]
546 {
547 if crate::ansi_support::supports_ansi() {
548 return;
549 }
550 }
551 };
552 }
553
554 #[cfg_attr(windows, test)]
555 #[cfg(windows)]
556 fn windows_always_truecolor() {
557 // This should always be true on supported Windows 10+,
558 // but downlevel Windows clients and other terminals may fail `cargo test` otherwise.
559 if crate::ansi_support::supports_ansi() {
560 assert_eq!(u16::MAX, available_color_count());
561 };
562 }
563
564 #[test]
565 fn colorterm_overrides_term() {
566 skip_windows_ansi_supported!();
567 temp_env::with_vars(
568 [
569 ("COLORTERM", Some("truecolor")),
570 ("TERM", Some("xterm-256color")),
571 ],
572 || {
573 assert_eq!(u16::MAX, available_color_count());
574 },
575 );
576 }
577
578 #[test]
579 fn term_24bits() {
580 skip_windows_ansi_supported!();
581 temp_env::with_vars(
582 [("COLORTERM", None), ("TERM", Some("xterm-24bits"))],
583 || {
584 assert_eq!(u16::MAX, available_color_count());
585 },
586 );
587 }
588
589 #[test]
590 fn term_256color() {
591 skip_windows_ansi_supported!();
592 temp_env::with_vars(
593 [("COLORTERM", None), ("TERM", Some("xterm-256color"))],
594 || {
595 assert_eq!(256u16, available_color_count());
596 },
597 );
598 }
599
600 #[test]
601 fn default_color_count() {
602 skip_windows_ansi_supported!();
603 temp_env::with_vars([("COLORTERM", None::<&str>), ("TERM", None)], || {
604 assert_eq!(8, available_color_count());
605 });
606 }
607
608 #[test]
609 fn unsupported_term_colorterm_values() {
610 skip_windows_ansi_supported!();
611 temp_env::with_vars(
612 [
613 ("COLORTERM", Some("gibberish")),
614 ("TERM", Some("gibberish")),
615 ],
616 || {
617 assert_eq!(8u16, available_color_count());
618 },
619 );
620 }
621}