Repository: rust-lang/annotate-snippets-rs
Pull request #37 (2020-08-24 18:13:20)

Fix character split when strip code
I had made an error to strip code in margin. In cases such as coverage, it could panic. 


Patch:
diff --git a/src/formatter/mod.rs b/src/formatter/mod.rs
index ed31b86..c93048d 100644
--- a/src/formatter/mod.rs
+++ b/src/formatter/mod.rs
@@ -1,6 +1,7 @@
 use std::{
     cmp,
     fmt::{self, Display, Write},
+    iter::once,
 };
 
 pub mod style;
@@ -217,19 +218,30 @@ impl<'a> DisplayList<'a> {
                     } else {
                         false
                     };
+                    // Specifies that it will end on the next character, so it will return
+                    // until the next one to the final condition.
+                    let mut ended = false;
                     let range = text
                         .char_indices()
                         .skip(left)
+                        // Complete char iterator with final character
+                        .chain(once((text.len(), '\0')))
+                        // Take until the next one to the final condition
                         .take_while(|(_, ch)| {
+                            // Fast return to iterate over final byte position
+                            if ended {
+                                return false;
+                            }
                             // Make sure that the trimming on the right will fall within the terminal width.
                             // FIXME: `unicode_width` sometimes disagrees with terminals on how wide a `char` is.
                             // For now, just accept that sometimes the code line will be longer than desired.
                             taken += unicode_width::UnicodeWidthChar::width(*ch).unwrap_or(1);
                             if taken > right - left {
-                                return false;
+                                ended = true;
                             }
                             true
                         })
+                        // Reduce to start and end byte position
                         .fold((None, 0), |acc, (i, _)| {
                             if acc.0.is_some() {
                                 (acc.0, i)
@@ -238,7 +250,8 @@ impl<'a> DisplayList<'a> {
                             }
                         });
 
-                    text[range.0.expect("One character at line")..=range.1].fmt(f)?;
+                    // Format text with margins
+                    text[range.0.expect("One character at line")..range.1].fmt(f)?;
 
                     if cut_right {
                         // We have stripped some code after the right-most span end, make it clear we did so.


Test patch:
diff --git a/tests/fixtures/no-color/strip_line_char.toml b/tests/fixtures/no-color/strip_line_char.toml
new file mode 100644
index 0000000..5b432be
--- /dev/null
+++ b/tests/fixtures/no-color/strip_line_char.toml
@@ -0,0 +1,25 @@
+[title]
+id = "E0308"
+label = "mismatched types"
+annotation_type = "Error"
+
+[[slices]]
+source = "                                                                                                                                                                                    let _: () = 42ñ"
+line_start = 4
+origin = "$DIR/whitespace-trimming.rs"
+
+[[slices.annotations]]
+label = "expected (), found integer"
+annotation_type = "Error"
+range = [192, 194]
+
+[opt]
+color = false
+anonymized_line_numbers = true
+[opt.margin]
+whitespace_left = 180
+span_left = 192
+span_right = 194
+label_right = 221
+column_width = 140
+max_line_len = 195
diff --git a/tests/fixtures/no-color/strip_line_char.txt b/tests/fixtures/no-color/strip_line_char.txt
new file mode 100644
index 0000000..3d4b700
--- /dev/null
+++ b/tests/fixtures/no-color/strip_line_char.txt
@@ -0,0 +1,6 @@
+error[E0308]: mismatched types
+  --> $DIR/whitespace-trimming.rs:4:193
+   |
+LL | ...                   let _: () = 42ñ
+   |                                   ^^ expected (), found integer
+   |