From 35d2ef68f2ad66f6bb54a38d8d5c32227e3e53c2 Mon Sep 17 00:00:00 2001 From: Scott Schafer Date: Thu, 3 Jul 2025 04:16:23 -0600 Subject: [PATCH 1/2] test: Snippet with no path --- tests/formatter.rs | 68 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/tests/formatter.rs b/tests/formatter.rs index 1d21d13..bd64dd7 100644 --- a/tests/formatter.rs +++ b/tests/formatter.rs @@ -2753,3 +2753,71 @@ fn main() { let renderer = Renderer::plain(); renderer.render(input); } + +#[test] +fn snippet_no_path() { + // Taken from: https://docs.python.org/3/library/typing.html#annotating-callable-objects + + let source = "def __call__(self, *vals: bytes, maxlen: int | None = None) -> list[bytes]: ..."; + let input = &[Group::with_title(Level::ERROR.title("")).element( + Snippet::source(source).annotation(AnnotationKind::Primary.span(4..12).label("annotation")), + )]; + + let expected_ascii = str![[r#" +error: + | +1 | def __call__(self, *vals: bytes, maxlen: int | None = None) -> list[bytes]: ... + | ^^^^^^^^ annotation +"#]]; + let renderer = Renderer::plain(); + assert_data_eq!(renderer.render(input), expected_ascii); + + let expected_unicode = str![[r#" +error: + │ +1 │ def __call__(self, *vals: bytes, maxlen: int | None = None) -> list[bytes]: ... + ╰╴ ━━━━━━━━ annotation +"#]]; + let renderer = Renderer::plain().theme(OutputTheme::Unicode); + assert_data_eq!(renderer.render(input), expected_unicode); +} + +#[test] +fn multiple_snippet_no_path() { + // Taken from: https://docs.python.org/3/library/typing.html#annotating-callable-objects + + let source = "def __call__(self, *vals: bytes, maxlen: int | None = None) -> list[bytes]: ..."; + let input = &[Group::with_title(Level::ERROR.title("")) + .element( + Snippet::source(source) + .annotation(AnnotationKind::Primary.span(4..12).label("annotation")), + ) + .element( + Snippet::source(source) + .annotation(AnnotationKind::Primary.span(4..12).label("annotation")), + )]; + + let expected_ascii = str![[r#" +error: + | +1 | def __call__(self, *vals: bytes, maxlen: int | None = None) -> list[bytes]: ... + | ^^^^^^^^ annotation + | +1 | def __call__(self, *vals: bytes, maxlen: int | None = None) -> list[bytes]: ... + | ^^^^^^^^ annotation +"#]]; + let renderer = Renderer::plain(); + assert_data_eq!(renderer.render(input), expected_ascii); + + let expected_unicode = str![[r#" +error: + │ +1 │ def __call__(self, *vals: bytes, maxlen: int | None = None) -> list[bytes]: ... + │ ━━━━━━━━ annotation + │ +1 │ def __call__(self, *vals: bytes, maxlen: int | None = None) -> list[bytes]: ... + ╰╴ ━━━━━━━━ annotation +"#]]; + let renderer = Renderer::plain().theme(OutputTheme::Unicode); + assert_data_eq!(renderer.render(input), expected_unicode); +} From f1fcddaf6e3736718cbf6d729d64080f2deab3ef Mon Sep 17 00:00:00 2001 From: Scott Schafer Date: Thu, 3 Jul 2025 04:36:09 -0600 Subject: [PATCH 2/2] fix: Show Group/File start for Snippets without a path --- src/renderer/mod.rs | 63 ++++++++++++++++++++++++++++++++---- tests/color/issue_9.term.svg | 16 +++++---- tests/formatter.rs | 14 ++++---- 3 files changed, 74 insertions(+), 19 deletions(-) diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index 3171233..80a8811 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -288,10 +288,14 @@ impl Renderer { } let mut message_iter = group.elements.iter().enumerate().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(); 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, @@ -329,11 +333,13 @@ 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; self.render_snippet_annotations( &mut buffer, max_line_num_len, cause, - primary_path, + is_primary, &source_map, &annotated_lines, max_depth, @@ -722,7 +728,7 @@ impl Renderer { buffer: &mut StyledBuffer, max_line_num_len: usize, snippet: &Snippet<'_, Annotation<'_>>, - primary_path: Option<&Cow<'_, str>>, + is_primary: bool, sm: &SourceMap<'_>, annotated_lines: &[AnnotatedLineInfo<'_>], multiline_depth: usize, @@ -732,7 +738,7 @@ impl Renderer { 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); + //let is_primary = primary_path == Some(&origin.path); if is_primary { origin.primary = true; @@ -776,11 +782,54 @@ impl Renderer { } let buffer_msg_line_offset = buffer.num_lines(); self.render_origin(buffer, max_line_num_len, &origin, buffer_msg_line_offset); - } + // Put in the spacer between the location and annotated source + self.draw_col_separator_no_space( + buffer, + buffer_msg_line_offset + 1, + max_line_num_len + 1, + ); + } else { + let buffer_msg_line_offset = buffer.num_lines(); + if is_primary { + if self.theme == OutputTheme::Unicode { + buffer.puts( + buffer_msg_line_offset, + max_line_num_len, + self.file_start(), + ElementStyle::LineNumber, + ); + } else { + self.draw_col_separator_no_space( + buffer, + buffer_msg_line_offset, + max_line_num_len + 1, + ); + } + } else { + // Add spacing line, as shown: + // --> $DIR/file:54:15 + // | + // LL | code + // | ^^^^ + // | (<- It prints *this* line) + // ::: $DIR/other_file.rs:15:5 + // | + // LL | code + // | ---- + self.draw_col_separator_no_space( + buffer, + buffer_msg_line_offset, + max_line_num_len + 1, + ); - // Put in the spacer between the location and annotated source - let buffer_msg_line_offset = buffer.num_lines(); - self.draw_col_separator_no_space(buffer, buffer_msg_line_offset, max_line_num_len + 1); + buffer.puts( + buffer_msg_line_offset + 1, + max_line_num_len, + self.secondary_file_start(), + ElementStyle::LineNumber, + ); + } + } // Contains the vertical lines' positions for active multiline annotations let mut multilines = Vec::new(); diff --git a/tests/color/issue_9.term.svg b/tests/color/issue_9.term.svg index 5ae5da7..da58ee8 100644 --- a/tests/color/issue_9.term.svg +++ b/tests/color/issue_9.term.svg @@ -1,4 +1,4 @@ - +