Repository: rust-lang/annotate-snippets-rs
Pull request #100 (2024-03-13 01:24:17)

test(render): Migrate to snapshot tests


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 3bfb779..0fb37c3 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -26,6 +26,7 @@ dependencies = [
  "criterion",
  "difference",
  "glob",
+ "itertools 0.12.1",
  "serde",
  "snapbox",
  "toml",
@@ -242,7 +243,7 @@ dependencies = [
  "clap",
  "criterion-plot",
  "is-terminal",
- "itertools",
+ "itertools 0.10.5",
  "num-traits",
  "once_cell",
  "oorandom",
@@ -263,7 +264,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
 dependencies = [
  "cast",
- "itertools",
+ "itertools 0.10.5",
 ]
 
 [[package]]
@@ -448,6 +449,15 @@ dependencies = [
  "either",
 ]
 
+[[package]]
+name = "itertools"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
+dependencies = [
+ "either",
+]
+
 [[package]]
 name = "itoa"
 version = "1.0.9"
@@ -738,9 +748,9 @@ checksum = "32fea41aca09ee824cc9724996433064c89f7777e60762749a4170a14abbfa21"
 
 [[package]]
 name = "snapbox"
-version = "0.5.8"
+version = "0.5.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6de4a00e16c1e859fbc29a6484c5aad30f18fb9f4c2c29033c28aef7e965b82e"
+checksum = "8ac441e1ecf678f68423d47f376d53fabce1afba92c8f68e31508eb27df8562a"
 dependencies = [
  "anstream",
  "anstyle",
diff --git a/Cargo.toml b/Cargo.toml
index f97dddb..908478b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -25,6 +25,7 @@ maintenance = { status = "actively-developed" }
 
 [dependencies]
 anstyle = "1.0.4"
+itertools = "0.12.1"
 unicode-width = "0.1.11"
 
 [dev-dependencies]
@@ -33,7 +34,7 @@ criterion = "0.5.1"
 difference = "2.0.0"
 glob = "0.3.1"
 serde = { version = "1.0.197", features = ["derive"] }
-snapbox = { version = "0.5.8", features = ["diff", "harness", "path", "term-svg", "cmd", "examples"] }
+snapbox = { version = "0.5.9", features = ["diff", "harness", "path", "term-svg", "cmd", "examples"] }
 toml = "0.5.11"
 
 [[bench]]
diff --git a/src/renderer/display_list.rs b/src/renderer/display_list.rs
index 5cc1e2c..95517df 100644
--- a/src/renderer/display_list.rs
+++ b/src/renderer/display_list.rs
@@ -32,6 +32,8 @@
 //!
 //! The above snippet has been built out of the following structure:
 use crate::snippet;
+use itertools::FoldWhile::{Continue, Done};
+use itertools::Itertools;
 use std::fmt::{Display, Write};
 use std::ops::Range;
 use std::{cmp, fmt};
@@ -828,7 +830,20 @@ fn format_header<'a>(
             } = item
             {
                 if main_range >= range.0 && main_range <= range.1 {
-                    let char_column = text[0..(main_range - range.0)].chars().count();
+                    let char_column = text
+                        .chars()
+                        .map(|c| unicode_width::UnicodeWidthChar::width(c).unwrap_or(0))
+                        .chain(std::iter::once(1)) // treat the end of line as single-width
+                        .enumerate()
+                        .fold_while((0, 0), |(count, acc), (i, width)| {
+                            if acc <= main_range - range.0 {
+                                Continue((i, acc + width))
+                            } else {
+                                Done((count, acc))
+                            }
+                        })
+                        .into_inner()
+                        .0;
                     col = char_column + 1;
                     line_offset = lineno.unwrap_or(1);
                     break;
@@ -969,11 +984,18 @@ fn format_body(
     let mut body = vec![];
     let mut current_line = snippet.line_start;
     let mut current_index = 0;
+    let mut line_info = vec![];
 
-    let mut annotation_line_count = 0;
-    let mut annotations = snippet.annotations;
-    for (idx, (line, end_line)) in CursorLines::new(snippet.source).enumerate() {
-        let line_length: usize = line.len();
+    struct LineInfo {
+        line_start_index: usize,
+        line_end_index: usize,
+    }
+
+    for (line, end_line) in CursorLines::new(snippet.source) {
+        let line_length: usize = line
+            .chars()
+            .map(|c| unicode_width::UnicodeWidthChar::width(c).unwrap_or(0))
+            .sum();
         let line_range = (current_index, current_index + line_length);
         body.push(DisplayLine::Source {
             lineno: Some(current_line),
@@ -983,11 +1005,24 @@ fn format_body(
                 range: line_range,
             },
         });
-        let line_start_index = line_range.0;
-        let line_end_index = line_range.1;
+        line_info.push(LineInfo {
+            line_start_index: line_range.0,
+            line_end_index: line_range.1,
+        });
         current_line += 1;
         current_index += line_length + end_line as usize;
+    }
 
+    let mut annotation_line_count = 0;
+    let mut annotations = snippet.annotations;
+    for (
+        idx,
+        LineInfo {
+            line_start_index,
+            line_end_index,
+        },
+    ) in line_info.into_iter().enumerate()
+    {
         let margin_left = margin
             .map(|m| m.left(line_end_index - line_start_index))
             .unwrap_or_default();
@@ -1005,20 +1040,8 @@ fn format_body(
                     if start >= line_start_index && end <= line_end_index
                         || start == line_end_index && end - start <= 1 =>
                 {
-                    let annotation_start_col = line[0..(start - line_start_index)]
-                        .chars()
-                        .map(|c| unicode_width::UnicodeWidthChar::width(c).unwrap_or(0))
-                        .sum::<usize>()
-                        - margin_left;
-                    // This allows for annotations to be placed one past the
-                    // last character
-                    let safe_end = (end - line_start_index).saturating_sub(line_length);
-                    let annotation_end_col = line[0..(end - line_start_index) - safe_end]
-                        .chars()
-                        .map(|c| unicode_width::UnicodeWidthChar::width(c).unwrap_or(0))
-                        .sum::<usize>()
-                        + safe_end
-                        - margin_left;
+                    let annotation_start_col = start - line_start_index - margin_left;
+                    let annotation_end_col = end - line_start_index - margin_left;
                     let range = (annotation_start_col, annotation_end_col);
                     body.insert(
                         body_idx + 1,
@@ -1057,10 +1080,7 @@ fn format_body(
                             });
                         }
                     } else {
-                        let annotation_start_col = line[0..(start - line_start_index)]
-                            .chars()
-                            .map(|c| unicode_width::UnicodeWidthChar::width(c).unwrap_or(0))
-                            .sum::<usize>();
+                        let annotation_start_col = start - line_start_index;
                         let range = (annotation_start_col, annotation_start_col + 1);
                         body.insert(
                             body_idx + 1,
@@ -1112,11 +1132,7 @@ fn format_body(
                         });
                     }
 
-                    let end_mark = line[0..(end - line_start_index)]
-                        .chars()
-                        .map(|c| unicode_width::UnicodeWidthChar::width(c).unwrap_or(0))
-                        .sum::<usize>()
-                        .saturating_sub(1);
+                    let end_mark = (end - line_start_index).saturating_sub(1);
                     let range = (end_mark - margin_left, (end_mark + 1) - margin_left);
                     body.insert(
                         body_idx + 1,
@@ -1196,6 +1212,10 @@ fn is_annotation_empty(annotation: &Annotation<'_>) -> bool {
 mod tests {
     use super::*;
 
+    use snapbox::assert_eq;
+    use snapbox::str;
+    use snapbox::ToDebug as _;
+
     const STYLESHEET: Stylesheet = Stylesheet::plain();
 
     fn from_display_lines(lines: Vec<DisplayLine<'_>>) -> DisplayList<'_> {
@@ -1210,19 +1230,35 @@ mod tests {
     #[test]
     fn test_format_title() {
         let input = snippet::Level::Error.title("This is a title").id("E0001");
-        let output = from_display_lines(vec![DisplayLine::Raw(DisplayRawLine::Annotation {
-            annotation: Annotation {
-                annotation_type: DisplayAnnotationType::Error,
-                id: Some("E0001"),
-                label: vec![DisplayTextFragment {
-                    content: "This is a title",
-                    style: DisplayTextStyle::Emphasis,
-                }],
-            },
-            source_aligned: false,
-            continuation: false,
-        })]);
-        assert_eq!(DisplayList::new(input, &STYLESHEET, false, None), output);
+        let output = str![[r#"
+            DisplayList {
+                body: [
+                    Raw(
+                        Annotation {
+                            annotation: Annotation {
+                                annotation_type: Error,
+                                id: Some(
+                                    "E0001",
+                                ),
+                                label: [
+                                    DisplayTextFragment {
+                                        content: "This is a title",
+                                        style: Emphasis,
+                                    },
+                                ],
+                            },
+                            source_aligned: false,
+                            continuation: false,
+                        },
+                    ),
+                ],
+                anonymized_line_numbers: false,
+            }
+        "#]];
+        assert_eq(
+            output,
+            DisplayList::new(input, &STYLESHEET, false, None).to_debug(),
+        );
     }
 
     #[test]
@@ -1233,55 +1269,75 @@ mod tests {
         let input = snippet::Level::Error
             .title("")
             .snippet(snippet::Snippet::source(&source).line_start(5402));
-        let output = from_display_lines(vec![
-            DisplayLine::Raw(DisplayRawLine::Annotation {
-                annotation: Annotation {
-                    annotation_type: DisplayAnnotationType::Error,
-                    id: None,
-                    label: vec![DisplayTextFragment {
-                        content: "",
-                        style: DisplayTextStyle::Emphasis,
-                    }],
-                },
-                source_aligned: false,
-                continuation: false,
-            }),
-            DisplayLine::Source {
-                lineno: None,
-                inline_marks: vec![],
-                line: DisplaySourceLine::Empty,
-            },
-            DisplayLine::Source {
-                lineno: Some(5402),
-                inline_marks: vec![],
-                line: DisplaySourceLine::Content {
-                    text: line_1,
-                    range: (0, line_1.len()),
-                },
-            },
-            DisplayLine::Source {
-                lineno: Some(5403),
-                inline_marks: vec![],
-                line: DisplaySourceLine::Content {
-                    range: (line_1.len() + 1, source.len()),
-                    text: line_2,
-                },
-            },
-            DisplayLine::Source {
-                lineno: None,
-                inline_marks: vec![],
-                line: DisplaySourceLine::Empty,
-            },
-        ]);
-        assert_eq!(DisplayList::new(input, &STYLESHEET, false, None), output);
+        let output = str![[r#"
+            DisplayList {
+                body: [
+                    Raw(
+                        Annotation {
+                            annotation: Annotation {
+                                annotation_type: Error,
+                                id: None,
+                                label: [
+                                    DisplayTextFragment {
+                                        content: "",
+                                        style: Emphasis,
+                                    },
+                                ],
+                            },
+                            source_aligned: false,
+                            continuation: false,
+                        },
+                    ),
+                    Source {
+                        lineno: None,
+                        inline_marks: [],
+                        line: Empty,
+                    },
+                    Source {
+                        lineno: Some(
+                            5402,
+                        ),
+                        inline_marks: [],
+                        line: Content {
+                            text: "This is line 1",
+                            range: (
+                                0,
+                                14,
+                            ),
+                        },
+                    },
+                    Source {
+                        lineno: Some(
+                            5403,
+                        ),
+                        inline_marks: [],
+                        line: Content {
+                            text: "This is line 2",
+                            range: (
+                                15,
+                                29,
+                            ),
+                        },
+                    },
+                    Source {
+                        lineno: None,
+                        inline_marks: [],
+                        line: Empty,
+                    },
+                ],
+                anonymized_line_numbers: false,
+            }
+        "#]];
+        assert_eq(
+            output,
+            DisplayList::new(input, &STYLESHEET, false, None).to_debug(),
+        );
     }
 
     #[test]
     fn test_format_slices_continuation() {
         let src_0 = "This is slice 1";
-        let src_0_len = src_0.len();
         let src_1 = "This is slice 2";
-        let src_1_len = src_1.len();
         let input = snippet::Level::Error
             .title("")
             .snippet(
@@ -1294,67 +1350,93 @@ mod tests {
                     .line_start(2)
                     .origin("file2.rs"),
             );
-        let output = from_display_lines(vec![
-            DisplayLine::Raw(DisplayRawLine::Annotation {
-                annotation: Annotation {
-                    annotation_type: DisplayAnnotationType::Error,
-                    id: None,
-                    label: vec![DisplayTextFragment {
-                        content: "",
-                        style: DisplayTextStyle::Emphasis,
-                    }],
-                },
-                source_aligned: false,
-                continuation: false,
-            }),
-            DisplayLine::Raw(DisplayRawLine::Origin {
-                path: "file1.rs",
-                pos: None,
-                header_type: DisplayHeaderType::Initial,
-            }),
-            DisplayLine::Source {
-                lineno: None,
-                inline_marks: vec![],
-                line: DisplaySourceLine::Empty,
-            },
-            DisplayLine::Source {
-                lineno: Some(5402),
-                inline_marks: vec![],
-                line: DisplaySourceLine::Content {
-                    text: src_0,
-                    range: (0, src_0_len),
-                },
-            },
-            DisplayLine::Source {
-                lineno: None,
-                inline_marks: vec![],
-                line: DisplaySourceLine::Empty,
-            },
-            DisplayLine::Raw(DisplayRawLine::Origin {
-                path: "file2.rs",
-                pos: None,
-                header_type: DisplayHeaderType::Continuation,
-            }),
-            DisplayLine::Source {
-                lineno: None,
-                inline_marks: vec![],
-                line: DisplaySourceLine::Empty,
-            },
-            DisplayLine::Source {
-                lineno: Some(2),
-                inline_marks: vec![],
-                line: DisplaySourceLine::Content {
-                    text: src_1,
-                    range: (0, src_1_len),
-                },
-            },
-            DisplayLine::Source {
-                lineno: None,
-                inline_marks: vec![],
-                line: DisplaySourceLine::Empty,
-            },
-        ]);
-        assert_eq!(DisplayList::new(input, &STYLESHEET, false, None), output);
+        let output = str![[r#"
+            DisplayList {
+                body: [
+                    Raw(
+                        Annotation {
+                            annotation: Annotation {
+                                annotation_type: Error,
+                                id: None,
+                                label: [
+                                    DisplayTextFragment {
+                                        content: "",
+                                        style: Emphasis,
+                                    },
+                                ],
+                            },
+                            source_aligned: false,
+                            continuation: false,
+                        },
+                    ),
+                    Raw(
+                        Origin {
+                            path: "file1.rs",
+                            pos: None,
+                            header_type: Initial,
+                        },
+                    ),
+                    Source {
+                        lineno: None,
+                        inline_marks: [],
+                        line: Empty,
+                    },
+                    Source {
+                        lineno: Some(
+                            5402,
+                        ),
+                        inline_marks: [],
+                        line: Content {
+                            text: "This is slice 1",
+                            range: (
+                                0,
+                                15,
+                            ),
+                        },
+                    },
+                    Source {
+                        lineno: None,
+                        inline_marks: [],
+                        line: Empty,
+                    },
+                    Raw(
+                        Origin {
+                            path: "file2.rs",
+                            pos: None,
+                            header_type: Continuation,
+                        },
+                    ),
+                    Source {
+                        lineno: None,
+                        inline_marks: [],
+                        line: Empty,
+                    },
+                    Source {
+                        lineno: Some(
+                            2,
+                        ),
+                        inline_marks: [],
+                        line: Content {
+                            text: "This is slice 2",
+                            range: (
+                                0,
+                                15,
+                            ),
+                        },
+                    },
+                    Source {
+                        lineno: None,
+                        inline_marks: [],
+                        line: Empty,
+                    },
+                ],
+                anonymized_line_numbers: false,
+            }
+        "#]];
+        assert_eq(
+            output,
+            DisplayList::new(input, &STYLESHEET, false, None).to_debug(),
+        );
     }
 
     #[test]
@@ -1373,67 +1455,91 @@ mod tests {
                         .label("Test annotation"),
                 ),
         );
-        let output = from_display_lines(vec![
-            DisplayLine::Raw(DisplayRawLine::Annotation {
-                annotation: Annotation {
-                    annotation_type: DisplayAnnotationType::Error,
-                    id: None,
-                    label: vec![DisplayTextFragment {
-                        content: "",
-                        style: DisplayTextStyle::Emphasis,
-                    }],
-                },
-                source_aligned: false,
-                continuation: false,
-            }),
-            DisplayLine::Source {
-                lineno: None,
-                inline_marks: vec![],
-                line: DisplaySourceLine::Empty,
-            },
-            DisplayLine::Source {
-                lineno: Some(5402),
-                inline_marks: vec![],
-                line: DisplaySourceLine::Content {
-                    range: (0, line_1.len()),
-                    text: line_1,
-                },
-            },
-            DisplayLine::Source {
-                lineno: Some(5403),
-                inline_marks: vec![],
-                line: DisplaySourceLine::Content {
-                    range: (line_1.len() + 1, source.len()),
-                    text: line_2,
-                },
-            },
-            DisplayLine::Source {
-                lineno: None,
-                inline_marks: vec![],
-                line: DisplaySourceLine::Annotation {
-                    annotation: Annotation {
-                        annotation_type: DisplayAnnotationType::Info,
-                        id: None,
-                        label: vec![DisplayTextFragment {
-                            content: "Test annotation",
-                            style: DisplayTextStyle::Regular,
-                        }],
-                    },
-                    range: (
-                        range.start - (line_1.len() + 1),
-                        range.end - (line_1.len() + 1),
+        let output = str![[r#"
+            DisplayList {
+                body: [
+                    Raw(
+                        Annotation {
+                            annotation: Annotation {
+                                annotation_type: Error,
+                                id: None,
+                                label: [
+                                    DisplayTextFragment {
+                                        content: "",
+                                        style: Emphasis,
+                                    },
+                                ],
+                            },
+                            source_aligned: false,
+                            continuation: false,
+                        },
                     ),
-                    annotation_type: DisplayAnnotationType::Info,
-                    annotation_part: DisplayAnnotationPart::Standalone,
-                },
-            },
-            DisplayLine::Source {
-                lineno: None,
-                inline_marks: vec![],
-                line: DisplaySourceLine::Empty,
-            },
-        ]);
-        assert_eq!(DisplayList::new(input, &STYLESHEET, false, None), output);
+                    Source {
+                        lineno: None,
+                        inline_marks: [],
+                        line: Empty,
+                    },
+                    Source {
+                        lineno: Some(
+                            5402,
+                        ),
+                        inline_marks: [],
+                        line: Content {
+                            text: "This is line 1",
+                            range: (
+                                0,
+                                14,
+                            ),
+                        },
+                    },
+                    Source {
+                        lineno: Some(
+                            5403,
+                        ),
+                        inline_marks: [],
+                        line: Content {
+                            text: "This is line 2",
+                            range: (
+                                15,
+                                29,
+                            ),
+                        },
+                    },
+                    Source {
+                        lineno: None,
+                        inline_marks: [],
+                        line: Annotation {
+                            annotation: Annotation {
+                                annotation_type: Info,
+                                id: None,
+                                label: [
+                                    DisplayTextFragment {
+                                        content: "Test annotation",
+                                        style: Regular,
+                                    },
+                                ],
+                            },
+                            range: (
+                                7,
+                                9,
+                            ),
+                            annotation_type: Info,
+                            annotation_part: Standalone,
+                        },
+                    },
+                    Source {
+                        lineno: None,
+                        inline_marks: [],
+                        line: Empty,
+                    },
+                ],
+                anonymized_line_numbers: false,
+            }
+        "#]];
+        assert_eq(
+            output,
+            DisplayList::new(input, &STYLESHEET, false, None).to_debug(),
+        );
     }
 
     #[test]
@@ -1441,33 +1547,49 @@ mod tests {
         let input = snippet::Level::Error
             .title("")
             .footer(snippet::Level::Error.title("This __is__ a title"));
-        let output = from_display_lines(vec![
-            DisplayLine::Raw(DisplayRawLine::Annotation {
-                annotation: Annotation {
-                    annotation_type: DisplayAnnotationType::Error,
-                    id: None,
-                    label: vec![DisplayTextFragment {
-                        content: "",
-                        style: DisplayTextStyle::Emphasis,
-                    }],
-                },
-                source_aligned: false,
-                continuation: false,
-            }),
-            DisplayLine::Raw(DisplayRawLine::Annotation {
-                annotation: Annotation {
-                    annotation_type: DisplayAnnotationType::Error,
-                    id: None,
-                    label: vec![DisplayTextFragment {
-                        content: "This __is__ a title",
-                        style: DisplayTextStyle::Regular,
-                    }],
-                },
-                source_aligned: true,
-                continuation: false,
-            }),
-        ]);
-        assert_eq!(DisplayList::new(input, &STYLESHEET, false, None), output);
+        let output = str![[r#"
+            DisplayList {
+                body: [
+                    Raw(
+                        Annotation {
+                            annotation: Annotation {
+                                annotation_type: Error,
+                                id: None,
+                                label: [
+                                    DisplayTextFragment {
+                                        content: "",
+                                        style: Emphasis,
+                                    },
+                                ],
+                            },
+                            source_aligned: false,
+                            continuation: false,
+                        },
+                    ),
+                    Raw(
+                        Annotation {
+                            annotation: Annotation {
+                                annotation_type: Error,
+                                id: None,
+                                label: [
+                                    DisplayTextFragment {
+                                        content: "This __is__ a title",
+                                        style: Regular,
+                                    },
+                                ],
+                            },
+                            source_aligned: true,
+                            continuation: false,
+                        },
+                    ),
+                ],
+                anonymized_line_numbers: false,
+            }
+        "#]];
+        assert_eq(
+            output,
+            DisplayList::new(input, &STYLESHEET, false, None).to_debug(),
+        );
     }
 
     #[test]
@@ -1485,79 +1607,109 @@ mod tests {
 
     #[test]
     fn test_i_29() {
-        let snippets = snippet::Level::Error.title("oops").snippet(
+        let input = snippet::Level::Error.title("oops").snippet(
             snippet::Snippet::source("First line\r\nSecond oops line")
                 .line_start(1)
                 .origin("<current file>")
                 .fold(true)
                 .annotation(snippet::Level::Error.span(19..23).label("oops")),
         );
-
-        let expected = from_display_lines(vec![
-            DisplayLine::Raw(DisplayRawLine::Annotation {
-                annotation: Annotation {
-                    annotation_type: DisplayAnnotationType::Error,
-                    id: None,
-                    label: vec![DisplayTextFragment {
-                        content: "oops",
-                        style: DisplayTextStyle::Emphasis,
-                    }],
-                },
-                source_aligned: false,
-                continuation: false,
-            }),
-            DisplayLine::Raw(DisplayRawLine::Origin {
-                path: "<current file>",
-                pos: Some((2, 8)),
-                header_type: DisplayHeaderType::Initial,
-            }),
-            DisplayLine::Source {
-                lineno: None,
-                inline_marks: vec![],
-                line: DisplaySourceLine::Empty,
-            },
-            DisplayLine::Source {
-                lineno: Some(1),
-                inline_marks: vec![],
-                line: DisplaySourceLine::Content {
-                    text: "First line",
-                    range: (0, 10),
-                },
-            },
-            DisplayLine::Source {
-                lineno: Some(2),
-                inline_marks: vec![],
-                line: DisplaySourceLine::Content {
-                    text: "Second oops line",
-                    range: (12, 28),
-                },
-            },
-            DisplayLine::Source {
-                lineno: None,
-                inline_marks: vec![],
-                line: DisplaySourceLine::Annotation {
-                    annotation: Annotation {
-                        annotation_type: DisplayAnnotationType::None,
-                        id: None,
-                        label: vec![DisplayTextFragment {
-                            content: "oops",
-                            style: DisplayTextStyle::Regular,
-                        }],
+        let output = str![[r#"
+            DisplayList {
+                body: [
+                    Raw(
+                        Annotation {
+                            annotation: Annotation {
+                                annotation_type: Error,
+                                id: None,
+                                label: [
+                                    DisplayTextFragment {
+                                        content: "oops",
+                                        style: Emphasis,
+                                    },
+                                ],
+                            },
+                            source_aligned: false,
+                            continuation: false,
+                        },
+                    ),
+                    Raw(
+                        Origin {
+                            path: "<current file>",
+                            pos: Some(
+                                (
+                                    2,
+                                    8,
+                                ),
+                            ),
+                            header_type: Initial,
+                        },
+                    ),
+                    Source {
+                        lineno: None,
+                        inline_marks: [],
+                        line: Empty,
                     },
-                    range: (7, 11),
-                    annotation_type: DisplayAnnotationType::Error,
-                    annotation_part: DisplayAnnotationPart::Standalone,
-                },
-            },
-            DisplayLine::Source {
-                lineno: None,
-                inline_marks: vec![],
-                line: DisplaySourceLine::Empty,
-            },
-        ]);
-        assert_eq!(
-            DisplayList::new(snippets, &STYLESHEET, false, None),
-            expected
+                    Source {
+                        lineno: Some(
+                            1,
+                        ),
+                        inline_marks: [],
+                        line: Content {
+                            text: "First line",
+                            range: (
+                                0,
+                                10,
+                            ),
+                        },
+                    },
+                    Source {
+                        lineno: Some(
+                            2,
+                        ),
+                        inline_marks: [],
+                        line: Content {
+                            text: "Second oops line",
+                            range: (
+                                12,
+                                28,
+                            ),
+                        },
+                    },
+                    Source {
+                        lineno: None,
+                        inline_marks: [],
+                        line: Annotation {
+                            annotation: Annotation {
+                                annotation_type: None,
+                                id: None,
+                                label: [
+                                    DisplayTextFragment {
+                                        content: "oops",
+                                        style: Regular,
+                                    },
+                                ],
+                            },
+                            range: (
+                                7,
+                                11,
+                            ),
+                            annotation_type: Error,
+                            annotation_part: Standalone,
+                        },
+                    },
+                    Source {
+                        lineno: None,
+                        inline_marks: [],
+                        line: Empty,
+                    },
+                ],
+                anonymized_line_numbers: false,
+            }
+        "#]];
+        assert_eq(
+            output,
+            DisplayList::new(input, &STYLESHEET, false, None).to_debug(),
         );
     }
 
@@ -1569,7 +1721,7 @@ mod tests {
             line: DisplaySourceLine::Empty,
         }]);
 
-        assert_eq!(dl.to_string(), " |");
+        assert_eq(str![" |"], dl.to_string());
     }
 
     #[test]
@@ -1593,9 +1745,11 @@ mod tests {
             },
         ]);
 
-        assert_eq!(
+        assert_eq(
+            str![[r#"
+            56 | This is an example
+            57 | of content lines"#]],
             dl.to_string(),
-            "56 | This is an example\n57 | of content lines"
         );
     }
 
@@ -1619,7 +1773,7 @@ mod tests {
             },
         }]);
 
-        assert_eq!(dl.to_string(), " | ^^^^^ Example string");
+        assert_eq(str![" | ^^^^^ Example string"], dl.to_string());
     }
 
     #[test]
@@ -1661,9 +1815,12 @@ mod tests {
             },
         ]);
 
-        assert_eq!(
+        assert_eq(
+            str![[r#"
+ | ----- help: Example string
+ |             Second line"#]]
+            .indent(false),
             dl.to_string(),
-            " | ----- help: Example string\n |             Second line"
         );
     }
 
@@ -1774,7 +1931,17 @@ mod tests {
             },
         ]);
 
-        assert_eq!(dl.to_string(), " | ----- info: Example string\n |             Second line\n |                Second line of the warning\n | ----- info: This is an info\n | ----- help: This is help\n |  This is an annotation of type none");
+        assert_eq(
+            str![[r#"
+ | ----- info: Example string
+ |             Second line
+ |                Second line of the warning
+ | ----- info: This is an info
+ | ----- help: This is help
+ |  This is an annotation of type none"#]]
+            .indent(false),
+            dl.to_string(),
+        );
     }
 
     #[test]
@@ -1801,9 +1968,12 @@ mod tests {
             },
         ]);
 
-        assert_eq!(
+        assert_eq(
+            str![[r#"
+                5 | This is line 5
+            ...
+            10021 | ... and now we're at line 10021"#]],
             dl.to_string(),
-            "    5 | This is line 5\n...\n10021 | ... and now we're at line 10021"
         );
     }
 
@@ -1815,7 +1985,7 @@ mod tests {
             header_type: DisplayHeaderType::Initial,
         })]);
 
-        assert_eq!(dl.to_string(), "--> src/test.rs");
+        assert_eq(str!["--> src/test.rs"], dl.to_string());
     }
 
     #[test]
@@ -1826,7 +1996,7 @@ mod tests {
             header_type: DisplayHeaderType::Initial,
         })]);
 
-        assert_eq!(dl.to_string(), "--> src/test.rs:23:15");
+        assert_eq(str!["--> src/test.rs:23:15"], dl.to_string());
     }
 
     #[test]
@@ -1837,7 +2007,7 @@ mod tests {
             header_type: DisplayHeaderType::Continuation,
         })]);
 
-        assert_eq!(dl.to_string(), "::: src/test.rs:23:15");
+        assert_eq(str!["::: src/test.rs:23:15"], dl.to_string());
     }
 
     #[test]
@@ -1855,7 +2025,7 @@ mod tests {
             continuation: false,
         })]);
 
-        assert_eq!(dl.to_string(), "error[E0001]: This is an error");
+        assert_eq(str!["error[E0001]: This is an error"], dl.to_string());
     }
 
     #[test]
@@ -1887,9 +2057,11 @@ mod tests {
             }),
         ]);
 
-        assert_eq!(
+        assert_eq(
+            str![[r#"
+            warning[E0001]: This is an error
+                            Second line of the error"#]],
             dl.to_string(),
-            "warning[E0001]: This is an error\n                Second line of the error"
         );
     }
 
@@ -1908,7 +2080,7 @@ mod tests {
             continuation: false,
         })]);
 
-        assert_eq!(dl.to_string(), " = error[E0001]: This is an error");
+        assert_eq(str![" = error[E0001]: This is an error"], dl.to_string());
     }
 
     #[test]
