Skip to content

Commit 1d2ae61

Browse files
authored
Merge pull request #243 from epage/message
fix: Split out `Message` from `Title`
2 parents 0bae055 + 4373542 commit 1d2ae61

File tree

9 files changed

+132
-41
lines changed

9 files changed

+132
-41
lines changed

examples/elide_header.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def foobar(door, bar={}):
1414
.fold(false)
1515
.annotation(AnnotationKind::Primary.span(56..58).label("B006")),
1616
)
17-
.element(Level::HELP.title("Replace with `None`; initialize within function"))];
17+
.element(Level::HELP.message("Replace with `None`; initialize within function"))];
1818

1919
let renderer = Renderer::styled();
2020
anstream::println!("{}", renderer.render(message));

examples/highlight_title.rs renamed to examples/highlight_message.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ fn main() {
2828
let magenta = annotate_snippets::renderer::AnsiColor::Magenta
2929
.on_default()
3030
.effects(Effects::BOLD);
31-
let title = format!(
31+
let message = format!(
3232
"expected fn pointer `{}for<'a>{} fn(Box<{}(dyn Any + Send + 'a){}>) -> Pin<_>`
3333
found fn item `fn(Box<{}(dyn Any + Send + 'static){}>) -> Pin<_> {}{{wrapped_fn}}{}`",
3434
magenta.render(),
@@ -57,7 +57,7 @@ fn main() {
5757
.label("arguments to this function are incorrect"),
5858
),
5959
)
60-
.element(Level::NOTE.pre_styled_title(&title)),
60+
.element(Level::NOTE.message(&message)),
6161
Group::with_title(Level::NOTE.title("function defined here")).element(
6262
Snippet::source(source)
6363
.path("$DIR/highlighting.rs")
File renamed without changes.

src/level.rs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use crate::renderer::stylesheet::Stylesheet;
44
use crate::snippet::{ERROR_TXT, HELP_TXT, INFO_TXT, NOTE_TXT, WARNING_TXT};
5-
use crate::{OptionCow, Title};
5+
use crate::{Message, OptionCow, Title};
66
use anstyle::Style;
77
use std::borrow::Cow;
88

@@ -73,22 +73,27 @@ impl<'a> Level<'a> {
7373
}
7474

7575
impl<'a> Level<'a> {
76+
/// A text [`Element`][crate::Element] to start a [`Group`][crate::Group]
77+
///
78+
/// See [`Group::with_title`][crate::Group::with_title]
79+
///
7680
/// <div class="warning">
7781
///
7882
/// Text passed to this function is considered "untrusted input", as such
7983
/// all text is passed through a normalization function. Pre-styled text is
8084
/// not allowed to be passed to this function.
8185
///
8286
/// </div>
83-
pub fn title(self, title: impl Into<Cow<'a, str>>) -> Title<'a> {
87+
pub fn title(self, text: impl Into<Cow<'a, str>>) -> Title<'a> {
8488
Title {
8589
level: self,
8690
id: None,
87-
title: title.into(),
88-
is_pre_styled: false,
91+
text: text.into(),
8992
}
9093
}
9194

95+
/// A text [`Element`][crate::Element] in a [`Group`][crate::Group]
96+
///
9297
/// <div class="warning">
9398
///
9499
/// Text passed to this function is allowed to be pre-styled, as such all
@@ -97,12 +102,10 @@ impl<'a> Level<'a> {
97102
/// used to normalize untrusted text before it is passed to this function.
98103
///
99104
/// </div>
100-
pub fn pre_styled_title(self, title: impl Into<Cow<'a, str>>) -> Title<'a> {
101-
Title {
105+
pub fn message(self, text: impl Into<Cow<'a, str>>) -> Message<'a> {
106+
Message {
102107
level: self,
103-
id: None,
104-
title: title.into(),
105-
is_pre_styled: true,
108+
text: text.into(),
106109
}
107110
}
108111

src/renderer/mod.rs

Lines changed: 85 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ use crate::renderer::source_map::{
4848
};
4949
use crate::renderer::styled_buffer::StyledBuffer;
5050
use crate::snippet::Id;
51-
use crate::{Annotation, AnnotationKind, Element, Group, Origin, Patch, Snippet, Title};
51+
use crate::{Annotation, AnnotationKind, Element, Group, Message, Origin, Patch, Snippet, Title};
5252
pub use anstyle::*;
5353
use margin::Margin;
5454
use std::borrow::Cow;
@@ -303,7 +303,20 @@ impl Renderer {
303303
title,
304304
max_line_num_len,
305305
title_style,
306-
matches!(peek, Some(Element::Title(_))),
306+
matches!(peek, Some(Element::Title(_) | Element::Message(_))),
307+
buffer_msg_line_offset,
308+
);
309+
last_was_suggestion = false;
310+
}
311+
Element::Message(title) => {
312+
let title_style = TitleStyle::Secondary;
313+
let buffer_msg_line_offset = buffer.num_lines();
314+
self.render_title(
315+
&mut buffer,
316+
title,
317+
max_line_num_len,
318+
title_style,
319+
matches!(peek, Some(Element::Title(_) | Element::Message(_))),
307320
buffer_msg_line_offset,
308321
);
309322
last_was_suggestion = false;
@@ -336,6 +349,16 @@ impl Renderer {
336349
);
337350
}
338351

352+
Some(Element::Message(level))
353+
if level.level.name != Some(None) =>
354+
{
355+
self.draw_col_separator_no_space(
356+
&mut buffer,
357+
current_line,
358+
max_line_num_len + 1,
359+
);
360+
}
361+
339362
None if group_len > 1 => self.draw_col_separator_end(
340363
&mut buffer,
341364
current_line,
@@ -384,7 +407,8 @@ impl Renderer {
384407
if g == 0
385408
&& (matches!(section, Element::Origin(_))
386409
|| (matches!(section, Element::Title(_)) && i == 0)
387-
|| matches!(section, Element::Title(level) if level.level.name == Some(None)))
410+
|| matches!(section, Element::Title(level) if level.level.name == Some(None))
411+
|| matches!(section, Element::Message(level) if level.level.name == Some(None)))
388412
{
389413
let current_line = buffer.num_lines();
390414
if peek.is_none() && group_len > 1 {
@@ -394,6 +418,13 @@ impl Renderer {
394418
max_line_num_len + 1,
395419
);
396420
} else if matches!(peek, Some(Element::Title(level)) if level.level.name != Some(None))
421+
{
422+
self.draw_col_separator_no_space(
423+
&mut buffer,
424+
current_line,
425+
max_line_num_len + 1,
426+
);
427+
} else if matches!(peek, Some(Element::Message(level)) if level.level.name != Some(None))
397428
{
398429
self.draw_col_separator_no_space(
399430
&mut buffer,
@@ -503,23 +534,23 @@ impl Renderer {
503534
fn render_title(
504535
&self,
505536
buffer: &mut StyledBuffer,
506-
title: &Title<'_>,
537+
title: &dyn MessageOrTitle,
507538
max_line_num_len: usize,
508539
title_style: TitleStyle,
509540
is_cont: bool,
510541
buffer_msg_line_offset: usize,
511542
) {
512543
let (label_style, title_element_style) = match title_style {
513544
TitleStyle::MainHeader => (
514-
ElementStyle::Level(title.level.level),
545+
ElementStyle::Level(title.level().level),
515546
if self.short_message {
516547
ElementStyle::NoStyle
517548
} else {
518549
ElementStyle::MainHeaderMsg
519550
},
520551
),
521552
TitleStyle::Header => (
522-
ElementStyle::Level(title.level.level),
553+
ElementStyle::Level(title.level().level),
523554
ElementStyle::HeaderMsg,
524555
),
525556
TitleStyle::Secondary => {
@@ -538,10 +569,10 @@ impl Renderer {
538569
};
539570
let mut label_width = 0;
540571

541-
if title.level.name != Some(None) {
542-
buffer.append(buffer_msg_line_offset, title.level.as_str(), label_style);
543-
label_width += title.level.as_str().len();
544-
if let Some(Id { id: Some(id), url }) = &title.id {
572+
if title.level().name != Some(None) {
573+
buffer.append(buffer_msg_line_offset, title.level().as_str(), label_style);
574+
label_width += title.level().as_str().len();
575+
if let Some(Id { id: Some(id), url }) = &title.id() {
545576
buffer.append(buffer_msg_line_offset, "[", label_style);
546577
if let Some(url) = url.as_ref() {
547578
buffer.append(
@@ -584,10 +615,10 @@ impl Renderer {
584615
label_width
585616
});
586617

587-
let (title_str, style) = if title.is_pre_styled {
588-
(title.title.to_string(), ElementStyle::NoStyle)
618+
let (title_str, style) = if title.is_pre_styled() {
619+
(title.text().to_owned(), ElementStyle::NoStyle)
589620
} else {
590-
(normalize_whitespace(&title.title), title_element_style)
621+
(normalize_whitespace(title.text()), title_element_style)
591622
};
592623
for (i, text) in title_str.lines().enumerate() {
593624
if i != 0 {
@@ -2532,6 +2563,43 @@ impl Renderer {
25322563
}
25332564
}
25342565

2566+
trait MessageOrTitle {
2567+
fn level(&self) -> &Level<'_>;
2568+
fn id(&self) -> Option<&Id<'_>>;
2569+
fn text(&self) -> &str;
2570+
fn is_pre_styled(&self) -> bool;
2571+
}
2572+
2573+
impl MessageOrTitle for Title<'_> {
2574+
fn level(&self) -> &Level<'_> {
2575+
&self.level
2576+
}
2577+
fn id(&self) -> Option<&Id<'_>> {
2578+
self.id.as_ref()
2579+
}
2580+
fn text(&self) -> &str {
2581+
self.text.as_ref()
2582+
}
2583+
fn is_pre_styled(&self) -> bool {
2584+
false
2585+
}
2586+
}
2587+
2588+
impl MessageOrTitle for Message<'_> {
2589+
fn level(&self) -> &Level<'_> {
2590+
&self.level
2591+
}
2592+
fn id(&self) -> Option<&Id<'_>> {
2593+
None
2594+
}
2595+
fn text(&self) -> &str {
2596+
self.text.as_ref()
2597+
}
2598+
fn is_pre_styled(&self) -> bool {
2599+
true
2600+
}
2601+
}
2602+
25352603
// instead of taking the String length or dividing by 10 while > 0, we multiply a limit by 10 until
25362604
// we're higher. If the loop isn't exited by the `return`, the last multiplication will wrap, which
25372605
// is OK, because while we cannot fit a higher power of 10 in a usize, the loop will end anyway.
@@ -2846,7 +2914,10 @@ fn max_line_number(groups: &[Group<'_>]) -> usize {
28462914
v.elements
28472915
.iter()
28482916
.map(|s| match s {
2849-
Element::Title(_) | Element::Origin(_) | Element::Padding(_) => 0,
2917+
Element::Title(_)
2918+
| Element::Message(_)
2919+
| Element::Origin(_)
2920+
| Element::Padding(_) => 0,
28502921
Element::Cause(cause) => {
28512922
let end = cause
28522923
.markers

src/snippet.rs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ impl<'a> Group<'a> {
6363
#[non_exhaustive]
6464
pub enum Element<'a> {
6565
Title(Title<'a>),
66+
Message(Message<'a>),
6667
Cause(Snippet<'a, Annotation<'a>>),
6768
Suggestion(Snippet<'a, Patch<'a>>),
6869
Origin(Origin<'a>),
@@ -75,6 +76,12 @@ impl<'a> From<Title<'a>> for Element<'a> {
7576
}
7677
}
7778

79+
impl<'a> From<Message<'a>> for Element<'a> {
80+
fn from(value: Message<'a>) -> Self {
81+
Element::Message(value)
82+
}
83+
}
84+
7885
impl<'a> From<Snippet<'a, Annotation<'a>>> for Element<'a> {
7986
fn from(value: Snippet<'a, Annotation<'a>>) -> Self {
8087
Element::Cause(value)
@@ -103,15 +110,14 @@ impl From<Padding> for Element<'_> {
103110
#[derive(Clone, Debug)]
104111
pub struct Padding;
105112

106-
/// A text [`Element`] in a [`Group`]
113+
/// A text [`Element`] to start a [`Group`]
107114
///
108115
/// See [`Level::title`] to create this.
109116
#[derive(Clone, Debug)]
110117
pub struct Title<'a> {
111118
pub(crate) level: Level<'a>,
112119
pub(crate) id: Option<Id<'a>>,
113-
pub(crate) title: Cow<'a, str>,
114-
pub(crate) is_pre_styled: bool,
120+
pub(crate) text: Cow<'a, str>,
115121
}
116122

117123
impl<'a> Title<'a> {
@@ -144,6 +150,15 @@ impl<'a> Title<'a> {
144150
}
145151
}
146152

153+
/// A text [`Element`] in a [`Group`]
154+
///
155+
/// See [`Level::message`] to create this.
156+
#[derive(Clone, Debug)]
157+
pub struct Message<'a> {
158+
pub(crate) level: Level<'a>,
159+
pub(crate) text: Cow<'a, str>,
160+
}
161+
147162
/// A source view [`Element`] in a [`Group`]
148163
///
149164
/// If you do not have [source][Snippet::source] available, see instead [`Origin`]

tests/examples.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ fn highlight_source() {
5050
}
5151

5252
#[test]
53-
fn highlight_title() {
54-
let target = "highlight_title";
55-
let expected = snapbox::file!["../examples/highlight_title.svg": TermSvg];
53+
fn highlight_message() {
54+
let target = "highlight_message";
55+
let expected = snapbox::file!["../examples/highlight_message.svg": TermSvg];
5656
assert_example(target, expected);
5757
}
5858

tests/formatter.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ error:
199199
#[test]
200200
fn test_format_footer_title() {
201201
let input = &[Group::with_title(Level::ERROR.title(""))
202-
.element(Level::ERROR.title("This __is__ a title"))];
202+
.element(Level::ERROR.message("This __is__ a title"))];
203203
let expected = str![[r#"
204204
error:
205205
|
@@ -2258,7 +2258,9 @@ fn main() {
22582258
.label("`+` cannot be used to concatenate two `&str` strings"),
22592259
),
22602260
)
2261-
.element(Level::NOTE.title("string concatenation requires an owned `String` on the left")),
2261+
.element(
2262+
Level::NOTE.message("string concatenation requires an owned `String` on the left"),
2263+
),
22622264
Group::with_title(Level::HELP.title("create an owned `String` from a string reference"))
22632265
.element(
22642266
Snippet::source(source)
@@ -2333,7 +2335,7 @@ fn foo() {
23332335

23342336
.annotation(AnnotationKind::Primary.span(0..0)),
23352337
)
2336-
.element(Level::NOTE.title("this error originates in the macro `include` (in Nightly builds, run with -Z macro-backtrace for more info)")),
2338+
.element(Level::NOTE.message("this error originates in the macro `include` (in Nightly builds, run with -Z macro-backtrace for more info)")),
23372339
];
23382340

23392341
let expected_ascii = str![[r#"

tests/rustc_tests.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1659,8 +1659,8 @@ fn main() {}
16591659
.annotation(AnnotationKind::Context.span(878..880).label("not covered"))
16601660
.annotation(AnnotationKind::Context.span(890..892).label("not covered"))
16611661
)
1662-
.element(Level::NOTE.title("the matched value is of type `NonEmptyEnum5`"))
1663-
.element(Level::NOTE.title("match arms with guards don't count towards exhaustivity")
1662+
.element(Level::NOTE.message("the matched value is of type `NonEmptyEnum5`"))
1663+
.element(Level::NOTE.message("match arms with guards don't count towards exhaustivity")
16641664
),
16651665
Group::with_title(
16661666
Level::HELP
@@ -1749,7 +1749,7 @@ fn main() {
17491749
.primary(true)
17501750
)
17511751
.element(Padding)
1752-
.element(Level::NOTE.title("...because it uses `Self` as a type parameter"))
1752+
.element(Level::NOTE.message("...because it uses `Self` as a type parameter"))
17531753
.element(
17541754
Snippet::source(source)
17551755
.line_start(1)
@@ -2795,9 +2795,9 @@ fn main() {
27952795
.path("lint_example.rs")
27962796
.annotation(AnnotationKind::Primary.span(40..49)),
27972797
)
2798-
.element(Level::WARNING.title("this changes meaning in Rust 2021"))
2799-
.element(Level::NOTE.title(long_title2))
2800-
.element(Level::NOTE.title("`#[warn(array_into_iter)]` on by default")),
2798+
.element(Level::WARNING.message("this changes meaning in Rust 2021"))
2799+
.element(Level::NOTE.message(long_title2))
2800+
.element(Level::NOTE.message("`#[warn(array_into_iter)]` on by default")),
28012801
Group::with_title(
28022802
Level::HELP.title("use `.iter()` instead of `.into_iter()` to avoid ambiguity"),
28032803
)

0 commit comments

Comments
 (0)