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_splitter: Option<textwrap::WordSplitter>,
|
||||
pub(crate) highlighter: Option<MietteHighlighter>,
|
||||
pub(crate) show_related_as_nested: Option<bool>,
|
||||
}
|
||||
|
||||
impl MietteHandlerOpts {
|
||||
|
|
@ -167,6 +168,18 @@ impl MietteHandlerOpts {
|
|||
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
|
||||
/// of whether or not the terminal supports them.
|
||||
///
|
||||
|
|
@ -332,6 +345,9 @@ impl MietteHandlerOpts {
|
|||
if let Some(s) = self.word_splitter {
|
||||
handler = handler.with_word_splitter(s)
|
||||
}
|
||||
if let Some(b) = self.show_related_as_nested {
|
||||
handler = handler.with_show_related_as_nested(b)
|
||||
}
|
||||
|
||||
MietteHandler {
|
||||
inner: Box::new(handler),
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ pub struct GraphicalReportHandler {
|
|||
pub(crate) word_splitter: Option<textwrap::WordSplitter>,
|
||||
pub(crate) highlighter: MietteHighlighter,
|
||||
pub(crate) link_display_text: Option<String>,
|
||||
pub(crate) show_related_as_nested: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
|
|
@ -64,6 +65,7 @@ impl GraphicalReportHandler {
|
|||
word_splitter: None,
|
||||
highlighter: MietteHighlighter::default(),
|
||||
link_display_text: None,
|
||||
show_related_as_nested: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -83,6 +85,7 @@ impl GraphicalReportHandler {
|
|||
word_splitter: None,
|
||||
highlighter: MietteHighlighter::default(),
|
||||
link_display_text: None,
|
||||
show_related_as_nested: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -177,6 +180,12 @@ impl GraphicalReportHandler {
|
|||
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
|
||||
/// [`Highlighter`]. See the [highlighters](crate::highlighters) crate
|
||||
/// for more details.
|
||||
|
|
@ -414,23 +423,83 @@ impl GraphicalReportHandler {
|
|||
diagnostic: &(dyn Diagnostic),
|
||||
parent_src: Option<&dyn SourceCode>,
|
||||
) -> fmt::Result {
|
||||
let src = diagnostic.source_code().or(parent_src);
|
||||
|
||||
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();
|
||||
// Re-enable the printing of nested cause chains for related errors
|
||||
inner_renderer.with_cause_chain = true;
|
||||
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)?;
|
||||
if self.show_related_as_nested {
|
||||
let width = self.termwidth.saturating_sub(2);
|
||||
let mut related = related.peekable();
|
||||
while let Some(rel) = related.next() {
|
||||
let is_last = related.peek().is_none();
|
||||
let char = if !is_last {
|
||||
self.theme.characters.lcross
|
||||
} else {
|
||||
self.theme.characters.lbot
|
||||
};
|
||||
let initial_indent = format!(
|
||||
" {}{}{} ",
|
||||
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(())
|
||||
|
|
|
|||
|
|
@ -300,6 +300,64 @@ fn test_nested_cause_chains_for_related_errors_are_output() {
|
|||
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")]
|
||||
#[derive(Debug, miette::Diagnostic, thiserror::Error)]
|
||||
#[error("A case1 error happened")]
|
||||
|
|
|
|||
Loading…
Reference in New Issue