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
34mod display_list;
35mod margin;
36pub(crate) mod stylesheet;
37
38use crate::snippet::Snippet;
39pub use anstyle::*;
40use display_list::DisplayList;
41pub use margin::Margin;
42use std::fmt::Display;
43use stylesheet::Stylesheet;
44
45/// A renderer for [`Snippet`]s
46#[derive(Clone)]
47pub struct Renderer {
48 anonymized_line_numbers: bool,
49 margin: Option<Margin>,
50 stylesheet: Stylesheet,
51}
52
53impl 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