fix(graphical): Fix panic with span extending past end of line (#215)

This also changes the behavior with spans including a CRLF line-ending.
Before the panic bug was introduced, these were rendered with the CRLF
being two visual columns wide. Now, any span extending past the EOL is
treated as including one extra visual column.
This commit is contained in:
Benjamin Lee 2022-11-02 19:16:41 -07:00
parent 3e25fd5b86
commit 151a7a9540
No known key found for this signature in database
GPG Key ID: FB9624E2885D55A4
2 changed files with 90 additions and 2 deletions

View File

@ -621,8 +621,22 @@ impl GraphicalReportHandler {
let line_range = line.offset..=(line.offset + line.length);
assert!(line_range.contains(&offset));
let text = &line.text[..offset - line.offset];
self.line_visual_char_width(text).sum()
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.

View File

@ -358,6 +358,80 @@ fn single_line_higlight_offset_end_of_line() -> Result<(), MietteError> {
Ok(())
}
#[test]
fn single_line_higlight_include_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: (9, 5).into(),
};
let out = fmt_report(err.into());
println!("Error: {}", out);
let expected = r#"oops::my::bad
× oops!
[bad_file.rs:1:1]
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(())
}
#[test]
fn single_line_higlight_include_end_of_line_crlf() -> 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\r\n text\r\n here".to_string();
let err = MyBad {
src: NamedSource::new("bad_file.rs", src),
highlight: (10, 6).into(),
};
let out = fmt_report(err.into());
println!("Error: {}", out);
let expected = r#"oops::my::bad
× oops!
[bad_file.rs:1:1]
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(())
}
#[test]
fn single_line_highlight_with_empty_span() -> Result<(), MietteError> {
#[derive(Debug, Diagnostic, Error)]