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