diff options
author | Valentin Popov <valentin@popov.link> | 2024-07-19 15:37:58 +0300 |
---|---|---|
committer | Valentin Popov <valentin@popov.link> | 2024-07-19 15:37:58 +0300 |
commit | a990de90fe41456a23e58bd087d2f107d321f3a1 (patch) | |
tree | 15afc392522a9e85dc3332235e311b7d39352ea9 /vendor/miette/src/handlers | |
parent | 3d48cd3f81164bbfc1a755dc1d4a9a02f98c8ddd (diff) | |
download | fparkan-a990de90fe41456a23e58bd087d2f107d321f3a1.tar.xz fparkan-a990de90fe41456a23e58bd087d2f107d321f3a1.zip |
Deleted vendor folder
Diffstat (limited to 'vendor/miette/src/handlers')
-rw-r--r-- | vendor/miette/src/handlers/debug.rs | 71 | ||||
-rw-r--r-- | vendor/miette/src/handlers/graphical.rs | 920 | ||||
-rw-r--r-- | vendor/miette/src/handlers/json.rs | 182 | ||||
-rw-r--r-- | vendor/miette/src/handlers/mod.rs | 24 | ||||
-rw-r--r-- | vendor/miette/src/handlers/narratable.rs | 423 | ||||
-rw-r--r-- | vendor/miette/src/handlers/theme.rs | 275 |
6 files changed, 0 insertions, 1895 deletions
diff --git a/vendor/miette/src/handlers/debug.rs b/vendor/miette/src/handlers/debug.rs deleted file mode 100644 index 50450a4..0000000 --- a/vendor/miette/src/handlers/debug.rs +++ /dev/null @@ -1,71 +0,0 @@ -use std::fmt; - -use crate::{protocol::Diagnostic, ReportHandler}; - -/** -[`ReportHandler`] that renders plain text and avoids extraneous graphics. -It's optimized for screen readers and braille users, but is also used in any -non-graphical environments, such as non-TTY output. -*/ -#[derive(Debug, Clone)] -pub struct DebugReportHandler; - -impl DebugReportHandler { - /// Create a new [`NarratableReportHandler`](crate::NarratableReportHandler) - /// There are no customization options. - pub const fn new() -> Self { - Self - } -} - -impl Default for DebugReportHandler { - fn default() -> Self { - Self::new() - } -} - -impl DebugReportHandler { - /// Render a [`Diagnostic`]. This function is mostly internal and meant to - /// be called by the toplevel [`ReportHandler`] handler, but is made public - /// to make it easier (possible) to test in isolation from global state. - pub fn render_report( - &self, - f: &mut fmt::Formatter<'_>, - diagnostic: &(dyn Diagnostic), - ) -> fmt::Result { - let mut diag = f.debug_struct("Diagnostic"); - diag.field("message", &format!("{}", diagnostic)); - if let Some(code) = diagnostic.code() { - diag.field("code", &code.to_string()); - } - if let Some(severity) = diagnostic.severity() { - diag.field("severity", &format!("{:?}", severity)); - } - if let Some(url) = diagnostic.url() { - diag.field("url", &url.to_string()); - } - if let Some(help) = diagnostic.help() { - diag.field("help", &help.to_string()); - } - if let Some(labels) = diagnostic.labels() { - let labels: Vec<_> = labels.collect(); - diag.field("labels", &format!("{:?}", labels)); - } - if let Some(cause) = diagnostic.diagnostic_source() { - diag.field("caused by", &format!("{:?}", cause)); - } - diag.finish()?; - writeln!(f)?; - writeln!(f, "NOTE: If you're looking for the fancy error reports, install miette with the `fancy` feature, or write your own and hook it up with miette::set_hook().") - } -} - -impl ReportHandler for DebugReportHandler { - fn debug(&self, diagnostic: &(dyn Diagnostic), f: &mut fmt::Formatter<'_>) -> fmt::Result { - if f.alternate() { - return fmt::Debug::fmt(diagnostic, f); - } - - self.render_report(f, diagnostic) - } -} diff --git a/vendor/miette/src/handlers/graphical.rs b/vendor/miette/src/handlers/graphical.rs deleted file mode 100644 index b5dd754..0000000 --- a/vendor/miette/src/handlers/graphical.rs +++ /dev/null @@ -1,920 +0,0 @@ -use std::fmt::{self, Write}; - -use owo_colors::{OwoColorize, Style}; -use unicode_width::UnicodeWidthChar; - -use crate::diagnostic_chain::{DiagnosticChain, ErrorKind}; -use crate::handlers::theme::*; -use crate::protocol::{Diagnostic, Severity}; -use crate::{LabeledSpan, MietteError, ReportHandler, SourceCode, SourceSpan, SpanContents}; - -/** -A [`ReportHandler`] that displays a given [`Report`](crate::Report) in a -quasi-graphical way, using terminal colors, unicode drawing characters, and -other such things. - -This is the default reporter bundled with `miette`. - -This printer can be customized by using [`new_themed()`](GraphicalReportHandler::new_themed) and handing it a -[`GraphicalTheme`] of your own creation (or using one of its own defaults!) - -See [`set_hook()`](crate::set_hook) for more details on customizing your global -printer. -*/ -#[derive(Debug, Clone)] -pub struct GraphicalReportHandler { - pub(crate) links: LinkStyle, - pub(crate) termwidth: usize, - pub(crate) theme: GraphicalTheme, - pub(crate) footer: Option<String>, - pub(crate) context_lines: usize, - pub(crate) tab_width: usize, - pub(crate) with_cause_chain: bool, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) enum LinkStyle { - None, - Link, - Text, -} - -impl GraphicalReportHandler { - /// Create a new `GraphicalReportHandler` with the default - /// [`GraphicalTheme`]. This will use both unicode characters and colors. - pub fn new() -> Self { - Self { - links: LinkStyle::Link, - termwidth: 200, - theme: GraphicalTheme::default(), - footer: None, - context_lines: 1, - tab_width: 4, - with_cause_chain: true, - } - } - - ///Create a new `GraphicalReportHandler` with a given [`GraphicalTheme`]. - pub fn new_themed(theme: GraphicalTheme) -> Self { - Self { - links: LinkStyle::Link, - termwidth: 200, - theme, - footer: None, - context_lines: 1, - tab_width: 4, - with_cause_chain: true, - } - } - - /// Set the displayed tab width in spaces. - pub fn tab_width(mut self, width: usize) -> Self { - self.tab_width = width; - self - } - - /// Whether to enable error code linkification using [`Diagnostic::url()`]. - pub fn with_links(mut self, links: bool) -> Self { - self.links = if links { - LinkStyle::Link - } else { - LinkStyle::Text - }; - self - } - - /// Include the cause chain of the top-level error in the graphical output, - /// if available. - pub fn with_cause_chain(mut self) -> Self { - self.with_cause_chain = true; - self - } - - /// Do not include the cause chain of the top-level error in the graphical - /// output. - pub fn without_cause_chain(mut self) -> Self { - self.with_cause_chain = false; - self - } - - /// Whether to include [`Diagnostic::url()`] in the output. - /// - /// Disabling this is not recommended, but can be useful for more easily - /// reproducible tests, as `url(docsrs)` links are version-dependent. - pub fn with_urls(mut self, urls: bool) -> Self { - self.links = match (self.links, urls) { - (_, false) => LinkStyle::None, - (LinkStyle::None, true) => LinkStyle::Link, - (links, true) => links, - }; - self - } - - /// Set a theme for this handler. - pub fn with_theme(mut self, theme: GraphicalTheme) -> Self { - self.theme = theme; - self - } - - /// Sets the width to wrap the report at. - pub fn with_width(mut self, width: usize) -> Self { - self.termwidth = width; - self - } - - /// Sets the 'global' footer for this handler. - pub fn with_footer(mut self, footer: String) -> Self { - self.footer = Some(footer); - self - } - - /// Sets the number of lines of context to show around each error. - pub fn with_context_lines(mut self, lines: usize) -> Self { - self.context_lines = lines; - self - } -} - -impl Default for GraphicalReportHandler { - fn default() -> Self { - Self::new() - } -} - -impl GraphicalReportHandler { - /// Render a [`Diagnostic`]. This function is mostly internal and meant to - /// be called by the toplevel [`ReportHandler`] handler, but is made public - /// to make it easier (possible) to test in isolation from global state. - pub fn render_report( - &self, - f: &mut impl fmt::Write, - diagnostic: &(dyn Diagnostic), - ) -> fmt::Result { - self.render_header(f, diagnostic)?; - self.render_causes(f, diagnostic)?; - let src = diagnostic.source_code(); - self.render_snippets(f, diagnostic, src)?; - self.render_footer(f, diagnostic)?; - self.render_related(f, diagnostic, src)?; - if let Some(footer) = &self.footer { - writeln!(f)?; - let width = self.termwidth.saturating_sub(4); - let opts = textwrap::Options::new(width) - .initial_indent(" ") - .subsequent_indent(" "); - writeln!(f, "{}", textwrap::fill(footer, opts))?; - } - Ok(()) - } - - fn render_header(&self, f: &mut impl fmt::Write, diagnostic: &(dyn Diagnostic)) -> fmt::Result { - let severity_style = match diagnostic.severity() { - Some(Severity::Error) | None => self.theme.styles.error, - Some(Severity::Warning) => self.theme.styles.warning, - Some(Severity::Advice) => self.theme.styles.advice, - }; - let mut header = String::new(); - if self.links == LinkStyle::Link && diagnostic.url().is_some() { - let url = diagnostic.url().unwrap(); // safe - let code = if let Some(code) = diagnostic.code() { - format!("{} ", code) - } else { - "".to_string() - }; - let link = format!( - "\u{1b}]8;;{}\u{1b}\\{}{}\u{1b}]8;;\u{1b}\\", - url, - code.style(severity_style), - "(link)".style(self.theme.styles.link) - ); - write!(header, "{}", link)?; - writeln!(f, "{}", header)?; - writeln!(f)?; - } else if let Some(code) = diagnostic.code() { - write!(header, "{}", code.style(severity_style),)?; - if self.links == LinkStyle::Text && diagnostic.url().is_some() { - let url = diagnostic.url().unwrap(); // safe - write!(header, " ({})", url.style(self.theme.styles.link))?; - } - writeln!(f, "{}", header)?; - writeln!(f)?; - } - Ok(()) - } - - fn render_causes(&self, f: &mut impl fmt::Write, diagnostic: &(dyn Diagnostic)) -> fmt::Result { - let (severity_style, severity_icon) = match diagnostic.severity() { - Some(Severity::Error) | None => (self.theme.styles.error, &self.theme.characters.error), - Some(Severity::Warning) => (self.theme.styles.warning, &self.theme.characters.warning), - Some(Severity::Advice) => (self.theme.styles.advice, &self.theme.characters.advice), - }; - - let initial_indent = format!(" {} ", severity_icon.style(severity_style)); - let rest_indent = format!(" {} ", self.theme.characters.vbar.style(severity_style)); - let width = self.termwidth.saturating_sub(2); - let opts = textwrap::Options::new(width) - .initial_indent(&initial_indent) - .subsequent_indent(&rest_indent); - - writeln!(f, "{}", textwrap::fill(&diagnostic.to_string(), opts))?; - - if !self.with_cause_chain { - return Ok(()); - } - - if let Some(mut cause_iter) = diagnostic - .diagnostic_source() - .map(DiagnosticChain::from_diagnostic) - .or_else(|| diagnostic.source().map(DiagnosticChain::from_stderror)) - .map(|it| it.peekable()) - { - while let Some(error) = cause_iter.next() { - let is_last = cause_iter.peek().is_none(); - let char = if !is_last { - self.theme.characters.lcross - } else { - self.theme.characters.lbot - }; - let initial_indent = format!( - " {}{}{} ", - char, self.theme.characters.hbar, self.theme.characters.rarrow - ) - .style(severity_style) - .to_string(); - let rest_indent = format!( - " {} ", - if is_last { - ' ' - } else { - self.theme.characters.vbar - } - ) - .style(severity_style) - .to_string(); - let opts = textwrap::Options::new(width) - .initial_indent(&initial_indent) - .subsequent_indent(&rest_indent); - match error { - ErrorKind::Diagnostic(diag) => { - let mut inner = String::new(); - - // Don't print footer for inner errors - let mut inner_renderer = self.clone(); - inner_renderer.footer = None; - inner_renderer.with_cause_chain = false; - inner_renderer.render_report(&mut inner, diag)?; - - writeln!(f, "{}", textwrap::fill(&inner, opts))?; - } - ErrorKind::StdError(err) => { - writeln!(f, "{}", textwrap::fill(&err.to_string(), opts))?; - } - } - } - } - - Ok(()) - } - - fn render_footer(&self, f: &mut impl fmt::Write, diagnostic: &(dyn Diagnostic)) -> fmt::Result { - if let Some(help) = diagnostic.help() { - let width = self.termwidth.saturating_sub(4); - let initial_indent = " help: ".style(self.theme.styles.help).to_string(); - let opts = textwrap::Options::new(width) - .initial_indent(&initial_indent) - .subsequent_indent(" "); - writeln!(f, "{}", textwrap::fill(&help.to_string(), opts))?; - } - Ok(()) - } - - fn render_related( - &self, - f: &mut impl fmt::Write, - diagnostic: &(dyn Diagnostic), - parent_src: Option<&dyn SourceCode>, - ) -> fmt::Result { - if let Some(related) = diagnostic.related() { - writeln!(f)?; - for rel in related { - match rel.severity() { - Some(Severity::Error) | None => write!(f, "Error: ")?, - Some(Severity::Warning) => write!(f, "Warning: ")?, - Some(Severity::Advice) => write!(f, "Advice: ")?, - }; - self.render_header(f, rel)?; - self.render_causes(f, rel)?; - let src = rel.source_code().or(parent_src); - self.render_snippets(f, rel, src)?; - self.render_footer(f, rel)?; - self.render_related(f, rel, src)?; - } - } - Ok(()) - } - - fn render_snippets( - &self, - f: &mut impl fmt::Write, - diagnostic: &(dyn Diagnostic), - opt_source: Option<&dyn SourceCode>, - ) -> fmt::Result { - if let Some(source) = opt_source { - if let Some(labels) = diagnostic.labels() { - let mut labels = labels.collect::<Vec<_>>(); - labels.sort_unstable_by_key(|l| l.inner().offset()); - if !labels.is_empty() { - let contents = labels - .iter() - .map(|label| { - source.read_span(label.inner(), self.context_lines, self.context_lines) - }) - .collect::<Result<Vec<Box<dyn SpanContents<'_>>>, MietteError>>() - .map_err(|_| fmt::Error)?; - let mut contexts = Vec::with_capacity(contents.len()); - for (right, right_conts) in labels.iter().cloned().zip(contents.iter()) { - if contexts.is_empty() { - contexts.push((right, right_conts)); - } else { - let (left, left_conts) = contexts.last().unwrap().clone(); - let left_end = left.offset() + left.len(); - let right_end = right.offset() + right.len(); - if left_conts.line() + left_conts.line_count() >= right_conts.line() { - // The snippets will overlap, so we create one Big Chunky Boi - let new_span = LabeledSpan::new( - left.label().map(String::from), - left.offset(), - if right_end >= left_end { - // Right end goes past left end - right_end - left.offset() - } else { - // right is contained inside left - left.len() - }, - ); - if source - .read_span( - new_span.inner(), - self.context_lines, - self.context_lines, - ) - .is_ok() - { - contexts.pop(); - contexts.push(( - // We'll throw this away later - new_span, left_conts, - )); - } else { - contexts.push((right, right_conts)); - } - } else { - contexts.push((right, right_conts)); - } - } - } - for (ctx, _) in contexts { - self.render_context(f, source, &ctx, &labels[..])?; - } - } - } - } - Ok(()) - } - - fn render_context<'a>( - &self, - f: &mut impl fmt::Write, - source: &'a dyn SourceCode, - context: &LabeledSpan, - labels: &[LabeledSpan], - ) -> fmt::Result { - let (contents, lines) = self.get_lines(source, context.inner())?; - - // sorting is your friend - let labels = labels - .iter() - .zip(self.theme.styles.highlights.iter().cloned().cycle()) - .map(|(label, st)| FancySpan::new(label.label().map(String::from), *label.inner(), st)) - .collect::<Vec<_>>(); - - // The max number of gutter-lines that will be active at any given - // point. We need this to figure out indentation, so we do one loop - // over the lines to see what the damage is gonna be. - let mut max_gutter = 0usize; - for line in &lines { - let mut num_highlights = 0; - for hl in &labels { - if !line.span_line_only(hl) && line.span_applies(hl) { - num_highlights += 1; - } - } - max_gutter = std::cmp::max(max_gutter, num_highlights); - } - - // Oh and one more thing: We need to figure out how much room our line - // numbers need! - let linum_width = lines[..] - .last() - .map(|line| line.line_number) - // It's possible for the source to be an empty string. - .unwrap_or(0) - .to_string() - .len(); - - // Header - write!( - f, - "{}{}{}", - " ".repeat(linum_width + 2), - self.theme.characters.ltop, - self.theme.characters.hbar, - )?; - - if let Some(source_name) = contents.name() { - let source_name = source_name.style(self.theme.styles.link); - writeln!( - f, - "[{}:{}:{}]", - source_name, - contents.line() + 1, - contents.column() + 1 - )?; - } else if lines.len() <= 1 { - writeln!(f, "{}", self.theme.characters.hbar.to_string().repeat(3))?; - } else { - writeln!(f, "[{}:{}]", contents.line() + 1, contents.column() + 1)?; - } - - // Now it's time for the fun part--actually rendering everything! - for line in &lines { - // Line number, appropriately padded. - self.write_linum(f, linum_width, line.line_number)?; - - // Then, we need to print the gutter, along with any fly-bys We - // have separate gutters depending on whether we're on the actual - // line, or on one of the "highlight lines" below it. - self.render_line_gutter(f, max_gutter, line, &labels)?; - - // And _now_ we can print out the line text itself! - self.render_line_text(f, &line.text)?; - - // Next, we write all the highlights that apply to this particular line. - let (single_line, multi_line): (Vec<_>, Vec<_>) = labels - .iter() - .filter(|hl| line.span_applies(hl)) - .partition(|hl| line.span_line_only(hl)); - if !single_line.is_empty() { - // no line number! - self.write_no_linum(f, linum_width)?; - // gutter _again_ - self.render_highlight_gutter(f, max_gutter, line, &labels)?; - self.render_single_line_highlights( - f, - line, - linum_width, - max_gutter, - &single_line, - &labels, - )?; - } - for hl in multi_line { - if hl.label().is_some() && line.span_ends(hl) && !line.span_starts(hl) { - // no line number! - self.write_no_linum(f, linum_width)?; - // gutter _again_ - self.render_highlight_gutter(f, max_gutter, line, &labels)?; - self.render_multi_line_end(f, hl)?; - } - } - } - writeln!( - f, - "{}{}{}", - " ".repeat(linum_width + 2), - self.theme.characters.lbot, - self.theme.characters.hbar.to_string().repeat(4), - )?; - Ok(()) - } - - fn render_line_gutter( - &self, - f: &mut impl fmt::Write, - max_gutter: usize, - line: &Line, - highlights: &[FancySpan], - ) -> fmt::Result { - if max_gutter == 0 { - return Ok(()); - } - let chars = &self.theme.characters; - let mut gutter = String::new(); - let applicable = highlights.iter().filter(|hl| line.span_applies(hl)); - let mut arrow = false; - for (i, hl) in applicable.enumerate() { - if line.span_starts(hl) { - gutter.push_str(&chars.ltop.style(hl.style).to_string()); - gutter.push_str( - &chars - .hbar - .to_string() - .repeat(max_gutter.saturating_sub(i)) - .style(hl.style) - .to_string(), - ); - gutter.push_str(&chars.rarrow.style(hl.style).to_string()); - arrow = true; - break; - } else if line.span_ends(hl) { - if hl.label().is_some() { - gutter.push_str(&chars.lcross.style(hl.style).to_string()); - } else { - gutter.push_str(&chars.lbot.style(hl.style).to_string()); - } - gutter.push_str( - &chars - .hbar - .to_string() - .repeat(max_gutter.saturating_sub(i)) - .style(hl.style) - .to_string(), - ); - gutter.push_str(&chars.rarrow.style(hl.style).to_string()); - arrow = true; - break; - } else if line.span_flyby(hl) { - gutter.push_str(&chars.vbar.style(hl.style).to_string()); - } else { - gutter.push(' '); - } - } - write!( - f, - "{}{}", - gutter, - " ".repeat( - if arrow { 1 } else { 3 } + max_gutter.saturating_sub(gutter.chars().count()) - ) - )?; - Ok(()) - } - - fn render_highlight_gutter( - &self, - f: &mut impl fmt::Write, - max_gutter: usize, - line: &Line, - highlights: &[FancySpan], - ) -> fmt::Result { - if max_gutter == 0 { - return Ok(()); - } - let chars = &self.theme.characters; - let mut gutter = String::new(); - let applicable = highlights.iter().filter(|hl| line.span_applies(hl)); - for (i, hl) in applicable.enumerate() { - if !line.span_line_only(hl) && line.span_ends(hl) { - gutter.push_str(&chars.lbot.style(hl.style).to_string()); - gutter.push_str( - &chars - .hbar - .to_string() - .repeat(max_gutter.saturating_sub(i) + 2) - .style(hl.style) - .to_string(), - ); - break; - } else { - gutter.push_str(&chars.vbar.style(hl.style).to_string()); - } - } - write!(f, "{:width$}", gutter, width = max_gutter + 1)?; - Ok(()) - } - - fn write_linum(&self, f: &mut impl fmt::Write, width: usize, linum: usize) -> fmt::Result { - write!( - f, - " {:width$} {} ", - linum.style(self.theme.styles.linum), - self.theme.characters.vbar, - width = width - )?; - Ok(()) - } - - fn write_no_linum(&self, f: &mut impl fmt::Write, width: usize) -> fmt::Result { - write!( - f, - " {:width$} {} ", - "", - self.theme.characters.vbar_break, - width = width - )?; - Ok(()) - } - - /// Returns an iterator over the visual width of each character in a line. - fn line_visual_char_width<'a>(&self, text: &'a str) -> impl Iterator<Item = usize> + 'a { - let mut column = 0; - let tab_width = self.tab_width; - text.chars().map(move |c| { - let width = if c == '\t' { - // Round up to the next multiple of tab_width - tab_width - column % tab_width - } else { - c.width().unwrap_or(0) - }; - column += width; - width - }) - } - - /// Returns the visual column position of a byte offset on a specific line. - fn visual_offset(&self, line: &Line, offset: usize) -> usize { - let line_range = line.offset..=(line.offset + line.length); - assert!(line_range.contains(&offset)); - - let text_index = offset - line.offset; - let text = &line.text[..text_index.min(line.text.len())]; - let text_width = self.line_visual_char_width(text).sum(); - if text_index > line.text.len() { - // Spans extending past the end of the line are always rendered as - // one column past the end of the visible line. - // - // This doesn't necessarily correspond to a specific byte-offset, - // since a span extending past the end of the line could contain: - // - an actual \n character (1 byte) - // - a CRLF (2 bytes) - // - EOF (0 bytes) - text_width + 1 - } else { - text_width - } - } - - /// Renders a line to the output formatter, replacing tabs with spaces. - fn render_line_text(&self, f: &mut impl fmt::Write, text: &str) -> fmt::Result { - for (c, width) in text.chars().zip(self.line_visual_char_width(text)) { - if c == '\t' { - for _ in 0..width { - f.write_char(' ')? - } - } else { - f.write_char(c)? - } - } - f.write_char('\n')?; - Ok(()) - } - - fn render_single_line_highlights( - &self, - f: &mut impl fmt::Write, - line: &Line, - linum_width: usize, - max_gutter: usize, - single_liners: &[&FancySpan], - all_highlights: &[FancySpan], - ) -> fmt::Result { - let mut underlines = String::new(); - let mut highest = 0; - - let chars = &self.theme.characters; - let vbar_offsets: Vec<_> = single_liners - .iter() - .map(|hl| { - let byte_start = hl.offset(); - let byte_end = hl.offset() + hl.len(); - let start = self.visual_offset(line, byte_start).max(highest); - let end = self.visual_offset(line, byte_end).max(start + 1); - - let vbar_offset = (start + end) / 2; - let num_left = vbar_offset - start; - let num_right = end - vbar_offset - 1; - if start < end { - underlines.push_str( - &format!( - "{:width$}{}{}{}", - "", - chars.underline.to_string().repeat(num_left), - if hl.len() == 0 { - chars.uarrow - } else if hl.label().is_some() { - chars.underbar - } else { - chars.underline - }, - chars.underline.to_string().repeat(num_right), - width = start.saturating_sub(highest), - ) - .style(hl.style) - .to_string(), - ); - } - highest = std::cmp::max(highest, end); - - (hl, vbar_offset) - }) - .collect(); - writeln!(f, "{}", underlines)?; - - for hl in single_liners.iter().rev() { - if let Some(label) = hl.label() { - self.write_no_linum(f, linum_width)?; - self.render_highlight_gutter(f, max_gutter, line, all_highlights)?; - let mut curr_offset = 1usize; - for (offset_hl, vbar_offset) in &vbar_offsets { - while curr_offset < *vbar_offset + 1 { - write!(f, " ")?; - curr_offset += 1; - } - if *offset_hl != hl { - write!(f, "{}", chars.vbar.to_string().style(offset_hl.style))?; - curr_offset += 1; - } else { - let lines = format!( - "{}{} {}", - chars.lbot, - chars.hbar.to_string().repeat(2), - label, - ); - writeln!(f, "{}", lines.style(hl.style))?; - break; - } - } - } - } - Ok(()) - } - - fn render_multi_line_end(&self, f: &mut impl fmt::Write, hl: &FancySpan) -> fmt::Result { - writeln!( - f, - "{} {}", - self.theme.characters.hbar.style(hl.style), - hl.label().unwrap_or_else(|| "".into()), - )?; - Ok(()) - } - - fn get_lines<'a>( - &'a self, - source: &'a dyn SourceCode, - context_span: &'a SourceSpan, - ) -> Result<(Box<dyn SpanContents<'a> + 'a>, Vec<Line>), fmt::Error> { - let context_data = source - .read_span(context_span, self.context_lines, self.context_lines) - .map_err(|_| fmt::Error)?; - let context = std::str::from_utf8(context_data.data()).expect("Bad utf8 detected"); - let mut line = context_data.line(); - let mut column = context_data.column(); - let mut offset = context_data.span().offset(); - let mut line_offset = offset; - let mut iter = context.chars().peekable(); - let mut line_str = String::new(); - let mut lines = Vec::new(); - while let Some(char) = iter.next() { - offset += char.len_utf8(); - let mut at_end_of_file = false; - match char { - '\r' => { - if iter.next_if_eq(&'\n').is_some() { - offset += 1; - line += 1; - column = 0; - } else { - line_str.push(char); - column += 1; - } - at_end_of_file = iter.peek().is_none(); - } - '\n' => { - at_end_of_file = iter.peek().is_none(); - line += 1; - column = 0; - } - _ => { - line_str.push(char); - column += 1; - } - } - - if iter.peek().is_none() && !at_end_of_file { - line += 1; - } - - if column == 0 || iter.peek().is_none() { - lines.push(Line { - line_number: line, - offset: line_offset, - length: offset - line_offset, - text: line_str.clone(), - }); - line_str.clear(); - line_offset = offset; - } - } - Ok((context_data, lines)) - } -} - -impl ReportHandler for GraphicalReportHandler { - fn debug(&self, diagnostic: &(dyn Diagnostic), f: &mut fmt::Formatter<'_>) -> fmt::Result { - if f.alternate() { - return fmt::Debug::fmt(diagnostic, f); - } - - self.render_report(f, diagnostic) - } -} - -/* -Support types -*/ - -#[derive(Debug)] -struct Line { - line_number: usize, - offset: usize, - length: usize, - text: String, -} - -impl Line { - fn span_line_only(&self, span: &FancySpan) -> bool { - span.offset() >= self.offset && span.offset() + span.len() <= self.offset + self.length - } - - fn span_applies(&self, span: &FancySpan) -> bool { - let spanlen = if span.len() == 0 { 1 } else { span.len() }; - // Span starts in this line - (span.offset() >= self.offset && span.offset() < self.offset + self.length) - // Span passes through this line - || (span.offset() < self.offset && span.offset() + spanlen > self.offset + self.length) //todo - // Span ends on this line - || (span.offset() + spanlen > self.offset && span.offset() + spanlen <= self.offset + self.length) - } - - // A 'flyby' is a multi-line span that technically covers this line, but - // does not begin or end within the line itself. This method is used to - // calculate gutters. - fn span_flyby(&self, span: &FancySpan) -> bool { - // The span itself starts before this line's starting offset (so, in a - // prev line). - span.offset() < self.offset - // ...and it stops after this line's end. - && span.offset() + span.len() > self.offset + self.length - } - - // Does this line contain the *beginning* of this multiline span? - // This assumes self.span_applies() is true already. - fn span_starts(&self, span: &FancySpan) -> bool { - span.offset() >= self.offset - } - - // Does this line contain the *end* of this multiline span? - // This assumes self.span_applies() is true already. - fn span_ends(&self, span: &FancySpan) -> bool { - span.offset() + span.len() >= self.offset - && span.offset() + span.len() <= self.offset + self.length - } -} - -#[derive(Debug, Clone)] -struct FancySpan { - label: Option<String>, - span: SourceSpan, - style: Style, -} - -impl PartialEq for FancySpan { - fn eq(&self, other: &Self) -> bool { - self.label == other.label && self.span == other.span - } -} - -impl FancySpan { - fn new(label: Option<String>, span: SourceSpan, style: Style) -> Self { - FancySpan { label, span, style } - } - - fn style(&self) -> Style { - self.style - } - - fn label(&self) -> Option<String> { - self.label - .as_ref() - .map(|l| l.style(self.style()).to_string()) - } - - fn offset(&self) -> usize { - self.span.offset() - } - - fn len(&self) -> usize { - self.span.len() - } -} diff --git a/vendor/miette/src/handlers/json.rs b/vendor/miette/src/handlers/json.rs deleted file mode 100644 index 29e21a0..0000000 --- a/vendor/miette/src/handlers/json.rs +++ /dev/null @@ -1,182 +0,0 @@ -use std::fmt::{self, Write}; - -use crate::{ - diagnostic_chain::DiagnosticChain, protocol::Diagnostic, ReportHandler, Severity, SourceCode, -}; - -/** -[`ReportHandler`] that renders JSON output. It's a machine-readable output. -*/ -#[derive(Debug, Clone)] -pub struct JSONReportHandler; - -impl JSONReportHandler { - /// Create a new [`JSONReportHandler`]. There are no customization - /// options. - pub const fn new() -> Self { - Self - } -} - -impl Default for JSONReportHandler { - fn default() -> Self { - Self::new() - } -} - -struct Escape<'a>(&'a str); - -impl fmt::Display for Escape<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - for c in self.0.chars() { - let escape = match c { - '\\' => Some(r"\\"), - '"' => Some(r#"\""#), - '\r' => Some(r"\r"), - '\n' => Some(r"\n"), - '\t' => Some(r"\t"), - '\u{08}' => Some(r"\b"), - '\u{0c}' => Some(r"\f"), - _ => None, - }; - if let Some(escape) = escape { - f.write_str(escape)?; - } else { - f.write_char(c)?; - } - } - Ok(()) - } -} - -const fn escape(input: &'_ str) -> Escape<'_> { - Escape(input) -} - -impl JSONReportHandler { - /// Render a [`Diagnostic`]. This function is mostly internal and meant to - /// be called by the toplevel [`ReportHandler`] handler, but is made public - /// to make it easier (possible) to test in isolation from global state. - pub fn render_report( - &self, - f: &mut impl fmt::Write, - diagnostic: &(dyn Diagnostic), - ) -> fmt::Result { - self._render_report(f, diagnostic, None) - } - - fn _render_report( - &self, - f: &mut impl fmt::Write, - diagnostic: &(dyn Diagnostic), - parent_src: Option<&dyn SourceCode>, - ) -> fmt::Result { - write!(f, r#"{{"message": "{}","#, escape(&diagnostic.to_string()))?; - if let Some(code) = diagnostic.code() { - write!(f, r#""code": "{}","#, escape(&code.to_string()))?; - } - let severity = match diagnostic.severity() { - Some(Severity::Error) | None => "error", - Some(Severity::Warning) => "warning", - Some(Severity::Advice) => "advice", - }; - write!(f, r#""severity": "{:}","#, severity)?; - if let Some(cause_iter) = diagnostic - .diagnostic_source() - .map(DiagnosticChain::from_diagnostic) - .or_else(|| diagnostic.source().map(DiagnosticChain::from_stderror)) - { - write!(f, r#""causes": ["#)?; - let mut add_comma = false; - for error in cause_iter { - if add_comma { - write!(f, ",")?; - } else { - add_comma = true; - } - write!(f, r#""{}""#, escape(&error.to_string()))?; - } - write!(f, "],")? - } else { - write!(f, r#""causes": [],"#)?; - } - if let Some(url) = diagnostic.url() { - write!(f, r#""url": "{}","#, &url.to_string())?; - } - if let Some(help) = diagnostic.help() { - write!(f, r#""help": "{}","#, escape(&help.to_string()))?; - } - let src = diagnostic.source_code().or(parent_src); - if let Some(src) = src { - self.render_snippets(f, diagnostic, src)?; - } - if let Some(labels) = diagnostic.labels() { - write!(f, r#""labels": ["#)?; - let mut add_comma = false; - for label in labels { - if add_comma { - write!(f, ",")?; - } else { - add_comma = true; - } - write!(f, "{{")?; - if let Some(label_name) = label.label() { - write!(f, r#""label": "{}","#, escape(label_name))?; - } - write!(f, r#""span": {{"#)?; - write!(f, r#""offset": {},"#, label.offset())?; - write!(f, r#""length": {}"#, label.len())?; - - write!(f, "}}}}")?; - } - write!(f, "],")?; - } else { - write!(f, r#""labels": [],"#)?; - } - if let Some(relateds) = diagnostic.related() { - write!(f, r#""related": ["#)?; - let mut add_comma = false; - for related in relateds { - if add_comma { - write!(f, ",")?; - } else { - add_comma = true; - } - self._render_report(f, related, src)?; - } - write!(f, "]")?; - } else { - write!(f, r#""related": []"#)?; - } - write!(f, "}}") - } - - fn render_snippets( - &self, - f: &mut impl fmt::Write, - diagnostic: &(dyn Diagnostic), - source: &dyn SourceCode, - ) -> fmt::Result { - if let Some(mut labels) = diagnostic.labels() { - if let Some(label) = labels.next() { - if let Ok(span_content) = source.read_span(label.inner(), 0, 0) { - let filename = span_content.name().unwrap_or_default(); - return write!(f, r#""filename": "{}","#, escape(filename)); - } - } - } - write!(f, r#""filename": "","#) - } -} - -impl ReportHandler for JSONReportHandler { - fn debug(&self, diagnostic: &(dyn Diagnostic), f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.render_report(f, diagnostic) - } -} - -#[test] -fn test_escape() { - assert_eq!(escape("a\nb").to_string(), r"a\nb"); - assert_eq!(escape("C:\\Miette").to_string(), r"C:\\Miette"); -} diff --git a/vendor/miette/src/handlers/mod.rs b/vendor/miette/src/handlers/mod.rs deleted file mode 100644 index fde2dc9..0000000 --- a/vendor/miette/src/handlers/mod.rs +++ /dev/null @@ -1,24 +0,0 @@ -/*! -Reporters included with `miette`. -*/ - -#[allow(unreachable_pub)] -pub use debug::*; -#[allow(unreachable_pub)] -#[cfg(feature = "fancy-no-backtrace")] -pub use graphical::*; -#[allow(unreachable_pub)] -pub use json::*; -#[allow(unreachable_pub)] -pub use narratable::*; -#[allow(unreachable_pub)] -#[cfg(feature = "fancy-no-backtrace")] -pub use theme::*; - -mod debug; -#[cfg(feature = "fancy-no-backtrace")] -mod graphical; -mod json; -mod narratable; -#[cfg(feature = "fancy-no-backtrace")] -mod theme; diff --git a/vendor/miette/src/handlers/narratable.rs b/vendor/miette/src/handlers/narratable.rs deleted file mode 100644 index c809124..0000000 --- a/vendor/miette/src/handlers/narratable.rs +++ /dev/null @@ -1,423 +0,0 @@ -use std::fmt; - -use unicode_width::{UnicodeWidthChar, UnicodeWidthStr}; - -use crate::diagnostic_chain::DiagnosticChain; -use crate::protocol::{Diagnostic, Severity}; -use crate::{LabeledSpan, MietteError, ReportHandler, SourceCode, SourceSpan, SpanContents}; - -/** -[`ReportHandler`] that renders plain text and avoids extraneous graphics. -It's optimized for screen readers and braille users, but is also used in any -non-graphical environments, such as non-TTY output. -*/ -#[derive(Debug, Clone)] -pub struct NarratableReportHandler { - context_lines: usize, - with_cause_chain: bool, - footer: Option<String>, -} - -impl NarratableReportHandler { - /// Create a new [`NarratableReportHandler`]. There are no customization - /// options. - pub const fn new() -> Self { - Self { - footer: None, - context_lines: 1, - with_cause_chain: true, - } - } - - /// Include the cause chain of the top-level error in the report, if - /// available. - pub const fn with_cause_chain(mut self) -> Self { - self.with_cause_chain = true; - self - } - - /// Do not include the cause chain of the top-level error in the report. - pub const fn without_cause_chain(mut self) -> Self { - self.with_cause_chain = false; - self - } - - /// Set the footer to be displayed at the end of the report. - pub fn with_footer(mut self, footer: String) -> Self { - self.footer = Some(footer); - self - } - - /// Sets the number of lines of context to show around each error. - pub const fn with_context_lines(mut self, lines: usize) -> Self { - self.context_lines = lines; - self - } -} - -impl Default for NarratableReportHandler { - fn default() -> Self { - Self::new() - } -} - -impl NarratableReportHandler { - /// Render a [`Diagnostic`]. This function is mostly internal and meant to - /// be called by the toplevel [`ReportHandler`] handler, but is - /// made public to make it easier (possible) to test in isolation from - /// global state. - pub fn render_report( - &self, - f: &mut impl fmt::Write, - diagnostic: &(dyn Diagnostic), - ) -> fmt::Result { - self.render_header(f, diagnostic)?; - if self.with_cause_chain { - self.render_causes(f, diagnostic)?; - } - let src = diagnostic.source_code(); - self.render_snippets(f, diagnostic, src)?; - self.render_footer(f, diagnostic)?; - self.render_related(f, diagnostic, src)?; - if let Some(footer) = &self.footer { - writeln!(f, "{}", footer)?; - } - Ok(()) - } - - fn render_header(&self, f: &mut impl fmt::Write, diagnostic: &(dyn Diagnostic)) -> fmt::Result { - writeln!(f, "{}", diagnostic)?; - let severity = match diagnostic.severity() { - Some(Severity::Error) | None => "error", - Some(Severity::Warning) => "warning", - Some(Severity::Advice) => "advice", - }; - writeln!(f, " Diagnostic severity: {}", severity)?; - Ok(()) - } - - fn render_causes(&self, f: &mut impl fmt::Write, diagnostic: &(dyn Diagnostic)) -> fmt::Result { - if let Some(cause_iter) = diagnostic - .diagnostic_source() - .map(DiagnosticChain::from_diagnostic) - .or_else(|| diagnostic.source().map(DiagnosticChain::from_stderror)) - { - for error in cause_iter { - writeln!(f, " Caused by: {}", error)?; - } - } - - Ok(()) - } - - fn render_footer(&self, f: &mut impl fmt::Write, diagnostic: &(dyn Diagnostic)) -> fmt::Result { - if let Some(help) = diagnostic.help() { - writeln!(f, "diagnostic help: {}", help)?; - } - if let Some(code) = diagnostic.code() { - writeln!(f, "diagnostic code: {}", code)?; - } - if let Some(url) = diagnostic.url() { - writeln!(f, "For more details, see:\n{}", url)?; - } - Ok(()) - } - - fn render_related( - &self, - f: &mut impl fmt::Write, - diagnostic: &(dyn Diagnostic), - parent_src: Option<&dyn SourceCode>, - ) -> fmt::Result { - if let Some(related) = diagnostic.related() { - writeln!(f)?; - for rel in related { - match rel.severity() { - Some(Severity::Error) | None => write!(f, "Error: ")?, - Some(Severity::Warning) => write!(f, "Warning: ")?, - Some(Severity::Advice) => write!(f, "Advice: ")?, - }; - self.render_header(f, rel)?; - writeln!(f)?; - self.render_causes(f, rel)?; - let src = rel.source_code().or(parent_src); - self.render_snippets(f, rel, src)?; - self.render_footer(f, rel)?; - self.render_related(f, rel, src)?; - } - } - Ok(()) - } - - fn render_snippets( - &self, - f: &mut impl fmt::Write, - diagnostic: &(dyn Diagnostic), - source_code: Option<&dyn SourceCode>, - ) -> fmt::Result { - if let Some(source) = source_code { - if let Some(labels) = diagnostic.labels() { - let mut labels = labels.collect::<Vec<_>>(); - labels.sort_unstable_by_key(|l| l.inner().offset()); - if !labels.is_empty() { - let contents = labels - .iter() - .map(|label| { - source.read_span(label.inner(), self.context_lines, self.context_lines) - }) - .collect::<Result<Vec<Box<dyn SpanContents<'_>>>, MietteError>>() - .map_err(|_| fmt::Error)?; - let mut contexts = Vec::new(); - for (right, right_conts) in labels.iter().cloned().zip(contents.iter()) { - if contexts.is_empty() { - contexts.push((right, right_conts)); - } else { - let (left, left_conts) = contexts.last().unwrap().clone(); - let left_end = left.offset() + left.len(); - let right_end = right.offset() + right.len(); - if left_conts.line() + left_conts.line_count() >= right_conts.line() { - // The snippets will overlap, so we create one Big Chunky Boi - let new_span = LabeledSpan::new( - left.label().map(String::from), - left.offset(), - if right_end >= left_end { - // Right end goes past left end - right_end - left.offset() - } else { - // right is contained inside left - left.len() - }, - ); - if source - .read_span( - new_span.inner(), - self.context_lines, - self.context_lines, - ) - .is_ok() - { - contexts.pop(); - contexts.push(( - new_span, // We'll throw this away later - left_conts, - )); - } else { - contexts.push((right, right_conts)); - } - } else { - contexts.push((right, right_conts)); - } - } - } - for (ctx, _) in contexts { - self.render_context(f, source, &ctx, &labels[..])?; - } - } - } - } - Ok(()) - } - - fn render_context( - &self, - f: &mut impl fmt::Write, - source: &dyn SourceCode, - context: &LabeledSpan, - labels: &[LabeledSpan], - ) -> fmt::Result { - let (contents, lines) = self.get_lines(source, context.inner())?; - write!(f, "Begin snippet")?; - if let Some(filename) = contents.name() { - write!(f, " for {}", filename,)?; - } - writeln!( - f, - " starting at line {}, column {}", - contents.line() + 1, - contents.column() + 1 - )?; - writeln!(f)?; - for line in &lines { - writeln!(f, "snippet line {}: {}", line.line_number, line.text)?; - let relevant = labels - .iter() - .filter_map(|l| line.span_attach(l.inner()).map(|a| (a, l))); - for (attach, label) in relevant { - match attach { - SpanAttach::Contained { col_start, col_end } if col_start == col_end => { - write!( - f, - " label at line {}, column {}", - line.line_number, col_start, - )?; - } - SpanAttach::Contained { col_start, col_end } => { - write!( - f, - " label at line {}, columns {} to {}", - line.line_number, col_start, col_end, - )?; - } - SpanAttach::Starts { col_start } => { - write!( - f, - " label starting at line {}, column {}", - line.line_number, col_start, - )?; - } - SpanAttach::Ends { col_end } => { - write!( - f, - " label ending at line {}, column {}", - line.line_number, col_end, - )?; - } - } - if let Some(label) = label.label() { - write!(f, ": {}", label)?; - } - writeln!(f)?; - } - } - Ok(()) - } - - fn get_lines<'a>( - &'a self, - source: &'a dyn SourceCode, - context_span: &'a SourceSpan, - ) -> Result<(Box<dyn SpanContents<'a> + 'a>, Vec<Line>), fmt::Error> { - let context_data = source - .read_span(context_span, self.context_lines, self.context_lines) - .map_err(|_| fmt::Error)?; - let context = std::str::from_utf8(context_data.data()).expect("Bad utf8 detected"); - let mut line = context_data.line(); - let mut column = context_data.column(); - let mut offset = context_data.span().offset(); - let mut line_offset = offset; - let mut iter = context.chars().peekable(); - let mut line_str = String::new(); - let mut lines = Vec::new(); - while let Some(char) = iter.next() { - offset += char.len_utf8(); - let mut at_end_of_file = false; - match char { - '\r' => { - if iter.next_if_eq(&'\n').is_some() { - offset += 1; - line += 1; - column = 0; - } else { - line_str.push(char); - column += 1; - } - at_end_of_file = iter.peek().is_none(); - } - '\n' => { - at_end_of_file = iter.peek().is_none(); - line += 1; - column = 0; - } - _ => { - line_str.push(char); - column += 1; - } - } - - if iter.peek().is_none() && !at_end_of_file { - line += 1; - } - - if column == 0 || iter.peek().is_none() { - lines.push(Line { - line_number: line, - offset: line_offset, - text: line_str.clone(), - at_end_of_file, - }); - line_str.clear(); - line_offset = offset; - } - } - Ok((context_data, lines)) - } -} - -impl ReportHandler for NarratableReportHandler { - fn debug(&self, diagnostic: &(dyn Diagnostic), f: &mut fmt::Formatter<'_>) -> fmt::Result { - if f.alternate() { - return fmt::Debug::fmt(diagnostic, f); - } - - self.render_report(f, diagnostic) - } -} - -/* -Support types -*/ - -struct Line { - line_number: usize, - offset: usize, - text: String, - at_end_of_file: bool, -} - -enum SpanAttach { - Contained { col_start: usize, col_end: usize }, - Starts { col_start: usize }, - Ends { col_end: usize }, -} - -/// Returns column at offset, and nearest boundary if offset is in the middle of -/// the character -fn safe_get_column(text: &str, offset: usize, start: bool) -> usize { - let mut column = text.get(0..offset).map(|s| s.width()).unwrap_or_else(|| { - let mut column = 0; - for (idx, c) in text.char_indices() { - if offset <= idx { - break; - } - column += c.width().unwrap_or(0); - } - column - }); - if start { - // Offset are zero-based, so plus one - column += 1; - } // On the other hand for end span, offset refers for the next column - // So we should do -1. column+1-1 == column - column -} - -impl Line { - fn span_attach(&self, span: &SourceSpan) -> Option<SpanAttach> { - let span_end = span.offset() + span.len(); - let line_end = self.offset + self.text.len(); - - let start_after = span.offset() >= self.offset; - let end_before = self.at_end_of_file || span_end <= line_end; - - if start_after && end_before { - let col_start = safe_get_column(&self.text, span.offset() - self.offset, true); - let col_end = if span.is_empty() { - col_start - } else { - // span_end refers to the next character after token - // while col_end refers to the exact character, so -1 - safe_get_column(&self.text, span_end - self.offset, false) - }; - return Some(SpanAttach::Contained { col_start, col_end }); - } - if start_after && span.offset() <= line_end { - let col_start = safe_get_column(&self.text, span.offset() - self.offset, true); - return Some(SpanAttach::Starts { col_start }); - } - if end_before && span_end >= self.offset { - let col_end = safe_get_column(&self.text, span_end - self.offset, false); - return Some(SpanAttach::Ends { col_end }); - } - None - } -} diff --git a/vendor/miette/src/handlers/theme.rs b/vendor/miette/src/handlers/theme.rs deleted file mode 100644 index 1f5236a..0000000 --- a/vendor/miette/src/handlers/theme.rs +++ /dev/null @@ -1,275 +0,0 @@ -use is_terminal::IsTerminal; -use owo_colors::Style; - -/** -Theme used by [`GraphicalReportHandler`](crate::GraphicalReportHandler) to -render fancy [`Diagnostic`](crate::Diagnostic) reports. - -A theme consists of two things: the set of characters to be used for drawing, -and the -[`owo_colors::Style`](https://docs.rs/owo-colors/latest/owo_colors/struct.Style.html)s to be used to paint various items. - -You can create your own custom graphical theme using this type, or you can use -one of the predefined ones using the methods below. -*/ -#[derive(Debug, Clone)] -pub struct GraphicalTheme { - /// Characters to be used for drawing. - pub characters: ThemeCharacters, - /// Styles to be used for painting. - pub styles: ThemeStyles, -} - -impl GraphicalTheme { - /// ASCII-art-based graphical drawing, with ANSI styling. - pub fn ascii() -> Self { - Self { - characters: ThemeCharacters::ascii(), - styles: ThemeStyles::ansi(), - } - } - - /// Graphical theme that draws using both ansi colors and unicode - /// characters. - /// - /// Note that full rgb colors aren't enabled by default because they're - /// an accessibility hazard, especially in the context of terminal themes - /// that can change the background color and make hardcoded colors illegible. - /// Such themes typically remap ansi codes properly, treating them more - /// like CSS classes than specific colors. - pub fn unicode() -> Self { - Self { - characters: ThemeCharacters::unicode(), - styles: ThemeStyles::ansi(), - } - } - - /// Graphical theme that draws in monochrome, while still using unicode - /// characters. - pub fn unicode_nocolor() -> Self { - Self { - characters: ThemeCharacters::unicode(), - styles: ThemeStyles::none(), - } - } - - /// A "basic" graphical theme that skips colors and unicode characters and - /// just does monochrome ascii art. If you want a completely non-graphical - /// rendering of your `Diagnostic`s, check out - /// [crate::NarratableReportHandler], or write your own - /// [crate::ReportHandler]! - pub fn none() -> Self { - Self { - characters: ThemeCharacters::ascii(), - styles: ThemeStyles::none(), - } - } -} - -impl Default for GraphicalTheme { - fn default() -> Self { - match std::env::var("NO_COLOR") { - _ if !std::io::stdout().is_terminal() || !std::io::stderr().is_terminal() => { - Self::ascii() - } - Ok(string) if string != "0" => Self::unicode_nocolor(), - _ => Self::unicode(), - } - } -} - -/** -Styles for various parts of graphical rendering for the [crate::GraphicalReportHandler]. -*/ -#[derive(Debug, Clone)] -pub struct ThemeStyles { - /// Style to apply to things highlighted as "error". - pub error: Style, - /// Style to apply to things highlighted as "warning". - pub warning: Style, - /// Style to apply to things highlighted as "advice". - pub advice: Style, - /// Style to apply to the help text. - pub help: Style, - /// Style to apply to filenames/links/URLs. - pub link: Style, - /// Style to apply to line numbers. - pub linum: Style, - /// Styles to cycle through (using `.iter().cycle()`), to render the lines - /// and text for diagnostic highlights. - pub highlights: Vec<Style>, -} - -fn style() -> Style { - Style::new() -} - -impl ThemeStyles { - /// Nice RGB colors. - /// [Credit](http://terminal.sexy/#FRUV0NDQFRUVrEFCkKlZ9L91ap-1qnWfdbWq0NDQUFBQrEFCkKlZ9L91ap-1qnWfdbWq9fX1). - pub fn rgb() -> Self { - Self { - error: style().fg_rgb::<255, 30, 30>(), - warning: style().fg_rgb::<244, 191, 117>(), - advice: style().fg_rgb::<106, 159, 181>(), - help: style().fg_rgb::<106, 159, 181>(), - link: style().fg_rgb::<92, 157, 255>().underline().bold(), - linum: style().dimmed(), - highlights: vec![ - style().fg_rgb::<246, 87, 248>(), - style().fg_rgb::<30, 201, 212>(), - style().fg_rgb::<145, 246, 111>(), - ], - } - } - - /// ANSI color-based styles. - pub fn ansi() -> Self { - Self { - error: style().red(), - warning: style().yellow(), - advice: style().cyan(), - help: style().cyan(), - link: style().cyan().underline().bold(), - linum: style().dimmed(), - highlights: vec![ - style().magenta().bold(), - style().yellow().bold(), - style().green().bold(), - ], - } - } - - /// No styling. Just regular ol' monochrome. - pub fn none() -> Self { - Self { - error: style(), - warning: style(), - advice: style(), - help: style(), - link: style(), - linum: style(), - highlights: vec![style()], - } - } -} - -// ---------------------------------------- -// Most of these characters were taken from -// https://github.com/zesterer/ariadne/blob/e3cb394cb56ecda116a0a1caecd385a49e7f6662/src/draw.rs - -/// Characters to be used when drawing when using -/// [crate::GraphicalReportHandler]. -#[allow(missing_docs)] -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct ThemeCharacters { - pub hbar: char, - pub vbar: char, - pub xbar: char, - pub vbar_break: char, - - pub uarrow: char, - pub rarrow: char, - - pub ltop: char, - pub mtop: char, - pub rtop: char, - pub lbot: char, - pub rbot: char, - pub mbot: char, - - pub lbox: char, - pub rbox: char, - - pub lcross: char, - pub rcross: char, - - pub underbar: char, - pub underline: char, - - pub error: String, - pub warning: String, - pub advice: String, -} - -impl ThemeCharacters { - /// Fancy unicode-based graphical elements. - pub fn unicode() -> Self { - Self { - hbar: '─', - vbar: '│', - xbar: '┼', - vbar_break: '·', - uarrow: '▲', - rarrow: '▶', - ltop: '╭', - mtop: '┬', - rtop: '╮', - lbot: '╰', - mbot: '┴', - rbot: '╯', - lbox: '[', - rbox: ']', - lcross: '├', - rcross: '┤', - underbar: '┬', - underline: '─', - error: "×".into(), - warning: "⚠".into(), - advice: "☞".into(), - } - } - - /// Emoji-heavy unicode characters. - pub fn emoji() -> Self { - Self { - hbar: '─', - vbar: '│', - xbar: '┼', - vbar_break: '·', - uarrow: '▲', - rarrow: '▶', - ltop: '╭', - mtop: '┬', - rtop: '╮', - lbot: '╰', - mbot: '┴', - rbot: '╯', - lbox: '[', - rbox: ']', - lcross: '├', - rcross: '┤', - underbar: '┬', - underline: '─', - error: "💥".into(), - warning: "⚠️".into(), - advice: "💡".into(), - } - } - /// ASCII-art-based graphical elements. Works well on older terminals. - pub fn ascii() -> Self { - Self { - hbar: '-', - vbar: '|', - xbar: '+', - vbar_break: ':', - uarrow: '^', - rarrow: '>', - ltop: ',', - mtop: 'v', - rtop: '.', - lbot: '`', - mbot: '^', - rbot: '\'', - lbox: '[', - rbox: ']', - lcross: '|', - rcross: '|', - underbar: '|', - underline: '^', - error: "x".into(), - warning: "!".into(), - advice: ">".into(), - } - } -} |