@@ -1940,9 +2112,12 @@ mod tests {
             }),
         ]);
 
-        assert_eq!(
+        assert_eq(
+            str![[r#"
+ = warning[E0001]: This is an error
+                   Second line of the error"#]]
+            .indent(false),
             dl.to_string(),
-            " = warning[E0001]: This is an error\n                   Second line of the error"
         );
     }
 
@@ -1987,9 +2162,12 @@ mod tests {
             }),
         ]);
 
-        assert_eq!(
+        assert_eq(
+            str![[r#"
+            note: This is a note
+            This is just a string
+              Second line of none type annotation"#]],
             dl.to_string(),
-            "note: This is a note\nThis is just a string\n  Second line of none type annotation",
         );
     }
 
@@ -2004,7 +2182,7 @@ mod tests {
             line: DisplaySourceLine::Empty,
         }]);
 
-        assert_eq!(dl.to_string(), " | |",);
+        assert_eq(str![[" | |"]], dl.to_string());
     }
 
     #[test]
@@ -2042,9 +2220,13 @@ mod tests {
         ]);
 
         dl.anonymized_line_numbers = true;
-        assert_eq!(
+        assert_eq(
+            str![[r#"
+            LL | This is an example
+            LL | of content lines
+               |
+               | abc"#]],
             dl.to_string(),
-            "LL | This is an example\nLL | of content lines\n   |\n   | abc"
         );
     }
 
@@ -2058,6 +2240,6 @@ mod tests {
 
         // Using anonymized_line_numbers should not affect the initial position
         dl.anonymized_line_numbers = true;
-        assert_eq!(dl.to_string(), "--> src/test.rs:23:15");
+        assert_eq(str!["--> src/test.rs:23:15"], dl.to_string());
     }
 }


Test patch:
diff --git a/tests/fixtures/no-color/ensure-emoji-highlight-width.svg b/tests/fixtures/no-color/ensure-emoji-highlight-width.svg
deleted file mode 100644
index 0840805..0000000
--- a/tests/fixtures/no-color/ensure-emoji-highlight-width.svg
+++ /dev/null
@@ -1,33 +0,0 @@
-<svg width="1356px" 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: invalid character ` ` in package name: `haha this isn't a valid name 🐛`, characters must be Unicode XID characters (numbers, `-`, `_`, or most letters)</tspan>
-</tspan>
-    <tspan x="10px" y="46px"><tspan> --&gt; &lt;file&gt;:7:1</tspan>
-</tspan>
-    <tspan x="10px" y="64px"><tspan>  |</tspan>
-</tspan>
-    <tspan x="10px" y="82px"><tspan>7 | "haha this isn't a valid name 🐛" = { package = "libc", version = "0.1" }</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/ensure-emoji-highlight-width.toml b/tests/fixtures/no-color/ensure-emoji-highlight-width.toml
deleted file mode 100644
index 7af05ff..0000000
--- a/tests/fixtures/no-color/ensure-emoji-highlight-width.toml
+++ /dev/null
@@ -1,15 +0,0 @@
-[message]
-title = "invalid character ` ` in package name: `haha this isn't a valid name 🐛`, characters must be Unicode XID characters (numbers, `-`, `_`, or most letters)"
-level = "Error"
-
-
-[[message.snippets]]
-source = """
-"haha this isn't a valid name 🐛" = { package = "libc", version = "0.1" }
-"""
-line_start = 7
-origin = "<file>"
-[[message.snippets.annotations]]
-label = ""
-level = "Error"
-range = [0, 35]
diff --git a/tests/formatter.rs b/tests/formatter.rs
index 035492a..76dcb27 100644
--- a/tests/formatter.rs
+++ b/tests/formatter.rs
@@ -25,7 +25,7 @@ fn test_point_to_double_width_characters() {
     let snippets = Level::Error.title("").snippet(
         Snippet::source("こんにちは、世界")
             .origin("<current file>")
-            .annotation(Level::Error.span(18..24).label("world")),
+            .annotation(Level::Error.span(12..16).label("world")),
     );
 
     let expected = r#"error
@@ -44,7 +44,7 @@ fn test_point_to_double_width_characters_across_lines() {
     let snippets = Level::Error.title("").snippet(
         Snippet::source("おはよう\nございます")
             .origin("<current file>")
-            .annotation(Level::Error.span(6..22).label("Good morning")),
+            .annotation(Level::Error.span(4..15).label("Good morning")),
     );
 
     let expected = r#"error
@@ -65,8 +65,8 @@ fn test_point_to_double_width_characters_multiple() {
     let snippets = Level::Error.title("").snippet(
         Snippet::source("お寿司\n食べたい🍣")
             .origin("<current file>")
-            .annotation(Level::Error.span(0..9).label("Sushi1"))
-            .annotation(Level::Note.span(16..22).label("Sushi2")),
+            .annotation(Level::Error.span(0..6).label("Sushi1"))
+            .annotation(Level::Note.span(11..15).label("Sushi2")),
     );
 
     let expected = r#"error
@@ -87,7 +87,7 @@ fn test_point_to_double_width_characters_mixed() {
     let snippets = Level::Error.title("").snippet(
         Snippet::source("こんにちは、新しいWorld！")
             .origin("<current file>")
-            .annotation(Level::Error.span(18..32).label("New world")),
+            .annotation(Level::Error.span(12..23).label("New world")),
     );
 
     let expected = r#"error