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)]