Skip to content

Color Primary annotations by their group level #211

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 34 additions & 22 deletions examples/highlight_title.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ fn main() {
let source = r#"// Make sure "highlighted" code is colored purple

//@ compile-flags: --error-format=human --color=always
//@ error-pattern:for<'a> 
//@ edition:2018

use core::pin::Pin;
Expand All @@ -24,8 +23,7 @@ fn wrapped_fn<'a>(_: Box<(dyn Any + Send)>) -> Pin<Box<(

fn main() {
query(wrapped_fn);
}
"#;
}"#;

let magenta = annotate_snippets::renderer::AnsiColor::Magenta
.on_default()
Expand All @@ -43,25 +41,39 @@ fn main() {
magenta.render_reset()
);

let message = Level::ERROR.header("mismatched types").id("E0308").group(
Group::new()
.element(
Snippet::source(source)
.fold(true)
.origin("$DIR/highlighting.rs")
.annotation(
AnnotationKind::Primary
.span(589..599)
.label("one type is more general than the other"),
)
.annotation(
AnnotationKind::Context
.span(583..588)
.label("arguments to this function are incorrect"),
),
)
.element(Level::NOTE.title(&title)),
);
let message = Level::ERROR
.header("mismatched types")
.id("E0308")
.group(
Group::new()
.element(
Snippet::source(source)
.fold(true)
.origin("$DIR/highlighting.rs")
.annotation(
AnnotationKind::Primary
.span(553..563)
.label("one type is more general than the other"),
)
.annotation(
AnnotationKind::Context
.span(547..552)
.label("arguments to this function are incorrect"),
),
)
.element(Level::NOTE.title(&title)),
)
.group(
Group::new()
.element(Level::NOTE.title("function defined here"))
.element(
Snippet::source(source)
.fold(true)
.origin("$DIR/highlighting.rs")
.annotation(AnnotationKind::Context.span(200..333).label(""))
.annotation(AnnotationKind::Primary.span(194..199)),
),
);

let renderer = Renderer::styled().anonymized_line_numbers(true);
anstream::println!("{}", renderer.render(message));
Expand Down
29 changes: 24 additions & 5 deletions examples/highlight_title.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
63 changes: 38 additions & 25 deletions src/renderer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ use margin::Margin;
use std::borrow::Cow;
use std::cmp::{max, min, Ordering, Reverse};
use std::collections::{HashMap, VecDeque};
use std::fmt;
use std::ops::Range;
use stylesheet::Stylesheet;

