mirror of https://github.com/zkat/miette.git
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
This commit is contained in:
parent
2d886c06a3
commit
61283e9efe
41
README.md
41
README.md
|
|
@ -30,7 +30,7 @@ diagnostic error code: ruget::api::bad_json
|
||||||
- [Features](#features)
|
- [Features](#features)
|
||||||
- [Installing](#installing)
|
- [Installing](#installing)
|
||||||
- [Example](#example)
|
- [Example](#example)
|
||||||
- [Usage](#usage)
|
- [Using](#using)
|
||||||
- [... in libraries](#-in-libraries)
|
- [... in libraries](#-in-libraries)
|
||||||
- [... in application code](#-in-application-code)
|
- [... in application code](#-in-application-code)
|
||||||
- [... in `main()`](#-in-main)
|
- [... in `main()`](#-in-main)
|
||||||
|
|
@ -85,11 +85,12 @@ use thiserror::Error;
|
||||||
)]
|
)]
|
||||||
struct MyBad {
|
struct MyBad {
|
||||||
// The Source that we're gonna be printing snippets out of.
|
// 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!
|
// 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,
|
snip: SourceSpan,
|
||||||
#[highlight(snip)]
|
#[highlight(snip, label("This bit here"))]
|
||||||
bad_bit: SourceSpan,
|
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
|
throughout your app (but NOT your libraries! Those should always return concrete
|
||||||
types!).
|
types!).
|
||||||
*/
|
*/
|
||||||
use miette::DiagnosticResult;
|
use miette::{DiagnosticResult, NamedSource};
|
||||||
fn this_fails() -> DiagnosticResult<()> {
|
fn this_fails() -> DiagnosticResult<()> {
|
||||||
// You can use plain strings as a `Source`, or anything that implements
|
// You can use plain strings as a `Source`, or anything that implements
|
||||||
// the one-method `Source` trait.
|
// the one-method `Source` trait.
|
||||||
|
|
@ -108,9 +109,9 @@ fn this_fails() -> DiagnosticResult<()> {
|
||||||
let len = src.len();
|
let len = src.len();
|
||||||
|
|
||||||
Err(MyBad {
|
Err(MyBad {
|
||||||
src,
|
src: NamedSource::new("bad_file.rs", src),
|
||||||
snip: ("bad_file.rs", 0, len).into(),
|
snip: (0, len).into(),
|
||||||
bad_bit: ("this bit here", 9, 4).into(),
|
bad_bit: (9, 4).into(),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -149,7 +150,7 @@ diagnostic help: try doing it better next time?
|
||||||
diagnostic error code: oops::my::bad
|
diagnostic error code: oops::my::bad
|
||||||
">
|
">
|
||||||
|
|
||||||
## Usage
|
## Using
|
||||||
|
|
||||||
### ... in libraries
|
### ... in libraries
|
||||||
|
|
||||||
|
|
@ -244,6 +245,7 @@ use miette::Diagnostic;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Error, Diagnostic, Debug)]
|
#[derive(Error, Diagnostic, Debug)]
|
||||||
|
#[error("kaboom")]
|
||||||
#[diagnostic(
|
#[diagnostic(
|
||||||
code(my_app::my_error),
|
code(my_app::my_error),
|
||||||
// You can do formatting!
|
// 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
|
// Will link users to https://docs.rs/my_crate/0.0.0/my_crate/struct.MyErr.html
|
||||||
url(docsrs)
|
url(docsrs)
|
||||||
)]
|
)]
|
||||||
|
#[error("kaboom")]
|
||||||
struct MyErr;
|
struct MyErr;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -297,17 +300,25 @@ pub struct MyErrorType {
|
||||||
// The `Source` that miette will use.
|
// The `Source` that miette will use.
|
||||||
src: String,
|
src: String,
|
||||||
|
|
||||||
// A snippet that points to `src`, our `Source`. The filename can be
|
// A snippet that points to `src`, our `Source`.
|
||||||
// provided at the callsite.
|
#[snippet(
|
||||||
#[snippet(src, "This is the snippet")]
|
src,
|
||||||
|
message("This is the snippet")
|
||||||
|
)]
|
||||||
snip: SourceSpan,
|
snip: SourceSpan,
|
||||||
|
|
||||||
// A highlight for the `snip` snippet we defined above. This will
|
// A highlight for the `snip` snippet we defined above. This will
|
||||||
// underline/mark the specific code inside the larger snippet context.
|
// underline/mark the specific code inside the larger snippet context.
|
||||||
//
|
#[highlight(snip, label("This is the highlight"))]
|
||||||
// The label is provided using `SourceSpan`'s label.
|
|
||||||
#[highlight(snip)]
|
|
||||||
err_span: SourceSpan,
|
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,
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -166,7 +166,7 @@ impl Diagnostic {
|
||||||
let code_body = code.gen_struct();
|
let code_body = code.gen_struct();
|
||||||
let help_body = help.as_ref().and_then(|x| x.gen_struct(fields));
|
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 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));
|
let url_body = url.as_ref().and_then(|x| x.gen_struct(ident, fields));
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
|
|
|
||||||
|
|
@ -1,72 +1,136 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::{format_ident, quote};
|
use quote::{format_ident, quote};
|
||||||
use syn::{
|
use syn::{
|
||||||
|
parenthesized,
|
||||||
parse::{Parse, ParseStream},
|
parse::{Parse, ParseStream},
|
||||||
punctuated::Punctuated,
|
|
||||||
spanned::Spanned,
|
spanned::Spanned,
|
||||||
Token,
|
Token,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::diagnostic::DiagnosticVariant;
|
use crate::fmt;
|
||||||
use crate::utils::MemberOrString;
|
use crate::{diagnostic::DiagnosticVariant, fmt::Display};
|
||||||
|
|
||||||
pub struct Snippets(Vec<Snippet>);
|
pub struct Snippets(Vec<Snippet>);
|
||||||
|
|
||||||
struct Snippet {
|
struct Snippet {
|
||||||
message: Option<MemberOrString>,
|
message: Option<Display>,
|
||||||
highlights: Vec<Highlight>,
|
highlights: Vec<Highlight>,
|
||||||
source: syn::Member,
|
source: syn::Member,
|
||||||
snippet: syn::Member,
|
snippet: syn::Member,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Highlight {
|
struct Highlight {
|
||||||
|
label: Option<Display>,
|
||||||
highlight: syn::Member,
|
highlight: syn::Member,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SnippetAttr {
|
struct SnippetAttr {
|
||||||
source: syn::Member,
|
source: syn::Member,
|
||||||
message: Option<MemberOrString>,
|
message: Option<Display>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct HighlightAttr {
|
struct HighlightAttr {
|
||||||
|
label: Option<Display>,
|
||||||
snippet: syn::Member,
|
snippet: syn::Member,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for SnippetAttr {
|
impl Parse for SnippetAttr {
|
||||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
let punc = Punctuated::<MemberOrString, Token![,]>::parse_terminated(input)?;
|
let source = input.parse::<syn::Member>()?;
|
||||||
let span = input.span();
|
let message = if input.peek(Token![,]) {
|
||||||
let mut iter = punc.into_iter();
|
input.parse::<Token![,]>()?;
|
||||||
let source = match iter.next() {
|
let ident = input.parse::<syn::Ident>()?;
|
||||||
Some(MemberOrString::Member(member)) => member,
|
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::<Token![,]>()?;
|
||||||
|
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::<Token![=]>()?;
|
||||||
|
Some(Display {
|
||||||
|
fmt: input.parse()?,
|
||||||
|
args: TokenStream::new(),
|
||||||
|
has_bonus_display: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
return Err(syn::Error::new(
|
return Err(syn::Error::new(
|
||||||
span,
|
ident.span(),
|
||||||
"Source must be an identifier that refers to a Source for this snippet.",
|
"Invalid sub-attribute. Only `message()` is allowed.",
|
||||||
))
|
));
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
};
|
};
|
||||||
let message = iter.next();
|
|
||||||
Ok(SnippetAttr { source, message })
|
Ok(SnippetAttr { source, message })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for HighlightAttr {
|
impl Parse for HighlightAttr {
|
||||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
let punc = Punctuated::<MemberOrString, Token![,]>::parse_terminated(input)?;
|
let snippet = input.parse::<syn::Member>()?;
|
||||||
let span = input.span();
|
let label = if input.peek(Token![,]) {
|
||||||
let mut iter = punc.into_iter();
|
input.parse::<Token![,]>()?;
|
||||||
let snippet =
|
let ident = input.parse::<syn::Ident>()?;
|
||||||
match iter.next() {
|
if ident == "label" {
|
||||||
Some(MemberOrString::Member(member)) => member,
|
let la = input.lookahead1();
|
||||||
_ => return Err(syn::Error::new(
|
if la.peek(syn::token::Paren) {
|
||||||
span,
|
let content;
|
||||||
"must be an identifier that refers to something with a #[snippet] attribute.",
|
parenthesized!(content in input);
|
||||||
)),
|
if content.peek(syn::LitStr) {
|
||||||
};
|
let fmt = content.parse()?;
|
||||||
Ok(HighlightAttr { snippet })
|
let args = if content.is_empty() {
|
||||||
|
TokenStream::new()
|
||||||
|
} else {
|
||||||
|
content.parse::<Token![,]>()?;
|
||||||
|
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::<Token![=]>()?;
|
||||||
|
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 (i, field) in fields.iter().enumerate() {
|
||||||
for attr in &field.attrs {
|
for attr in &field.attrs {
|
||||||
if attr.path.is_ident("highlight") {
|
if attr.path.is_ident("highlight") {
|
||||||
let HighlightAttr { snippet } = attr.parse_args::<HighlightAttr>()?;
|
let HighlightAttr { snippet, label } = attr.parse_args::<HighlightAttr>()?;
|
||||||
if let Some(snippet) = snippets.get_mut(&snippet) {
|
if let Some(snippet) = snippets.get_mut(&snippet) {
|
||||||
let member = if let Some(ident) = field.ident.clone() {
|
let member = if let Some(ident) = field.ident.clone() {
|
||||||
syn::Member::Named(ident)
|
syn::Member::Named(ident)
|
||||||
|
|
@ -123,7 +187,10 @@ impl Snippets {
|
||||||
span: field.span(),
|
span: field.span(),
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
snippet.highlights.push(Highlight { highlight: member });
|
snippet.highlights.push(Highlight {
|
||||||
|
highlight: member,
|
||||||
|
label,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
return Err(syn::Error::new(snippet.span(), "Highlight must refer to an existing field with a #[snippet(...)] attribute."));
|
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<TokenStream> {
|
pub(crate) fn gen_struct(&self, fields: &syn::Fields) -> Option<TokenStream> {
|
||||||
let snippets = self.0.iter().map(|snippet| {
|
let snippets = self.0.iter().map(|snippet| {
|
||||||
// snippet message
|
// snippet message
|
||||||
let msg = snippet
|
let msg = if let Some(display) = &snippet.message {
|
||||||
.message
|
let members: HashSet<syn::Member> = fields
|
||||||
.as_ref()
|
.iter()
|
||||||
.map(|msg| match msg {
|
.enumerate()
|
||||||
MemberOrString::String(str) => {
|
.map(|(i, field)| {
|
||||||
quote! {
|
if let Some(ident) = field.ident.as_ref().cloned() {
|
||||||
message: std::option::Option::Some(#str),
|
syn::Member::Named(ident)
|
||||||
|
} else {
|
||||||
|
syn::Member::Unnamed(syn::Index {
|
||||||
|
index: i as u32,
|
||||||
|
span: field.span(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
MemberOrString::Member(m) => {
|
.collect();
|
||||||
quote! {
|
let mut display = display.clone();
|
||||||
message: std::option::Option::Some(self.#m.as_ref()),
|
display.expand_shorthand(&members);
|
||||||
}
|
let Display { fmt, args, .. } = display;
|
||||||
}
|
quote! {
|
||||||
})
|
message: std::option::Option::Some(format!(#fmt, #args)),
|
||||||
.unwrap_or_else(|| {
|
}
|
||||||
quote! {
|
} else {
|
||||||
message: std::option::Option::None,
|
quote! {
|
||||||
}
|
message: std::option::Option::None,
|
||||||
});
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Source field
|
// Source field
|
||||||
let src_ident = &snippet.source;
|
let src_ident = &snippet.source;
|
||||||
|
|
@ -175,9 +248,20 @@ impl Snippets {
|
||||||
|
|
||||||
// Highlights
|
// Highlights
|
||||||
let highlights = snippet.highlights.iter().map(|highlight| {
|
let highlights = snippet.highlights.iter().map(|highlight| {
|
||||||
let Highlight { highlight } = highlight;
|
let Highlight { highlight, label } = highlight;
|
||||||
quote! {
|
if let Some(Display { fmt, args, .. }) = label {
|
||||||
self.#highlight.clone().into()
|
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! {
|
let highlights = quote! {
|
||||||
|
|
@ -211,33 +295,25 @@ impl Snippets {
|
||||||
variant.snippets.as_ref().map(|snippets| {
|
variant.snippets.as_ref().map(|snippets| {
|
||||||
let variant_snippets = snippets.0.iter().map(|snippet| {
|
let variant_snippets = snippets.0.iter().map(|snippet| {
|
||||||
// snippet message
|
// snippet message
|
||||||
let msg = snippet
|
let msg = if let Some(display) = &snippet.message {
|
||||||
.message
|
let members: HashSet<syn::Member> = variant.fields.iter().enumerate().map(|(i, field)| {
|
||||||
.as_ref()
|
if let Some(ident) = field.ident.as_ref().cloned() {
|
||||||
.map(|msg| match msg {
|
syn::Member::Named(ident)
|
||||||
MemberOrString::String(str) => {
|
} else {
|
||||||
quote! {
|
syn::Member::Unnamed(syn::Index { index: i as u32, span: field.span() })
|
||||||
message: std::option::Option::Some(#str),
|
}
|
||||||
}
|
}).collect();
|
||||||
}
|
let mut display = display.clone();
|
||||||
MemberOrString::Member(m) => {
|
display.expand_shorthand(&members);
|
||||||
let m = match m {
|
let Display { fmt, args, .. } = display;
|
||||||
syn::Member::Named(id) => id.clone(),
|
quote! {
|
||||||
syn::Member::Unnamed(syn::Index { index, .. }) => {
|
message: std::option::Option::Some(format!(#fmt, #args)),
|
||||||
format_ident!("_{}", index)
|
}
|
||||||
}
|
} else {
|
||||||
};
|
quote! {
|
||||||
quote! {
|
message: std::option::Option::None,
|
||||||
message: std::option::Option::Some(#m.as_ref()),
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
})
|
|
||||||
.unwrap_or_else(|| {
|
|
||||||
quote! {
|
|
||||||
message: std::option::Option::None,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Source field
|
// Source field
|
||||||
let src_ident = match &snippet.source {
|
let src_ident = match &snippet.source {
|
||||||
syn::Member::Named(id) => id.clone(),
|
syn::Member::Named(id) => id.clone(),
|
||||||
|
|
@ -263,15 +339,24 @@ impl Snippets {
|
||||||
|
|
||||||
// Highlights
|
// Highlights
|
||||||
let highlights = snippet.highlights.iter().map(|highlight| {
|
let highlights = snippet.highlights.iter().map(|highlight| {
|
||||||
let Highlight { highlight } = highlight;
|
let Highlight { highlight, label } = highlight;
|
||||||
let m = match highlight {
|
let m = match highlight {
|
||||||
syn::Member::Named(id) => id.clone(),
|
syn::Member::Named(id) => id.clone(),
|
||||||
syn::Member::Unnamed(syn::Index { index, .. }) => {
|
syn::Member::Unnamed(syn::Index { index, .. }) => {
|
||||||
format_ident!("_{}", index)
|
format_ident!("_{}", index)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
quote! {
|
if let Some(Display { fmt, args, ..}) = label {
|
||||||
#m.clone().into()
|
quote! {
|
||||||
|
(
|
||||||
|
std::option::Option::Some(format!(#fmt, #args)),
|
||||||
|
#m.clone().into()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! {
|
||||||
|
(std::option::Option::None, #m.clone().into())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let highlights = quote! {
|
let highlights = quote! {
|
||||||
|
|
|
||||||
|
|
@ -159,11 +159,11 @@ impl GraphicalReportPrinter {
|
||||||
// gutter size.
|
// gutter size.
|
||||||
let mut highlights = snippet.highlights.clone().unwrap_or_else(Vec::new);
|
let mut highlights = snippet.highlights.clone().unwrap_or_else(Vec::new);
|
||||||
// sorting is your friend.
|
// sorting is your friend.
|
||||||
highlights.sort_unstable_by_key(|h| h.offset());
|
highlights.sort_unstable_by_key(|(_, h)| h.offset());
|
||||||
let highlights = highlights
|
let highlights = highlights
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.zip(self.theme.styles.highlights.iter().cloned().cycle())
|
.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::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
// The max number of gutter-lines that will be active at any given
|
// The max number of gutter-lines that will be active at any given
|
||||||
|
|
@ -189,7 +189,7 @@ impl GraphicalReportPrinter {
|
||||||
.len();
|
.len();
|
||||||
|
|
||||||
// Header
|
// 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);
|
let source_name = source_name.style(self.theme.styles.filename);
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
|
|
@ -558,13 +558,14 @@ impl Line {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FancySpan {
|
struct FancySpan {
|
||||||
|
label: Option<String>,
|
||||||
span: SourceSpan,
|
span: SourceSpan,
|
||||||
style: Style,
|
style: Style,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FancySpan {
|
impl FancySpan {
|
||||||
fn new(span: SourceSpan, style: Style) -> Self {
|
fn new(label: Option<String>, span: SourceSpan, style: Style) -> Self {
|
||||||
FancySpan { span, style }
|
FancySpan { label, span, style }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn style(&self) -> Style {
|
fn style(&self) -> Style {
|
||||||
|
|
@ -572,7 +573,9 @@ impl FancySpan {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn label(&self) -> Option<String> {
|
fn label(&self) -> Option<String> {
|
||||||
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 {
|
fn offset(&self) -> usize {
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
/*!
|
/*!
|
||||||
Reporters included with `miette`.
|
Reporters included with `miette`.
|
||||||
*/
|
*/
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
use atty::Stream;
|
use atty::Stream;
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
|
|
||||||
use crate::protocol::{Diagnostic, DiagnosticReportPrinter, Severity};
|
use crate::protocol::DiagnosticReportPrinter;
|
||||||
use crate::MietteError;
|
use crate::MietteError;
|
||||||
|
|
||||||
// NOTE(zkat): I don't understand why these three are "unreachable" when
|
// NOTE(zkat): I don't understand why these three are "unreachable" when
|
||||||
|
|
@ -55,41 +53,3 @@ fn get_default_printer() -> Box<dyn DiagnosticReportPrinter + Send + Sync + 'sta
|
||||||
Box::new(NarratableReportPrinter)
|
Box::new(NarratableReportPrinter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Literally what it says on the tin.
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct JokePrinter;
|
|
||||||
|
|
||||||
impl DiagnosticReportPrinter for JokePrinter {
|
|
||||||
fn debug(&self, diagnostic: &(dyn Diagnostic), f: &mut fmt::Formatter<'_>) -> 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::<Option<Vec<String>>>()
|
|
||||||
}).flatten().map(|x| x.join(", ")).unwrap_or_else(||"try and cause miette to panic".into())
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ impl NarratableReportPrinter {
|
||||||
let (contents, lines) = self.get_lines(snippet)?;
|
let (contents, lines) = self.get_lines(snippet)?;
|
||||||
|
|
||||||
write!(f, "Begin snippet")?;
|
write!(f, "Begin snippet")?;
|
||||||
if let Some(filename) = snippet.context.label() {
|
if let Some(filename) = snippet.source.name() {
|
||||||
write!(f, " for {}", filename,)?;
|
write!(f, " for {}", filename,)?;
|
||||||
}
|
}
|
||||||
writeln!(
|
writeln!(
|
||||||
|
|
@ -106,13 +106,13 @@ impl NarratableReportPrinter {
|
||||||
// gutter size.
|
// gutter size.
|
||||||
let mut highlights = snippet.highlights.clone().unwrap_or_else(Vec::new);
|
let mut highlights = snippet.highlights.clone().unwrap_or_else(Vec::new);
|
||||||
// sorting is your friend.
|
// 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!
|
// Now it's time for the fun part--actually rendering everything!
|
||||||
for line in &lines {
|
for line in &lines {
|
||||||
writeln!(f, "snippet line {}: {}", line.line_number, line.text)?;
|
writeln!(f, "snippet line {}: {}", line.line_number, line.text)?;
|
||||||
let relevant = highlights.iter().filter(|hl| line.span_starts(hl));
|
let relevant = highlights.iter().filter(|(_, hl)| line.span_starts(hl));
|
||||||
for hl in relevant {
|
for (label, hl) in relevant {
|
||||||
let contents = snippet.source.read_span(hl).map_err(|_| fmt::Error)?;
|
let contents = snippet.source.read_span(hl).map_err(|_| fmt::Error)?;
|
||||||
if contents.line() + 1 == line.line_number {
|
if contents.line() + 1 == line.line_number {
|
||||||
write!(
|
write!(
|
||||||
|
|
@ -121,7 +121,7 @@ impl NarratableReportPrinter {
|
||||||
contents.line() + 1,
|
contents.line() + 1,
|
||||||
contents.column() + 1
|
contents.column() + 1
|
||||||
)?;
|
)?;
|
||||||
if let Some(label) = hl.label() {
|
if let Some(label) = label {
|
||||||
write!(f, ": {}", label)?;
|
write!(f, ": {}", label)?;
|
||||||
}
|
}
|
||||||
writeln!(f)?;
|
writeln!(f)?;
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ pub trait Diagnostic: std::error::Error {
|
||||||
|
|
||||||
/// Additional contextual snippets. This is typically used for adding
|
/// Additional contextual snippets. This is typically used for adding
|
||||||
/// marked-up source file output the way compilers often do.
|
/// marked-up source file output the way compilers often do.
|
||||||
fn snippets(&self) -> Option<Box<dyn Iterator<Item = DiagnosticSnippet<'_>> + '_>> {
|
fn snippets<'a>(&'a self) -> Option<Box<dyn Iterator<Item = DiagnosticSnippet<'a>> + 'a>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -157,6 +157,11 @@ pub trait Source: std::fmt::Debug + Send + Sync {
|
||||||
&'a self,
|
&'a self,
|
||||||
span: &SourceSpan,
|
span: &SourceSpan,
|
||||||
) -> Result<Box<dyn SpanContents + 'a>, MietteError>;
|
) -> Result<Box<dyn SpanContents + 'a>, MietteError>;
|
||||||
|
|
||||||
|
/// Optional name, usually a filename, for this source.
|
||||||
|
fn name(&self) -> Option<String> {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -212,7 +217,7 @@ A snippet from a [Source] to be displayed with a message and possibly some highl
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct DiagnosticSnippet<'a> {
|
pub struct DiagnosticSnippet<'a> {
|
||||||
/// Explanation of this specific diagnostic snippet.
|
/// Explanation of this specific diagnostic snippet.
|
||||||
pub message: Option<&'a str>,
|
pub message: Option<String>,
|
||||||
/// A [Source] that can be used to read the actual text of a source.
|
/// A [Source] that can be used to read the actual text of a source.
|
||||||
pub source: &'a (dyn Source),
|
pub source: &'a (dyn Source),
|
||||||
/// The primary [SourceSpan] where this diagnostic is located.
|
/// 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
|
/// Additional [SourceSpan]s that mark specific sections of the span, for
|
||||||
/// example, to underline specific text within the larger span. They're
|
/// example, to underline specific text within the larger span. They're
|
||||||
/// paired with labels that should be applied to those sections.
|
/// paired with labels that should be applied to those sections.
|
||||||
pub highlights: Option<Vec<SourceSpan>>,
|
pub highlights: Option<Vec<(Option<String>, SourceSpan)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -228,9 +233,6 @@ Span within a [Source] with an associated message.
|
||||||
*/
|
*/
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct SourceSpan {
|
pub struct SourceSpan {
|
||||||
/// An optional label for this span. Rendered differently depending on
|
|
||||||
/// context.
|
|
||||||
label: Option<String>,
|
|
||||||
/// The start of the span.
|
/// The start of the span.
|
||||||
offset: SourceOffset,
|
offset: SourceOffset,
|
||||||
/// The total length of the span. Think of this as an offset from `start`.
|
/// The total length of the span. Think of this as an offset from `start`.
|
||||||
|
|
@ -241,16 +243,6 @@ impl SourceSpan {
|
||||||
/// Create a new [SourceSpan].
|
/// Create a new [SourceSpan].
|
||||||
pub fn new(start: SourceOffset, length: SourceOffset) -> Self {
|
pub fn new(start: SourceOffset, length: SourceOffset) -> Self {
|
||||||
Self {
|
Self {
|
||||||
label: None,
|
|
||||||
offset: start,
|
|
||||||
length,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new [SourceSpan] with a label.
|
|
||||||
pub fn new_labeled(label: impl AsRef<str>, start: SourceOffset, length: SourceOffset) -> Self {
|
|
||||||
Self {
|
|
||||||
label: Some(label.as_ref().into()),
|
|
||||||
offset: start,
|
offset: start,
|
||||||
length,
|
length,
|
||||||
}
|
}
|
||||||
|
|
@ -261,15 +253,6 @@ impl SourceSpan {
|
||||||
self.offset.offset()
|
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.
|
/// Total length of the [SourceSpan], in bytes.
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.length.offset()
|
self.length.offset()
|
||||||
|
|
@ -285,17 +268,6 @@ impl SourceSpan {
|
||||||
impl From<(ByteOffset, ByteOffset)> for SourceSpan {
|
impl From<(ByteOffset, ByteOffset)> for SourceSpan {
|
||||||
fn from((start, len): (ByteOffset, ByteOffset)) -> Self {
|
fn from((start, len): (ByteOffset, ByteOffset)) -> Self {
|
||||||
Self {
|
Self {
|
||||||
label: None,
|
|
||||||
offset: start.into(),
|
|
||||||
length: len.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: AsRef<str>> 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(),
|
offset: start.into(),
|
||||||
length: len.into(),
|
length: len.into(),
|
||||||
}
|
}
|
||||||
|
|
@ -305,17 +277,6 @@ impl<T: AsRef<str>> From<(T, ByteOffset, ByteOffset)> for SourceSpan {
|
||||||
impl From<(SourceOffset, SourceOffset)> for SourceSpan {
|
impl From<(SourceOffset, SourceOffset)> for SourceSpan {
|
||||||
fn from((start, len): (SourceOffset, SourceOffset)) -> Self {
|
fn from((start, len): (SourceOffset, SourceOffset)) -> Self {
|
||||||
Self {
|
Self {
|
||||||
label: None,
|
|
||||||
offset: start,
|
|
||||||
length: len,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: AsRef<str>> From<(T, SourceOffset, SourceOffset)> for SourceSpan {
|
|
||||||
fn from((label, start, len): (T, SourceOffset, SourceOffset)) -> Self {
|
|
||||||
Self {
|
|
||||||
label: Some(label.as_ref().into()),
|
|
||||||
offset: start,
|
offset: start,
|
||||||
length: len,
|
length: len,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
39
src/utils.rs
39
src/utils.rs
|
|
@ -2,7 +2,7 @@ use std::fmt;
|
||||||
|
|
||||||
use thiserror::Error;
|
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()`
|
/// Convenience alias. This is intended to be used as the return type for `main()`
|
||||||
pub type DiagnosticResult<T> = Result<T, DiagnosticReport>;
|
pub type DiagnosticResult<T> = Result<T, DiagnosticReport>;
|
||||||
|
|
@ -47,3 +47,40 @@ impl<T, E: std::error::Error + Send + Sync + 'static> IntoDiagnostic<T, E> 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<dyn Source + Send + Sync + 'static>,
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NamedSource {
|
||||||
|
/// Create a new [NamedSource] using a regular [Source] and giving it a [Source::name].
|
||||||
|
pub fn new(name: impl AsRef<str>, 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<Box<dyn crate::SpanContents + 'a>, crate::MietteError> {
|
||||||
|
self.source.read_span(span)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self) -> Option<String> {
|
||||||
|
Some(self.name.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -212,7 +212,7 @@ fn test_snippet_named_struct() {
|
||||||
// / [my_snippet]: hi this is where the thing went wrong.
|
// / [my_snippet]: hi this is where the thing went wrong.
|
||||||
// 1 | hello
|
// 1 | hello
|
||||||
// 2 | world
|
// 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`
|
snip: SourceSpan, // Defines filename using `label`
|
||||||
|
|
||||||
// "Highlights" are the specific highlights _inside_ the snippet.
|
// "Highlights" are the specific highlights _inside_ the snippet.
|
||||||
|
|
@ -230,13 +230,7 @@ fn test_snippet_named_struct() {
|
||||||
var1: SourceSpan,
|
var1: SourceSpan,
|
||||||
#[highlight(snip)]
|
#[highlight(snip)]
|
||||||
// Anything that's Clone + Into<SourceSpan> can be used here.
|
// Anything that's Clone + Into<SourceSpan> can be used here.
|
||||||
var2: (String, usize, usize),
|
var2: (usize, usize),
|
||||||
|
|
||||||
// Now with member source names
|
|
||||||
filename: String,
|
|
||||||
second_message: String,
|
|
||||||
#[snippet(src, filename, second_message)]
|
|
||||||
snip2: SourceSpan,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -247,12 +241,12 @@ fn test_snippet_unnamed_struct() {
|
||||||
#[diagnostic(code(foo::bar::baz))]
|
#[diagnostic(code(foo::bar::baz))]
|
||||||
struct Foo(
|
struct Foo(
|
||||||
String,
|
String,
|
||||||
#[snippet(0, "hi")] SourceSpan,
|
#[snippet(0, message("hi"))] SourceSpan,
|
||||||
#[highlight(1)] SourceSpan,
|
#[highlight(1)] SourceSpan,
|
||||||
#[highlight(1)] SourceSpan,
|
#[highlight(1)] SourceSpan,
|
||||||
// referenced source name
|
// referenced source name
|
||||||
String,
|
String,
|
||||||
#[snippet(0, 4)] SourceSpan,
|
#[snippet(0, message("{}", self.4))] SourceSpan,
|
||||||
#[highlight(5)] SourceSpan,
|
#[highlight(5)] SourceSpan,
|
||||||
#[highlight(5)] SourceSpan,
|
#[highlight(5)] SourceSpan,
|
||||||
);
|
);
|
||||||
|
|
@ -267,29 +261,23 @@ fn test_snippet_enum() {
|
||||||
#[diagnostic(code(foo::a))]
|
#[diagnostic(code(foo::a))]
|
||||||
A {
|
A {
|
||||||
src: String,
|
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,
|
snip: SourceSpan,
|
||||||
#[highlight(snip)]
|
#[highlight(snip)]
|
||||||
var1: SourceSpan,
|
var1: SourceSpan,
|
||||||
#[highlight(snip)]
|
#[highlight(snip)]
|
||||||
var2: SourceSpan,
|
var2: SourceSpan,
|
||||||
filename: String,
|
|
||||||
second_message: String,
|
|
||||||
#[snippet(src, filename, second_message)]
|
|
||||||
snip2: SourceSpan,
|
|
||||||
},
|
},
|
||||||
#[diagnostic(code(foo::b))]
|
#[diagnostic(code(foo::b))]
|
||||||
B(
|
B(
|
||||||
String,
|
String,
|
||||||
#[snippet(0, "hi")] SourceSpan,
|
#[snippet(0, message("hi"))] SourceSpan,
|
||||||
#[highlight(1)] SourceSpan,
|
#[highlight(1)] SourceSpan,
|
||||||
#[highlight(1, "var 2")] SourceSpan,
|
#[highlight(1, label("var 2"))] SourceSpan,
|
||||||
// referenced source name
|
// referenced source name
|
||||||
String,
|
#[snippet(0)] SourceSpan,
|
||||||
String,
|
#[highlight(4)] SourceSpan,
|
||||||
#[snippet(0, 4, 5)] SourceSpan,
|
#[highlight(4)] SourceSpan,
|
||||||
#[highlight(6)] SourceSpan,
|
|
||||||
#[highlight(6)] SourceSpan,
|
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use miette::{
|
use miette::{
|
||||||
Diagnostic, DiagnosticReport, GraphicalReportPrinter, GraphicalTheme, MietteError,
|
Diagnostic, DiagnosticReport, GraphicalReportPrinter, GraphicalTheme, MietteError, NamedSource,
|
||||||
NarratableReportPrinter, SourceSpan,
|
NarratableReportPrinter, SourceSpan,
|
||||||
};
|
};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
@ -25,19 +25,19 @@ fn single_line_highlight() -> Result<(), MietteError> {
|
||||||
#[error("oops!")]
|
#[error("oops!")]
|
||||||
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
|
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
|
||||||
struct MyBad {
|
struct MyBad {
|
||||||
src: String,
|
src: NamedSource,
|
||||||
#[snippet(src, "This is the part that broke")]
|
#[snippet(src, message("This is the part that broke"))]
|
||||||
ctx: SourceSpan,
|
ctx: SourceSpan,
|
||||||
#[highlight(ctx)]
|
#[highlight(ctx, label = "this bit here")]
|
||||||
highlight: SourceSpan,
|
highlight: SourceSpan,
|
||||||
}
|
}
|
||||||
|
|
||||||
let src = "source\n text\n here".to_string();
|
let src = "source\n text\n here".to_string();
|
||||||
let len = src.len();
|
let len = src.len();
|
||||||
let err = MyBad {
|
let err = MyBad {
|
||||||
src,
|
src: NamedSource::new("bad_file.rs", src),
|
||||||
ctx: ("bad_file.rs", 0, len).into(),
|
ctx: (0, len).into(),
|
||||||
highlight: ("this bit here", 9, 4).into(),
|
highlight: (9, 4).into(),
|
||||||
};
|
};
|
||||||
let out = fmt_report(err.into());
|
let out = fmt_report(err.into());
|
||||||
println!("{}", out);
|
println!("{}", out);
|
||||||
|
|
@ -67,8 +67,8 @@ fn single_line_highlight_no_label() -> Result<(), MietteError> {
|
||||||
#[error("oops!")]
|
#[error("oops!")]
|
||||||
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
|
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
|
||||||
struct MyBad {
|
struct MyBad {
|
||||||
src: String,
|
src: NamedSource,
|
||||||
#[snippet(src, "This is the part that broke")]
|
#[snippet(src, message("This is the part that broke"))]
|
||||||
ctx: SourceSpan,
|
ctx: SourceSpan,
|
||||||
#[highlight(ctx)]
|
#[highlight(ctx)]
|
||||||
highlight: SourceSpan,
|
highlight: SourceSpan,
|
||||||
|
|
@ -77,8 +77,8 @@ fn single_line_highlight_no_label() -> Result<(), MietteError> {
|
||||||
let src = "source\n text\n here".to_string();
|
let src = "source\n text\n here".to_string();
|
||||||
let len = src.len();
|
let len = src.len();
|
||||||
let err = MyBad {
|
let err = MyBad {
|
||||||
src,
|
src: NamedSource::new("bad_file.rs", src),
|
||||||
ctx: ("bad_file.rs", 0, len).into(),
|
ctx: (0, len).into(),
|
||||||
highlight: (9, 4).into(),
|
highlight: (9, 4).into(),
|
||||||
};
|
};
|
||||||
let out = fmt_report(err.into());
|
let out = fmt_report(err.into());
|
||||||
|
|
@ -109,22 +109,22 @@ fn multiple_same_line_highlights() -> Result<(), MietteError> {
|
||||||
#[error("oops!")]
|
#[error("oops!")]
|
||||||
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
|
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
|
||||||
struct MyBad {
|
struct MyBad {
|
||||||
src: String,
|
src: NamedSource,
|
||||||
#[snippet(src, "This is the part that broke")]
|
#[snippet(src, message("This is the part that broke"))]
|
||||||
ctx: SourceSpan,
|
ctx: SourceSpan,
|
||||||
#[highlight(ctx)]
|
#[highlight(ctx, label = "this bit here")]
|
||||||
highlight1: SourceSpan,
|
highlight1: SourceSpan,
|
||||||
#[highlight(ctx)]
|
#[highlight(ctx, label = "also this bit")]
|
||||||
highlight2: SourceSpan,
|
highlight2: SourceSpan,
|
||||||
}
|
}
|
||||||
|
|
||||||
let src = "source\n text text text text text\n here".to_string();
|
let src = "source\n text text text text text\n here".to_string();
|
||||||
let len = src.len();
|
let len = src.len();
|
||||||
let err = MyBad {
|
let err = MyBad {
|
||||||
src,
|
src: NamedSource::new("bad_file.rs", src),
|
||||||
ctx: ("bad_file.rs", 0, len).into(),
|
ctx: (0, len).into(),
|
||||||
highlight1: ("this bit here", 9, 4).into(),
|
highlight1: (9, 4).into(),
|
||||||
highlight2: ("also this bit", 14, 4).into(),
|
highlight2: (14, 4).into(),
|
||||||
};
|
};
|
||||||
let out = fmt_report(err.into());
|
let out = fmt_report(err.into());
|
||||||
println!("{}", out);
|
println!("{}", out);
|
||||||
|
|
@ -155,19 +155,19 @@ fn multiline_highlight_adjacent() -> Result<(), MietteError> {
|
||||||
#[error("oops!")]
|
#[error("oops!")]
|
||||||
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
|
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
|
||||||
struct MyBad {
|
struct MyBad {
|
||||||
src: String,
|
src: NamedSource,
|
||||||
#[snippet(src, "This is the part that broke")]
|
#[snippet(src, message("This is the part that broke"))]
|
||||||
ctx: SourceSpan,
|
ctx: SourceSpan,
|
||||||
#[highlight(ctx)]
|
#[highlight(ctx, label = "these two lines")]
|
||||||
highlight: SourceSpan,
|
highlight: SourceSpan,
|
||||||
}
|
}
|
||||||
|
|
||||||
let src = "source\n text\n here".to_string();
|
let src = "source\n text\n here".to_string();
|
||||||
let len = src.len();
|
let len = src.len();
|
||||||
let err = MyBad {
|
let err = MyBad {
|
||||||
src,
|
src: NamedSource::new("bad_file.rs", src),
|
||||||
ctx: ("bad_file.rs", 0, len).into(),
|
ctx: (0, len).into(),
|
||||||
highlight: ("these two lines", 9, 11).into(),
|
highlight: (9, 11).into(),
|
||||||
};
|
};
|
||||||
let out = fmt_report(err.into());
|
let out = fmt_report(err.into());
|
||||||
println!("{}", out);
|
println!("{}", out);
|
||||||
|
|
@ -197,12 +197,12 @@ fn multiline_highlight_flyby() -> Result<(), MietteError> {
|
||||||
#[error("oops!")]
|
#[error("oops!")]
|
||||||
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
|
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
|
||||||
struct MyBad {
|
struct MyBad {
|
||||||
src: String,
|
src: NamedSource,
|
||||||
#[snippet(src, "This is the part that broke")]
|
#[snippet(src, message("This is the part that broke"))]
|
||||||
ctx: SourceSpan,
|
ctx: SourceSpan,
|
||||||
#[highlight(ctx)]
|
#[highlight(ctx, label = "block 1")]
|
||||||
highlight1: SourceSpan,
|
highlight1: SourceSpan,
|
||||||
#[highlight(ctx)]
|
#[highlight(ctx, label = "block 2")]
|
||||||
highlight2: SourceSpan,
|
highlight2: SourceSpan,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -215,10 +215,10 @@ line5
|
||||||
.to_string();
|
.to_string();
|
||||||
let len = src.len();
|
let len = src.len();
|
||||||
let err = MyBad {
|
let err = MyBad {
|
||||||
src,
|
src: NamedSource::new("bad_file.rs", src),
|
||||||
ctx: ("bad_file.rs", 0, len).into(),
|
ctx: (0, len).into(),
|
||||||
highlight1: ("block 1", 0, len).into(),
|
highlight1: (0, len).into(),
|
||||||
highlight2: ("block 2", 10, 9).into(),
|
highlight2: (10, 9).into(),
|
||||||
};
|
};
|
||||||
let out = fmt_report(err.into());
|
let out = fmt_report(err.into());
|
||||||
println!("{}", out);
|
println!("{}", out);
|
||||||
|
|
@ -251,10 +251,10 @@ fn multiline_highlight_no_label() -> Result<(), MietteError> {
|
||||||
#[error("oops!")]
|
#[error("oops!")]
|
||||||
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
|
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
|
||||||
struct MyBad {
|
struct MyBad {
|
||||||
src: String,
|
src: NamedSource,
|
||||||
#[snippet(src, "This is the part that broke")]
|
#[snippet(src, message("This is the part that broke"))]
|
||||||
ctx: SourceSpan,
|
ctx: SourceSpan,
|
||||||
#[highlight(ctx)]
|
#[highlight(ctx, label = "block 1")]
|
||||||
highlight1: SourceSpan,
|
highlight1: SourceSpan,
|
||||||
#[highlight(ctx)]
|
#[highlight(ctx)]
|
||||||
highlight2: SourceSpan,
|
highlight2: SourceSpan,
|
||||||
|
|
@ -269,9 +269,9 @@ line5
|
||||||
.to_string();
|
.to_string();
|
||||||
let len = src.len();
|
let len = src.len();
|
||||||
let err = MyBad {
|
let err = MyBad {
|
||||||
src,
|
src: NamedSource::new("bad_file.rs", src),
|
||||||
ctx: ("bad_file.rs", 0, len).into(),
|
ctx: (0, len).into(),
|
||||||
highlight1: ("block 1", 0, len).into(),
|
highlight1: (0, len).into(),
|
||||||
highlight2: (10, 9).into(),
|
highlight2: (10, 9).into(),
|
||||||
};
|
};
|
||||||
let out = fmt_report(err.into());
|
let out = fmt_report(err.into());
|
||||||
|
|
@ -305,22 +305,22 @@ fn multiple_multiline_highlights_adjacent() -> Result<(), MietteError> {
|
||||||
#[error("oops!")]
|
#[error("oops!")]
|
||||||
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
|
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
|
||||||
struct MyBad {
|
struct MyBad {
|
||||||
src: String,
|
src: NamedSource,
|
||||||
#[snippet(src, "This is the part that broke")]
|
#[snippet(src, message("This is the part that broke"))]
|
||||||
ctx: SourceSpan,
|
ctx: SourceSpan,
|
||||||
#[highlight(ctx)]
|
#[highlight(ctx, label = "this bit here")]
|
||||||
highlight1: SourceSpan,
|
highlight1: SourceSpan,
|
||||||
#[highlight(ctx)]
|
#[highlight(ctx, label = "also this bit")]
|
||||||
highlight2: SourceSpan,
|
highlight2: SourceSpan,
|
||||||
}
|
}
|
||||||
|
|
||||||
let src = "source\n text\n here\nmore here".to_string();
|
let src = "source\n text\n here\nmore here".to_string();
|
||||||
let len = src.len();
|
let len = src.len();
|
||||||
let err = MyBad {
|
let err = MyBad {
|
||||||
src,
|
src: NamedSource::new("bad_file.rs", src),
|
||||||
ctx: ("bad_file.rs", 0, len).into(),
|
ctx: (0, len).into(),
|
||||||
highlight1: ("this bit here", 0, 10).into(),
|
highlight1: (0, 10).into(),
|
||||||
highlight2: ("also this bit", 20, 6).into(),
|
highlight2: (20, 6).into(),
|
||||||
};
|
};
|
||||||
let out = fmt_report(err.into());
|
let out = fmt_report(err.into());
|
||||||
println!("{}", out);
|
println!("{}", out);
|
||||||
|
|
@ -354,22 +354,22 @@ fn multiple_multiline_highlights_overlapping_lines() -> Result<(), MietteError>
|
||||||
#[error("oops!")]
|
#[error("oops!")]
|
||||||
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
|
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
|
||||||
struct MyBad {
|
struct MyBad {
|
||||||
src: String,
|
src: NamedSource,
|
||||||
#[snippet(src, "This is the part that broke")]
|
#[snippet(src, message("This is the part that broke"))]
|
||||||
ctx: SourceSpan,
|
ctx: SourceSpan,
|
||||||
#[highlight(ctx)]
|
#[highlight(ctx, label = "this bit here")]
|
||||||
highlight1: SourceSpan,
|
highlight1: SourceSpan,
|
||||||
#[highlight(ctx)]
|
#[highlight(ctx, label = "also this bit")]
|
||||||
highlight2: SourceSpan,
|
highlight2: SourceSpan,
|
||||||
}
|
}
|
||||||
|
|
||||||
let src = "source\n text\n here".to_string();
|
let src = "source\n text\n here".to_string();
|
||||||
let len = src.len();
|
let len = src.len();
|
||||||
let err = MyBad {
|
let err = MyBad {
|
||||||
src,
|
src: NamedSource::new("bad_file.rs", src),
|
||||||
ctx: ("bad_file.rs", 0, len).into(),
|
ctx: (0, len).into(),
|
||||||
highlight1: ("this bit here", 0, 8).into(),
|
highlight1: (0, 8).into(),
|
||||||
highlight2: ("also this bit", 9, 10).into(),
|
highlight2: (9, 10).into(),
|
||||||
};
|
};
|
||||||
let out = fmt_report(err.into());
|
let out = fmt_report(err.into());
|
||||||
println!("{}", out);
|
println!("{}", out);
|
||||||
|
|
@ -402,22 +402,22 @@ fn multiple_multiline_highlights_overlapping_offsets() -> Result<(), MietteError
|
||||||
#[error("oops!")]
|
#[error("oops!")]
|
||||||
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
|
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
|
||||||
struct MyBad {
|
struct MyBad {
|
||||||
src: String,
|
src: NamedSource,
|
||||||
#[snippet(src, "This is the part that broke")]
|
#[snippet(src, message("This is the part that broke"))]
|
||||||
ctx: SourceSpan,
|
ctx: SourceSpan,
|
||||||
#[highlight(ctx)]
|
#[highlight(ctx, label = "this bit here")]
|
||||||
highlight1: SourceSpan,
|
highlight1: SourceSpan,
|
||||||
#[highlight(ctx)]
|
#[highlight(ctx, label = "also this bit")]
|
||||||
highlight2: SourceSpan,
|
highlight2: SourceSpan,
|
||||||
}
|
}
|
||||||
|
|
||||||
let src = "source\n text\n here".to_string();
|
let src = "source\n text\n here".to_string();
|
||||||
let len = src.len();
|
let len = src.len();
|
||||||
let err = MyBad {
|
let err = MyBad {
|
||||||
src,
|
src: NamedSource::new("bad_file.rs", src),
|
||||||
ctx: ("bad_file.rs", 0, len).into(),
|
ctx: (0, len).into(),
|
||||||
highlight1: ("this bit here", 0, 8).into(),
|
highlight1: (0, 8).into(),
|
||||||
highlight2: ("also this bit", 10, 10).into(),
|
highlight2: (10, 10).into(),
|
||||||
};
|
};
|
||||||
let out = fmt_report(err.into());
|
let out = fmt_report(err.into());
|
||||||
println!("{}", out);
|
println!("{}", out);
|
||||||
|
|
|
||||||
126
tests/printer.rs
126
tests/printer.rs
|
|
@ -1,5 +1,5 @@
|
||||||
use miette::{
|
use miette::{
|
||||||
Diagnostic, DiagnosticReport, GraphicalReportPrinter, GraphicalTheme, MietteError,
|
Diagnostic, DiagnosticReport, GraphicalReportPrinter, GraphicalTheme, MietteError, NamedSource,
|
||||||
NarratableReportPrinter, SourceSpan,
|
NarratableReportPrinter, SourceSpan,
|
||||||
};
|
};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
@ -29,19 +29,19 @@ fn single_line_highlight() -> Result<(), MietteError> {
|
||||||
#[error("oops!")]
|
#[error("oops!")]
|
||||||
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
|
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
|
||||||
struct MyBad {
|
struct MyBad {
|
||||||
src: String,
|
src: NamedSource,
|
||||||
#[snippet(src, "This is the part that broke")]
|
#[snippet(src, message("This is the part that broke"))]
|
||||||
ctx: SourceSpan,
|
ctx: SourceSpan,
|
||||||
#[highlight(ctx)]
|
#[highlight(ctx, label = "this bit here")]
|
||||||
highlight: SourceSpan,
|
highlight: SourceSpan,
|
||||||
}
|
}
|
||||||
|
|
||||||
let src = "source\n text\n here".to_string();
|
let src = "source\n text\n here".to_string();
|
||||||
let len = src.len();
|
let len = src.len();
|
||||||
let err = MyBad {
|
let err = MyBad {
|
||||||
src,
|
src: NamedSource::new("bad_file.rs", src),
|
||||||
ctx: ("bad_file.rs", 0, len).into(),
|
ctx: (0, len).into(),
|
||||||
highlight: ("this bit here", 9, 4).into(),
|
highlight: (9, 4).into(),
|
||||||
};
|
};
|
||||||
let out = fmt_report(err.into());
|
let out = fmt_report(err.into());
|
||||||
println!("{}", out);
|
println!("{}", out);
|
||||||
|
|
@ -71,8 +71,8 @@ fn single_line_highlight_no_label() -> Result<(), MietteError> {
|
||||||
#[error("oops!")]
|
#[error("oops!")]
|
||||||
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
|
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
|
||||||
struct MyBad {
|
struct MyBad {
|
||||||
src: String,
|
src: NamedSource,
|
||||||
#[snippet(src, "This is the part that broke")]
|
#[snippet(src, message("This is the part that broke"))]
|
||||||
ctx: SourceSpan,
|
ctx: SourceSpan,
|
||||||
#[highlight(ctx)]
|
#[highlight(ctx)]
|
||||||
highlight: SourceSpan,
|
highlight: SourceSpan,
|
||||||
|
|
@ -81,8 +81,8 @@ fn single_line_highlight_no_label() -> Result<(), MietteError> {
|
||||||
let src = "source\n text\n here".to_string();
|
let src = "source\n text\n here".to_string();
|
||||||
let len = src.len();
|
let len = src.len();
|
||||||
let err = MyBad {
|
let err = MyBad {
|
||||||
src,
|
src: NamedSource::new("bad_file.rs", src),
|
||||||
ctx: ("bad_file.rs", 0, len).into(),
|
ctx: (0, len).into(),
|
||||||
highlight: (9, 4).into(),
|
highlight: (9, 4).into(),
|
||||||
};
|
};
|
||||||
let out = fmt_report(err.into());
|
let out = fmt_report(err.into());
|
||||||
|
|
@ -112,22 +112,22 @@ fn multiple_same_line_highlights() -> Result<(), MietteError> {
|
||||||
#[error("oops!")]
|
#[error("oops!")]
|
||||||
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
|
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
|
||||||
struct MyBad {
|
struct MyBad {
|
||||||
src: String,
|
src: NamedSource,
|
||||||
#[snippet(src, "This is the part that broke")]
|
#[snippet(src, message("This is the part that broke"))]
|
||||||
ctx: SourceSpan,
|
ctx: SourceSpan,
|
||||||
#[highlight(ctx)]
|
#[highlight(ctx, label = "this bit here")]
|
||||||
highlight1: SourceSpan,
|
highlight1: SourceSpan,
|
||||||
#[highlight(ctx)]
|
#[highlight(ctx, label = "also this bit")]
|
||||||
highlight2: SourceSpan,
|
highlight2: SourceSpan,
|
||||||
}
|
}
|
||||||
|
|
||||||
let src = "source\n text text text text text\n here".to_string();
|
let src = "source\n text text text text text\n here".to_string();
|
||||||
let len = src.len();
|
let len = src.len();
|
||||||
let err = MyBad {
|
let err = MyBad {
|
||||||
src,
|
src: NamedSource::new("bad_file.rs", src),
|
||||||
ctx: ("bad_file.rs", 0, len).into(),
|
ctx: (0, len).into(),
|
||||||
highlight1: ("this bit here", 9, 4).into(),
|
highlight1: (9, 4).into(),
|
||||||
highlight2: ("also this bit", 14, 4).into(),
|
highlight2: (14, 4).into(),
|
||||||
};
|
};
|
||||||
let out = fmt_report(err.into());
|
let out = fmt_report(err.into());
|
||||||
println!("{}", out);
|
println!("{}", out);
|
||||||
|
|
@ -158,19 +158,19 @@ fn multiline_highlight_adjacent() -> Result<(), MietteError> {
|
||||||
#[error("oops!")]
|
#[error("oops!")]
|
||||||
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
|
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
|
||||||
struct MyBad {
|
struct MyBad {
|
||||||
src: String,
|
src: NamedSource,
|
||||||
#[snippet(src, "This is the part that broke")]
|
#[snippet(src, message("This is the part that broke"))]
|
||||||
ctx: SourceSpan,
|
ctx: SourceSpan,
|
||||||
#[highlight(ctx)]
|
#[highlight(ctx, label = "these two lines")]
|
||||||
highlight: SourceSpan,
|
highlight: SourceSpan,
|
||||||
}
|
}
|
||||||
|
|
||||||
let src = "source\n text\n here".to_string();
|
let src = "source\n text\n here".to_string();
|
||||||
let len = src.len();
|
let len = src.len();
|
||||||
let err = MyBad {
|
let err = MyBad {
|
||||||
src,
|
src: NamedSource::new("bad_file.rs", src),
|
||||||
ctx: ("bad_file.rs", 0, len).into(),
|
ctx: (0, len).into(),
|
||||||
highlight: ("these two lines", 9, 11).into(),
|
highlight: (9, 11).into(),
|
||||||
};
|
};
|
||||||
let out = fmt_report(err.into());
|
let out = fmt_report(err.into());
|
||||||
println!("{}", out);
|
println!("{}", out);
|
||||||
|
|
@ -199,12 +199,12 @@ fn multiline_highlight_flyby() -> Result<(), MietteError> {
|
||||||
#[error("oops!")]
|
#[error("oops!")]
|
||||||
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
|
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
|
||||||
struct MyBad {
|
struct MyBad {
|
||||||
src: String,
|
src: NamedSource,
|
||||||
#[snippet(src, "This is the part that broke")]
|
#[snippet(src, message("This is the part that broke"))]
|
||||||
ctx: SourceSpan,
|
ctx: SourceSpan,
|
||||||
#[highlight(ctx)]
|
#[highlight(ctx, label = "block 1")]
|
||||||
highlight1: SourceSpan,
|
highlight1: SourceSpan,
|
||||||
#[highlight(ctx)]
|
#[highlight(ctx, label = "block 2")]
|
||||||
highlight2: SourceSpan,
|
highlight2: SourceSpan,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -217,10 +217,10 @@ line5
|
||||||
.to_string();
|
.to_string();
|
||||||
let len = src.len();
|
let len = src.len();
|
||||||
let err = MyBad {
|
let err = MyBad {
|
||||||
src,
|
src: NamedSource::new("bad_file.rs", src),
|
||||||
ctx: ("bad_file.rs", 0, len).into(),
|
ctx: (0, len).into(),
|
||||||
highlight1: ("block 1", 0, len).into(),
|
highlight1: (0, len).into(),
|
||||||
highlight2: ("block 2", 10, 9).into(),
|
highlight2: (10, 9).into(),
|
||||||
};
|
};
|
||||||
let out = fmt_report(err.into());
|
let out = fmt_report(err.into());
|
||||||
println!("{}", out);
|
println!("{}", out);
|
||||||
|
|
@ -252,10 +252,10 @@ fn multiline_highlight_no_label() -> Result<(), MietteError> {
|
||||||
#[error("oops!")]
|
#[error("oops!")]
|
||||||
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
|
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
|
||||||
struct MyBad {
|
struct MyBad {
|
||||||
src: String,
|
src: NamedSource,
|
||||||
#[snippet(src, "This is the part that broke")]
|
#[snippet(src, message("This is the part that broke"))]
|
||||||
ctx: SourceSpan,
|
ctx: SourceSpan,
|
||||||
#[highlight(ctx)]
|
#[highlight(ctx, label = "block 1")]
|
||||||
highlight1: SourceSpan,
|
highlight1: SourceSpan,
|
||||||
#[highlight(ctx)]
|
#[highlight(ctx)]
|
||||||
highlight2: SourceSpan,
|
highlight2: SourceSpan,
|
||||||
|
|
@ -270,9 +270,9 @@ line5
|
||||||
.to_string();
|
.to_string();
|
||||||
let len = src.len();
|
let len = src.len();
|
||||||
let err = MyBad {
|
let err = MyBad {
|
||||||
src,
|
src: NamedSource::new("bad_file.rs", src),
|
||||||
ctx: ("bad_file.rs", 0, len).into(),
|
ctx: (0, len).into(),
|
||||||
highlight1: ("block 1", 0, len).into(),
|
highlight1: (0, len).into(),
|
||||||
highlight2: (10, 9).into(),
|
highlight2: (10, 9).into(),
|
||||||
};
|
};
|
||||||
let out = fmt_report(err.into());
|
let out = fmt_report(err.into());
|
||||||
|
|
@ -304,22 +304,22 @@ fn multiple_multiline_highlights_adjacent() -> Result<(), MietteError> {
|
||||||
#[error("oops!")]
|
#[error("oops!")]
|
||||||
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
|
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
|
||||||
struct MyBad {
|
struct MyBad {
|
||||||
src: String,
|
src: NamedSource,
|
||||||
#[snippet(src, "This is the part that broke")]
|
#[snippet(src, message("This is the part that broke"))]
|
||||||
ctx: SourceSpan,
|
ctx: SourceSpan,
|
||||||
#[highlight(ctx)]
|
#[highlight(ctx, label = "this bit here")]
|
||||||
highlight1: SourceSpan,
|
highlight1: SourceSpan,
|
||||||
#[highlight(ctx)]
|
#[highlight(ctx, label = "also this bit")]
|
||||||
highlight2: SourceSpan,
|
highlight2: SourceSpan,
|
||||||
}
|
}
|
||||||
|
|
||||||
let src = "source\n text\n here\nmore here".to_string();
|
let src = "source\n text\n here\nmore here".to_string();
|
||||||
let len = src.len();
|
let len = src.len();
|
||||||
let err = MyBad {
|
let err = MyBad {
|
||||||
src,
|
src: NamedSource::new("bad_file.rs", src),
|
||||||
ctx: ("bad_file.rs", 0, len).into(),
|
ctx: (0, len).into(),
|
||||||
highlight1: ("this bit here", 0, 10).into(),
|
highlight1: (0, 10).into(),
|
||||||
highlight2: ("also this bit", 20, 6).into(),
|
highlight2: (20, 6).into(),
|
||||||
};
|
};
|
||||||
let out = fmt_report(err.into());
|
let out = fmt_report(err.into());
|
||||||
println!("{}", out);
|
println!("{}", out);
|
||||||
|
|
@ -354,22 +354,22 @@ fn multiple_multiline_highlights_overlapping_lines() -> Result<(), MietteError>
|
||||||
#[error("oops!")]
|
#[error("oops!")]
|
||||||
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
|
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
|
||||||
struct MyBad {
|
struct MyBad {
|
||||||
src: String,
|
src: NamedSource,
|
||||||
#[snippet(src, "This is the part that broke")]
|
#[snippet(src, message("This is the part that broke"))]
|
||||||
ctx: SourceSpan,
|
ctx: SourceSpan,
|
||||||
#[highlight(ctx)]
|
#[highlight(ctx, label = "this bit here")]
|
||||||
highlight1: SourceSpan,
|
highlight1: SourceSpan,
|
||||||
#[highlight(ctx)]
|
#[highlight(ctx, label = "also this bit")]
|
||||||
highlight2: SourceSpan,
|
highlight2: SourceSpan,
|
||||||
}
|
}
|
||||||
|
|
||||||
let src = "source\n text\n here".to_string();
|
let src = "source\n text\n here".to_string();
|
||||||
let len = src.len();
|
let len = src.len();
|
||||||
let err = MyBad {
|
let err = MyBad {
|
||||||
src,
|
src: NamedSource::new("bad_file.rs", src),
|
||||||
ctx: ("bad_file.rs", 0, len).into(),
|
ctx: (0, len).into(),
|
||||||
highlight1: ("this bit here", 0, 8).into(),
|
highlight1: (0, 8).into(),
|
||||||
highlight2: ("also this bit", 9, 10).into(),
|
highlight2: (9, 10).into(),
|
||||||
};
|
};
|
||||||
let out = fmt_report(err.into());
|
let out = fmt_report(err.into());
|
||||||
println!("{}", out);
|
println!("{}", out);
|
||||||
|
|
@ -385,22 +385,22 @@ fn multiple_multiline_highlights_overlapping_offsets() -> Result<(), MietteError
|
||||||
#[error("oops!")]
|
#[error("oops!")]
|
||||||
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
|
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
|
||||||
struct MyBad {
|
struct MyBad {
|
||||||
src: String,
|
src: NamedSource,
|
||||||
#[snippet(src, "This is the part that broke")]
|
#[snippet(src, message("This is the part that broke"))]
|
||||||
ctx: SourceSpan,
|
ctx: SourceSpan,
|
||||||
#[highlight(ctx)]
|
#[highlight(ctx, label = "this bit here")]
|
||||||
highlight1: SourceSpan,
|
highlight1: SourceSpan,
|
||||||
#[highlight(ctx)]
|
#[highlight(ctx, label = "also this bit")]
|
||||||
highlight2: SourceSpan,
|
highlight2: SourceSpan,
|
||||||
}
|
}
|
||||||
|
|
||||||
let src = "source\n text\n here".to_string();
|
let src = "source\n text\n here".to_string();
|
||||||
let len = src.len();
|
let len = src.len();
|
||||||
let err = MyBad {
|
let err = MyBad {
|
||||||
src,
|
src: NamedSource::new("bad_file.rs", src),
|
||||||
ctx: ("bad_file.rs", 0, len).into(),
|
ctx: (0, len).into(),
|
||||||
highlight1: ("this bit here", 0, 8).into(),
|
highlight1: (0, 8).into(),
|
||||||
highlight2: ("also this bit", 10, 10).into(),
|
highlight2: (10, 10).into(),
|
||||||
};
|
};
|
||||||
let out = fmt_report(err.into());
|
let out = fmt_report(err.into());
|
||||||
println!("{}", out);
|
println!("{}", out);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue