1 | //! The renderer for [`Snippet`]s |
2 | //! |
3 | //! # Example |
4 | //! ``` |
5 | //! use annotate_snippets::{Annotation, AnnotationType, Renderer, Slice, Snippet}; |
6 | //! let snippet = Snippet { |
7 | //! title: Some(Annotation { |
8 | //! label: Some("mismatched types" ), |
9 | //! id: None, |
10 | //! annotation_type: AnnotationType::Error, |
11 | //! }), |
12 | //! footer: vec![], |
13 | //! slices: vec![ |
14 | //! Slice { |
15 | //! source: "Foo" , |
16 | //! line_start: 51, |
17 | //! origin: Some("src/format.rs" ), |
18 | //! fold: false, |
19 | //! annotations: vec![], |
20 | //! }, |
21 | //! Slice { |
22 | //! source: "Faa" , |
23 | //! line_start: 129, |
24 | //! origin: Some("src/display.rs" ), |
25 | //! fold: false, |
26 | //! annotations: vec![], |
27 | //! }, |
28 | //! ], |
29 | //! }; |
30 | //! |
31 | //! let renderer = Renderer::styled(); |
32 | //! println!("{}" , renderer.render(snippet)); |
33 | |
34 | mod display_list; |
35 | mod margin; |
36 | pub(crate) mod stylesheet; |
37 | |
38 | use crate::snippet::Snippet; |
39 | pub use anstyle::*; |
40 | use display_list::DisplayList; |
41 | pub use margin::Margin; |
42 | use std::fmt::Display; |
43 | use stylesheet::Stylesheet; |
44 | |
45 | /// A renderer for [`Snippet`]s |
46 | #[derive (Clone)] |
47 | pub struct Renderer { |
48 | anonymized_line_numbers: bool, |
49 | margin: Option<Margin>, |
50 | stylesheet: Stylesheet, |
51 | } |
52 | |
53 | impl Renderer { |
54 | /// No terminal styling |
55 | pub const fn plain() -> Self { |
56 | Self { |
57 | anonymized_line_numbers: false, |
58 | margin: None, |
59 | stylesheet: Stylesheet::plain(), |
60 | } |
61 | } |
62 | |
63 | /// Default terminal styling |
64 | pub const fn styled() -> Self { |
65 | const BRIGHT_BLUE: Style = if cfg!(windows) { |
66 | AnsiColor::BrightCyan.on_default() |
67 | } else { |
68 | AnsiColor::BrightBlue.on_default() |
69 | }; |
70 | Self { |
71 | stylesheet: Stylesheet { |
72 | error: AnsiColor::BrightRed.on_default().effects(Effects::BOLD), |
73 | warning: if cfg!(windows) { |
74 | AnsiColor::BrightYellow.on_default() |
75 | } else { |
76 | AnsiColor::Yellow.on_default() |
77 | } |
78 | .effects(Effects::BOLD), |
79 | info: BRIGHT_BLUE.effects(Effects::BOLD), |
80 | note: AnsiColor::BrightGreen.on_default().effects(Effects::BOLD), |
81 | help: AnsiColor::BrightCyan.on_default().effects(Effects::BOLD), |
82 | line_no: BRIGHT_BLUE.effects(Effects::BOLD), |
83 | emphasis: if cfg!(windows) { |
84 | AnsiColor::BrightWhite.on_default() |
85 | } else { |
86 | Style::new() |
87 | } |
88 | .effects(Effects::BOLD), |
89 | none: Style::new(), |
90 | }, |
91 | ..Self::plain() |
92 | } |
93 | } |
94 | |
95 | /// Anonymize line numbers |
96 | /// |
97 | /// This enables (or disables) line number anonymization. When enabled, line numbers are replaced |
98 | /// with `LL`. |
99 | /// |
100 | /// # Example |
101 | /// |
102 | /// ```text |
103 | /// --> $DIR/whitespace-trimming.rs:4:193 |
104 | /// | |
105 | /// LL | ... let _: () = 42; |
106 | /// | ^^ expected (), found integer |
107 | /// | |
108 | /// ``` |
109 | pub const fn anonymized_line_numbers(mut self, anonymized_line_numbers: bool) -> Self { |
110 | self.anonymized_line_numbers = anonymized_line_numbers; |
111 | self |
112 | } |
113 | |
114 | /// Set the margin for the output |
115 | /// |
116 | /// This controls the various margins of the output. |
117 | /// |
118 | /// # Example |
119 | /// |
120 | /// ```text |
121 | /// error: expected type, found `22` |
122 | /// --> examples/footer.rs:29:25 |
123 | /// | |
124 | /// 26 | ... annotations: vec![SourceAnnotation { |
125 | /// | ---------------- info: while parsing this struct |
126 | /// ... |
127 | /// 29 | ... range: <22, 25>, |
128 | /// | ^^ |
129 | /// | |
130 | /// ``` |
131 | pub const fn margin(mut self, margin: Option<Margin>) -> Self { |
132 | self.margin = margin; |
133 | self |
134 | } |
135 | |
136 | /// Set the output style for `error` |
137 | pub const fn error(mut self, style: Style) -> Self { |
138 | self.stylesheet.error = style; |
139 | self |
140 | } |
141 | |
142 | /// Set the output style for `warning` |
143 | pub const fn warning(mut self, style: Style) -> Self { |
144 | self.stylesheet.warning = style; |
145 | self |
146 | } |
147 | |
148 | /// Set the output style for `info` |
149 | pub const fn info(mut self, style: Style) -> Self { |
150 | self.stylesheet.info = style; |
151 | self |
152 | } |
153 | |
154 | /// Set the output style for `note` |
155 | pub const fn note(mut self, style: Style) -> Self { |
156 | self.stylesheet.note = style; |
157 | self |
158 | } |
159 | |
160 | /// Set the output style for `help` |
161 | pub const fn help(mut self, style: Style) -> Self { |
162 | self.stylesheet.help = style; |
163 | self |
164 | } |
165 | |
166 | /// Set the output style for line numbers |
167 | pub const fn line_no(mut self, style: Style) -> Self { |
168 | self.stylesheet.line_no = style; |
169 | self |
170 | } |
171 | |
172 | /// Set the output style for emphasis |
173 | pub const fn emphasis(mut self, style: Style) -> Self { |
174 | self.stylesheet.emphasis = style; |
175 | self |
176 | } |
177 | |
178 | /// Set the output style for none |
179 | pub const fn none(mut self, style: Style) -> Self { |
180 | self.stylesheet.none = style; |
181 | self |
182 | } |
183 | |
184 | /// Render a snippet into a `Display`able object |
185 | pub fn render<'a>(&'a self, snippet: Snippet<'a>) -> impl Display + 'a { |
186 | DisplayList::new( |
187 | snippet, |
188 | &self.stylesheet, |
189 | self.anonymized_line_numbers, |
190 | self.margin, |
191 | ) |
192 | } |
193 | } |
194 | |