diff --git a/src/handlers/graphical.rs b/src/handlers/graphical.rs index fcbec05..47e496e 100644 --- a/src/handlers/graphical.rs +++ b/src/handlers/graphical.rs @@ -33,6 +33,7 @@ pub struct GraphicalReportHandler { pub(crate) with_cause_chain: bool, pub(crate) wrap_lines: bool, pub(crate) break_words: bool, + pub(crate) with_primary_span_start: bool, pub(crate) word_separator: Option, pub(crate) word_splitter: Option, pub(crate) highlighter: MietteHighlighter, @@ -61,6 +62,7 @@ impl GraphicalReportHandler { with_cause_chain: true, wrap_lines: true, break_words: true, + with_primary_span_start: true, word_separator: None, word_splitter: None, highlighter: MietteHighlighter::default(), @@ -80,6 +82,7 @@ impl GraphicalReportHandler { tab_width: 4, wrap_lines: true, with_cause_chain: true, + with_primary_span_start: true, break_words: true, word_separator: None, word_splitter: None, @@ -119,6 +122,20 @@ impl GraphicalReportHandler { self } + /// Include the line and column for the the start of the primary span when the + /// snippet extends multiple lines + pub fn with_primary_span_start(mut self) -> Self { + self.with_primary_span_start = true; + self + } + + /// Do not include the line and column for the the start of the primary span + /// when the snippet extends multiple lines + pub fn without_primary_span_start(mut self) -> Self { + self.with_primary_span_start = false; + self + } + /// Whether to include [`Diagnostic::url()`] in the output. /// /// Disabling this is not recommended, but can be useful for more easily @@ -654,26 +671,34 @@ impl GraphicalReportHandler { }; if let Some(source_name) = primary_contents.name() { - writeln!( - f, - "[{}]", - format_args!( - "{}:{}:{}", - source_name, - primary_contents.line() + 1, - primary_contents.column() + 1 - ) - .style(self.theme.styles.link) - )?; - } else if lines.len() <= 1 { - writeln!(f, "{}", self.theme.characters.hbar.to_string().repeat(3))?; - } else { + if self.with_primary_span_start { + writeln!( + f, + "[{}]", + format_args!( + "{}:{}:{}", + source_name, + primary_contents.line() + 1, + primary_contents.column() + 1 + ) + .style(self.theme.styles.link) + )?; + } else { + writeln!( + f, + "[{}]", + format_args!("{}", source_name,).style(self.theme.styles.link) + )?; + } + } else if self.with_primary_span_start && lines.len() > 1 { writeln!( f, "[{}:{}]", primary_contents.line() + 1, primary_contents.column() + 1 )?; + } else { + writeln!(f, "{}", self.theme.characters.hbar.to_string().repeat(3))?; } // Now it's time for the fun part--actually rendering everything! diff --git a/tests/graphical.rs b/tests/graphical.rs index 3176812..f93a6a3 100644 --- a/tests/graphical.rs +++ b/tests/graphical.rs @@ -2510,3 +2510,63 @@ fn after_invalid_unicode() -> Result<(), MietteError> { Ok(()) } + +#[test] +fn option_include_primary_span_start() { + #[derive(Debug, Clone, Diagnostic, Error)] + #[error("decoding error")] + #[diagnostic(code(decode_err))] + struct E { + #[label("valid data here")] + src: SourceSpan, + } + + let invalid_source: &[u8] = b"malformed\nh\xf0\x93\x8aXYZ"; + + let (x_index, _) = invalid_source + .iter() + .enumerate() + .find(|&(_, &x)| x == b'X') + .unwrap(); + + // make err pointing at the X + let err = E { + src: SourceSpan::from((x_index, 1)), + }; + + let result = fmt_report_with_settings( + Report::new(err.clone()).with_source_code(invalid_source), + |handler| handler, + ); + + let expected = "decode_err + + × decoding error + ╭─[2:5] + 1 │ malformed + 2 │ h�XYZ + · ┬ + · ╰── valid data here + ╰──── +"; + + assert_eq!(expected, result); + + let result = fmt_report_with_settings( + Report::new(err).with_source_code(invalid_source), + |handler| handler.without_primary_span_start(), + ); + + let expected = "decode_err + + × decoding error + ╭──── + 1 │ malformed + 2 │ h�XYZ + · ┬ + · ╰── valid data here + ╰──── +"; + + assert_eq!(expected, result); +}