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