mirror of https://github.com/zkat/miette.git
feat: Add show_related_errors_as_nested
This commit is contained in:
parent
c7eeada1e0
commit
2a7e32cdc7
|
|
@ -57,6 +57,7 @@ pub struct MietteHandlerOpts {
|
||||||
pub(crate) word_separator: Option<textwrap::WordSeparator>,
|
pub(crate) word_separator: Option<textwrap::WordSeparator>,
|
||||||
pub(crate) word_splitter: Option<textwrap::WordSplitter>,
|
pub(crate) word_splitter: Option<textwrap::WordSplitter>,
|
||||||
pub(crate) highlighter: Option<MietteHighlighter>,
|
pub(crate) highlighter: Option<MietteHighlighter>,
|
||||||
|
pub(crate) show_related_as_nested: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MietteHandlerOpts {
|
impl MietteHandlerOpts {
|
||||||
|
|
@ -167,6 +168,18 @@ impl MietteHandlerOpts {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Show related errors as siblings.
|
||||||
|
pub fn show_related_errors_as_siblings(mut self) -> Self {
|
||||||
|
self.show_related_as_nested = Some(false);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Show related errors as nested errors.
|
||||||
|
pub fn show_related_errors_as_nested(mut self) -> Self {
|
||||||
|
self.show_related_as_nested = Some(true);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// If true, colors will be used during graphical rendering, regardless
|
/// If true, colors will be used during graphical rendering, regardless
|
||||||
/// of whether or not the terminal supports them.
|
/// of whether or not the terminal supports them.
|
||||||
///
|
///
|
||||||
|
|
@ -332,6 +345,9 @@ impl MietteHandlerOpts {
|
||||||
if let Some(s) = self.word_splitter {
|
if let Some(s) = self.word_splitter {
|
||||||
handler = handler.with_word_splitter(s)
|
handler = handler.with_word_splitter(s)
|
||||||
}
|
}
|
||||||
|
if let Some(b) = self.show_related_as_nested {
|
||||||
|
handler = handler.with_show_related_as_nested(b)
|
||||||
|
}
|
||||||
|
|
||||||
MietteHandler {
|
MietteHandler {
|
||||||
inner: Box::new(handler),
|
inner: Box::new(handler),
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ pub struct GraphicalReportHandler {
|
||||||
pub(crate) word_splitter: Option<textwrap::WordSplitter>,
|
pub(crate) word_splitter: Option<textwrap::WordSplitter>,
|
||||||
pub(crate) highlighter: MietteHighlighter,
|
pub(crate) highlighter: MietteHighlighter,
|
||||||
pub(crate) link_display_text: Option<String>,
|
pub(crate) link_display_text: Option<String>,
|
||||||
|
pub(crate) show_related_as_nested: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
|
@ -64,6 +65,7 @@ impl GraphicalReportHandler {
|
||||||
word_splitter: None,
|
word_splitter: None,
|
||||||
highlighter: MietteHighlighter::default(),
|
highlighter: MietteHighlighter::default(),
|
||||||
link_display_text: None,
|
link_display_text: None,
|
||||||
|
show_related_as_nested: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -83,6 +85,7 @@ impl GraphicalReportHandler {
|
||||||
word_splitter: None,
|
word_splitter: None,
|
||||||
highlighter: MietteHighlighter::default(),
|
highlighter: MietteHighlighter::default(),
|
||||||
link_display_text: None,
|
link_display_text: None,
|
||||||
|
show_related_as_nested: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -177,6 +180,12 @@ impl GraphicalReportHandler {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets whether to render related errors as nested errors.
|
||||||
|
pub fn with_show_related_as_nested(mut self, show_related_as_nested: bool) -> Self {
|
||||||
|
self.show_related_as_nested = show_related_as_nested;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Enable syntax highlighting for source code snippets, using the given
|
/// Enable syntax highlighting for source code snippets, using the given
|
||||||
/// [`Highlighter`]. See the [highlighters](crate::highlighters) crate
|
/// [`Highlighter`]. See the [highlighters](crate::highlighters) crate
|
||||||
/// for more details.
|
/// for more details.
|
||||||
|
|
@ -414,23 +423,83 @@ impl GraphicalReportHandler {
|
||||||
diagnostic: &(dyn Diagnostic),
|
diagnostic: &(dyn Diagnostic),
|
||||||
parent_src: Option<&dyn SourceCode>,
|
parent_src: Option<&dyn SourceCode>,
|
||||||
) -> fmt::Result {
|
) -> fmt::Result {
|
||||||
|
let src = diagnostic.source_code().or(parent_src);
|
||||||
|
|
||||||
if let Some(related) = diagnostic.related() {
|
if let Some(related) = diagnostic.related() {
|
||||||
|
let severity_style = match diagnostic.severity() {
|
||||||
|
Some(Severity::Error) | None => self.theme.styles.error,
|
||||||
|
Some(Severity::Warning) => self.theme.styles.warning,
|
||||||
|
Some(Severity::Advice) => self.theme.styles.advice,
|
||||||
|
};
|
||||||
|
|
||||||
let mut inner_renderer = self.clone();
|
let mut inner_renderer = self.clone();
|
||||||
// Re-enable the printing of nested cause chains for related errors
|
// Re-enable the printing of nested cause chains for related errors
|
||||||
inner_renderer.with_cause_chain = true;
|
inner_renderer.with_cause_chain = true;
|
||||||
for rel in related {
|
if self.show_related_as_nested {
|
||||||
writeln!(f)?;
|
let width = self.termwidth.saturating_sub(2);
|
||||||
match rel.severity() {
|
let mut related = related.peekable();
|
||||||
Some(Severity::Error) | None => write!(f, "Error: ")?,
|
while let Some(rel) = related.next() {
|
||||||
Some(Severity::Warning) => write!(f, "Warning: ")?,
|
let is_last = related.peek().is_none();
|
||||||
Some(Severity::Advice) => write!(f, "Advice: ")?,
|
let char = if !is_last {
|
||||||
};
|
self.theme.characters.lcross
|
||||||
inner_renderer.render_header(f, rel)?;
|
} else {
|
||||||
let src = rel.source_code().or(parent_src);
|
self.theme.characters.lbot
|
||||||
inner_renderer.render_causes(f, rel, src)?;
|
};
|
||||||
inner_renderer.render_snippets(f, rel, src)?;
|
let initial_indent = format!(
|
||||||
inner_renderer.render_footer(f, rel)?;
|
" {}{}{} ",
|
||||||
inner_renderer.render_related(f, rel, src)?;
|
char, self.theme.characters.hbar, self.theme.characters.rarrow
|
||||||
|
)
|
||||||
|
.style(severity_style)
|
||||||
|
.to_string();
|
||||||
|
let rest_indent = format!(
|
||||||
|
" {} ",
|
||||||
|
if is_last {
|
||||||
|
' '
|
||||||
|
} else {
|
||||||
|
self.theme.characters.vbar
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.style(severity_style)
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let mut opts = textwrap::Options::new(width)
|
||||||
|
.initial_indent(&initial_indent)
|
||||||
|
.subsequent_indent(&rest_indent)
|
||||||
|
.break_words(self.break_words);
|
||||||
|
if let Some(word_separator) = self.word_separator {
|
||||||
|
opts = opts.word_separator(word_separator);
|
||||||
|
}
|
||||||
|
if let Some(word_splitter) = self.word_splitter.clone() {
|
||||||
|
opts = opts.word_splitter(word_splitter);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut inner = String::new();
|
||||||
|
|
||||||
|
let mut inner_renderer = self.clone();
|
||||||
|
inner_renderer.footer = None;
|
||||||
|
inner_renderer.with_cause_chain = false;
|
||||||
|
inner_renderer.termwidth -= rest_indent.width();
|
||||||
|
inner_renderer.render_report_inner(&mut inner, rel, src)?;
|
||||||
|
|
||||||
|
// If there was no header, remove the leading newline
|
||||||
|
let inner = inner.trim_matches('\n');
|
||||||
|
writeln!(f, "{}", self.wrap(inner, opts))?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for rel in related {
|
||||||
|
writeln!(f)?;
|
||||||
|
match rel.severity() {
|
||||||
|
Some(Severity::Error) | None => write!(f, "Error: ")?,
|
||||||
|
Some(Severity::Warning) => write!(f, "Warning: ")?,
|
||||||
|
Some(Severity::Advice) => write!(f, "Advice: ")?,
|
||||||
|
};
|
||||||
|
inner_renderer.render_header(f, rel)?;
|
||||||
|
let src = rel.source_code().or(parent_src);
|
||||||
|
inner_renderer.render_causes(f, rel, src)?;
|
||||||
|
inner_renderer.render_snippets(f, rel, src)?;
|
||||||
|
inner_renderer.render_footer(f, rel)?;
|
||||||
|
inner_renderer.render_related(f, rel, src)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -300,6 +300,64 @@ fn test_nested_cause_chains_for_related_errors_are_output() {
|
||||||
assert_eq!(expected, out);
|
assert_eq!(expected, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "fancy-no-backtrace")]
|
||||||
|
#[test]
|
||||||
|
fn test_display_related_errors_as_nested() {
|
||||||
|
let inner_error = TestStructError {
|
||||||
|
asdf_inner_foo: SourceError {
|
||||||
|
code: String::from("This is another error"),
|
||||||
|
help: String::from("You should fix this"),
|
||||||
|
label: (3, 4),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let first_error = NestedError {
|
||||||
|
code: String::from("right here"),
|
||||||
|
label: (6, 4),
|
||||||
|
the_other_err: Box::new(inner_error),
|
||||||
|
};
|
||||||
|
let second_error = SourceError {
|
||||||
|
code: String::from("You're actually a mess"),
|
||||||
|
help: String::from("Get a grip..."),
|
||||||
|
label: (3, 4),
|
||||||
|
};
|
||||||
|
let diag = MultiError {
|
||||||
|
related_errs: vec![
|
||||||
|
Box::new(MultiError {
|
||||||
|
related_errs: vec![Box::new(first_error), Box::new(AnErr)],
|
||||||
|
}),
|
||||||
|
Box::new(second_error),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut out = String::new();
|
||||||
|
miette::GraphicalReportHandler::new_themed(miette::GraphicalTheme::unicode_nocolor())
|
||||||
|
.with_width(80)
|
||||||
|
.with_show_related_as_nested(true)
|
||||||
|
.render_report(&mut out, &diag)
|
||||||
|
.unwrap();
|
||||||
|
println!("{}", out);
|
||||||
|
|
||||||
|
let expected = r#"
|
||||||
|
× A multi-error happened
|
||||||
|
├─▶ × A multi-error happened
|
||||||
|
│ ├─▶ × A nested error happened
|
||||||
|
│ │ ╭────
|
||||||
|
│ │ 1 │ right here
|
||||||
|
│ │ · ──┬─
|
||||||
|
│ │ · ╰── here
|
||||||
|
│ │ ╰────
|
||||||
|
│ ╰─▶ × AnErr
|
||||||
|
╰─▶ × A complex error happened
|
||||||
|
╭────
|
||||||
|
1 │ You're actually a mess
|
||||||
|
· ──┬─
|
||||||
|
· ╰── here
|
||||||
|
╰────
|
||||||
|
help: Get a grip...
|
||||||
|
"#;
|
||||||
|
assert_eq!(expected, out);
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "fancy-no-backtrace")]
|
#[cfg(feature = "fancy-no-backtrace")]
|
||||||
#[derive(Debug, miette::Diagnostic, thiserror::Error)]
|
#[derive(Debug, miette::Diagnostic, thiserror::Error)]
|
||||||
#[error("A case1 error happened")]
|
#[error("A case1 error happened")]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue