1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
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(),
}
}
}