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)
|
||||
- [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,
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -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! {
|
||||
|
|
|
|||
|
|
@ -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<Snippet>);
|
||||
|
||||
struct Snippet {
|
||||
message: Option<MemberOrString>,
|
||||
message: Option<Display>,
|
||||
highlights: Vec<Highlight>,
|
||||
source: syn::Member,
|
||||
snippet: syn::Member,
|
||||
}
|
||||
|
||||
struct Highlight {
|
||||
label: Option<Display>,
|
||||
highlight: syn::Member,
|
||||
}
|
||||
|
||||
struct SnippetAttr {
|
||||
source: syn::Member,
|
||||
message: Option<MemberOrString>,
|
||||
message: Option<Display>,
|
||||
}
|
||||
|
||||
struct HighlightAttr {
|
||||
label: Option<Display>,
|
||||
snippet: syn::Member,
|
||||
}
|
||||
|
||||
impl Parse for SnippetAttr {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let punc = Punctuated::<MemberOrString, Token![,]>::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::<syn::Member>()?;
|
||||
let message = if input.peek(Token![,]) {
|
||||
input.parse::<Token![,]>()?;
|
||||
let ident = input.parse::<syn::Ident>()?;
|
||||
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(
|
||||
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<Self> {
|
||||
let punc = Punctuated::<MemberOrString, Token![,]>::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::<syn::Member>()?;
|
||||
let label = if input.peek(Token![,]) {
|
||||
input.parse::<Token![,]>()?;
|
||||
let ident = input.parse::<syn::Ident>()?;
|
||||
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::<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 attr in &field.attrs {
|
||||
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) {
|
||||
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<TokenStream> {
|
||||
pub(crate) fn gen_struct(&self, fields: &syn::Fields) -> Option<TokenStream> {
|
||||
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<syn::Member> = 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<syn::Member> = 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! {
|
||||
|
|
|
|||
|
|
@ -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::<Vec<_>>();
|
||||
|
||||
// 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<String>,
|
||||
span: SourceSpan,
|
||||
style: Style,
|
||||
}
|
||||
|
||||
impl FancySpan {
|
||||
fn new(span: SourceSpan, style: Style) -> Self {
|
||||
FancySpan { span, style }
|
||||
fn new(label: Option<String>, span: SourceSpan, style: Style) -> Self {
|
||||
FancySpan { label, span, style }
|
||||
}
|
||||
|
||||
fn style(&self) -> Style {
|
||||
|
|
@ -572,7 +573,9 @@ impl FancySpan {
|
|||
}
|
||||
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -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<dyn DiagnosticReportPrinter + Send + Sync + 'sta
|
|||
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)?;
|
||||
|
||||
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)?;
|
||||
|
|
|
|||
|
|
@ -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<Box<dyn Iterator<Item = DiagnosticSnippet<'_>> + '_>> {
|
||||
fn snippets<'a>(&'a self) -> Option<Box<dyn Iterator<Item = DiagnosticSnippet<'a>> + 'a>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
@ -157,6 +157,11 @@ pub trait Source: std::fmt::Debug + Send + Sync {
|
|||
&'a self,
|
||||
span: &SourceSpan,
|
||||
) -> 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)]
|
||||
pub struct DiagnosticSnippet<'a> {
|
||||
/// 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.
|
||||
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<Vec<SourceSpan>>,
|
||||
pub highlights: Option<Vec<(Option<String>, 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<String>,
|
||||
/// 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<str>, 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<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(),
|
||||
length: len.into(),
|
||||
}
|
||||
|
|
@ -305,17 +277,6 @@ impl<T: AsRef<str>> 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<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,
|
||||
length: len,
|
||||
}
|
||||
|
|
|
|||
39
src/utils.rs
39
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<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.
|
||||
// 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<SourceSpan> 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,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
126
tests/printer.rs
126
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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue