blob: 7b834d709783a3bbb70e85787c1c70ded53a18c2 [file] [log] [blame]
use termcolor::{Color, ColorSpec};
use crate::diagnostic::{LabelStyle, Severity};
/// Configures how a diagnostic is rendered.
#[derive(Clone, Debug)]
pub struct Config {
/// The display style to use when rendering diagnostics.
/// Defaults to: [`DisplayStyle::Rich`].
///
/// [`DisplayStyle::Rich`]: DisplayStyle::Rich
pub display_style: DisplayStyle,
/// Column width of tabs.
/// Defaults to: `4`.
pub tab_width: usize,
/// Styles to use when rendering the diagnostic.
pub styles: Styles,
/// Characters to use when rendering the diagnostic.
pub chars: Chars,
}
impl Default for Config {
fn default() -> Config {
Config {
display_style: DisplayStyle::Rich,
tab_width: 4,
styles: Styles::default(),
chars: Chars::default(),
}
}
}
/// The display style to use when rendering diagnostics.
#[derive(Clone, Debug)]
pub enum DisplayStyle {
/// Output a richly formatted diagnostic, with source code previews.
///
/// ```text
/// error[E0001]: unexpected type in `+` application
/// ┌─ test:2:9
/// │
/// 2 │ (+ test "")
/// │ ^^ expected `Int` but found `String`
/// │
/// = expected type `Int`
/// found type `String`
///
/// error[E0002]: Bad config found
///
/// ```
Rich,
/// Output a short diagnostic, with a line number, severity, and message.
///
/// ```text
/// test:2:9: error[E0001]: unexpected type in `+` application
/// error[E0002]: Bad config found
/// ```
Short,
}
/// Styles to use when rendering the diagnostic.
#[derive(Clone, Debug)]
pub struct Styles {
/// The style to use when rendering bug headers.
/// Defaults to `fg:red bold intense`.
pub header_bug: ColorSpec,
/// The style to use when rendering error headers.
/// Defaults to `fg:red bold intense`.
pub header_error: ColorSpec,
/// The style to use when rendering warning headers.
/// Defaults to `fg:yellow bold intense`.
pub header_warning: ColorSpec,
/// The style to use when rendering note headers.
/// Defaults to `fg:green bold intense`.
pub header_note: ColorSpec,
/// The style to use when rendering help headers.
/// Defaults to `fg:cyan bold intense`.
pub header_help: ColorSpec,
/// The style to use when the main diagnostic message.
/// Defaults to `bold intense`.
pub header_message: ColorSpec,
/// The style to use when rendering bug labels.
/// Defaults to `fg:red`.
pub primary_label_bug: ColorSpec,
/// The style to use when rendering error labels.
/// Defaults to `fg:red`.
pub primary_label_error: ColorSpec,
/// The style to use when rendering warning labels.
/// Defaults to `fg:yellow`.
pub primary_label_warning: ColorSpec,
/// The style to use when rendering note labels.
/// Defaults to `fg:green`.
pub primary_label_note: ColorSpec,
/// The style to use when rendering help labels.
/// Defaults to `fg:cyan`.
pub primary_label_help: ColorSpec,
/// The style to use when rendering secondary labels.
/// Defaults `fg:blue` (or `fg:cyan` on windows).
pub secondary_label: ColorSpec,
/// The style to use when rendering the line numbers.
/// Defaults `fg:blue` (or `fg:cyan` on windows).
pub line_number: ColorSpec,
/// The style to use when rendering the source code borders.
/// Defaults `fg:blue` (or `fg:cyan` on windows).
pub source_border: ColorSpec,
/// The style to use when rendering the note bullets.
/// Defaults `fg:blue` (or `fg:cyan` on windows).
pub note_bullet: ColorSpec,
}
impl Styles {
/// The style used to mark a header at a given severity.
pub fn header(&self, severity: Severity) -> &ColorSpec {
match severity {
Severity::Bug => &self.header_bug,
Severity::Error => &self.header_error,
Severity::Warning => &self.header_warning,
Severity::Note => &self.header_note,
Severity::Help => &self.header_help,
}
}
/// The style used to mark a primary or secondary label at a given severity.
pub fn label(&self, severity: Severity, label_style: LabelStyle) -> &ColorSpec {
match (label_style, severity) {
(LabelStyle::Primary, Severity::Bug) => &self.primary_label_bug,
(LabelStyle::Primary, Severity::Error) => &self.primary_label_error,
(LabelStyle::Primary, Severity::Warning) => &self.primary_label_warning,
(LabelStyle::Primary, Severity::Note) => &self.primary_label_note,
(LabelStyle::Primary, Severity::Help) => &self.primary_label_help,
(LabelStyle::Secondary, _) => &self.secondary_label,
}
}
#[doc(hidden)]
pub fn with_blue(blue: Color) -> Styles {
let header = ColorSpec::new().set_bold(true).set_intense(true).clone();
Styles {
header_bug: header.clone().set_fg(Some(Color::Red)).clone(),
header_error: header.clone().set_fg(Some(Color::Red)).clone(),
header_warning: header.clone().set_fg(Some(Color::Yellow)).clone(),
header_note: header.clone().set_fg(Some(Color::Green)).clone(),
header_help: header.clone().set_fg(Some(Color::Cyan)).clone(),
header_message: header,
primary_label_bug: ColorSpec::new().set_fg(Some(Color::Red)).clone(),
primary_label_error: ColorSpec::new().set_fg(Some(Color::Red)).clone(),
primary_label_warning: ColorSpec::new().set_fg(Some(Color::Yellow)).clone(),
primary_label_note: ColorSpec::new().set_fg(Some(Color::Green)).clone(),
primary_label_help: ColorSpec::new().set_fg(Some(Color::Cyan)).clone(),
secondary_label: ColorSpec::new().set_fg(Some(blue)).clone(),
line_number: ColorSpec::new().set_fg(Some(blue)).clone(),
source_border: ColorSpec::new().set_fg(Some(blue)).clone(),
note_bullet: ColorSpec::new().set_fg(Some(blue)).clone(),
}
}
}
impl Default for Styles {
fn default() -> Styles {
// Blue is really difficult to see on the standard windows command line
#[cfg(windows)]
const BLUE: Color = Color::Cyan;
#[cfg(not(windows))]
const BLUE: Color = Color::Blue;
Self::with_blue(BLUE)
}
}
/// Characters to use when rendering the diagnostic.
#[derive(Clone, Debug)]
pub struct Chars {
/// The character to use for the top-left border of the source.
/// Defaults to: `'┌'`.
pub source_border_top_left: char,
/// The character to use for the top border of the source.
/// Defaults to: `'─'`.
pub source_border_top: char,
/// The character to use for the left border of the source.
/// Defaults to: `'│'`.
pub source_border_left: char,
/// The character to use for the left border break of the source.
/// Defaults to: `'·'`.
pub source_border_left_break: char,
/// The character to use for the note bullet.
/// Defaults to: `'='`.
pub note_bullet: char,
/// The character to use for marking a single-line primary label.
/// Defaults to: `'^'`.
pub single_primary_caret: char,
/// The character to use for marking a single-line secondary label.
/// Defaults to: `'-'`.
pub single_secondary_caret: char,
/// The character to use for marking the start of a multi-line primary label.
/// Defaults to: `'^'`.
pub multi_primary_caret_start: char,
/// The character to use for marking the end of a multi-line primary label.
/// Defaults to: `'^'`.
pub multi_primary_caret_end: char,
/// The character to use for marking the start of a multi-line secondary label.
/// Defaults to: `'\''`.
pub multi_secondary_caret_start: char,
/// The character to use for marking the end of a multi-line secondary label.
/// Defaults to: `'\''`.
pub multi_secondary_caret_end: char,
/// The character to use for the top-left corner of a multi-line label.
/// Defaults to: `'╭'`.
pub multi_top_left: char,
/// The character to use for the top of a multi-line label.
/// Defaults to: `'─'`.
pub multi_top: char,
/// The character to use for the bottom-left corner of a multi-line label.
/// Defaults to: `'╰'`.
pub multi_bottom_left: char,
/// The character to use when marking the bottom of a multi-line label.
/// Defaults to: `'─'`.
pub multi_bottom: char,
/// The character to use for the left of a multi-line label.
/// Defaults to: `'│'`.
pub multi_left: char,
/// The character to use for the left of a pointer underneath a caret.
/// Defaults to: `'│'`.
pub pointer_left: char,
}
impl Default for Chars {
fn default() -> Chars {
Chars {
source_border_top_left: '┌',
source_border_top: '─',
source_border_left: '│',
source_border_left_break: '·',
note_bullet: '=',
single_primary_caret: '^',
single_secondary_caret: '-',
multi_primary_caret_start: '^',
multi_primary_caret_end: '^',
multi_secondary_caret_start: '\'',
multi_secondary_caret_end: '\'',
multi_top_left: '╭',
multi_top: '─',
multi_bottom_left: '╰',
multi_bottom: '─',
multi_left: '│',
pointer_left: '│',
}
}
}