-
-
Notifications
You must be signed in to change notification settings - Fork 103
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Group related diagnostics visually #171
base: main
Are you sure you want to change the base?
Changes from all commits
8ba24da
3914726
1643cab
db8949f
54ede01
2b9b6af
c7ebcaf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ use miette::{ | |
NarratableReportHandler, Report, SourceSpan, | ||
}; | ||
use thiserror::Error; | ||
use pretty_assertions::assert_eq; | ||
|
||
fn fmt_report(diag: Report) -> String { | ||
let mut out = String::new(); | ||
|
@@ -784,11 +785,22 @@ fn related() -> Result<(), MietteError> { | |
let err = MyBad { | ||
src: NamedSource::new("bad_file.rs", src.clone()), | ||
highlight: (9, 4).into(), | ||
related: vec![MyBad { | ||
src: NamedSource::new("bad_file.rs", src), | ||
highlight: (0, 6).into(), | ||
related: vec![], | ||
}], | ||
related: vec![ | ||
MyBad { | ||
src: NamedSource::new("bad_file.rs", src.clone()), | ||
highlight: (0, 6).into(), | ||
related: vec![MyBad { | ||
src: NamedSource::new("bad_file.rs", src.clone()), | ||
highlight: (0, 6).into(), | ||
related: vec![], | ||
}], | ||
}, | ||
MyBad { | ||
src: NamedSource::new("bad_file.rs", src.clone()), | ||
highlight: (0, 6).into(), | ||
related: vec![], | ||
}, | ||
], | ||
}; | ||
let out = fmt_report(err.into()); | ||
println!("Error: {}", out); | ||
|
@@ -803,18 +815,46 @@ fn related() -> Result<(), MietteError> { | |
3 │ here | ||
╰──── | ||
help: try doing it better next time? | ||
|
||
Error: oops::my::bad | ||
|
||
× oops! | ||
╭─[bad_file.rs:1:1] | ||
1 │ source | ||
· ───┬── | ||
· ╰── this bit here | ||
2 │ text | ||
╰──── | ||
help: try doing it better next time? | ||
|
||
╭─There were 2 related diagnostics: | ||
├─ 1.Error: oops::my::bad | ||
│ | ||
│ | ||
│ × oops! | ||
│ ╭─[bad_file.rs:1:1] | ||
│ 1 │ source | ||
│ · ───┬── | ||
│ · ╰── this bit here | ||
│ 2 │ text | ||
│ ╰──── | ||
│ help: try doing it better next time? | ||
│ ╭─There were 1 related diagnostics: | ||
│ ├─ 1.Error: oops::my::bad | ||
│ │ | ||
│ │ | ||
│ │ × oops! | ||
│ │ ╭─[bad_file.rs:1:1] | ||
│ │ 1 │ source | ||
│ │ · ───┬── | ||
│ │ · ╰── this bit here | ||
│ │ 2 │ text | ||
│ │ ╰──── | ||
│ │ help: try doing it better next time? | ||
│ │ ├─There were 0 related diagnostics: | ||
│ │ | ||
│ | ||
├─ 2.Error: oops::my::bad | ||
│ | ||
│ | ||
│ × oops! | ||
│ ╭─[bad_file.rs:1:1] | ||
│ 1 │ source | ||
│ · ───┬── | ||
│ · ╰── this bit here | ||
│ 2 │ text | ||
│ ╰──── | ||
│ help: try doing it better next time? | ||
│ ├─There were 0 related diagnostics: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd expect this line to simply be omitted when there are no related diagnostics (what I expect to be the most common case to be fair). |
||
│ | ||
"# | ||
.trim_start() | ||
.to_string(); | ||
|
@@ -865,16 +905,18 @@ fn related_source_code_propagation() -> Result<(), MietteError> { | |
3 │ here | ||
╰──── | ||
help: try doing it better next time? | ||
|
||
Error: oops::my::bad | ||
|
||
× oops! | ||
╭─[bad_file.rs:1:1] | ||
1 │ source | ||
· ───┬── | ||
· ╰── this bit here | ||
2 │ text | ||
╰──── | ||
╭─There were 1 related diagnostics: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The vertical I wonder if this could be helped by nesting the vertical line itself a bit more - e.g.
|
||
├─ 1.Error: oops::my::bad | ||
│ | ||
│ | ||
│ × oops! | ||
│ ╭─[bad_file.rs:1:1] | ||
│ 1 │ source | ||
│ · ───┬── | ||
│ · ╰── this bit here | ||
│ 2 │ text | ||
│ ╰──── | ||
│ | ||
"# | ||
.trim_start() | ||
.to_string(); | ||
|
@@ -900,8 +942,7 @@ fn zero_length_eol_span() { | |
let out = fmt_report(err.into()); | ||
println!("Error: {}", out); | ||
|
||
let expected = r#" | ||
× oops! | ||
let expected = r#" × oops! | ||
╭─[issue:1:1] | ||
1 │ this is the first line | ||
2 │ this is the second line | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,103 @@ | ||
use miette::Diagnostic; | ||
|
||
#[derive(Debug, miette::Diagnostic, thiserror::Error)] | ||
#[error("A complex error happened")] | ||
struct SourceError { | ||
#[source_code] | ||
code: String, | ||
#[help] | ||
help: String, | ||
#[label("here")] | ||
label: (usize, usize), | ||
} | ||
|
||
#[derive(Debug, miette::Diagnostic, thiserror::Error)] | ||
#[error("AnErr")] | ||
struct AnErr; | ||
|
||
#[derive(Debug, miette::Diagnostic, thiserror::Error)] | ||
#[error("TestError")] | ||
struct TestError { | ||
struct TestStructError { | ||
#[diagnostic_source] | ||
asdf_inner_foo: AnErr, | ||
} | ||
|
||
#[derive(Debug, miette::Diagnostic, thiserror::Error)] | ||
#[error("TestError")] | ||
enum TestEnumError { | ||
Without, | ||
WithTuple(#[diagnostic_source] AnErr), | ||
WithStruct { | ||
#[diagnostic_source] | ||
inner: AnErr, | ||
}, | ||
} | ||
|
||
#[derive(Debug, miette::Diagnostic, thiserror::Error)] | ||
#[error("TestError")] | ||
struct TestTupleError(#[diagnostic_source] AnErr); | ||
|
||
#[derive(Debug, miette::Diagnostic, thiserror::Error)] | ||
#[error("TestError")] | ||
struct TestBoxedError(#[diagnostic_source] Box<dyn Diagnostic>); | ||
|
||
#[derive(Debug, miette::Diagnostic, thiserror::Error)] | ||
#[error("TestError")] | ||
struct TestArcedError(#[diagnostic_source] std::sync::Arc<dyn Diagnostic>); | ||
|
||
#[test] | ||
fn test_diagnostic_source() { | ||
let error = TestError { | ||
let error = TestStructError { | ||
asdf_inner_foo: AnErr, | ||
}; | ||
assert!(error.diagnostic_source().is_some()); | ||
|
||
let error = TestEnumError::Without; | ||
assert!(error.diagnostic_source().is_none()); | ||
|
||
let error = TestEnumError::WithTuple(AnErr); | ||
assert!(error.diagnostic_source().is_some()); | ||
|
||
let error = TestEnumError::WithStruct { inner: AnErr }; | ||
assert!(error.diagnostic_source().is_some()); | ||
|
||
let error = TestTupleError(AnErr); | ||
assert!(error.diagnostic_source().is_some()); | ||
|
||
let error = TestBoxedError(Box::new(AnErr)); | ||
assert!(error.diagnostic_source().is_some()); | ||
|
||
let error = TestArcedError(std::sync::Arc::new(AnErr)); | ||
assert!(error.diagnostic_source().is_some()); | ||
} | ||
|
||
#[test] | ||
fn test_diagnostic_source_pass_extra_info() { | ||
let diag = TestBoxedError(Box::new(SourceError { | ||
code: String::from("Hello\nWorld!"), | ||
help: format!("Have you tried turning it on and off again?"), | ||
label: (1, 4), | ||
})); | ||
let mut out = String::new(); | ||
miette::GraphicalReportHandler::new_themed(miette::GraphicalTheme::unicode_nocolor()) | ||
.with_width(80) | ||
.with_footer("this is a footer".into()) | ||
.render_report(&mut out, &diag) | ||
.unwrap(); | ||
println!("Error: {}", out); | ||
let expected = r#" × TestError | ||
╰─▶ × A complex error happened | ||
╭─[1:1] | ||
1 │ Hello | ||
· ──┬─ | ||
· ╰── here | ||
2 │ World! | ||
╰──── | ||
help: Have you tried turning it on and off again? | ||
|
||
|
||
this is a footer | ||
"# | ||
.to_string(); | ||
assert_eq!(expected, out); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is doubling the number of empty new lines deliberate or an oversight?
It does hurt the sense of visual grouping for me.