Expand Down Expand Up @@ -198,35 +199,28 @@ impl Renderer {

impl Renderer {
pub fn render(&self, mut message: Message<'_>) -> String {
let mut buffer = StyledBuffer::new();
let max_line_num_len = if self.anonymized_line_numbers {
ANONYMIZED_LINE_NUM.len()
} else {
let n = message.max_line_number();
num_decimal_digits(n)
};
let title = message.groups.remove(0).elements.remove(0);
let level = if let Element::Title(title) = &title {
title.level.clone()
} else {
panic!("Expected a title as the first element of the message")
};
if let Some(first) = message.groups.first_mut() {
first.elements.insert(0, title);
} else {
message.groups.push(Group::new().element(title));
}
self.render_message(&mut buffer, message, max_line_num_len);

buffer.render(level, &self.stylesheet).unwrap()
self.render_message(message, max_line_num_len).unwrap()
}

fn render_message(
&self,
buffer: &mut StyledBuffer,
message: Message<'_>,
max_line_num_len: usize,
) {
) -> Result<String, fmt::Error> {
let mut out_string = String::new();

let og_primary_origin = message
.groups
.iter()
Expand Down Expand Up @@ -264,6 +258,7 @@ impl Renderer {
);
let group_len = message.groups.len();
for (g, group) in message.groups.into_iter().enumerate() {
let mut buffer = StyledBuffer::new();
let primary_origin = group
.elements
.iter()
Expand Down Expand Up @@ -295,6 +290,14 @@ impl Renderer {
})
.unwrap_or_default(),
);
let level = group
.elements
.iter()
.find_map(|s| match &s {
Element::Title(title) => Some(title.level.clone()),
_ => None,
})
.unwrap_or(Level::ERROR);
let mut source_map_annotated_lines = VecDeque::new();
let mut max_depth = 0;
for e in &group.elements {
Expand All @@ -313,7 +316,7 @@ impl Renderer {
match &section {
Element::Title(title) => {
self.render_title(
buffer,
&mut buffer,
title,
peek,
max_line_num_len,
Expand All @@ -334,7 +337,7 @@ impl Renderer {
source_map_annotated_lines.pop_front()
{
self.render_snippet_annotations(
buffer,
&mut buffer,
max_line_num_len,
cause,
primary_origin,
Expand All @@ -345,19 +348,20 @@ impl Renderer {
);

if g == 0 && group_len > 1 {
let current_line = buffer.num_lines();
if matches!(peek, Some(Element::Title(level)) if level.level.name != Some(None))
{
self.draw_col_separator_no_space(
buffer,
buffer.num_lines(),
&mut buffer,
current_line,
max_line_num_len + 1,
);
// We want to draw the separator when it is
// requested, or when it is the last element
} else if peek.is_none() {
self.draw_col_separator_end(
buffer,
buffer.num_lines(),
&mut buffer,
current_line,
max_line_num_len + 1,
);
}
Expand All @@ -369,7 +373,7 @@ impl Renderer {
Element::Suggestion(suggestion) => {
let source_map = SourceMap::new(suggestion.source, suggestion.line_start);
self.emit_suggestion_default(
buffer,
&mut buffer,
suggestion,
max_line_num_len,
&source_map,
Expand All @@ -380,13 +384,14 @@ impl Renderer {
}

Element::Origin(origin) => {
self.render_origin(buffer, max_line_num_len, origin);
self.render_origin(&mut buffer, max_line_num_len, origin);
last_was_suggestion = false;
}
Element::Padding(_) => {
let current_line = buffer.num_lines();
self.draw_col_separator_no_space(
buffer,
buffer.num_lines(),
&mut buffer,
current_line,
max_line_num_len + 1,
);
}
Expand All @@ -396,23 +401,31 @@ impl Renderer {
|| (matches!(section, Element::Title(_)) && i == 0)
|| matches!(section, Element::Title(level) if level.level.name == Some(None)))
{
let current_line = buffer.num_lines();
if peek.is_none() && group_len > 1 {
self.draw_col_separator_end(
buffer,
buffer.num_lines(),
&mut buffer,
current_line,
max_line_num_len + 1,
);
} else if matches!(peek, Some(Element::Title(level)) if level.level.name != Some(None))
{
self.draw_col_separator_no_space(
buffer,
buffer.num_lines(),
&mut buffer,
current_line,
max_line_num_len + 1,
);
}
}
}
buffer.render(level, &self.stylesheet, &mut out_string)?;
if g != group_len - 1 {
use std::fmt::Write;

writeln!(out_string)?;
}
}
Ok(out_string)
}

#[allow(clippy::too_many_arguments)]
Expand Down
6 changes: 3 additions & 3 deletions src/renderer/styled_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ impl StyledBuffer {
&self,
level: Level<'_>,
stylesheet: &Stylesheet,
) -> Result<String, fmt::Error> {
let mut str = String::new();
str: &mut String,
) -> Result<(), fmt::Error> {
for (i, line) in self.lines.iter().enumerate() {
let mut current_style = stylesheet.none;
for StyledChar { ch, style } in line {
Expand All @@ -63,7 +63,7 @@ impl StyledBuffer {
writeln!(str)?;
}
}
Ok(str)
Ok(())
}

/// Sets `chr` with `style` for given `line`, `col`.
Expand Down