From 61283e9efe2825425c41027b3dbb5f4f9c9d83fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kat=20March=C3=A1n?= Date: Sun, 22 Aug 2021 19:06:26 -0700 Subject: [PATCH] feat(snippets): Overhauled how snippets handle labels, sources, and messages, including the derive macro BREAKING CHANGE: this will probably break a lot of your stuff. hopefully rustc helps --- README.md | 41 +++-- miette-derive/src/diagnostic.rs | 2 +- miette-derive/src/snippets.rs | 251 ++++++++++++++++++++---------- src/printer/graphical_printer.rs | 15 +- src/printer/mod.rs | 42 +---- src/printer/narratable_printer.rs | 10 +- src/protocol.rs | 55 +------ src/utils.rs | 39 ++++- tests/derive.rs | 32 ++-- tests/narrated.rs | 126 +++++++-------- tests/printer.rs | 126 +++++++-------- 11 files changed, 392 insertions(+), 347 deletions(-) diff --git a/README.md b/README.md index 0483998..a4ad50d 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ diagnostic error code: ruget::api::bad_json - [Features](#features) - [Installing](#installing) - [Example](#example) -- [Usage](#usage) +- [Using](#using) - [... in libraries](#-in-libraries) - [... in application code](#-in-application-code) - [... in `main()`](#-in-main) @@ -85,11 +85,12 @@ use thiserror::Error; )] struct MyBad { // The Source that we're gonna be printing snippets out of. - src: String, + // This can be a String if you don't have or care about file names. + src: NamedSource, // Snippets and highlights can be included in the diagnostic! - #[snippet(src, "This is the part that broke")] + #[snippet(src, message("This is the part that broke"))] snip: SourceSpan, - #[highlight(snip)] + #[highlight(snip, label("This bit here"))] bad_bit: SourceSpan, } @@ -100,7 +101,7 @@ Use this DiagnosticResult type (or its expanded version) as the return type throughout your app (but NOT your libraries! Those should always return concrete types!). */ -use miette::DiagnosticResult; +use miette::{DiagnosticResult, NamedSource}; fn this_fails() -> DiagnosticResult<()> { // You can use plain strings as a `Source`, or anything that implements // the one-method `Source` trait. @@ -108,9 +109,9 @@ fn this_fails() -> DiagnosticResult<()> { let len = src.len(); Err(MyBad { - src, - snip: ("bad_file.rs", 0, len).into(), - bad_bit: ("this bit here", 9, 4).into(), + src: NamedSource::new("bad_file.rs", src), + snip: (0, len).into(), + bad_bit: (9, 4).into(), })?; Ok(()) @@ -149,7 +150,7 @@ diagnostic help: try doing it better next time? diagnostic error code: oops::my::bad "> -## Usage +## Using ### ... in libraries @@ -244,6 +245,7 @@ use miette::Diagnostic; use thiserror::Error; #[derive(Error, Diagnostic, Debug)] +#[error("kaboom")] #[diagnostic( code(my_app::my_error), // You can do formatting! @@ -268,6 +270,7 @@ use thiserror::Error; // Will link users to https://docs.rs/my_crate/0.0.0/my_crate/struct.MyErr.html url(docsrs) )] +#[error("kaboom")] struct MyErr; ``` @@ -297,17 +300,25 @@ pub struct MyErrorType { // The `Source` that miette will use. src: String, - // A snippet that points to `src`, our `Source`. The filename can be - // provided at the callsite. - #[snippet(src, "This is the snippet")] + // A snippet that points to `src`, our `Source`. + #[snippet( + src, + message("This is the snippet") + )] snip: SourceSpan, // A highlight for the `snip` snippet we defined above. This will // underline/mark the specific code inside the larger snippet context. - // - // The label is provided using `SourceSpan`'s label. - #[highlight(snip)] + #[highlight(snip, label("This is the highlight"))] err_span: SourceSpan, + + // You can add as many snippets as you want against the same Source. + // They'll be rendered sequentially. + #[snippet( + src, + message("This is a warning") + )] + snip2: SourceSpan, } ``` diff --git a/miette-derive/src/diagnostic.rs b/miette-derive/src/diagnostic.rs index 421da7f..ce61085 100644 --- a/miette-derive/src/diagnostic.rs +++ b/miette-derive/src/diagnostic.rs @@ -166,7 +166,7 @@ impl Diagnostic { let code_body = code.gen_struct(); let help_body = help.as_ref().and_then(|x| x.gen_struct(fields)); let sev_body = severity.as_ref().and_then(|x| x.gen_struct()); - let snip_body = snippets.as_ref().and_then(|x| x.gen_struct()); + let snip_body = snippets.as_ref().and_then(|x| x.gen_struct(fields)); let url_body = url.as_ref().and_then(|x| x.gen_struct(ident, fields)); quote! { diff --git a/miette-derive/src/snippets.rs b/miette-derive/src/snippets.rs index 9957581..62e06ed 100644 --- a/miette-derive/src/snippets.rs +++ b/miette-derive/src/snippets.rs @@ -1,72 +1,136 @@ -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use proc_macro2::TokenStream; use quote::{format_ident, quote}; use syn::{ + parenthesized, parse::{Parse, ParseStream}, - punctuated::Punctuated, spanned::Spanned, Token, }; -use crate::diagnostic::DiagnosticVariant; -use crate::utils::MemberOrString; +use crate::fmt; +use crate::{diagnostic::DiagnosticVariant, fmt::Display}; pub struct Snippets(Vec); struct Snippet { - message: Option, + message: Option, highlights: Vec, source: syn::Member, snippet: syn::Member, } struct Highlight { + label: Option, highlight: syn::Member, } struct SnippetAttr { source: syn::Member, - message: Option, + message: Option, } struct HighlightAttr { + label: Option, snippet: syn::Member, } impl Parse for SnippetAttr { fn parse(input: ParseStream) -> syn::Result { - let punc = Punctuated::::parse_terminated(input)?; - let span = input.span(); - let mut iter = punc.into_iter(); - let source = match iter.next() { - Some(MemberOrString::Member(member)) => member, - _ => { + let source = input.parse::()?; + let message = if input.peek(Token![,]) { + input.parse::()?; + let ident = input.parse::()?; + if ident == "message" { + let la = input.lookahead1(); + if la.peek(syn::token::Paren) { + let content; + parenthesized!(content in input); + if content.peek(syn::LitStr) { + let fmt = content.parse()?; + let args = if content.is_empty() { + TokenStream::new() + } else { + content.parse::()?; + fmt::parse_token_expr(&content, false)? + }; + let display = Display { + fmt, + args, + has_bonus_display: false, + }; + Some(display) + } else { + return Err(syn::Error::new(ident.span(), "Invalid argument to message() sub-attribute. The first argument must be a literal string.")); + } + } else { + input.parse::()?; + Some(Display { + fmt: input.parse()?, + args: TokenStream::new(), + has_bonus_display: false, + }) + } + } else { return Err(syn::Error::new( - span, - "Source must be an identifier that refers to a Source for this snippet.", - )) + ident.span(), + "Invalid sub-attribute. Only `message()` is allowed.", + )); } + } else { + None }; - let message = iter.next(); Ok(SnippetAttr { source, message }) } } impl Parse for HighlightAttr { fn parse(input: ParseStream) -> syn::Result { - let punc = Punctuated::::parse_terminated(input)?; - let span = input.span(); - let mut iter = punc.into_iter(); - let snippet = - match iter.next() { - Some(MemberOrString::Member(member)) => member, - _ => return Err(syn::Error::new( - span, - "must be an identifier that refers to something with a #[snippet] attribute.", - )), - }; - Ok(HighlightAttr { snippet }) + let snippet = input.parse::()?; + let label = if input.peek(Token![,]) { + input.parse::()?; + let ident = input.parse::()?; + if ident == "label" { + let la = input.lookahead1(); + if la.peek(syn::token::Paren) { + let content; + parenthesized!(content in input); + if content.peek(syn::LitStr) { + let fmt = content.parse()?; + let args = if content.is_empty() { + TokenStream::new() + } else { + content.parse::()?; + fmt::parse_token_expr(&content, false)? + }; + let display = Display { + fmt, + args, + has_bonus_display: false, + }; + Some(display) + } else { + return Err(syn::Error::new(ident.span(), "Invalid argument to label() sub-attribute. The first argument must be a literal string.")); + } + } else { + input.parse::()?; + Some(Display { + fmt: input.parse()?, + args: TokenStream::new(), + has_bonus_display: false, + }) + } + } else { + return Err(syn::Error::new( + ident.span(), + "Invalid sub-attribute. Only `label()` is allowed.", + )); + } + } else { + None + }; + Ok(HighlightAttr { snippet, label }) } } @@ -113,7 +177,7 @@ impl Snippets { for (i, field) in fields.iter().enumerate() { for attr in &field.attrs { if attr.path.is_ident("highlight") { - let HighlightAttr { snippet } = attr.parse_args::()?; + let HighlightAttr { snippet, label } = attr.parse_args::()?; if let Some(snippet) = snippets.get_mut(&snippet) { let member = if let Some(ident) = field.ident.clone() { syn::Member::Named(ident) @@ -123,7 +187,10 @@ impl Snippets { span: field.span(), }) }; - snippet.highlights.push(Highlight { highlight: member }); + snippet.highlights.push(Highlight { + highlight: member, + label, + }); } else { return Err(syn::Error::new(snippet.span(), "Highlight must refer to an existing field with a #[snippet(...)] attribute.")); } @@ -137,29 +204,35 @@ impl Snippets { } } - pub(crate) fn gen_struct(&self) -> Option { + pub(crate) fn gen_struct(&self, fields: &syn::Fields) -> Option { let snippets = self.0.iter().map(|snippet| { // snippet message - let msg = snippet - .message - .as_ref() - .map(|msg| match msg { - MemberOrString::String(str) => { - quote! { - message: std::option::Option::Some(#str), + let msg = if let Some(display) = &snippet.message { + let members: HashSet = fields + .iter() + .enumerate() + .map(|(i, field)| { + if let Some(ident) = field.ident.as_ref().cloned() { + syn::Member::Named(ident) + } else { + syn::Member::Unnamed(syn::Index { + index: i as u32, + span: field.span(), + }) } - } - MemberOrString::Member(m) => { - quote! { - message: std::option::Option::Some(self.#m.as_ref()), - } - } - }) - .unwrap_or_else(|| { - quote! { - message: std::option::Option::None, - } - }); + }) + .collect(); + let mut display = display.clone(); + display.expand_shorthand(&members); + let Display { fmt, args, .. } = display; + quote! { + message: std::option::Option::Some(format!(#fmt, #args)), + } + } else { + quote! { + message: std::option::Option::None, + } + }; // Source field let src_ident = &snippet.source; @@ -175,9 +248,20 @@ impl Snippets { // Highlights let highlights = snippet.highlights.iter().map(|highlight| { - let Highlight { highlight } = highlight; - quote! { - self.#highlight.clone().into() + let Highlight { highlight, label } = highlight; + if let Some(Display { fmt, args, .. }) = label { + quote! { + ( + std::option::Option::Some( + format!(#fmt, #args) + ), + self.#highlight.clone().into() + ) + } + } else { + quote! { + (std::option::Option::None, self.#highlight.clone().into()) + } } }); let highlights = quote! { @@ -211,33 +295,25 @@ impl Snippets { variant.snippets.as_ref().map(|snippets| { let variant_snippets = snippets.0.iter().map(|snippet| { // snippet message - let msg = snippet - .message - .as_ref() - .map(|msg| match msg { - MemberOrString::String(str) => { - quote! { - message: std::option::Option::Some(#str), - } - } - MemberOrString::Member(m) => { - let m = match m { - syn::Member::Named(id) => id.clone(), - syn::Member::Unnamed(syn::Index { index, .. }) => { - format_ident!("_{}", index) - } - }; - quote! { - message: std::option::Option::Some(#m.as_ref()), - } - } - }) - .unwrap_or_else(|| { - quote! { - message: std::option::Option::None, - } - }); - + let msg = if let Some(display) = &snippet.message { + let members: HashSet = variant.fields.iter().enumerate().map(|(i, field)| { + if let Some(ident) = field.ident.as_ref().cloned() { + syn::Member::Named(ident) + } else { + syn::Member::Unnamed(syn::Index { index: i as u32, span: field.span() }) + } + }).collect(); + let mut display = display.clone(); + display.expand_shorthand(&members); + let Display { fmt, args, .. } = display; + quote! { + message: std::option::Option::Some(format!(#fmt, #args)), + } + } else { + quote! { + message: std::option::Option::None, + } + }; // Source field let src_ident = match &snippet.source { syn::Member::Named(id) => id.clone(), @@ -263,15 +339,24 @@ impl Snippets { // Highlights let highlights = snippet.highlights.iter().map(|highlight| { - let Highlight { highlight } = highlight; + let Highlight { highlight, label } = highlight; let m = match highlight { syn::Member::Named(id) => id.clone(), syn::Member::Unnamed(syn::Index { index, .. }) => { format_ident!("_{}", index) } }; - quote! { - #m.clone().into() + if let Some(Display { fmt, args, ..}) = label { + quote! { + ( + std::option::Option::Some(format!(#fmt, #args)), + #m.clone().into() + ) + } + } else { + quote! { + (std::option::Option::None, #m.clone().into()) + } } }); let highlights = quote! { diff --git a/src/printer/graphical_printer.rs b/src/printer/graphical_printer.rs index d9dd2f9..17e6e36 100644 --- a/src/printer/graphical_printer.rs +++ b/src/printer/graphical_printer.rs @@ -159,11 +159,11 @@ impl GraphicalReportPrinter { // gutter size. let mut highlights = snippet.highlights.clone().unwrap_or_else(Vec::new); // sorting is your friend. - highlights.sort_unstable_by_key(|h| h.offset()); + highlights.sort_unstable_by_key(|(_, h)| h.offset()); let highlights = highlights .into_iter() .zip(self.theme.styles.highlights.iter().cloned().cycle()) - .map(|(hl, st)| FancySpan::new(hl, st)) + .map(|((label, hl), st)| FancySpan::new(label, hl, st)) .collect::>(); // The max number of gutter-lines that will be active at any given @@ -189,7 +189,7 @@ impl GraphicalReportPrinter { .len(); // Header - if let Some(source_name) = snippet.context.label() { + if let Some(source_name) = snippet.source.name() { let source_name = source_name.style(self.theme.styles.filename); write!( f, @@ -558,13 +558,14 @@ impl Line { } struct FancySpan { + label: Option, span: SourceSpan, style: Style, } impl FancySpan { - fn new(span: SourceSpan, style: Style) -> Self { - FancySpan { span, style } + fn new(label: Option, span: SourceSpan, style: Style) -> Self { + FancySpan { label, span, style } } fn style(&self) -> Style { @@ -572,7 +573,9 @@ impl FancySpan { } fn label(&self) -> Option { - self.span.label().map(|l| l.style(self.style()).to_string()) + self.label + .as_ref() + .map(|l| l.style(self.style()).to_string()) } fn offset(&self) -> usize { diff --git a/src/printer/mod.rs b/src/printer/mod.rs index 74dc385..25f21e1 100644 --- a/src/printer/mod.rs +++ b/src/printer/mod.rs @@ -1,12 +1,10 @@ /*! Reporters included with `miette`. */ -use std::fmt; - use atty::Stream; use once_cell::sync::OnceCell; -use crate::protocol::{Diagnostic, DiagnosticReportPrinter, Severity}; +use crate::protocol::DiagnosticReportPrinter; use crate::MietteError; // NOTE(zkat): I don't understand why these three are "unreachable" when @@ -55,41 +53,3 @@ fn get_default_printer() -> Box) -> fmt::Result { - if f.alternate() { - return fmt::Debug::fmt(diagnostic, f); - } - - let sev = match diagnostic.severity() { - Some(Severity::Error) | None => "error", - Some(Severity::Warning) => "warning", - Some(Severity::Advice) => "advice", - }; - writeln!( - f, - "me, with {} {}: {}", - sev, - diagnostic, - diagnostic - .help() - .unwrap_or_else(|| Box::new(&"have you tried not failing?")) - )?; - writeln!( - f, - "miette, her eyes enormous: you {} miette? you {}? oh! oh! jail for mother! jail for mother for One Thousand Years!!!!", - diagnostic.code(), - diagnostic.snippets().map(|snippets| { - snippets.map(|snippet| snippet.message.map(|x| x.to_owned())) - .collect::>>() - }).flatten().map(|x| x.join(", ")).unwrap_or_else(||"try and cause miette to panic".into()) - )?; - - Ok(()) - } -} diff --git a/src/printer/narratable_printer.rs b/src/printer/narratable_printer.rs index 436fb07..37c4c83 100644 --- a/src/printer/narratable_printer.rs +++ b/src/printer/narratable_printer.rs @@ -90,7 +90,7 @@ impl NarratableReportPrinter { let (contents, lines) = self.get_lines(snippet)?; write!(f, "Begin snippet")?; - if let Some(filename) = snippet.context.label() { + if let Some(filename) = snippet.source.name() { write!(f, " for {}", filename,)?; } writeln!( @@ -106,13 +106,13 @@ impl NarratableReportPrinter { // gutter size. let mut highlights = snippet.highlights.clone().unwrap_or_else(Vec::new); // sorting is your friend. - highlights.sort_unstable_by_key(|h| h.offset()); + highlights.sort_unstable_by_key(|(_, h)| h.offset()); // Now it's time for the fun part--actually rendering everything! for line in &lines { writeln!(f, "snippet line {}: {}", line.line_number, line.text)?; - let relevant = highlights.iter().filter(|hl| line.span_starts(hl)); - for hl in relevant { + let relevant = highlights.iter().filter(|(_, hl)| line.span_starts(hl)); + for (label, hl) in relevant { let contents = snippet.source.read_span(hl).map_err(|_| fmt::Error)?; if contents.line() + 1 == line.line_number { write!( @@ -121,7 +121,7 @@ impl NarratableReportPrinter { contents.line() + 1, contents.column() + 1 )?; - if let Some(label) = hl.label() { + if let Some(label) = label { write!(f, ": {}", label)?; } writeln!(f)?; diff --git a/src/protocol.rs b/src/protocol.rs index 95b43a2..3c80f9b 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -45,7 +45,7 @@ pub trait Diagnostic: std::error::Error { /// Additional contextual snippets. This is typically used for adding /// marked-up source file output the way compilers often do. - fn snippets(&self) -> Option> + '_>> { + fn snippets<'a>(&'a self) -> Option> + 'a>> { None } } @@ -157,6 +157,11 @@ pub trait Source: std::fmt::Debug + Send + Sync { &'a self, span: &SourceSpan, ) -> Result, MietteError>; + + /// Optional name, usually a filename, for this source. + fn name(&self) -> Option { + None + } } /** @@ -212,7 +217,7 @@ A snippet from a [Source] to be displayed with a message and possibly some highl #[derive(Clone, Debug)] pub struct DiagnosticSnippet<'a> { /// Explanation of this specific diagnostic snippet. - pub message: Option<&'a str>, + pub message: Option, /// A [Source] that can be used to read the actual text of a source. pub source: &'a (dyn Source), /// The primary [SourceSpan] where this diagnostic is located. @@ -220,7 +225,7 @@ pub struct DiagnosticSnippet<'a> { /// Additional [SourceSpan]s that mark specific sections of the span, for /// example, to underline specific text within the larger span. They're /// paired with labels that should be applied to those sections. - pub highlights: Option>, + pub highlights: Option, SourceSpan)>>, } /** @@ -228,9 +233,6 @@ Span within a [Source] with an associated message. */ #[derive(Clone, Debug)] pub struct SourceSpan { - /// An optional label for this span. Rendered differently depending on - /// context. - label: Option, /// The start of the span. offset: SourceOffset, /// The total length of the span. Think of this as an offset from `start`. @@ -241,16 +243,6 @@ impl SourceSpan { /// Create a new [SourceSpan]. pub fn new(start: SourceOffset, length: SourceOffset) -> Self { Self { - label: None, - offset: start, - length, - } - } - - /// Create a new [SourceSpan] with a label. - pub fn new_labeled(label: impl AsRef, start: SourceOffset, length: SourceOffset) -> Self { - Self { - label: Some(label.as_ref().into()), offset: start, length, } @@ -261,15 +253,6 @@ impl SourceSpan { self.offset.offset() } - /// Returns a reference to this [SourceSpan]'s label. This label may be - /// used for different things in different contexts. In highlights, it - /// will be interpreted as the text on the other end of an underscored - /// section of text. In snippet spans, this will be treated as the source - /// name. - pub fn label(&self) -> Option<&str> { - self.label.as_ref().map(|x| &x[..]) - } - /// Total length of the [SourceSpan], in bytes. pub fn len(&self) -> usize { self.length.offset() @@ -285,17 +268,6 @@ impl SourceSpan { impl From<(ByteOffset, ByteOffset)> for SourceSpan { fn from((start, len): (ByteOffset, ByteOffset)) -> Self { Self { - label: None, - offset: start.into(), - length: len.into(), - } - } -} - -impl> From<(T, ByteOffset, ByteOffset)> for SourceSpan { - fn from((label, start, len): (T, ByteOffset, ByteOffset)) -> Self { - Self { - label: Some(label.as_ref().into()), offset: start.into(), length: len.into(), } @@ -305,17 +277,6 @@ impl> From<(T, ByteOffset, ByteOffset)> for SourceSpan { impl From<(SourceOffset, SourceOffset)> for SourceSpan { fn from((start, len): (SourceOffset, SourceOffset)) -> Self { Self { - label: None, - offset: start, - length: len, - } - } -} - -impl> From<(T, SourceOffset, SourceOffset)> for SourceSpan { - fn from((label, start, len): (T, SourceOffset, SourceOffset)) -> Self { - Self { - label: Some(label.as_ref().into()), offset: start, length: len, } diff --git a/src/utils.rs b/src/utils.rs index f80ff0e..6b8d4c9 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -2,7 +2,7 @@ use std::fmt; use thiserror::Error; -use crate::{Diagnostic, DiagnosticReport}; +use crate::{Diagnostic, DiagnosticReport, Source}; /// Convenience alias. This is intended to be used as the return type for `main()` pub type DiagnosticResult = Result; @@ -47,3 +47,40 @@ impl IntoDiagnostic for R }) } } + +/// Utility struct for when you have a regular [Source] type, such as a String, +/// that doesn't implement `name`, or if you want to override the `.name()` +/// returned by the `Source`. +#[derive(Debug)] +pub struct NamedSource { + source: Box, + name: String, +} + +impl NamedSource { + /// Create a new [NamedSource] using a regular [Source] and giving it a [Source::name]. + pub fn new(name: impl AsRef, source: impl Source + Send + Sync + 'static) -> Self { + Self { + source: Box::new(source), + name: name.as_ref().to_string(), + } + } + + /// Returns a reference the inner [Source] type for this [NamedSource]. + pub fn inner(&self) -> &(dyn Source + Send + Sync + 'static) { + &*self.source + } +} + +impl Source for NamedSource { + fn read_span<'a>( + &'a self, + span: &crate::SourceSpan, + ) -> Result, crate::MietteError> { + self.source.read_span(span) + } + + fn name(&self) -> Option { + Some(self.name.clone()) + } +} diff --git a/tests/derive.rs b/tests/derive.rs index abc11bc..59666de 100644 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -212,7 +212,7 @@ fn test_snippet_named_struct() { // / [my_snippet]: hi this is where the thing went wrong. // 1 | hello // 2 | world - #[snippet(src, "hi this is where the thing went wrong")] + #[snippet(src, message("hi this is where the thing went wrong"))] snip: SourceSpan, // Defines filename using `label` // "Highlights" are the specific highlights _inside_ the snippet. @@ -230,13 +230,7 @@ fn test_snippet_named_struct() { var1: SourceSpan, #[highlight(snip)] // Anything that's Clone + Into can be used here. - var2: (String, usize, usize), - - // Now with member source names - filename: String, - second_message: String, - #[snippet(src, filename, second_message)] - snip2: SourceSpan, + var2: (usize, usize), } } @@ -247,12 +241,12 @@ fn test_snippet_unnamed_struct() { #[diagnostic(code(foo::bar::baz))] struct Foo( String, - #[snippet(0, "hi")] SourceSpan, + #[snippet(0, message("hi"))] SourceSpan, #[highlight(1)] SourceSpan, #[highlight(1)] SourceSpan, // referenced source name String, - #[snippet(0, 4)] SourceSpan, + #[snippet(0, message("{}", self.4))] SourceSpan, #[highlight(5)] SourceSpan, #[highlight(5)] SourceSpan, ); @@ -267,29 +261,23 @@ fn test_snippet_enum() { #[diagnostic(code(foo::a))] A { src: String, - #[snippet(src, "hi this is where the thing went wrong")] + #[snippet(src, message("hi this is where the thing went wrong"))] snip: SourceSpan, #[highlight(snip)] var1: SourceSpan, #[highlight(snip)] var2: SourceSpan, - filename: String, - second_message: String, - #[snippet(src, filename, second_message)] - snip2: SourceSpan, }, #[diagnostic(code(foo::b))] B( String, - #[snippet(0, "hi")] SourceSpan, + #[snippet(0, message("hi"))] SourceSpan, #[highlight(1)] SourceSpan, - #[highlight(1, "var 2")] SourceSpan, + #[highlight(1, label("var 2"))] SourceSpan, // referenced source name - String, - String, - #[snippet(0, 4, 5)] SourceSpan, - #[highlight(6)] SourceSpan, - #[highlight(6)] SourceSpan, + #[snippet(0)] SourceSpan, + #[highlight(4)] SourceSpan, + #[highlight(4)] SourceSpan, ), } } diff --git a/tests/narrated.rs b/tests/narrated.rs index 00248ae..f6f143e 100644 --- a/tests/narrated.rs +++ b/tests/narrated.rs @@ -1,5 +1,5 @@ use miette::{ - Diagnostic, DiagnosticReport, GraphicalReportPrinter, GraphicalTheme, MietteError, + Diagnostic, DiagnosticReport, GraphicalReportPrinter, GraphicalTheme, MietteError, NamedSource, NarratableReportPrinter, SourceSpan, }; use thiserror::Error; @@ -25,19 +25,19 @@ fn single_line_highlight() -> Result<(), MietteError> { #[error("oops!")] #[diagnostic(code(oops::my::bad), help("try doing it better next time?"))] struct MyBad { - src: String, - #[snippet(src, "This is the part that broke")] + src: NamedSource, + #[snippet(src, message("This is the part that broke"))] ctx: SourceSpan, - #[highlight(ctx)] + #[highlight(ctx, label = "this bit here")] highlight: SourceSpan, } let src = "source\n text\n here".to_string(); let len = src.len(); let err = MyBad { - src, - ctx: ("bad_file.rs", 0, len).into(), - highlight: ("this bit here", 9, 4).into(), + src: NamedSource::new("bad_file.rs", src), + ctx: (0, len).into(), + highlight: (9, 4).into(), }; let out = fmt_report(err.into()); println!("{}", out); @@ -67,8 +67,8 @@ fn single_line_highlight_no_label() -> Result<(), MietteError> { #[error("oops!")] #[diagnostic(code(oops::my::bad), help("try doing it better next time?"))] struct MyBad { - src: String, - #[snippet(src, "This is the part that broke")] + src: NamedSource, + #[snippet(src, message("This is the part that broke"))] ctx: SourceSpan, #[highlight(ctx)] highlight: SourceSpan, @@ -77,8 +77,8 @@ fn single_line_highlight_no_label() -> Result<(), MietteError> { let src = "source\n text\n here".to_string(); let len = src.len(); let err = MyBad { - src, - ctx: ("bad_file.rs", 0, len).into(), + src: NamedSource::new("bad_file.rs", src), + ctx: (0, len).into(), highlight: (9, 4).into(), }; let out = fmt_report(err.into()); @@ -109,22 +109,22 @@ fn multiple_same_line_highlights() -> Result<(), MietteError> { #[error("oops!")] #[diagnostic(code(oops::my::bad), help("try doing it better next time?"))] struct MyBad { - src: String, - #[snippet(src, "This is the part that broke")] + src: NamedSource, + #[snippet(src, message("This is the part that broke"))] ctx: SourceSpan, - #[highlight(ctx)] + #[highlight(ctx, label = "this bit here")] highlight1: SourceSpan, - #[highlight(ctx)] + #[highlight(ctx, label = "also this bit")] highlight2: SourceSpan, } let src = "source\n text text text text text\n here".to_string(); let len = src.len(); let err = MyBad { - src, - ctx: ("bad_file.rs", 0, len).into(), - highlight1: ("this bit here", 9, 4).into(), - highlight2: ("also this bit", 14, 4).into(), + src: NamedSource::new("bad_file.rs", src), + ctx: (0, len).into(), + highlight1: (9, 4).into(), + highlight2: (14, 4).into(), }; let out = fmt_report(err.into()); println!("{}", out); @@ -155,19 +155,19 @@ fn multiline_highlight_adjacent() -> Result<(), MietteError> { #[error("oops!")] #[diagnostic(code(oops::my::bad), help("try doing it better next time?"))] struct MyBad { - src: String, - #[snippet(src, "This is the part that broke")] + src: NamedSource, + #[snippet(src, message("This is the part that broke"))] ctx: SourceSpan, - #[highlight(ctx)] + #[highlight(ctx, label = "these two lines")] highlight: SourceSpan, } let src = "source\n text\n here".to_string(); let len = src.len(); let err = MyBad { - src, - ctx: ("bad_file.rs", 0, len).into(), - highlight: ("these two lines", 9, 11).into(), + src: NamedSource::new("bad_file.rs", src), + ctx: (0, len).into(), + highlight: (9, 11).into(), }; let out = fmt_report(err.into()); println!("{}", out); @@ -197,12 +197,12 @@ fn multiline_highlight_flyby() -> Result<(), MietteError> { #[error("oops!")] #[diagnostic(code(oops::my::bad), help("try doing it better next time?"))] struct MyBad { - src: String, - #[snippet(src, "This is the part that broke")] + src: NamedSource, + #[snippet(src, message("This is the part that broke"))] ctx: SourceSpan, - #[highlight(ctx)] + #[highlight(ctx, label = "block 1")] highlight1: SourceSpan, - #[highlight(ctx)] + #[highlight(ctx, label = "block 2")] highlight2: SourceSpan, } @@ -215,10 +215,10 @@ line5 .to_string(); let len = src.len(); let err = MyBad { - src, - ctx: ("bad_file.rs", 0, len).into(), - highlight1: ("block 1", 0, len).into(), - highlight2: ("block 2", 10, 9).into(), + src: NamedSource::new("bad_file.rs", src), + ctx: (0, len).into(), + highlight1: (0, len).into(), + highlight2: (10, 9).into(), }; let out = fmt_report(err.into()); println!("{}", out); @@ -251,10 +251,10 @@ fn multiline_highlight_no_label() -> Result<(), MietteError> { #[error("oops!")] #[diagnostic(code(oops::my::bad), help("try doing it better next time?"))] struct MyBad { - src: String, - #[snippet(src, "This is the part that broke")] + src: NamedSource, + #[snippet(src, message("This is the part that broke"))] ctx: SourceSpan, - #[highlight(ctx)] + #[highlight(ctx, label = "block 1")] highlight1: SourceSpan, #[highlight(ctx)] highlight2: SourceSpan, @@ -269,9 +269,9 @@ line5 .to_string(); let len = src.len(); let err = MyBad { - src, - ctx: ("bad_file.rs", 0, len).into(), - highlight1: ("block 1", 0, len).into(), + src: NamedSource::new("bad_file.rs", src), + ctx: (0, len).into(), + highlight1: (0, len).into(), highlight2: (10, 9).into(), }; let out = fmt_report(err.into()); @@ -305,22 +305,22 @@ fn multiple_multiline_highlights_adjacent() -> Result<(), MietteError> { #[error("oops!")] #[diagnostic(code(oops::my::bad), help("try doing it better next time?"))] struct MyBad { - src: String, - #[snippet(src, "This is the part that broke")] + src: NamedSource, + #[snippet(src, message("This is the part that broke"))] ctx: SourceSpan, - #[highlight(ctx)] + #[highlight(ctx, label = "this bit here")] highlight1: SourceSpan, - #[highlight(ctx)] + #[highlight(ctx, label = "also this bit")] highlight2: SourceSpan, } let src = "source\n text\n here\nmore here".to_string(); let len = src.len(); let err = MyBad { - src, - ctx: ("bad_file.rs", 0, len).into(), - highlight1: ("this bit here", 0, 10).into(), - highlight2: ("also this bit", 20, 6).into(), + src: NamedSource::new("bad_file.rs", src), + ctx: (0, len).into(), + highlight1: (0, 10).into(), + highlight2: (20, 6).into(), }; let out = fmt_report(err.into()); println!("{}", out); @@ -354,22 +354,22 @@ fn multiple_multiline_highlights_overlapping_lines() -> Result<(), MietteError> #[error("oops!")] #[diagnostic(code(oops::my::bad), help("try doing it better next time?"))] struct MyBad { - src: String, - #[snippet(src, "This is the part that broke")] + src: NamedSource, + #[snippet(src, message("This is the part that broke"))] ctx: SourceSpan, - #[highlight(ctx)] + #[highlight(ctx, label = "this bit here")] highlight1: SourceSpan, - #[highlight(ctx)] + #[highlight(ctx, label = "also this bit")] highlight2: SourceSpan, } let src = "source\n text\n here".to_string(); let len = src.len(); let err = MyBad { - src, - ctx: ("bad_file.rs", 0, len).into(), - highlight1: ("this bit here", 0, 8).into(), - highlight2: ("also this bit", 9, 10).into(), + src: NamedSource::new("bad_file.rs", src), + ctx: (0, len).into(), + highlight1: (0, 8).into(), + highlight2: (9, 10).into(), }; let out = fmt_report(err.into()); println!("{}", out); @@ -402,22 +402,22 @@ fn multiple_multiline_highlights_overlapping_offsets() -> Result<(), MietteError #[error("oops!")] #[diagnostic(code(oops::my::bad), help("try doing it better next time?"))] struct MyBad { - src: String, - #[snippet(src, "This is the part that broke")] + src: NamedSource, + #[snippet(src, message("This is the part that broke"))] ctx: SourceSpan, - #[highlight(ctx)] + #[highlight(ctx, label = "this bit here")] highlight1: SourceSpan, - #[highlight(ctx)] + #[highlight(ctx, label = "also this bit")] highlight2: SourceSpan, } let src = "source\n text\n here".to_string(); let len = src.len(); let err = MyBad { - src, - ctx: ("bad_file.rs", 0, len).into(), - highlight1: ("this bit here", 0, 8).into(), - highlight2: ("also this bit", 10, 10).into(), + src: NamedSource::new("bad_file.rs", src), + ctx: (0, len).into(), + highlight1: (0, 8).into(), + highlight2: (10, 10).into(), }; let out = fmt_report(err.into()); println!("{}", out); diff --git a/tests/printer.rs b/tests/printer.rs index dfd5858..cafa6c5 100644 --- a/tests/printer.rs +++ b/tests/printer.rs @@ -1,5 +1,5 @@ use miette::{ - Diagnostic, DiagnosticReport, GraphicalReportPrinter, GraphicalTheme, MietteError, + Diagnostic, DiagnosticReport, GraphicalReportPrinter, GraphicalTheme, MietteError, NamedSource, NarratableReportPrinter, SourceSpan, }; use thiserror::Error; @@ -29,19 +29,19 @@ fn single_line_highlight() -> Result<(), MietteError> { #[error("oops!")] #[diagnostic(code(oops::my::bad), help("try doing it better next time?"))] struct MyBad { - src: String, - #[snippet(src, "This is the part that broke")] + src: NamedSource, + #[snippet(src, message("This is the part that broke"))] ctx: SourceSpan, - #[highlight(ctx)] + #[highlight(ctx, label = "this bit here")] highlight: SourceSpan, } let src = "source\n text\n here".to_string(); let len = src.len(); let err = MyBad { - src, - ctx: ("bad_file.rs", 0, len).into(), - highlight: ("this bit here", 9, 4).into(), + src: NamedSource::new("bad_file.rs", src), + ctx: (0, len).into(), + highlight: (9, 4).into(), }; let out = fmt_report(err.into()); println!("{}", out); @@ -71,8 +71,8 @@ fn single_line_highlight_no_label() -> Result<(), MietteError> { #[error("oops!")] #[diagnostic(code(oops::my::bad), help("try doing it better next time?"))] struct MyBad { - src: String, - #[snippet(src, "This is the part that broke")] + src: NamedSource, + #[snippet(src, message("This is the part that broke"))] ctx: SourceSpan, #[highlight(ctx)] highlight: SourceSpan, @@ -81,8 +81,8 @@ fn single_line_highlight_no_label() -> Result<(), MietteError> { let src = "source\n text\n here".to_string(); let len = src.len(); let err = MyBad { - src, - ctx: ("bad_file.rs", 0, len).into(), + src: NamedSource::new("bad_file.rs", src), + ctx: (0, len).into(), highlight: (9, 4).into(), }; let out = fmt_report(err.into()); @@ -112,22 +112,22 @@ fn multiple_same_line_highlights() -> Result<(), MietteError> { #[error("oops!")] #[diagnostic(code(oops::my::bad), help("try doing it better next time?"))] struct MyBad { - src: String, - #[snippet(src, "This is the part that broke")] + src: NamedSource, + #[snippet(src, message("This is the part that broke"))] ctx: SourceSpan, - #[highlight(ctx)] + #[highlight(ctx, label = "this bit here")] highlight1: SourceSpan, - #[highlight(ctx)] + #[highlight(ctx, label = "also this bit")] highlight2: SourceSpan, } let src = "source\n text text text text text\n here".to_string(); let len = src.len(); let err = MyBad { - src, - ctx: ("bad_file.rs", 0, len).into(), - highlight1: ("this bit here", 9, 4).into(), - highlight2: ("also this bit", 14, 4).into(), + src: NamedSource::new("bad_file.rs", src), + ctx: (0, len).into(), + highlight1: (9, 4).into(), + highlight2: (14, 4).into(), }; let out = fmt_report(err.into()); println!("{}", out); @@ -158,19 +158,19 @@ fn multiline_highlight_adjacent() -> Result<(), MietteError> { #[error("oops!")] #[diagnostic(code(oops::my::bad), help("try doing it better next time?"))] struct MyBad { - src: String, - #[snippet(src, "This is the part that broke")] + src: NamedSource, + #[snippet(src, message("This is the part that broke"))] ctx: SourceSpan, - #[highlight(ctx)] + #[highlight(ctx, label = "these two lines")] highlight: SourceSpan, } let src = "source\n text\n here".to_string(); let len = src.len(); let err = MyBad { - src, - ctx: ("bad_file.rs", 0, len).into(), - highlight: ("these two lines", 9, 11).into(), + src: NamedSource::new("bad_file.rs", src), + ctx: (0, len).into(), + highlight: (9, 11).into(), }; let out = fmt_report(err.into()); println!("{}", out); @@ -199,12 +199,12 @@ fn multiline_highlight_flyby() -> Result<(), MietteError> { #[error("oops!")] #[diagnostic(code(oops::my::bad), help("try doing it better next time?"))] struct MyBad { - src: String, - #[snippet(src, "This is the part that broke")] + src: NamedSource, + #[snippet(src, message("This is the part that broke"))] ctx: SourceSpan, - #[highlight(ctx)] + #[highlight(ctx, label = "block 1")] highlight1: SourceSpan, - #[highlight(ctx)] + #[highlight(ctx, label = "block 2")] highlight2: SourceSpan, } @@ -217,10 +217,10 @@ line5 .to_string(); let len = src.len(); let err = MyBad { - src, - ctx: ("bad_file.rs", 0, len).into(), - highlight1: ("block 1", 0, len).into(), - highlight2: ("block 2", 10, 9).into(), + src: NamedSource::new("bad_file.rs", src), + ctx: (0, len).into(), + highlight1: (0, len).into(), + highlight2: (10, 9).into(), }; let out = fmt_report(err.into()); println!("{}", out); @@ -252,10 +252,10 @@ fn multiline_highlight_no_label() -> Result<(), MietteError> { #[error("oops!")] #[diagnostic(code(oops::my::bad), help("try doing it better next time?"))] struct MyBad { - src: String, - #[snippet(src, "This is the part that broke")] + src: NamedSource, + #[snippet(src, message("This is the part that broke"))] ctx: SourceSpan, - #[highlight(ctx)] + #[highlight(ctx, label = "block 1")] highlight1: SourceSpan, #[highlight(ctx)] highlight2: SourceSpan, @@ -270,9 +270,9 @@ line5 .to_string(); let len = src.len(); let err = MyBad { - src, - ctx: ("bad_file.rs", 0, len).into(), - highlight1: ("block 1", 0, len).into(), + src: NamedSource::new("bad_file.rs", src), + ctx: (0, len).into(), + highlight1: (0, len).into(), highlight2: (10, 9).into(), }; let out = fmt_report(err.into()); @@ -304,22 +304,22 @@ fn multiple_multiline_highlights_adjacent() -> Result<(), MietteError> { #[error("oops!")] #[diagnostic(code(oops::my::bad), help("try doing it better next time?"))] struct MyBad { - src: String, - #[snippet(src, "This is the part that broke")] + src: NamedSource, + #[snippet(src, message("This is the part that broke"))] ctx: SourceSpan, - #[highlight(ctx)] + #[highlight(ctx, label = "this bit here")] highlight1: SourceSpan, - #[highlight(ctx)] + #[highlight(ctx, label = "also this bit")] highlight2: SourceSpan, } let src = "source\n text\n here\nmore here".to_string(); let len = src.len(); let err = MyBad { - src, - ctx: ("bad_file.rs", 0, len).into(), - highlight1: ("this bit here", 0, 10).into(), - highlight2: ("also this bit", 20, 6).into(), + src: NamedSource::new("bad_file.rs", src), + ctx: (0, len).into(), + highlight1: (0, 10).into(), + highlight2: (20, 6).into(), }; let out = fmt_report(err.into()); println!("{}", out); @@ -354,22 +354,22 @@ fn multiple_multiline_highlights_overlapping_lines() -> Result<(), MietteError> #[error("oops!")] #[diagnostic(code(oops::my::bad), help("try doing it better next time?"))] struct MyBad { - src: String, - #[snippet(src, "This is the part that broke")] + src: NamedSource, + #[snippet(src, message("This is the part that broke"))] ctx: SourceSpan, - #[highlight(ctx)] + #[highlight(ctx, label = "this bit here")] highlight1: SourceSpan, - #[highlight(ctx)] + #[highlight(ctx, label = "also this bit")] highlight2: SourceSpan, } let src = "source\n text\n here".to_string(); let len = src.len(); let err = MyBad { - src, - ctx: ("bad_file.rs", 0, len).into(), - highlight1: ("this bit here", 0, 8).into(), - highlight2: ("also this bit", 9, 10).into(), + src: NamedSource::new("bad_file.rs", src), + ctx: (0, len).into(), + highlight1: (0, 8).into(), + highlight2: (9, 10).into(), }; let out = fmt_report(err.into()); println!("{}", out); @@ -385,22 +385,22 @@ fn multiple_multiline_highlights_overlapping_offsets() -> Result<(), MietteError #[error("oops!")] #[diagnostic(code(oops::my::bad), help("try doing it better next time?"))] struct MyBad { - src: String, - #[snippet(src, "This is the part that broke")] + src: NamedSource, + #[snippet(src, message("This is the part that broke"))] ctx: SourceSpan, - #[highlight(ctx)] + #[highlight(ctx, label = "this bit here")] highlight1: SourceSpan, - #[highlight(ctx)] + #[highlight(ctx, label = "also this bit")] highlight2: SourceSpan, } let src = "source\n text\n here".to_string(); let len = src.len(); let err = MyBad { - src, - ctx: ("bad_file.rs", 0, len).into(), - highlight1: ("this bit here", 0, 8).into(), - highlight2: ("also this bit", 10, 10).into(), + src: NamedSource::new("bad_file.rs", src), + ctx: (0, len).into(), + highlight1: (0, 8).into(), + highlight2: (10, 10).into(), }; let out = fmt_report(err.into()); println!("{}", out);