• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use std::io::IsTerminal;
2 
3 use owo_colors::Style;
4 
5 /**
6 Theme used by [`GraphicalReportHandler`](crate::GraphicalReportHandler) to
7 render fancy [`Diagnostic`](crate::Diagnostic) reports.
8 
9 A theme consists of two things: the set of characters to be used for drawing,
10 and the
11 [`owo_colors::Style`](https://docs.rs/owo-colors/latest/owo_colors/struct.Style.html)s to be used to paint various items.
12 
13 You can create your own custom graphical theme using this type, or you can use
14 one of the predefined ones using the methods below.
15 */
16 #[derive(Debug, Clone)]
17 pub struct GraphicalTheme {
18     /// Characters to be used for drawing.
19     pub characters: ThemeCharacters,
20     /// Styles to be used for painting.
21     pub styles: ThemeStyles,
22 }
23 
24 impl GraphicalTheme {
25     /// ASCII-art-based graphical drawing, with ANSI styling.
ascii() -> Self26     pub fn ascii() -> Self {
27         Self {
28             characters: ThemeCharacters::ascii(),
29             styles: ThemeStyles::ansi(),
30         }
31     }
32 
33     /// Graphical theme that draws using both ansi colors and unicode
34     /// characters.
35     ///
36     /// Note that full rgb colors aren't enabled by default because they're
37     /// an accessibility hazard, especially in the context of terminal themes
38     /// that can change the background color and make hardcoded colors illegible.
39     /// Such themes typically remap ansi codes properly, treating them more
40     /// like CSS classes than specific colors.
unicode() -> Self41     pub fn unicode() -> Self {
42         Self {
43             characters: ThemeCharacters::unicode(),
44             styles: ThemeStyles::ansi(),
45         }
46     }
47 
48     /// Graphical theme that draws in monochrome, while still using unicode
49     /// characters.
unicode_nocolor() -> Self50     pub fn unicode_nocolor() -> Self {
51         Self {
52             characters: ThemeCharacters::unicode(),
53             styles: ThemeStyles::none(),
54         }
55     }
56 
57     /// A "basic" graphical theme that skips colors and unicode characters and
58     /// just does monochrome ascii art. If you want a completely non-graphical
59     /// rendering of your [`Diagnostic`](crate::Diagnostic)s, check out
60     /// [`NarratableReportHandler`](crate::NarratableReportHandler), or write
61     /// your own [`ReportHandler`](crate::ReportHandler)
none() -> Self62     pub fn none() -> Self {
63         Self {
64             characters: ThemeCharacters::ascii(),
65             styles: ThemeStyles::none(),
66         }
67     }
68 }
69 
70 impl Default for GraphicalTheme {
default() -> Self71     fn default() -> Self {
72         match std::env::var("NO_COLOR") {
73             _ if !std::io::stdout().is_terminal() || !std::io::stderr().is_terminal() => {
74                 Self::ascii()
75             }
76             Ok(string) if string != "0" => Self::unicode_nocolor(),
77             _ => Self::unicode(),
78         }
79     }
80 }
81 
82 /**
83 Styles for various parts of graphical rendering for the
84 [`GraphicalReportHandler`](crate::GraphicalReportHandler).
85 */
86 #[derive(Debug, Clone)]
87 pub struct ThemeStyles {
88     /// Style to apply to things highlighted as "error".
89     pub error: Style,
90     /// Style to apply to things highlighted as "warning".
91     pub warning: Style,
92     /// Style to apply to things highlighted as "advice".
93     pub advice: Style,
94     /// Style to apply to the help text.
95     pub help: Style,
96     /// Style to apply to filenames/links/URLs.
97     pub link: Style,
98     /// Style to apply to line numbers.
99     pub linum: Style,
100     /// Styles to cycle through (using `.iter().cycle()`), to render the lines
101     /// and text for diagnostic highlights.
102     pub highlights: Vec<Style>,
103 }
104 
style() -> Style105 fn style() -> Style {
106     Style::new()
107 }
108 
109 impl ThemeStyles {
110     /// Nice RGB colors.
111     /// [Credit](http://terminal.sexy/#FRUV0NDQFRUVrEFCkKlZ9L91ap-1qnWfdbWq0NDQUFBQrEFCkKlZ9L91ap-1qnWfdbWq9fX1).
rgb() -> Self112     pub fn rgb() -> Self {
113         Self {
114             error: style().fg_rgb::<255, 30, 30>(),
115             warning: style().fg_rgb::<244, 191, 117>(),
116             advice: style().fg_rgb::<106, 159, 181>(),
117             help: style().fg_rgb::<106, 159, 181>(),
118             link: style().fg_rgb::<92, 157, 255>().underline().bold(),
119             linum: style().dimmed(),
120             highlights: vec![
121                 style().fg_rgb::<246, 87, 248>(),
122                 style().fg_rgb::<30, 201, 212>(),
123                 style().fg_rgb::<145, 246, 111>(),
124             ],
125         }
126     }
127 
128     /// ANSI color-based styles.
ansi() -> Self129     pub fn ansi() -> Self {
130         Self {
131             error: style().red(),
132             warning: style().yellow(),
133             advice: style().cyan(),
134             help: style().cyan(),
135             link: style().cyan().underline().bold(),
136             linum: style().dimmed(),
137             highlights: vec![
138                 style().magenta().bold(),
139                 style().yellow().bold(),
140                 style().green().bold(),
141             ],
142         }
143     }
144 
145     /// No styling. Just regular ol' monochrome.
none() -> Self146     pub fn none() -> Self {
147         Self {
148             error: style(),
149             warning: style(),
150             advice: style(),
151             help: style(),
152             link: style(),
153             linum: style(),
154             highlights: vec![style()],
155         }
156     }
157 }
158 
159 // ----------------------------------------
160 // Most of these characters were taken from
161 // https://github.com/zesterer/ariadne/blob/e3cb394cb56ecda116a0a1caecd385a49e7f6662/src/draw.rs
162 
163 /// Characters to be used when drawing when using
164 /// [`GraphicalReportHandler`](crate::GraphicalReportHandler).
165 #[allow(missing_docs)]
166 #[derive(Debug, Clone, Eq, PartialEq)]
167 pub struct ThemeCharacters {
168     pub hbar: char,
169     pub vbar: char,
170     pub xbar: char,
171     pub vbar_break: char,
172 
173     pub uarrow: char,
174     pub rarrow: char,
175 
176     pub ltop: char,
177     pub mtop: char,
178     pub rtop: char,
179     pub lbot: char,
180     pub rbot: char,
181     pub mbot: char,
182 
183     pub lbox: char,
184     pub rbox: char,
185 
186     pub lcross: char,
187     pub rcross: char,
188 
189     pub underbar: char,
190     pub underline: char,
191 
192     pub error: String,
193     pub warning: String,
194     pub advice: String,
195 }
196 
197 impl ThemeCharacters {
198     /// Fancy unicode-based graphical elements.
unicode() -> Self199     pub fn unicode() -> Self {
200         Self {
201             hbar: '─',
202             vbar: '│',
203             xbar: '┼',
204             vbar_break: '·',
205             uarrow: '▲',
206             rarrow: '▶',
207             ltop: '╭',
208             mtop: '┬',
209             rtop: '╮',
210             lbot: '╰',
211             mbot: '┴',
212             rbot: '╯',
213             lbox: '[',
214             rbox: ']',
215             lcross: '├',
216             rcross: '┤',
217             underbar: '┬',
218             underline: '─',
219             error: "×".into(),
220             warning: "⚠".into(),
221             advice: "☞".into(),
222         }
223     }
224 
225     /// Emoji-heavy unicode characters.
emoji() -> Self226     pub fn emoji() -> Self {
227         Self {
228             hbar: '─',
229             vbar: '│',
230             xbar: '┼',
231             vbar_break: '·',
232             uarrow: '▲',
233             rarrow: '▶',
234             ltop: '╭',
235             mtop: '┬',
236             rtop: '╮',
237             lbot: '╰',
238             mbot: '┴',
239             rbot: '╯',
240             lbox: '[',
241             rbox: ']',
242             lcross: '├',
243             rcross: '┤',
244             underbar: '┬',
245             underline: '─',
246             error: "��".into(),
247             warning: "⚠️".into(),
248             advice: "��".into(),
249         }
250     }
251     /// ASCII-art-based graphical elements. Works well on older terminals.
ascii() -> Self252     pub fn ascii() -> Self {
253         Self {
254             hbar: '-',
255             vbar: '|',
256             xbar: '+',
257             vbar_break: ':',
258             uarrow: '^',
259             rarrow: '>',
260             ltop: ',',
261             mtop: 'v',
262             rtop: '.',
263             lbot: '`',
264             mbot: '^',
265             rbot: '\'',
266             lbox: '[',
267             rbox: ']',
268             lcross: '|',
269             rcross: '|',
270             underbar: '|',
271             underline: '^',
272             error: "x".into(),
273             warning: "!".into(),
274             advice: ">".into(),
275         }
276     }
277 }
278