diff --git a/examples/elide_header.rs b/examples/elide_header.rs
new file mode 100644
index 00000000..a280616c
--- /dev/null
+++ b/examples/elide_header.rs
@@ -0,0 +1,22 @@
+use annotate_snippets::{AnnotationKind, Group, Level, Renderer, Snippet};
+
+fn main() {
+ let source = r#"# Docstring followed by a newline
+
+def foobar(door, bar={}):
+ """
+ """
+"#;
+
+ let message = &[Group::new()
+ .primary_level(Level::NOTE)
+ .element(
+ Snippet::source(source)
+ .fold(false)
+ .annotation(AnnotationKind::Primary.span(56..58).label("B006")),
+ )
+ .element(Level::HELP.title("Replace with `None`; initialize within function"))];
+
+ let renderer = Renderer::styled();
+ anstream::println!("{}", renderer.render(message));
+}
diff --git a/examples/elide_header.svg b/examples/elide_header.svg
new file mode 100644
index 00000000..ccec3e10
--- /dev/null
+++ b/examples/elide_header.svg
@@ -0,0 +1,44 @@
+
diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs
index 85bdf628..9d89026c 100644
--- a/src/renderer/mod.rs
+++ b/src/renderer/mod.rs
@@ -275,14 +275,16 @@ impl Renderer {
if og_primary_path.is_none() && primary_path.is_some() {
og_primary_path = primary_path;
}
- let level = group
- .elements
- .iter()
- .find_map(|s| match &s {
- Element::Title(title) => Some(title.level.clone()),
- _ => None,
- })
- .unwrap_or(Level::ERROR);
+ let level = group.primary_level.clone().unwrap_or_else(|| {
+ group
+ .elements
+ .first()
+ .and_then(|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 {
diff --git a/src/snippet.rs b/src/snippet.rs
index c2ed5c77..8206f600 100644
--- a/src/snippet.rs
+++ b/src/snippet.rs
@@ -20,6 +20,7 @@ pub(crate) struct Id<'a> {
/// An [`Element`] container
#[derive(Clone, Debug)]
pub struct Group<'a> {
+ pub(crate) primary_level: Option>,
pub(crate) elements: Vec>,
}
@@ -31,7 +32,10 @@ impl Default for Group<'_> {
impl<'a> Group<'a> {
pub fn new() -> Self {
- Self { elements: vec![] }
+ Self {
+ primary_level: None,
+ elements: vec![],
+ }
}
pub fn element(mut self, section: impl Into>) -> Self {
@@ -44,6 +48,15 @@ impl<'a> Group<'a> {
self
}
+ /// Set the primary [`Level`] for this [`Group`].
+ ///
+ /// If not specified, use the [`Level`] of the first element in a [`Group`]
+ /// if it is a [`Title`]. If not it will default to [`Level::ERROR`].
+ pub fn primary_level(mut self, level: Level<'a>) -> Self {
+ self.primary_level = Some(level);
+ self
+ }
+
pub fn is_empty(&self) -> bool {
self.elements.is_empty()
}
@@ -262,10 +275,16 @@ impl<'a> Annotation<'a> {
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[non_exhaustive]
pub enum AnnotationKind {
- /// Color to the [`Level`] the first [`Title`] in [`Group`]. If no [`Title`]
- /// is present, it will default to `error`.
+ /// Match the primary [`Level`] of the group.
+ ///
+ /// See [`Group::primary_level`] for details about how this is determined
Primary,
- /// "secondary"; fixed color
+ /// Additional context to explain the [`Primary`][Self::Primary]
+ /// [`Annotation`]
+ ///
+ /// See also [`Renderer::context`].
+ ///
+ /// [`Renderer::context`]: crate::renderer::Renderer
Context,
}
diff --git a/tests/examples.rs b/tests/examples.rs
index ec2643e9..db00bc1f 100644
--- a/tests/examples.rs
+++ b/tests/examples.rs
@@ -14,6 +14,13 @@ fn custom_level() {
assert_example(target, expected);
}
+#[test]
+fn elide_header() {
+ let target = "elide_header";
+ let expected = snapbox::file!["../examples/elide_header.svg": TermSvg];
+ assert_example(target, expected);
+}
+
#[test]
fn expected_type() {
let target = "expected_type";