mirror of https://github.com/zkat/miette.git
fix(graphical): Fix panic with span extending past end of line (#221)
Fixes: #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:
parent
c88f0b5aa0
commit
8b56d277ef
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
Loading…
Reference in New Issue