| use is_terminal::IsTerminal; |
| use owo_colors::Style; |
| |
| /** |
| Theme used by [`GraphicalReportHandler`](crate::GraphicalReportHandler) to |
| render fancy [`Diagnostic`](crate::Diagnostic) reports. |
| |
| A theme consists of two things: the set of characters to be used for drawing, |
| and the |
| [`owo_colors::Style`](https://docs.rs/owo-colors/latest/owo_colors/struct.Style.html)s to be used to paint various items. |
| |
| You can create your own custom graphical theme using this type, or you can use |
| one of the predefined ones using the methods below. |
| */ |
| #[derive(Debug, Clone)] |
| pub struct GraphicalTheme { |
| /// Characters to be used for drawing. |
| pub characters: ThemeCharacters, |
| /// Styles to be used for painting. |
| pub styles: ThemeStyles, |
| } |
| |
| impl GraphicalTheme { |
| /// ASCII-art-based graphical drawing, with ANSI styling. |
| pub fn ascii() -> Self { |
| Self { |
| characters: ThemeCharacters::ascii(), |
| styles: ThemeStyles::ansi(), |
| } |
| } |
| |
| /// Graphical theme that draws using both ansi colors and unicode |
| /// characters. |
| /// |
| /// Note that full rgb colors aren't enabled by default because they're |
| /// an accessibility hazard, especially in the context of terminal themes |
| /// that can change the background color and make hardcoded colors illegible. |
| /// Such themes typically remap ansi codes properly, treating them more |
| /// like CSS classes than specific colors. |
| pub fn unicode() -> Self { |
| Self { |
| characters: ThemeCharacters::unicode(), |
| styles: ThemeStyles::ansi(), |
| } |
| } |
| |
| /// Graphical theme that draws in monochrome, while still using unicode |
| /// characters. |
| pub fn unicode_nocolor() -> Self { |
| Self { |
| characters: ThemeCharacters::unicode(), |
| styles: ThemeStyles::none(), |
| } |
| } |
| |
| /// A "basic" graphical theme that skips colors and unicode characters and |
| /// just does monochrome ascii art. If you want a completely non-graphical |
| /// rendering of your `Diagnostic`s, check out |
| /// [crate::NarratableReportHandler], or write your own |
| /// [crate::ReportHandler]! |
| pub fn none() -> Self { |
| Self { |
| characters: ThemeCharacters::ascii(), |
| styles: ThemeStyles::none(), |
| } |
| } |
| } |
| |
| impl Default for GraphicalTheme { |
| fn default() -> Self { |
| match std::env::var("NO_COLOR") { |
| _ if !std::io::stdout().is_terminal() || !std::io::stderr().is_terminal() => { |
| Self::ascii() |
| } |
| Ok(string) if string != "0" => Self::unicode_nocolor(), |
| _ => Self::unicode(), |
| } |
| } |
| } |
| |
| /** |
| Styles for various parts of graphical rendering for the [crate::GraphicalReportHandler]. |
| */ |
| #[derive(Debug, Clone)] |
| pub struct ThemeStyles { |
| /// Style to apply to things highlighted as "error". |
| pub error: Style, |
| /// Style to apply to things highlighted as "warning". |
| pub warning: Style, |
| /// Style to apply to things highlighted as "advice". |
| pub advice: Style, |
| /// Style to apply to the help text. |
| pub help: Style, |
| /// Style to apply to filenames/links/URLs. |
| pub link: Style, |
| /// Style to apply to line numbers. |
| pub linum: Style, |
| /// Styles to cycle through (using `.iter().cycle()`), to render the lines |
| /// and text for diagnostic highlights. |
| pub highlights: Vec<Style>, |
| } |
| |
| fn style() -> Style { |
| Style::new() |
| } |
| |
| impl ThemeStyles { |
| /// Nice RGB colors. |
| /// [Credit](http://terminal.sexy/#FRUV0NDQFRUVrEFCkKlZ9L91ap-1qnWfdbWq0NDQUFBQrEFCkKlZ9L91ap-1qnWfdbWq9fX1). |
| pub fn rgb() -> Self { |
| Self { |
| error: style().fg_rgb::<255, 30, 30>(), |
| warning: style().fg_rgb::<244, 191, 117>(), |
| advice: style().fg_rgb::<106, 159, 181>(), |
| help: style().fg_rgb::<106, 159, 181>(), |
| link: style().fg_rgb::<92, 157, 255>().underline().bold(), |
| linum: style().dimmed(), |
| highlights: vec![ |
| style().fg_rgb::<246, 87, 248>(), |
| style().fg_rgb::<30, 201, 212>(), |
| style().fg_rgb::<145, 246, 111>(), |
| ], |
| } |
| } |
| |
| /// ANSI color-based styles. |
| pub fn ansi() -> Self { |
| Self { |
| error: style().red(), |
| warning: style().yellow(), |
| advice: style().cyan(), |
| help: style().cyan(), |
| link: style().cyan().underline().bold(), |
| linum: style().dimmed(), |
| highlights: vec![ |
| style().magenta().bold(), |
| style().yellow().bold(), |
| style().green().bold(), |
| ], |
| } |
| } |
| |
| /// No styling. Just regular ol' monochrome. |
| pub fn none() -> Self { |
| Self { |
| error: style(), |
| warning: style(), |
| advice: style(), |
| help: style(), |
| link: style(), |
| linum: style(), |
| highlights: vec![style()], |
| } |
| } |
| } |
| |
| // ---------------------------------------- |
| // Most of these characters were taken from |
| // https://github.com/zesterer/ariadne/blob/e3cb394cb56ecda116a0a1caecd385a49e7f6662/src/draw.rs |
| |
| /// Characters to be used when drawing when using |
| /// [crate::GraphicalReportHandler]. |
| #[allow(missing_docs)] |
| #[derive(Debug, Clone, Eq, PartialEq)] |
| pub struct ThemeCharacters { |
| pub hbar: char, |
| pub vbar: char, |
| pub xbar: char, |
| pub vbar_break: char, |
| |
| pub uarrow: char, |
| pub rarrow: char, |
| |
| pub ltop: char, |
| pub mtop: char, |
| pub rtop: char, |
| pub lbot: char, |
| pub rbot: char, |
| pub mbot: char, |
| |
| pub lbox: char, |
| pub rbox: char, |
| |
| pub lcross: char, |
| pub rcross: char, |
| |
| pub underbar: char, |
| pub underline: char, |
| |
| pub error: String, |
| pub warning: String, |
| pub advice: String, |
| } |
| |
| impl ThemeCharacters { |
| /// Fancy unicode-based graphical elements. |
| pub fn unicode() -> Self { |
| Self { |
| hbar: '─', |
| vbar: '│', |
| xbar: '┼', |
| vbar_break: '·', |
| uarrow: '▲', |
| rarrow: '▶', |
| ltop: '╭', |
| mtop: '┬', |
| rtop: '╮', |
| lbot: '╰', |
| mbot: '┴', |
| rbot: '╯', |
| lbox: '[', |
| rbox: ']', |
| lcross: '├', |
| rcross: '┤', |
| underbar: '┬', |
| underline: '─', |
| error: "×".into(), |
| warning: "⚠".into(), |
| advice: "☞".into(), |
| } |
| } |
| |
| /// Emoji-heavy unicode characters. |
| pub fn emoji() -> Self { |
| Self { |
| hbar: '─', |
| vbar: '│', |
| xbar: '┼', |
| vbar_break: '·', |
| uarrow: '▲', |
| rarrow: '▶', |
| ltop: '╭', |
| mtop: '┬', |
| rtop: '╮', |
| lbot: '╰', |
| mbot: '┴', |
| rbot: '╯', |
| lbox: '[', |
| rbox: ']', |
| lcross: '├', |
| rcross: '┤', |
| underbar: '┬', |
| underline: '─', |
| error: "💥".into(), |
| warning: "⚠️".into(), |
| advice: "💡".into(), |
| } |
| } |
| /// ASCII-art-based graphical elements. Works well on older terminals. |
| pub fn ascii() -> Self { |
| Self { |
| hbar: '-', |
| vbar: '|', |
| xbar: '+', |
| vbar_break: ':', |
| uarrow: '^', |
| rarrow: '>', |
| ltop: ',', |
| mtop: 'v', |
| rtop: '.', |
| lbot: '`', |
| mbot: '^', |
| rbot: '\'', |
| lbox: '[', |
| rbox: ']', |
| lcross: '|', |
| rcross: '|', |
| underbar: '|', |
| underline: '^', |
| error: "x".into(), |
| warning: "!".into(), |
| advice: ">".into(), |
| } |
| } |
| } |