Repository: rust-lang/annotate-snippets-rs
Pull request #109 (2024-03-21 18:10:12)

fix(render): On fold, strip prefix/suffix rather than fold 
As we don't show `...` on non-first lines without fold, it seems to make
sense to do the same thing for fold.
To show this, we likely would want to add more nuance than just `fold`.

This has the benefit of offering a "magic mode" where the annotated
lines get selected automatically.

This was implemented by pre-processing things which should keep overhead
low as other calculations down the line aren't done.


Patch:
diff --git a/src/renderer/display_list.rs b/src/renderer/display_list.rs
index 4d2836a..fa8274c 100644
--- a/src/renderer/display_list.rs
+++ b/src/renderer/display_list.rs
@@ -696,17 +696,19 @@ impl<'a> Iterator for CursorLines<'a> {
 }
 
 fn format_message(
-    snippet::Message {
+    message: snippet::Message<'_>,
+    term_width: usize,
+    anonymized_line_numbers: bool,
+    primary: bool,
+) -> Vec<DisplaySet<'_>> {
+    let snippet::Message {
         level,
         id,
         title,
         footer,
         snippets,
-    }: snippet::Message<'_>,
-    term_width: usize,
-    anonymized_line_numbers: bool,
-    primary: bool,
-) -> Vec<DisplaySet<'_>> {
+    } = message;
+
     let mut sets = vec![];
     let body = if !snippets.is_empty() || primary {
         vec![format_title(level, id, title)]
@@ -715,6 +717,7 @@ fn format_message(
     };
 
     for (idx, snippet) in snippets.into_iter().enumerate() {
+        let snippet = fold_prefix_suffix(snippet);
         sets.push(format_snippet(
             snippet,
             idx == 0,
@@ -876,6 +879,46 @@ fn format_header<'a>(
     None
 }
 
+fn fold_prefix_suffix(mut snippet: snippet::Snippet<'_>) -> snippet::Snippet<'_> {
+    if !snippet.fold {
+        return snippet;
+    }
+
+    let ann_start = snippet
+        .annotations
+        .iter()
+        .map(|ann| ann.range.start)
+        .min()
+        .unwrap_or(0);
+    if let Some(before_new_start) = snippet.source[0..ann_start].rfind('\n') {
+        let new_start = before_new_start + 1;
+
+        let line_offset = snippet.source[..new_start].lines().count();
+        snippet.line_start += line_offset;
+
+        snippet.source = &snippet.source[new_start..];
+
+        for ann in &mut snippet.annotations {
+            let range_start = ann.range.start - new_start;
+            let range_end = ann.range.end - new_start;
+            ann.range = range_start..range_end;
+        }
+    }
+
+    let ann_end = snippet
+        .annotations
+        .iter()
+        .map(|ann| ann.range.end)
+        .max()
+        .unwrap_or(snippet.source.len());
+    if let Some(end_offset) = snippet.source[ann_end..].find('\n') {
+        let new_end = ann_end + end_offset;
+        snippet.source = &snippet.source[..new_end];
+    }
+
+    snippet
+}
+
 fn fold_body(mut body: Vec<DisplayLine<'_>>) -> Vec<DisplayLine<'_>> {
     enum Line {
         Fold(usize),


Test patch:
diff --git a/tests/fixtures/no-color/multiline_annotation2.svg b/tests/fixtures/no-color/ann_multiline.svg
similarity index 100%
rename from tests/fixtures/no-color/multiline_annotation2.svg
rename to tests/fixtures/no-color/ann_multiline.svg
diff --git a/tests/fixtures/no-color/multiline_annotation2.toml b/tests/fixtures/no-color/ann_multiline.toml
similarity index 100%
rename from tests/fixtures/no-color/multiline_annotation2.toml
rename to tests/fixtures/no-color/ann_multiline.toml
diff --git a/tests/fixtures/no-color/multiline_annotation3.svg b/tests/fixtures/no-color/ann_multiline2.svg
similarity index 100%
rename from tests/fixtures/no-color/multiline_annotation3.svg
rename to tests/fixtures/no-color/ann_multiline2.svg
diff --git a/tests/fixtures/no-color/multiline_annotation3.toml b/tests/fixtures/no-color/ann_multiline2.toml
similarity index 100%
rename from tests/fixtures/no-color/multiline_annotation3.toml
rename to tests/fixtures/no-color/ann_multiline2.toml
diff --git a/tests/fixtures/no-color/multiline_annotation.svg b/tests/fixtures/no-color/fold_ann_multiline.svg
similarity index 100%
rename from tests/fixtures/no-color/multiline_annotation.svg
rename to tests/fixtures/no-color/fold_ann_multiline.svg
diff --git a/tests/fixtures/no-color/multiline_annotation.toml b/tests/fixtures/no-color/fold_ann_multiline.toml
similarity index 100%
rename from tests/fixtures/no-color/multiline_annotation.toml
rename to tests/fixtures/no-color/fold_ann_multiline.toml
diff --git a/tests/fixtures/no-color/issue_52.svg b/tests/fixtures/no-color/fold_bad_origin_line.svg
similarity index 69%
rename from tests/fixtures/no-color/issue_52.svg
rename to tests/fixtures/no-color/fold_bad_origin_line.svg
index 5ed2228..13a0834 100644
--- a/tests/fixtures/no-color/issue_52.svg
+++ b/tests/fixtures/no-color/fold_bad_origin_line.svg
@@ -1,4 +1,4 @@
-<svg width="740px" height="146px" xmlns="http://www.w3.org/2000/svg">
+<svg width="740px" height="128px" xmlns="http://www.w3.org/2000/svg">
   <style>
     .fg { fill: #AAAAAA }
     .bg { background: #000000 }
@@ -22,13 +22,11 @@
 </tspan>
     <tspan x="10px" y="64px"><tspan>  |</tspan>
 </tspan>
-    <tspan x="10px" y="82px"><tspan>...</tspan>
+    <tspan x="10px" y="82px"><tspan>3 | invalid syntax</tspan>
 </tspan>
-    <tspan x="10px" y="100px"><tspan>3 | invalid syntax</tspan>
+    <tspan x="10px" y="100px"><tspan>  | -------------- error here</tspan>
 </tspan>
-    <tspan x="10px" y="118px"><tspan>  | -------------- error here</tspan>
-</tspan>
-    <tspan x="10px" y="136px"><tspan>  |</tspan>
+    <tspan x="10px" y="118px"><tspan>  |</tspan>
 </tspan>
   </text>
 
diff --git a/tests/fixtures/no-color/issue_52.toml b/tests/fixtures/no-color/fold_bad_origin_line.toml
similarity index 100%
rename from tests/fixtures/no-color/issue_52.toml
rename to tests/fixtures/no-color/fold_bad_origin_line.toml
diff --git a/tests/fixtures/no-color/fold_leading.svg b/tests/fixtures/no-color/fold_leading.svg
new file mode 100644
index 0000000..72887a2
--- /dev/null
+++ b/tests/fixtures/no-color/fold_leading.svg
@@ -0,0 +1,33 @@
+<svg width="740px" height="128px" xmlns="http://www.w3.org/2000/svg">
+  <style>
+    .fg { fill: #AAAAAA }
+    .bg { background: #000000 }
+    .container {
+      padding: 0 10px;
+      line-height: 18px;
+    }
+    tspan {
+      font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
+      white-space: pre;
+      line-height: 18px;
+    }
+  </style>
+
+  <rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
+
+  <text xml:space="preserve" class="container fg">
+    <tspan x="10px" y="28px"><tspan>error[E0308]: invalid type: integer `20`, expected a bool</tspan>
+</tspan>
+    <tspan x="10px" y="46px"><tspan>  --&gt; Cargo.toml:11:13</tspan>
+</tspan>
+    <tspan x="10px" y="64px"><tspan>   |</tspan>
+</tspan>
+    <tspan x="10px" y="82px"><tspan>11 | workspace = 20</tspan>
+</tspan>
+    <tspan x="10px" y="100px"><tspan>   |             ^^</tspan>
+</tspan>
+    <tspan x="10px" y="118px"><tspan>   |</tspan>
+</tspan>
+  </text>
+
+</svg>
diff --git a/tests/fixtures/no-color/fold_leading.toml b/tests/fixtures/no-color/fold_leading.toml
new file mode 100644
index 0000000..e3fc696
--- /dev/null
+++ b/tests/fixtures/no-color/fold_leading.toml
@@ -0,0 +1,26 @@
+[message]
+level = "Error"
+id = "E0308"
+title = "invalid type: integer `20`, expected a bool"
+
+[[message.snippets]]
+source = """
+[workspace]
+
+[package]
+name = "hello"
+version = "1.0.0"
+license = "MIT"
+rust-version = "1.70"
+edition = "2021"
+
+[lints]
+workspace = 20
+"""
+line_start = 1
+origin = "Cargo.toml"
+fold = true
+[[message.snippets.annotations]]
+label = ""
+level = "Error"
+range = [132, 134]
diff --git a/tests/fixtures/no-color/fold_trailing.svg b/tests/fixtures/no-color/fold_trailing.svg
new file mode 100644
index 0000000..15c9850
--- /dev/null
+++ b/tests/fixtures/no-color/fold_trailing.svg
@@ -0,0 +1,33 @@
+<svg width="740px" height="128px" xmlns="http://www.w3.org/2000/svg">
+  <style>
+    .fg { fill: #AAAAAA }
+    .bg { background: #000000 }
+    .container {
+      padding: 0 10px;
+      line-height: 18px;
+    }
+    tspan {
+      font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
+      white-space: pre;
+      line-height: 18px;
+    }
+  </style>
+
+  <rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
+
+  <text xml:space="preserve" class="container fg">
+    <tspan x="10px" y="28px"><tspan>error[E0308]: invalid type: integer `20`, expected a lints table</tspan>
+</tspan>
+    <tspan x="10px" y="46px"><tspan> --&gt; Cargo.toml:1:9</tspan>
+</tspan>
+    <tspan x="10px" y="64px"><tspan>  |</tspan>
+</tspan>
+    <tspan x="10px" y="82px"><tspan>1 | lints = 20</tspan>
+</tspan>
+    <tspan x="10px" y="100px"><tspan>  |         ^^</tspan>
+</tspan>
+    <tspan x="10px" y="118px"><tspan>  |</tspan>
+</tspan>
+  </text>
+
+</svg>
diff --git a/tests/fixtures/no-color/fold_trailing.toml b/tests/fixtures/no-color/fold_trailing.toml
new file mode 100644
index 0000000..8ee4c05
--- /dev/null
+++ b/tests/fixtures/no-color/fold_trailing.toml
@@ -0,0 +1,25 @@
+[message]
+level = "Error"
+id = "E0308"
+title = "invalid type: integer `20`, expected a lints table"
+
+[[message.snippets]]
+source = """
+lints = 20
+
+[workspace]
+
+[package]
+name = "hello"
+version = "1.0.0"
+license = "MIT"
+rust-version = "1.70"
+edition = "2021"
+"""
+line_start = 1
+origin = "Cargo.toml"
+fold = true
+[[message.snippets.annotations]]
+label = ""
+level = "Error"
+range = [8, 10]
diff --git a/tests/formatter.rs b/tests/formatter.rs
index 3b02489..ebe03ff 100644
--- a/tests/formatter.rs
+++ b/tests/formatter.rs
@@ -14,7 +14,6 @@ fn test_i_29() {
 error: oops
  --> <current file>:2:8
   |
-1 | First line
 2 | Second oops line
   |        ^^^^ oops
   |"#]]