From ee4ba27532d70643d9ef597fc695443a4ffbd323 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 3 Jul 2025 09:48:03 -0500 Subject: [PATCH 01/14] docs: Add message to Level's docs --- src/level.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/level.rs b/src/level.rs index 8eaaa87..b718d72 100644 --- a/src/level.rs +++ b/src/level.rs @@ -36,7 +36,7 @@ pub const HELP: Level<'_> = Level { level: LevelInner::Help, }; -/// [`Title`] severity level +/// Severity level for [`Title`]s and [`Message`]s #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct Level<'a> { pub(crate) name: Option>>, From e4efef941776256682a1b75c7896362d1295a56b Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 3 Jul 2025 09:48:11 -0500 Subject: [PATCH 02/14] docs: Make Origin's summary consistent with the rest --- src/snippet.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/snippet.rs b/src/snippet.rs index ef92ff4..df0ccee 100644 --- a/src/snippet.rs +++ b/src/snippet.rs @@ -386,7 +386,7 @@ impl<'a> Patch<'a> { } } -/// The referenced location (e.g. a path) +/// A source location [`Element`] in a [`Group`] /// /// If you have source available, see instead [`Snippet`] #[derive(Clone, Debug)] From 8375e0c310ef8d14178f2a994cbaa2f26544186e Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 3 Jul 2025 09:53:26 -0500 Subject: [PATCH 03/14] docs: Use inline format args --- examples/highlight_message.rs | 12 ++---------- src/renderer/styled_buffer.rs | 6 +++--- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/examples/highlight_message.rs b/examples/highlight_message.rs index aaf4910..ddcfb01 100644 --- a/examples/highlight_message.rs +++ b/examples/highlight_message.rs @@ -29,16 +29,8 @@ fn main() { .on_default() .effects(Effects::BOLD); let message = format!( - "expected fn pointer `{}for<'a>{} fn(Box<{}(dyn Any + Send + 'a){}>) -> Pin<_>` - found fn item `fn(Box<{}(dyn Any + Send + 'static){}>) -> Pin<_> {}{{wrapped_fn}}{}`", - magenta.render(), - magenta.render_reset(), - magenta.render(), - magenta.render_reset(), - magenta.render(), - magenta.render_reset(), - magenta.render(), - magenta.render_reset() + "expected fn pointer `{magenta}for<'a>{magenta:#} fn(Box<{magenta}(dyn Any + Send + 'a){magenta:#}>) -> Pin<_>` + found fn item `fn(Box<{magenta}(dyn Any + Send + 'static){magenta:#}>) -> Pin<_> {magenta}{{wrapped_fn}}{magenta:#}`", ); let message = &[ diff --git a/src/renderer/styled_buffer.rs b/src/renderer/styled_buffer.rs index de3d081..b64aef9 100644 --- a/src/renderer/styled_buffer.rs +++ b/src/renderer/styled_buffer.rs @@ -51,14 +51,14 @@ impl StyledBuffer { let ch_style = style.color_spec(level, stylesheet); if ch_style != current_style { if !line.is_empty() { - write!(str, "{}", current_style.render_reset())?; + write!(str, "{current_style:#}")?; } current_style = ch_style; - write!(str, "{}", current_style.render())?; + write!(str, "{current_style}")?; } write!(str, "{ch}")?; } - write!(str, "{}", current_style.render_reset())?; + write!(str, "{current_style:#}")?; if i != self.lines.len() - 1 { writeln!(str)?; } From da5100c70ea385c1341c04608a09915f5509bcb2 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 3 Jul 2025 09:55:33 -0500 Subject: [PATCH 04/14] docs: Make styling standout more --- examples/highlight_message.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/highlight_message.rs b/examples/highlight_message.rs index ddcfb01..4ebe5f5 100644 --- a/examples/highlight_message.rs +++ b/examples/highlight_message.rs @@ -1,5 +1,7 @@ use annotate_snippets::{AnnotationKind, Group, Level, Renderer, Snippet}; +use anstyle::AnsiColor; use anstyle::Effects; +use anstyle::Style; fn main() { let source = r#"// Make sure "highlighted" code is colored purple @@ -25,12 +27,10 @@ fn main() { query(wrapped_fn); }"#; - let magenta = annotate_snippets::renderer::AnsiColor::Magenta - .on_default() - .effects(Effects::BOLD); + const MAGENTA: Style = AnsiColor::Magenta.on_default().effects(Effects::BOLD); let message = format!( - "expected fn pointer `{magenta}for<'a>{magenta:#} fn(Box<{magenta}(dyn Any + Send + 'a){magenta:#}>) -> Pin<_>` - found fn item `fn(Box<{magenta}(dyn Any + Send + 'static){magenta:#}>) -> Pin<_> {magenta}{{wrapped_fn}}{magenta:#}`", + "expected fn pointer `{MAGENTA}for<'a>{MAGENTA:#} fn(Box<{MAGENTA}(dyn Any + Send + 'a){MAGENTA:#}>) -> Pin<_>` + found fn item `fn(Box<{MAGENTA}(dyn Any + Send + 'static){MAGENTA:#}>) -> Pin<_> {MAGENTA}{{wrapped_fn}}{MAGENTA:#}`", ); let message = &[ From 88072d6b081bd01596084bed04f9bf0de17861c0 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 3 Jul 2025 09:51:31 -0500 Subject: [PATCH 05/14] docs: Provide an example of multiple groups --- src/snippet.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/snippet.rs b/src/snippet.rs index df0ccee..984d9c9 100644 --- a/src/snippet.rs +++ b/src/snippet.rs @@ -22,6 +22,13 @@ pub(crate) struct Id<'a> { /// A [diagnostic][crate::Renderer::render] is made of several `Group`s. /// `Group`s are used to [annotate][AnnotationKind::Primary] [`Snippet`]s /// with different [semantic reasons][Title]. +/// +/// # Example +/// +/// ```rust +#[doc = include_str!("../examples/highlight_message.rs")] +/// ``` +#[doc = include_str!("../examples/highlight_message.svg")] #[derive(Clone, Debug)] pub struct Group<'a> { pub(crate) primary_level: Level<'a>, From 453a88f3b176997f86689def1acd844fd60895f9 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 3 Jul 2025 09:58:13 -0500 Subject: [PATCH 06/14] docs: Provide example for Group::with_level --- src/snippet.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/snippet.rs b/src/snippet.rs index 984d9c9..8fa7656 100644 --- a/src/snippet.rs +++ b/src/snippet.rs @@ -43,6 +43,13 @@ impl<'a> Group<'a> { } /// Create a title-less group with a primary [`Level`] for [`Annotation`]s + /// + /// # Example + /// + /// ```rust + #[doc = include_str!("../examples/elide_header.rs")] + /// ``` + #[doc = include_str!("../examples/elide_header.svg")] pub fn with_level(level: Level<'a>) -> Self { Self { primary_level: level, From 37c641c7507a32bcc731e55ab23d54d6cd985601 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 3 Jul 2025 10:10:55 -0500 Subject: [PATCH 07/14] test: Switch child elements from Title to Message These were missed --- tests/formatter.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/formatter.rs b/tests/formatter.rs index fe16ca9..b8405b8 100644 --- a/tests/formatter.rs +++ b/tests/formatter.rs @@ -2400,7 +2400,7 @@ fn secondary_title_no_level_text() { .element( Level::NOTE .no_name() - .title("expected reference `&str`\nfound reference `&'static [u8; 0]`"), + .message("expected reference `&str`\nfound reference `&'static [u8; 0]`"), ), ]; @@ -2445,7 +2445,7 @@ fn secondary_title_custom_level_text() { .element( Level::NOTE .with_name(Some("custom")) - .title("expected reference `&str`\nfound reference `&'static [u8; 0]`"), + .message("expected reference `&str`\nfound reference `&'static [u8; 0]`"), ), ]; From 7059fd6aed2a86b0b5277cd26dfe2b8c2e34e517 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 3 Jul 2025 10:03:35 -0500 Subject: [PATCH 08/14] docs: Provide example for Level::with_name --- src/level.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/level.rs b/src/level.rs index b718d72..a0c21b0 100644 --- a/src/level.rs +++ b/src/level.rs @@ -59,6 +59,13 @@ impl<'a> Level<'a> { /// not allowed to be passed to this function. /// /// + /// + /// # Example + /// + /// ```rust + #[doc = include_str!("../examples/custom_level.rs")] + /// ``` + #[doc = include_str!("../examples/custom_level.svg")] pub fn with_name(self, name: impl Into>) -> Level<'a> { Level { name: Some(name.into().0), From c8ac04b1549db352a2857860085c1f4aec1530f8 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 3 Jul 2025 10:03:42 -0500 Subject: [PATCH 09/14] docs: Provide example for Level::no_name --- src/level.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/level.rs b/src/level.rs index a0c21b0..150c6a8 100644 --- a/src/level.rs +++ b/src/level.rs @@ -74,6 +74,38 @@ impl<'a> Level<'a> { } /// Do not show the [`Level`]s name + /// + /// # Example + /// + /// ```rust + /// # use annotate_snippets::{Group, Snippet, AnnotationKind, Level}; + ///let source = r#"fn main() { + /// let b: &[u8] = include_str!("file.txt"); //~ ERROR mismatched types + /// let s: &str = include_bytes!("file.txt"); //~ ERROR mismatched types + /// }"#; + /// let input = &[ + /// Group::with_title(Level::ERROR.title("mismatched types").id("E0308")) + /// .element( + /// Snippet::source(source) + /// .path("$DIR/mismatched-types.rs") + /// .annotation( + /// AnnotationKind::Primary + /// .span(105..131) + /// .label("expected `&str`, found `&[u8; 0]`"), + /// ) + /// .annotation( + /// AnnotationKind::Context + /// .span(98..102) + /// .label("expected due to this"), + /// ), + /// ) + /// .element( + /// Level::NOTE + /// .no_name() + /// .message("expected reference `&str`\nfound reference `&'static [u8; 0]`"), + /// ), + /// ]; + /// ``` pub fn no_name(self) -> Level<'a> { self.with_name(None::<&str>) } From 683bea74cbcddb70a1e0b681a111b1029a137d51 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 3 Jul 2025 10:08:32 -0500 Subject: [PATCH 10/14] docs: Order Level by likelihood of use --- src/level.rs | 112 ++++++++++++++++++++++++++------------------------- 1 file changed, 58 insertions(+), 54 deletions(-) diff --git a/src/level.rs b/src/level.rs index 150c6a8..b1b7e4e 100644 --- a/src/level.rs +++ b/src/level.rs @@ -43,13 +43,71 @@ pub struct Level<'a> { pub(crate) level: LevelInner, } +/// # Constructors impl<'a> Level<'a> { pub const ERROR: Level<'a> = ERROR; pub const WARNING: Level<'a> = WARNING; pub const INFO: Level<'a> = INFO; pub const NOTE: Level<'a> = NOTE; pub const HELP: Level<'a> = HELP; +} + +impl<'a> Level<'a> { + /// A text [`Element`][crate::Element] to start a [`Group`][crate::Group] + /// + /// See [`Group::with_title`][crate::Group::with_title] + /// + ///
+ /// + /// Text passed to this function is considered "untrusted input", as such + /// all text is passed through a normalization function. Pre-styled text is + /// not allowed to be passed to this function. + /// + ///
+ pub fn title(self, text: impl Into>) -> Title<'a> { + Title { + level: self, + id: None, + text: text.into(), + } + } + + /// A text [`Element`][crate::Element] in a [`Group`][crate::Group] + /// + ///
+ /// + /// Text passed to this function is allowed to be pre-styled, as such all + /// text is considered "trusted input" and has no normalizations applied to + /// it. [`normalize_untrusted_str`](crate::normalize_untrusted_str) can be + /// used to normalize untrusted text before it is passed to this function. + /// + ///
+ pub fn message(self, text: impl Into>) -> Message<'a> { + Message { + level: self, + text: text.into(), + } + } + + pub(crate) fn as_str(&'a self) -> &'a str { + match (&self.name, self.level) { + (Some(Some(name)), _) => name.as_ref(), + (Some(None), _) => "", + (None, LevelInner::Error) => ERROR_TXT, + (None, LevelInner::Warning) => WARNING_TXT, + (None, LevelInner::Info) => INFO_TXT, + (None, LevelInner::Note) => NOTE_TXT, + (None, LevelInner::Help) => HELP_TXT, + } + } + + pub(crate) fn style(&self, stylesheet: &Stylesheet) -> Style { + self.level.style(stylesheet) + } +} +/// # Customize the `Level` +impl<'a> Level<'a> { /// Replace the name describing this [`Level`] /// ///
@@ -111,60 +169,6 @@ impl<'a> Level<'a> { } } -impl<'a> Level<'a> { - /// A text [`Element`][crate::Element] to start a [`Group`][crate::Group] - /// - /// See [`Group::with_title`][crate::Group::with_title] - /// - ///
- /// - /// Text passed to this function is considered "untrusted input", as such - /// all text is passed through a normalization function. Pre-styled text is - /// not allowed to be passed to this function. - /// - ///
- pub fn title(self, text: impl Into>) -> Title<'a> { - Title { - level: self, - id: None, - text: text.into(), - } - } - - /// A text [`Element`][crate::Element] in a [`Group`][crate::Group] - /// - ///
- /// - /// Text passed to this function is allowed to be pre-styled, as such all - /// text is considered "trusted input" and has no normalizations applied to - /// it. [`normalize_untrusted_str`](crate::normalize_untrusted_str) can be - /// used to normalize untrusted text before it is passed to this function. - /// - ///
- pub fn message(self, text: impl Into>) -> Message<'a> { - Message { - level: self, - text: text.into(), - } - } - - pub(crate) fn as_str(&'a self) -> &'a str { - match (&self.name, self.level) { - (Some(Some(name)), _) => name.as_ref(), - (Some(None), _) => "", - (None, LevelInner::Error) => ERROR_TXT, - (None, LevelInner::Warning) => WARNING_TXT, - (None, LevelInner::Info) => INFO_TXT, - (None, LevelInner::Note) => NOTE_TXT, - (None, LevelInner::Help) => HELP_TXT, - } - } - - pub(crate) fn style(&self, stylesheet: &Stylesheet) -> Style { - self.level.style(stylesheet) - } -} - #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub(crate) enum LevelInner { Error, From eb0d16da8bf43d21d28dc45953b17360a6806672 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 3 Jul 2025 10:08:57 -0500 Subject: [PATCH 11/14] docs: Provide an example for Level::title --- src/level.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/level.rs b/src/level.rs index b1b7e4e..4685fb0 100644 --- a/src/level.rs +++ b/src/level.rs @@ -64,6 +64,15 @@ impl<'a> Level<'a> { /// not allowed to be passed to this function. /// ///
+ /// + /// # Example + /// + /// ```rust + /// # use annotate_snippets::{Group, Snippet, AnnotationKind, Level}; + /// let input = &[ + /// Group::with_title(Level::ERROR.title("mismatched types").id("E0308")) + /// ]; + /// ``` pub fn title(self, text: impl Into>) -> Title<'a> { Title { level: self, From c74a346907d659121a2c59055b19c8674ddd7cab Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 3 Jul 2025 10:08:51 -0500 Subject: [PATCH 12/14] docs: Provide an example for Level::message --- src/level.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/level.rs b/src/level.rs index 4685fb0..6596bf8 100644 --- a/src/level.rs +++ b/src/level.rs @@ -91,6 +91,20 @@ impl<'a> Level<'a> { /// used to normalize untrusted text before it is passed to this function. /// /// + /// + /// # Example + /// + /// ```rust + /// # use annotate_snippets::{Group, Snippet, AnnotationKind, Level}; + /// let input = &[ + /// Group::with_title(Level::ERROR.title("mismatched types").id("E0308")) + /// .element( + /// Level::NOTE + /// .no_name() + /// .message("expected reference `&str`\nfound reference `&'static [u8; 0]`"), + /// ), + /// ]; + /// ``` pub fn message(self, text: impl Into>) -> Message<'a> { Message { level: self, From 6129319507d78e1118894e90ce6c6fbe3935999d Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 3 Jul 2025 10:14:25 -0500 Subject: [PATCH 13/14] docs: Provide an example for Origin --- src/snippet.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/snippet.rs b/src/snippet.rs index 8fa7656..390f338 100644 --- a/src/snippet.rs +++ b/src/snippet.rs @@ -403,6 +403,18 @@ impl<'a> Patch<'a> { /// A source location [`Element`] in a [`Group`] /// /// If you have source available, see instead [`Snippet`] +/// +/// # Example +/// +/// ```rust +/// # use annotate_snippets::{Group, Snippet, AnnotationKind, Level, Origin}; +/// let input = &[ +/// Group::with_title(Level::ERROR.title("mismatched types").id("E0308")) +/// .element( +/// Origin::new("$DIR/mismatched-types.rs") +/// ) +/// ]; +/// ``` #[derive(Clone, Debug)] pub struct Origin<'a> { pub(crate) path: Cow<'a, str>, From e2b0339fdb978b469a18cbce4939c7c8e03275ad Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 3 Jul 2025 10:16:20 -0500 Subject: [PATCH 14/14] fix: Rename Origin::new to Origin::path This is to align with `Snippet::source` --- src/renderer/mod.rs | 4 ++-- src/snippet.rs | 6 +++--- tests/color/multiline_removal_suggestion.rs | 2 +- tests/rustc_tests.rs | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index b5eff06..759fbfb 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -484,7 +484,7 @@ impl Renderer { } if let Some(path) = &cause.path { - let mut origin = Origin::new(path.as_ref()); + let mut origin = Origin::path(path.as_ref()); origin.primary = true; let source_map = SourceMap::new(&cause.source, cause.line_start); @@ -719,7 +719,7 @@ impl Renderer { is_cont: bool, ) { if let Some(path) = &snippet.path { - let mut origin = Origin::new(path.as_ref()); + let mut origin = Origin::path(path.as_ref()); // print out the span location and spacer before we print the annotated source // to do this, we need to know if this span will be primary let is_primary = primary_path == Some(&origin.path); diff --git a/src/snippet.rs b/src/snippet.rs index 390f338..1d0d0e8 100644 --- a/src/snippet.rs +++ b/src/snippet.rs @@ -411,7 +411,7 @@ impl<'a> Patch<'a> { /// let input = &[ /// Group::with_title(Level::ERROR.title("mismatched types").id("E0308")) /// .element( -/// Origin::new("$DIR/mismatched-types.rs") +/// Origin::path("$DIR/mismatched-types.rs") /// ) /// ]; /// ``` @@ -431,7 +431,7 @@ impl<'a> Origin<'a> { /// not allowed to be passed to this function. /// /// - pub fn new(path: impl Into>) -> Self { + pub fn path(path: impl Into>) -> Self { Self { path: path.into(), line: None, @@ -467,7 +467,7 @@ impl<'a> Origin<'a> { impl<'a> From> for Origin<'a> { fn from(origin: Cow<'a, str>) -> Self { - Self::new(origin) + Self::path(origin) } } diff --git a/tests/color/multiline_removal_suggestion.rs b/tests/color/multiline_removal_suggestion.rs index 8559ee9..2442947 100644 --- a/tests/color/multiline_removal_suggestion.rs +++ b/tests/color/multiline_removal_suggestion.rs @@ -88,7 +88,7 @@ fn main() {} ), Group::with_title(Level::NOTE.title("required by a bound in `flatten`")) .element( - Origin::new("/rustc/FAKE_PREFIX/library/core/src/iter/traits/iterator.rs") + Origin::path("/rustc/FAKE_PREFIX/library/core/src/iter/traits/iterator.rs") .line(1556) .char_column(4), ), diff --git a/tests/rustc_tests.rs b/tests/rustc_tests.rs index c9bba62..112a5c7 100644 --- a/tests/rustc_tests.rs +++ b/tests/rustc_tests.rs @@ -1743,7 +1743,7 @@ fn main() { Level::NOTE .title("for a trait to be dyn compatible it needs to allow building a vtable\nfor more information, visit ")) .element( - Origin::new("$SRC_DIR/core/src/cmp.rs") + Origin::path("$SRC_DIR/core/src/cmp.rs") .line(334) .char_column(14) .primary(true)