From 3d6f903df0e7c9d0eb9a1fdbbf0028bab5496429 Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Thu, 9 Nov 2023 13:21:32 -0800 Subject: [PATCH] fix(formatting): Fix formatting bug when an empty span is not aligned to a char boundary (#314) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previous output looked like this: ---- single_line_with_wide_char_unaligned_span_empty stdout ---- Error: oops::my::bad × oops! ╭─[bad_file.rs:2:4] 1 │ source 2 │ 👼🏼text · ─▲ · ╰── this bit here 3 │ here ╰──── help: try doing it better next time? Note that the .max(start + 1) term is still necessary in the nonempty branch, since it's possible to have a nonempty span covering zero-width text. * remove uncessary if statement start > end in all cases. --- src/handlers/graphical.rs | 44 ++++++++++++++++++++------------------- tests/graphical.rs | 37 ++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 21 deletions(-) diff --git a/src/handlers/graphical.rs b/src/handlers/graphical.rs index 44d92b77..8cec88b2 100644 --- a/src/handlers/graphical.rs +++ b/src/handlers/graphical.rs @@ -718,31 +718,33 @@ impl GraphicalReportHandler { let byte_start = hl.offset(); let byte_end = hl.offset() + hl.len(); let start = self.visual_offset(line, byte_start, true).max(highest); - let end = self.visual_offset(line, byte_end, false).max(start + 1); + let end = if hl.len() == 0 { + start + 1 + } else { + self.visual_offset(line, byte_end, false).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(), - ); - } + 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) diff --git a/tests/graphical.rs b/tests/graphical.rs index 31db4dc1..d24879ad 100644 --- a/tests/graphical.rs +++ b/tests/graphical.rs @@ -1321,3 +1321,40 @@ fn single_line_with_wide_char_unaligned_span_end() -> Result<(), MietteError> { assert_eq!(expected, out); Ok(()) } + +#[test] +fn single_line_with_wide_char_unaligned_span_empty() -> 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: (10, 0).into(), + }; + let out = fmt_report(err.into()); + println!("Error: {}", out); + let expected = r#"oops::my::bad + + × oops! + ╭─[bad_file.rs:2:4] + 1 │ source + 2 │ 👼🏼text + · ▲ + · ╰── this bit here + 3 │ here + ╰──── + help: try doing it better next time? +"# + .trim_start() + .to_string(); + assert_eq!(expected, out); + Ok(()) +}