pub(crate) mod render;
pub(crate) mod source_map;
pub(crate) mod stylesheet;
mod margin;
mod styled_buffer;
use alloc::string::String;
use crate::Report;
pub(crate) use render::ElementStyle;
pub(crate) use render::UnderlineParts;
pub(crate) use render::normalize_whitespace;
pub(crate) use render::{LineAnnotation, LineAnnotationType, char_width, num_overlap};
pub(crate) use stylesheet::Stylesheet;
pub use anstyle::*;
pub const DEFAULT_TERM_WIDTH: usize = 140;
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()
};
pub const DEFAULT_ERROR_STYLE: Style = AnsiColor::BrightRed.on_default().effects(Effects::BOLD);
pub const DEFAULT_WARNING_STYLE: Style = if USE_WINDOWS_COLORS {
AnsiColor::BrightYellow.on_default()
} else {
AnsiColor::Yellow.on_default()
}
.effects(Effects::BOLD);
pub const DEFAULT_INFO_STYLE: Style = BRIGHT_BLUE.effects(Effects::BOLD);
pub const DEFAULT_NOTE_STYLE: Style = AnsiColor::BrightGreen.on_default().effects(Effects::BOLD);
pub const DEFAULT_HELP_STYLE: Style = AnsiColor::BrightCyan.on_default().effects(Effects::BOLD);
pub const DEFAULT_LINE_NUM_STYLE: Style = BRIGHT_BLUE.effects(Effects::BOLD);
pub const DEFAULT_EMPHASIS_STYLE: Style = if USE_WINDOWS_COLORS {
AnsiColor::BrightWhite.on_default()
} else {
Style::new()
}
.effects(Effects::BOLD);
pub const DEFAULT_NONE_STYLE: Style = Style::new();
pub const DEFAULT_CONTEXT_STYLE: Style = BRIGHT_BLUE.effects(Effects::BOLD);
pub const DEFAULT_ADDITION_STYLE: Style = AnsiColor::BrightGreen.on_default();
pub const DEFAULT_REMOVAL_STYLE: Style = AnsiColor::BrightRed.on_default();
#[derive(Clone, Debug)]
pub struct Renderer {
anonymized_line_numbers: bool,
term_width: usize,
decor_style: DecorStyle,
stylesheet: Stylesheet,
short_message: bool,
}
impl Renderer {
pub const fn plain() -> Self {
Self {
anonymized_line_numbers: false,
term_width: DEFAULT_TERM_WIDTH,
decor_style: DecorStyle::Ascii,
stylesheet: Stylesheet::plain(),
short_message: false,
}
}
pub const fn styled() -> Self {
Self {
stylesheet: Stylesheet {
error: DEFAULT_ERROR_STYLE,
warning: DEFAULT_WARNING_STYLE,
info: DEFAULT_INFO_STYLE,
note: DEFAULT_NOTE_STYLE,
help: DEFAULT_HELP_STYLE,
line_num: DEFAULT_LINE_NUM_STYLE,
emphasis: DEFAULT_EMPHASIS_STYLE,
none: DEFAULT_NONE_STYLE,
context: DEFAULT_CONTEXT_STYLE,
addition: DEFAULT_ADDITION_STYLE,
removal: DEFAULT_REMOVAL_STYLE,
},
..Self::plain()
}
}
pub const fn short_message(mut self, short_message: bool) -> Self {
self.short_message = short_message;
self
}
pub const fn term_width(mut self, term_width: usize) -> Self {
self.term_width = term_width;
self
}
pub const fn decor_style(mut self, decor_style: DecorStyle) -> Self {
self.decor_style = decor_style;
self
}
pub const fn anonymized_line_numbers(mut self, anonymized_line_numbers: bool) -> Self {
self.anonymized_line_numbers = anonymized_line_numbers;
self
}
}
impl Renderer {
pub fn render(&self, groups: Report<'_>) -> String {
render::render(self, groups)
}
}
impl Renderer {
pub const fn error(mut self, style: Style) -> Self {
self.stylesheet.error = style;
self
}
pub const fn warning(mut self, style: Style) -> Self {
self.stylesheet.warning = style;
self
}
pub const fn info(mut self, style: Style) -> Self {
self.stylesheet.info = style;
self
}
pub const fn note(mut self, style: Style) -> Self {
self.stylesheet.note = style;
self
}
pub const fn help(mut self, style: Style) -> Self {
self.stylesheet.help = style;
self
}
pub const fn line_num(mut self, style: Style) -> Self {
self.stylesheet.line_num = style;
self
}
pub const fn emphasis(mut self, style: Style) -> Self {
self.stylesheet.emphasis = style;
self
}
pub const fn context(mut self, style: Style) -> Self {
self.stylesheet.context = style;
self
}
pub const fn addition(mut self, style: Style) -> Self {
self.stylesheet.addition = style;
self
}
pub const fn removal(mut self, style: Style) -> Self {
self.stylesheet.removal = style;
self
}
pub const fn none(mut self, style: Style) -> Self {
self.stylesheet.none = style;
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DecorStyle {
Ascii,
Unicode,
}
impl DecorStyle {
fn col_separator(&self) -> char {
match self {
DecorStyle::Ascii => '|',
DecorStyle::Unicode => '│',
}
}
fn note_separator(&self, is_cont: bool) -> &str {
match self {
DecorStyle::Ascii => "= ",
DecorStyle::Unicode if is_cont => "├ ",
DecorStyle::Unicode => "╰ ",
}
}
fn multi_suggestion_separator(&self) -> &'static str {
match self {
DecorStyle::Ascii => "|",
DecorStyle::Unicode => "├╴",
}
}
fn file_start(&self, is_first: bool, alone: bool) -> &'static str {
match self {
DecorStyle::Ascii => "--> ",
DecorStyle::Unicode if is_first && alone => " ─▸ ",
DecorStyle::Unicode if is_first => " ╭▸ ",
DecorStyle::Unicode => " ├▸ ",
}
}
fn secondary_file_start(&self) -> &'static str {
match self {
DecorStyle::Ascii => "::: ",
DecorStyle::Unicode => " ⸬ ",
}
}
fn diff(&self) -> char {
match self {
DecorStyle::Ascii => '~',
DecorStyle::Unicode => '±',
}
}
fn margin(&self) -> &'static str {
match self {
DecorStyle::Ascii => "...",
DecorStyle::Unicode => "…",
}
}
fn underline(&self, is_primary: bool) -> UnderlineParts {
match (self, is_primary) {
(DecorStyle::Ascii, true) => UnderlineParts {
style: ElementStyle::UnderlinePrimary,
underline: '^',
label_start: '^',
vertical_text_line: '|',
multiline_vertical: '|',
multiline_horizontal: '_',
multiline_whole_line: '/',
multiline_start_down: '^',
bottom_right: '|',
top_left: ' ',
top_right_flat: '^',
bottom_left: '|',
multiline_end_up: '^',
multiline_end_same_line: '^',
multiline_bottom_right_with_text: '|',
},
(DecorStyle::Ascii, false) => UnderlineParts {
style: ElementStyle::UnderlineSecondary,
underline: '-',
label_start: '-',
vertical_text_line: '|',
multiline_vertical: '|',
multiline_horizontal: '_',
multiline_whole_line: '/',
multiline_start_down: '-',
bottom_right: '|',
top_left: ' ',
top_right_flat: '-',
bottom_left: '|',
multiline_end_up: '-',
multiline_end_same_line: '-',
multiline_bottom_right_with_text: '|',
},
(DecorStyle::Unicode, true) => UnderlineParts {
style: ElementStyle::UnderlinePrimary,
underline: '━',
label_start: '┯',
vertical_text_line: '│',
multiline_vertical: '┃',
multiline_horizontal: '━',
multiline_whole_line: '┏',
multiline_start_down: '╿',
bottom_right: '┙',
top_left: '┏',
top_right_flat: '┛',
bottom_left: '┗',
multiline_end_up: '╿',
multiline_end_same_line: '┛',
multiline_bottom_right_with_text: '┥',
},
(DecorStyle::Unicode, false) => UnderlineParts {
style: ElementStyle::UnderlineSecondary,
underline: '─',
label_start: '┬',
vertical_text_line: '│',
multiline_vertical: '│',
multiline_horizontal: '─',
multiline_whole_line: '┌',
multiline_start_down: '│',
bottom_right: '┘',
top_left: '┌',
top_right_flat: '┘',
bottom_left: '└',
multiline_end_up: '│',
multiline_end_same_line: '┘',
multiline_bottom_right_with_text: '┤',
},
}
}
}