From b8810ee3d8aee7d7723e081616dd4f2fe8748abe Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Fri, 28 Oct 2022 16:20:10 -0700 Subject: [PATCH] fix(graphical): Fix panic with zero-width span at end of line (#204) This was introduced in 196c09ce7af9e54b63aaa5dae4cd199f2ecba3fa, and is a simple off-by-one error. --- src/handlers/graphical.rs | 6 ++-- tests/graphical.rs | 68 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/src/handlers/graphical.rs b/src/handlers/graphical.rs index 795aa1a..66760ff 100644 --- a/src/handlers/graphical.rs +++ b/src/handlers/graphical.rs @@ -618,7 +618,7 @@ impl GraphicalReportHandler { /// 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); + let line_range = line.offset..=(line.offset + line.length); assert!(line_range.contains(&offset)); let text = &line.text[..offset - line.offset]; @@ -657,9 +657,9 @@ impl GraphicalReportHandler { .iter() .map(|hl| { let byte_start = hl.offset(); - let byte_end = hl.offset() + hl.len().max(1); + 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); + let end = self.visual_offset(line, byte_end).max(start + 1); let vbar_offset = (start + end) / 2; let num_left = vbar_offset - start; diff --git a/tests/graphical.rs b/tests/graphical.rs index 420c9ae..dd18449 100644 --- a/tests/graphical.rs +++ b/tests/graphical.rs @@ -67,6 +67,38 @@ fn empty_source() -> Result<(), MietteError> { Ok(()) } +#[test] +fn single_line_highlight_span_full_line() { + #[derive(Error, Debug, Diagnostic)] + #[error("oops!")] + #[diagnostic(severity(Error))] + struct MyBad { + #[source_code] + src: NamedSource, + #[label("This bit here")] + bad_bit: SourceSpan, + } + let err = MyBad { + src: NamedSource::new("issue", "source\ntext"), + bad_bit: (7, 4).into(), + }; + let out = fmt_report(err.into()); + println!("Error: {}", out); + + let expected = r#" + × oops! + ╭─[issue:1:1] + 1 │ source + 2 │ text + · ──┬─ + · ╰── This bit here + ╰──── +"# + .to_string(); + + assert_eq!(expected, out); +} + #[test] fn single_line_with_wide_char() -> Result<(), MietteError> { #[derive(Debug, Diagnostic, Error)] @@ -290,6 +322,42 @@ fn single_line_highlight_offset_zero() -> Result<(), MietteError> { Ok(()) } +#[test] +fn single_line_higlight_offset_end_of_line() -> Result<(), MietteError> { + #[derive(Debug, Diagnostic, Error)] + #[error("oops!")] + #[diagnostic(code(oops::my::bad), help("try doing it better next time?"))] + struct MyBad { + #[source_code] + src: NamedSource, + #[label("this bit here")] + highlight: SourceSpan, + } + + let src = "source\n text\n here".to_string(); + let err = MyBad { + src: NamedSource::new("bad_file.rs", src), + highlight: (6, 0).into(), + }; + let out = fmt_report(err.into()); + println!("Error: {}", out); + let expected = r#"oops::my::bad + + × oops! + ╭─[bad_file.rs:1:1] + 1 │ source + · ▲ + · ╰── this bit here + 2 │ text + ╰──── + help: try doing it better next time? +"# + .trim_start() + .to_string(); + assert_eq!(expected, out); + Ok(()) +} + #[test] fn single_line_highlight_with_empty_span() -> Result<(), MietteError> { #[derive(Debug, Diagnostic, Error)]