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
13mod display_list;
14mod margin;
15mod styled_buffer;
16pub(crate) mod stylesheet;
17
18use crate::snippet::Message;
19pub use anstyle::*;
20use display_list::DisplayList;
21use margin::Margin;
22use std::fmt::Display;
23use stylesheet::Stylesheet;
24
25pub const DEFAULT_TERM_WIDTH: usize = 140;
26
27/// A renderer for [`Message`]s
28#[derive(Clone, Debug)]
29pub struct Renderer {
30 anonymized_line_numbers: bool,
31 term_width: usize,
32 stylesheet: Stylesheet,
33}
34
35impl 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