diff --git a/src/eyreish/wrapper.rs b/src/eyreish/wrapper.rs index 6e65eb7..d940fed 100644 --- a/src/eyreish/wrapper.rs +++ b/src/eyreish/wrapper.rs @@ -6,35 +6,9 @@ use crate::{Diagnostic, LabeledSpan, Report, SourceCode}; use crate as miette; -#[repr(transparent)] -pub(crate) struct DisplayError(pub(crate) M); - #[repr(transparent)] pub(crate) struct MessageError(pub(crate) M); -pub(crate) struct NoneError; - -impl Debug for DisplayError -where - M: Display, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - Display::fmt(&self.0, f) - } -} - -impl Display for DisplayError -where - M: Display, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - Display::fmt(&self.0, f) - } -} - -impl StdError for DisplayError where M: Display + 'static {} -impl Diagnostic for DisplayError where M: Display + 'static {} - impl Debug for MessageError where M: Display + Debug, @@ -56,21 +30,6 @@ where impl StdError for MessageError where M: Display + Debug + 'static {} impl Diagnostic for MessageError where M: Display + Debug + 'static {} -impl Debug for NoneError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - Debug::fmt("Option was None", f) - } -} - -impl Display for NoneError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - Display::fmt("Option was None", f) - } -} - -impl StdError for NoneError {} -impl Diagnostic for NoneError {} - #[repr(transparent)] pub(crate) struct BoxedError(pub(crate) Box); diff --git a/src/handlers/graphical.rs b/src/handlers/graphical.rs index a4474f9..9c1d7ab 100644 --- a/src/handlers/graphical.rs +++ b/src/handlers/graphical.rs @@ -1,7 +1,7 @@ use std::fmt::{self, Write}; use owo_colors::{OwoColorize, Style, StyledList}; -use unicode_width::UnicodeWidthChar; +use unicode_width::{UnicodeWidthChar, UnicodeWidthStr}; use crate::diagnostic_chain::{DiagnosticChain, ErrorKind}; use crate::handlers::theme::*; @@ -225,7 +225,7 @@ impl GraphicalReportHandler { self.render_related(f, diagnostic, src)?; if let Some(footer) = &self.footer { writeln!(f)?; - let width = self.termwidth.saturating_sub(4); + let width = self.termwidth.saturating_sub(2); let mut opts = textwrap::Options::new(width) .initial_indent(" ") .subsequent_indent(" ") @@ -265,7 +265,6 @@ impl GraphicalReportHandler { ); write!(header, "{}", link)?; writeln!(f, "{}", header)?; - writeln!(f)?; } else if let Some(code) = diagnostic.code() { write!(header, "{}", code.style(severity_style),)?; if self.links == LinkStyle::Text && diagnostic.url().is_some() { @@ -273,8 +272,8 @@ impl GraphicalReportHandler { write!(header, " ({})", url.style(self.theme.styles.link))?; } writeln!(f, "{}", header)?; - writeln!(f)?; } + writeln!(f)?; Ok(()) } @@ -354,8 +353,12 @@ impl GraphicalReportHandler { inner_renderer.footer = None; // Cause chains are already flattened, so don't double-print the nested error inner_renderer.with_cause_chain = false; + // Since everything from here on is indented, shrink the virtual terminal + inner_renderer.termwidth -= rest_indent.width(); inner_renderer.render_report(&mut inner, diag)?; + // If there was no header, remove the leading newline + let inner = inner.trim_start_matches('\n'); writeln!(f, "{}", self.wrap(&inner, opts))?; } ErrorKind::StdError(err) => { @@ -370,7 +373,7 @@ impl GraphicalReportHandler { fn render_footer(&self, f: &mut impl fmt::Write, diagnostic: &(dyn Diagnostic)) -> fmt::Result { if let Some(help) = diagnostic.help() { - let width = self.termwidth.saturating_sub(4); + let width = self.termwidth.saturating_sub(2); let initial_indent = " help: ".style(self.theme.styles.help).to_string(); let mut opts = textwrap::Options::new(width) .initial_indent(&initial_indent) @@ -398,8 +401,8 @@ impl GraphicalReportHandler { let mut inner_renderer = self.clone(); // Re-enable the printing of nested cause chains for related errors inner_renderer.with_cause_chain = true; - writeln!(f)?; for rel in related { + writeln!(f)?; match rel.severity() { Some(Severity::Error) | None => write!(f, "Error: ")?, Some(Severity::Warning) => write!(f, "Warning: ")?, diff --git a/tests/derive.rs b/tests/derive.rs index ac29eee..aa631dc 100644 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -6,12 +6,14 @@ fn related() { #[derive(Error, Debug, Diagnostic)] #[error("welp")] #[diagnostic(code(foo::bar::baz))] + #[allow(dead_code)] struct Foo { #[related] related: Vec, } #[derive(Error, Debug, Diagnostic)] + #[allow(dead_code)] enum Bar { #[error("variant1")] #[diagnostic(code(foo::bar::baz))] @@ -29,6 +31,7 @@ fn related() { #[derive(Error, Debug, Diagnostic)] #[error("welp2")] + #[allow(dead_code)] struct Baz; } @@ -37,6 +40,7 @@ fn related_report() { #[derive(Error, Debug, Diagnostic)] #[error("welp")] #[diagnostic(code(foo::bar::baz))] + #[allow(dead_code)] struct Foo { #[related] related: Vec, @@ -288,6 +292,7 @@ fn test_snippet_named_struct() { #[derive(Debug, Diagnostic, Error)] #[error("welp")] #[diagnostic(code(foo::bar::baz))] + #[allow(dead_code)] struct Foo<'a> { #[source_code] src: &'a str, @@ -310,6 +315,7 @@ fn test_snippet_unnamed_struct() { #[derive(Debug, Diagnostic, Error)] #[error("welp")] #[diagnostic(code(foo::bar::baz))] + #[allow(dead_code)] struct Foo<'a>( #[source_code] &'a str, #[label("{0}")] SourceSpan, diff --git a/tests/graphical.rs b/tests/graphical.rs index 4763117..c6b5e1a 100644 --- a/tests/graphical.rs +++ b/tests/graphical.rs @@ -59,14 +59,15 @@ fn word_wrap_options() -> Result<(), MietteError> { let out = fmt_report_with_settings(Report::msg("abcdefghijklmnopqrstuvwxyz"), |handler| handler); - let expected = " × abcdefghijklmnopqrstuvwxyz\n".to_string(); + let expected = "\n × abcdefghijklmnopqrstuvwxyz\n".to_string(); assert_eq!(expected, out); // A long word can break with a smaller width let out = fmt_report_with_settings(Report::msg("abcdefghijklmnopqrstuvwxyz"), |handler| { handler.with_width(10) }); - let expected = r#" × abcd + let expected = r#" + × abcd │ efgh │ ijkl │ mnop @@ -81,7 +82,7 @@ fn word_wrap_options() -> Result<(), MietteError> { let out = fmt_report_with_settings(Report::msg("abcdefghijklmnopqrstuvwxyz"), |handler| { handler.with_width(10).with_break_words(false) }); - let expected = " × abcdefghijklmnopqrstuvwxyz\n".to_string(); + let expected = "\n × abcdefghijklmnopqrstuvwxyz\n".to_string(); assert_eq!(expected, out); // Breaks should start at the boundary of each word if possible @@ -89,7 +90,8 @@ fn word_wrap_options() -> Result<(), MietteError> { Report::msg("12 123 1234 12345 123456 1234567 1234567890"), |handler| handler.with_width(10), ); - let expected = r#" × 12 + let expected = r#" + × 12 │ 123 │ 1234 │ 1234 @@ -110,7 +112,8 @@ fn word_wrap_options() -> Result<(), MietteError> { Report::msg("12 123 1234 12345 123456 1234567 1234567890"), |handler| handler.with_width(10).with_break_words(false), ); - let expected = r#" × 12 + let expected = r#" + × 12 │ 123 │ 1234 │ 12345 @@ -126,7 +129,8 @@ fn word_wrap_options() -> Result<(), MietteError> { Report::msg("a-b a-b-c a-b-c-d a-b-c-d-e a-b-c-d-e-f a-b-c-d-e-f-g a-b-c-d-e-f-g-h"), |handler| handler.with_width(10).with_break_words(false), ); - let expected = r#" × a-b + let expected = r#" + × a-b │ a-b- │ c a- │ b-c- @@ -158,7 +162,8 @@ fn word_wrap_options() -> Result<(), MietteError> { .with_word_splitter(textwrap::WordSplitter::NoHyphenation) }, ); - let expected = r#" × a-b + let expected = r#" + × a-b │ a-b-c │ a-b-c-d │ a-b-c-d-e @@ -174,7 +179,8 @@ fn word_wrap_options() -> Result<(), MietteError> { Report::msg("a/b a/b/c a/b/c/d a/b/c/d/e a/b/c/d/e/f a/b/c/d/e/f/g a/b/c/d/e/f/g/h"), |handler| handler.with_width(10).with_break_words(false), ); - let expected = r#" × a/b + let expected = r#" + × a/b │ a/b/ │ c a/ │ b/c/ @@ -206,7 +212,8 @@ fn word_wrap_options() -> Result<(), MietteError> { .with_word_separator(textwrap::WordSeparator::AsciiSpace) }, ); - let expected = r#" × a/b + let expected = r#" + × a/b │ a/b/c │ a/b/c/d │ a/b/c/d/e @@ -227,7 +234,8 @@ fn wrap_option() -> Result<(), MietteError> { Report::msg("abc def ghi jkl mno pqr stu vwx yz abc def ghi jkl mno pqr stu vwx yz"), |handler| handler.with_width(15), ); - let expected = r#" × abc def + let expected = r#" + × abc def │ ghi jkl │ mno pqr │ stu vwx @@ -246,7 +254,7 @@ fn wrap_option() -> Result<(), MietteError> { |handler| handler.with_width(15).with_wrap_lines(false), ); let expected = - " × abc def ghi jkl mno pqr stu vwx yz abc def ghi jkl mno pqr stu vwx yz\n".to_string(); + "\n × abc def ghi jkl mno pqr stu vwx yz abc def ghi jkl mno pqr stu vwx yz\n".to_string(); assert_eq!(expected, out); // Then, user-defined new lines should be preserved wrapping is disabled @@ -254,7 +262,8 @@ fn wrap_option() -> Result<(), MietteError> { Report::msg("abc def ghi jkl mno pqr stu vwx yz\nabc def ghi jkl mno pqr stu vwx yz\nabc def ghi jkl mno pqr stu vwx yz"), |handler| handler.with_width(15).with_wrap_lines(false), ); - let expected = r#" × abc def ghi jkl mno pqr stu vwx yz + let expected = r#" + × abc def ghi jkl mno pqr stu vwx yz │ abc def ghi jkl mno pqr stu vwx yz │ abc def ghi jkl mno pqr stu vwx yz "# @@ -264,6 +273,143 @@ fn wrap_option() -> Result<(), MietteError> { Ok(()) } +#[test] +fn wrapping_nested_errors() -> Result<(), MietteError> { + #[derive(Debug, Diagnostic, Error)] + #[error("This is the parent error, the error withhhhh the children, kiddos, pups, as it were, and so on...")] + #[diagnostic( + code(mama::error), + help( + "try doing it better next time? I mean, you could have also done better thisssss time, but no?" + ) + )] + struct MamaError { + #[diagnostic_source] + baby: BabyError, + } + + #[derive(Debug, Diagnostic, Error)] + #[error("Wah wah: I may be small, but I'll cause a proper bout of trouble — justt try wrapping this mess of a line, buddo!")] + #[diagnostic( + code(baby::error), + help( + "it cannot be helped... woulddddddd you really want to get rid of an error that's so cute?" + ) + )] + struct BabyError; + + let err = MamaError { baby: BabyError }; + let out = fmt_report_with_settings(err.into(), |handler| handler.with_width(50)); + let expected = r#"mama::error + + × This is the parent error, the error withhhhh + │ the children, kiddos, pups, as it were, and + │ so on... + ╰─▶ baby::error + + × Wah wah: I may be small, but I'll + │ cause a proper bout of trouble — justt + │ try wrapping this mess of a line, + │ buddo! + help: it cannot be helped... woulddddddd + you really want to get rid of an + error that's so cute? + + help: try doing it better next time? I mean, + you could have also done better thisssss + time, but no? +"#; + assert_eq!(expected, out); + Ok(()) +} + +#[test] +fn wrapping_related_errors() -> Result<(), MietteError> { + #[derive(Debug, Diagnostic, Error)] + #[error("This is the parent error, the error withhhhh the children, kiddos, pups, as it were, and so on...")] + #[diagnostic( + code(mama::error), + help( + "try doing it better next time? I mean, you could have also done better thisssss time, but no?" + ) + )] + struct MamaError { + #[diagnostic_source] + baby: BrotherError, + } + + #[derive(Debug, Diagnostic, Error)] + #[error("Welcome to the brother-error brotherhood — where all of the wee baby errors join into a formidable force")] + #[diagnostic(code(brother::error))] + struct BrotherError { + #[related] + brethren: Vec>, + } + + #[derive(Debug, Diagnostic, Error)] + #[error("Wah wah: I may be small, but I'll cause a proper bout of trouble — justt try wrapping this mess of a line, buddo!")] + #[diagnostic(help( + "it cannot be helped... woulddddddd you really want to get rid of an error that's so cute?" + ))] + struct BabyError; + + #[derive(Debug, Diagnostic, Error)] + #[error("Wah wah: I may be small, but I'll cause a proper bout of trouble — justt try wrapping this mess of a line, buddo!")] + #[diagnostic(severity(Warning))] + struct BabyWarning; + + #[derive(Debug, Diagnostic, Error)] + #[error("Wah wah: I may be small, but I'll cause a proper bout of trouble — justt try wrapping this mess of a line, buddo!")] + #[diagnostic(severity(Advice))] + struct BabyAdvice; + + let err = MamaError { + baby: BrotherError { + brethren: vec![BabyError.into(), BabyWarning.into(), BabyAdvice.into()], + }, + }; + let out = fmt_report_with_settings(err.into(), |handler| handler.with_width(50)); + let expected = r#"mama::error + + × This is the parent error, the error withhhhh + │ the children, kiddos, pups, as it were, and + │ so on... + ╰─▶ brother::error + + × Welcome to the brother-error + │ brotherhood — where all of the wee + │ baby errors join into a formidable + │ force + + Error: + × Wah wah: I may be small, but I'll + │ cause a proper bout of trouble — justt + │ try wrapping this mess of a line, + │ buddo! + help: it cannot be helped... woulddddddd + you really want to get rid of an + error that's so cute? + + Warning: + ⚠ Wah wah: I may be small, but I'll + │ cause a proper bout of trouble — justt + │ try wrapping this mess of a line, + │ buddo! + + Advice: + ☞ Wah wah: I may be small, but I'll + │ cause a proper bout of trouble — justt + │ try wrapping this mess of a line, + │ buddo! + + help: try doing it better next time? I mean, + you could have also done better thisssss + time, but no? +"#; + assert_eq!(expected, out); + Ok(()) +} + #[test] fn empty_source() -> Result<(), MietteError> { #[derive(Debug, Diagnostic, Error)] @@ -326,7 +472,8 @@ if true { let out = fmt_report(err.into()); println!("Error: {}", out); - let expected = r#" × oops! + let expected = r#" + × oops! ╭─[issue:1:1] 1 │ ╭─▶ if true { 2 │ │ a @@ -361,7 +508,8 @@ fn single_line_highlight_span_full_line() { let out = fmt_report(err.into()); println!("Error: {}", out); - let expected = r#" × oops! + let expected = r#" + × oops! ╭─[issue:2:1] 1 │ source 2 │ text @@ -1411,7 +1559,6 @@ Error: oops::my::bad 2 │ text ╰──── help: try doing it better next time? - "# .trim_start() .to_string(); @@ -1578,6 +1725,7 @@ Error: oops::my::related::error 2 │ text ╰──── help: try doing it better next time? + Warning: oops::my::related::warning ⚠ oops! @@ -1588,6 +1736,7 @@ Warning: oops::my::related::warning 2 │ text ╰──── help: try doing it better next time? + Advice: oops::my::related::advice ☞ oops! @@ -1623,7 +1772,8 @@ fn zero_length_eol_span() { let out = fmt_report(err.into()); println!("Error: {}", out); - let expected = r#" × oops! + let expected = r#" + × oops! ╭─[issue:2:1] 1 │ this is the first line 2 │ this is the second line @@ -1657,7 +1807,8 @@ fn primary_label() { println!("Error: {}", out); // line 2 should be the primary, not line 1 - let expected = r#" × oops! + let expected = r#" + × oops! ╭─[issue:2:2] 1 │ this is the first line · ──── @@ -1808,7 +1959,8 @@ fn syntax_highlighter() { GraphicalReportHandler::new_themed(GraphicalTheme::unicode()) .render_report(&mut out, &err) .unwrap(); - let expected = r#" × This is an error + let expected = r#" + × This is an error ╭─[hello_world:2:5] 1 │ fn main() { 2 │ println!("Hello, World!"); @@ -1862,7 +2014,8 @@ fn syntax_highlighter_on_real_file() { .unwrap(); let expected = format!( - r#" × This is an error + r#" + × This is an error ╭─[{filename}:{l2}:{CO}] {l1} │ {l2} │ let (filename, line) = (file!(), line!() as usize); diff --git a/tests/test_diagnostic_source_macro.rs b/tests/test_diagnostic_source_macro.rs index 939812c..4f15ea0 100644 --- a/tests/test_diagnostic_source_macro.rs +++ b/tests/test_diagnostic_source_macro.rs @@ -104,7 +104,8 @@ fn test_diagnostic_source_pass_extra_info() { .render_report(&mut out, &diag) .unwrap(); println!("Error: {}", out); - let expected = r#" × TestError + let expected = r#" + × TestError ╰─▶ × A complex error happened ╭─[1:2] 1 │ Hello @@ -138,7 +139,8 @@ fn test_diagnostic_source_is_output() { .unwrap(); println!("{}", out); - let expected = r#" × TestError + let expected = r#" + × TestError ╰─▶ × A complex error happened ╭──── 1 │ right here @@ -186,7 +188,8 @@ fn test_nested_diagnostic_source_is_output() { .unwrap(); println!("{}", out); - let expected = r#" × A nested error happened + let expected = r#" + × A nested error happened ├─▶ × TestError │ ╰─▶ × A complex error happened @@ -252,10 +255,12 @@ fn test_nested_cause_chains_for_related_errors_are_output() { .unwrap(); println!("{}", out); - let expected = r#" × A nested error happened + let expected = r#" + × A nested error happened ╰─▶ × A multi-error happened - Error: × A nested error happened + Error: + × A nested error happened ├─▶ × TestError │ ╰─▶ × A complex error happened @@ -271,7 +276,9 @@ fn test_nested_cause_chains_for_related_errors_are_output() { · ──┬─ · ╰── here ╰──── - Error: × A complex error happened + + Error: + × A complex error happened ╭──── 1 │ You're actually a mess · ──┬─