diff --git a/examples/highlight_source.rs b/examples/highlight_source.rs
index 22ab0d6..5cedfa5 100644
--- a/examples/highlight_source.rs
+++ b/examples/highlight_source.rs
@@ -22,7 +22,7 @@ fn main() {}
),
)
.element(
- Level::NOTE.title("The runtime heap is not yet available at compile-time, so no runtime heap allocations can be created."),
+ Level::NOTE.message("The runtime heap is not yet available at compile-time, so no runtime heap allocations can be created."),
)];
diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs
index cf72d02..8620d79 100644
--- a/src/renderer/mod.rs
+++ b/src/renderer/mod.rs
@@ -285,32 +285,45 @@ impl Renderer {
source_map_annotated_lines.push_back((source_map, annotated_lines));
}
}
- let mut message_iter = group.elements.iter().enumerate().peekable();
+ let mut message_iter = group.elements.iter().peekable();
let mut last_was_suggestion = false;
- let mut first_was_title = false;
- while let Some((i, section)) = message_iter.next() {
- let peek = message_iter.peek().map(|(_, s)| s).copied();
+ if let Some(title) = &group.title {
+ let peek = message_iter.peek().copied();
+ let title_style = if g == 0 {
+ TitleStyle::MainHeader
+ } else {
+ TitleStyle::Header
+ };
+ let buffer_msg_line_offset = buffer.num_lines();
+ self.render_title(
+ &mut buffer,
+ title,
+ max_line_num_len,
+ title_style,
+ matches!(peek, Some(Element::Message(_))),
+ buffer_msg_line_offset,
+ );
+ let buffer_msg_line_offset = buffer.num_lines();
+
+ if matches!(peek, Some(Element::Message(_))) {
+ self.draw_col_separator_no_space(
+ &mut buffer,
+ buffer_msg_line_offset,
+ max_line_num_len + 1,
+ );
+ }
+ if peek.is_none() && g == 0 && group_len > 1 {
+ self.draw_col_separator_end(
+ &mut buffer,
+ buffer_msg_line_offset,
+ max_line_num_len + 1,
+ );
+ }
+ }
+ let mut seen_primary = false;
+ while let Some(section) = message_iter.next() {
+ let peek = message_iter.peek().copied();
match §ion {
- Element::Title(title) => {
- if i == 0 {
- first_was_title = true;
- }
- let title_style = match (i == 0, g == 0) {
- (true, true) => TitleStyle::MainHeader,
- (true, false) => TitleStyle::Header,
- (false, _) => TitleStyle::Secondary,
- };
- let buffer_msg_line_offset = buffer.num_lines();
- self.render_title(
- &mut buffer,
- title,
- max_line_num_len,
- title_style,
- matches!(peek, Some(Element::Title(_) | Element::Message(_))),
- buffer_msg_line_offset,
- );
- last_was_suggestion = false;
- }
Element::Message(title) => {
let title_style = TitleStyle::Secondary;
let buffer_msg_line_offset = buffer.num_lines();
@@ -321,8 +334,7 @@ impl Renderer {
title_style,
matches!(
peek,
- Some(Element::Title(_) | Element::Message(_))
- | Some(Element::Padding(_))
+ Some(Element::Message(_)) | Some(Element::Padding(_))
),
buffer_msg_line_offset,
);
@@ -332,8 +344,9 @@ impl Renderer {
if let Some((source_map, annotated_lines)) =
source_map_annotated_lines.pop_front()
{
- let is_primary = primary_path == cause.path.as_ref()
- && i == first_was_title as usize;
+ let is_primary =
+ primary_path == cause.path.as_ref() && !seen_primary;
+ seen_primary |= is_primary;
self.render_snippet_annotations(
&mut buffer,
max_line_num_len,
@@ -348,7 +361,7 @@ impl Renderer {
if g == 0 {
let current_line = buffer.num_lines();
match peek {
- Some(Element::Message(_) | Element::Title(_)) => {
+ Some(Element::Message(_)) => {
self.draw_col_separator_no_space(
&mut buffer,
current_line,
@@ -389,6 +402,8 @@ impl Renderer {
Element::Origin(origin) => {
let buffer_msg_line_offset = buffer.num_lines();
+ let is_primary = primary_path == Some(&origin.path) && !seen_primary;
+ seen_primary |= is_primary;
self.render_origin(
&mut buffer,
max_line_num_len,
@@ -414,10 +429,7 @@ impl Renderer {
}
}
}
- if g == 0
- && (matches!(section, Element::Origin(_))
- || (matches!(section, Element::Title(_)) && i == 0))
- {
+ if g == 0 && matches!(section, Element::Origin(_)) {
let current_line = buffer.num_lines();
if peek.is_none() && group_len > 1 {
self.draw_col_separator_end(
@@ -425,7 +437,7 @@ impl Renderer {
current_line,
max_line_num_len + 1,
);
- } else if matches!(peek, Some(Element::Message(_) | Element::Title(_))) {
+ } else if matches!(peek, Some(Element::Message(_))) {
self.draw_col_separator_no_space(
&mut buffer,
current_line,
@@ -452,11 +464,8 @@ impl Renderer {
let mut labels = None;
let group = groups.first().expect("Expected at least one group");
- let Some(Element::Title(title)) = group.elements.first() else {
- panic!(
- "Expected first element to be a Title, got: {:?}",
- group.elements.first()
- );
+ let Some(title) = &group.title else {
+ panic!("Expected a Title");
};
if let Some(Element::Cause(cause)) = group
@@ -2952,10 +2961,7 @@ fn max_line_number(groups: &[Group<'_>]) -> usize {
v.elements
.iter()
.map(|s| match s {
- Element::Title(_)
- | Element::Message(_)
- | Element::Origin(_)
- | Element::Padding(_) => 0,
+ Element::Message(_) | Element::Origin(_) | Element::Padding(_) => 0,
Element::Cause(cause) => {
if cause.fold {
let end = cause
diff --git a/src/snippet.rs b/src/snippet.rs
index 3ec5d02..d38fe01 100644
--- a/src/snippet.rs
+++ b/src/snippet.rs
@@ -33,6 +33,7 @@ pub(crate) struct Id<'a> {
#[derive(Clone, Debug)]
pub struct Group<'a> {
pub(crate) primary_level: Level<'a>,
+ pub(crate) title: Option
>,
pub(crate) elements: Vec>,
}
@@ -40,7 +41,9 @@ impl<'a> Group<'a> {
/// Create group with a title, deriving the primary [`Level`] for [`Annotation`]s from it
pub fn with_title(title: Title<'a>) -> Self {
let level = title.level.clone();
- Self::with_level(level).element(title)
+ let mut x = Self::with_level(level);
+ x.title = Some(title);
+ x
}
/// Create a title-less group with a primary [`Level`] for [`Annotation`]s
@@ -55,6 +58,7 @@ impl<'a> Group<'a> {
pub fn with_level(level: Level<'a>) -> Self {
Self {
primary_level: level,
+ title: None,
elements: vec![],
}
}
@@ -70,7 +74,7 @@ impl<'a> Group<'a> {
}
pub fn is_empty(&self) -> bool {
- self.elements.is_empty()
+ self.elements.is_empty() && self.title.is_none()
}
}
@@ -78,7 +82,6 @@ impl<'a> Group<'a> {
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum Element<'a> {
- Title(Title<'a>),
Message(Message<'a>),
Cause(Snippet<'a, Annotation<'a>>),
Suggestion(Snippet<'a, Patch<'a>>),
@@ -86,12 +89,6 @@ pub enum Element<'a> {
Padding(Padding),
}
-impl<'a> From> for Element<'a> {
- fn from(value: Title<'a>) -> Self {
- Element::Title(value)
- }
-}
-
impl<'a> From> for Element<'a> {
fn from(value: Message<'a>) -> Self {
Element::Message(value)
diff --git a/tests/color/multiline_removal_suggestion.rs b/tests/color/multiline_removal_suggestion.rs
index 2442947..c5b7ecb 100644
--- a/tests/color/multiline_removal_suggestion.rs
+++ b/tests/color/multiline_removal_suggestion.rs
@@ -81,10 +81,10 @@ fn main() {}
)
.element(
Level::HELP
- .title("the trait `Iterator` is not implemented for `(bool, HashSet)`"),
+ .message("the trait `Iterator` is not implemented for `(bool, HashSet)`"),
)
.element(
- Level::NOTE.title("required for `(bool, HashSet)` to implement `IntoIterator`"),
+ Level::NOTE.message("required for `(bool, HashSet)` to implement `IntoIterator`"),
),
Group::with_title(Level::NOTE.title("required by a bound in `flatten`"))
.element(
diff --git a/tests/formatter.rs b/tests/formatter.rs
index efb9793..c0e9752 100644
--- a/tests/formatter.rs
+++ b/tests/formatter.rs
@@ -1666,7 +1666,7 @@ fn main() {
.annotation(AnnotationKind::Primary.span(89..90))
).element(
Level::NOTE
- .title("required for the cast from `Box>, ()>>, ()>>, ()>>` to `Box<(dyn Future + 'static)>`")
+ .message("required for the cast from `Box>, ()>>, ()>>, ()>>` to `Box<(dyn Future + 'static)>`")
,
)];
@@ -1752,9 +1752,9 @@ fn main() {
.annotation(AnnotationKind::Primary.span(89..90))
).element(
Level::NOTE
- .title("required for the cast from `Box>, ()>>, ()>>, ()>>` to `Box<(dyn Future + 'static)>`")
+ .message("required for the cast from `Box>, ()>>, ()>>, ()>>` to `Box<(dyn Future + 'static)>`")
).element(
- Level::NOTE.title("a second note"),
+ Level::NOTE.message("a second note"),
)];
let expected = str![[r#"
@@ -1902,13 +1902,13 @@ fn main() {
)
).element(
Level::NOTE
- .title("expected struct `Atype, i32>`\n found enum `Result, _>`")
+ .message("expected struct `Atype, i32>`\n found enum `Result, _>`")
).element(
Level::NOTE
- .title("the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt'")
+ .message("the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt'")
).element(
Level::NOTE
- .title("consider using `--verbose` to print the full type name to the console")
+ .message("consider using `--verbose` to print the full type name to the console")
,
)];
@@ -1986,7 +1986,7 @@ fn main() {
),
).element(
Level::NOTE
- .title("expected fn pointer `for<'a> fn(Box<(dyn Any + Send + 'a)>) -> Pin<_>`\n found fn item `fn(Box<(dyn Any + Send + 'static)>) -> Pin<_> {wrapped_fn}`")
+ .message("expected fn pointer `for<'a> fn(Box<(dyn Any + Send + 'a)>) -> Pin<_>`\n found fn item `fn(Box<(dyn Any + Send + 'static)>) -> Pin<_> {wrapped_fn}`")
,
),Group::with_title(
Level::NOTE.title("function defined here"),
@@ -2077,7 +2077,7 @@ fn unicode_cut_handling2() {
.fold(false)
.annotation(AnnotationKind::Primary.span(499..500).label("expected item"))
).element(
- Level::NOTE.title("for a full list of items that can appear in modules, see ")
+ Level::NOTE.message("for a full list of items that can appear in modules, see ")
)];
@@ -2114,7 +2114,7 @@ fn unicode_cut_handling3() {
.fold(false)
.annotation(AnnotationKind::Primary.span(251..254).label("expected item"))
).element(
- Level::NOTE.title("for a full list of items that can appear in modules, see ")
+ Level::NOTE.message("for a full list of items that can appear in modules, see ")
)];
@@ -2151,7 +2151,7 @@ fn unicode_cut_handling4() {
.fold(false)
.annotation(AnnotationKind::Primary.span(334..335).label("expected item"))
).element(
- Level::NOTE.title("for a full list of items that can appear in modules, see ")
+ Level::NOTE.message("for a full list of items that can appear in modules, see ")
)];
diff --git a/tests/rustc_tests.rs b/tests/rustc_tests.rs
index 330e302..5de1643 100644
--- a/tests/rustc_tests.rs
+++ b/tests/rustc_tests.rs
@@ -1365,11 +1365,11 @@ outer_macro!(FirstStruct, FirstAttrStruct);
)
.element(
Level::HELP
- .title("remove the `#[macro_export]` or move this `macro_rules!` outside the of the current function `main`")
+ .message("remove the `#[macro_export]` or move this `macro_rules!` outside the of the current function `main`")
)
.element(
Level::NOTE
- .title("a `macro_rules!` definition is non-local if it is nested inside an item and has a `#[macro_export]` attribute")
+ .message("a `macro_rules!` definition is non-local if it is nested inside an item and has a `#[macro_export]` attribute")
),
Group::with_title(Level::NOTE.title("the lint level is defined here"))
.element(
@@ -1965,7 +1965,7 @@ fn main() {
.annotation(AnnotationKind::Primary.span(267..380)),
)
.element(
- Level::NOTE.title("`Iterator::map`, like many of the methods on `Iterator`, gets executed lazily, meaning that its effects won't be visible until it is iterated")),
+ Level::NOTE.message("`Iterator::map`, like many of the methods on `Iterator`, gets executed lazily, meaning that its effects won't be visible until it is iterated")),
Group::with_title(Level::HELP.title("you might have meant to use `Iterator::for_each`"))
.element(
Snippet::source(source)
@@ -2555,7 +2555,8 @@ fn mismatched_types1() {
),
)
.element(
- Level::NOTE.title("expected reference `&[u8]`\n found reference `&'static str`"),
+ Level::NOTE
+ .message("expected reference `&[u8]`\n found reference `&'static str`"),
),
];
@@ -2607,7 +2608,7 @@ fn mismatched_types2() {
)
.element(
Level::NOTE
- .title("expected reference `&str`\n found reference `&'static [u8; 0]`"),
+ .message("expected reference `&str`\n found reference `&'static [u8; 0]`"),
),
];
@@ -2734,7 +2735,7 @@ pub struct Foo; //~^ ERROR
.annotation(AnnotationKind::Primary.span(111..126)),
)
.element(
- Level::NOTE.title("bare URLs are not automatically turned into clickable links"),
+ Level::NOTE.message("bare URLs are not automatically turned into clickable links"),
),
Group::with_title(Level::NOTE.title("the lint level is defined here")).element(
Snippet::source(source_0)
@@ -3427,7 +3428,7 @@ fn main() {
.label("associated type `Pr` not found for this struct"),
),
)
- .element(Level::NOTE.title("the associated type was found for\n"))];
+ .element(Level::NOTE.message("the associated type was found for\n"))];
let expected = str![[r#"
error[E0220]: associated type `Pr` not found for `S` in the current scope