aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorMatthew Maurer <mmaurer@google.com>2023-06-27 00:15:42 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2023-06-27 00:15:42 +0000
commit086658932e888b58cc0b5b142b1191cc838e8ffc (patch)
tree76f7103dd2954389ae44cd97b3eedf1743949cc3 /tests
parentb6dd285066fc9a179493011e9cc3e2d944213b80 (diff)
parentbfa03b2a304429aeb60a3ad8e43b6c07daf6b9a2 (diff)
downloadannotate-snippets-086658932e888b58cc0b5b142b1191cc838e8ffc.tar.gz
Initial import annotate-snippets 0.9.1 am: 597ccd59dc am: d32ba61500 am: acdbb099bd am: a44e2bf1f4 am: bfa03b2a30
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/annotate-snippets/+/2638614 Change-Id: I633ef0a90e9e907a3a4c2303ba865d9d3bd1454e Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
Diffstat (limited to 'tests')
-rw-r--r--tests/diff/mod.rs43
-rw-r--r--tests/dl_from_snippet.rs407
-rw-r--r--tests/fixtures/no-color/issue_9.toml28
-rw-r--r--tests/fixtures/no-color/issue_9.txt12
-rw-r--r--tests/fixtures/no-color/multiline_annotation.toml40
-rw-r--r--tests/fixtures/no-color/multiline_annotation.txt14
-rw-r--r--tests/fixtures/no-color/multiline_annotation2.toml18
-rw-r--r--tests/fixtures/no-color/multiline_annotation2.txt9
-rw-r--r--tests/fixtures/no-color/multiline_annotation3.toml18
-rw-r--r--tests/fixtures/no-color/multiline_annotation3.txt9
-rw-r--r--tests/fixtures/no-color/multiple_annotations.toml25
-rw-r--r--tests/fixtures/no-color/multiple_annotations.txt14
-rw-r--r--tests/fixtures/no-color/simple.toml18
-rw-r--r--tests/fixtures/no-color/simple.txt9
-rw-r--r--tests/fixtures/no-color/strip_line.toml25
-rw-r--r--tests/fixtures/no-color/strip_line.txt6
-rw-r--r--tests/fixtures/no-color/strip_line_char.toml25
-rw-r--r--tests/fixtures/no-color/strip_line_char.txt6
-rw-r--r--tests/fixtures/no-color/strip_line_non_ws.toml25
-rw-r--r--tests/fixtures/no-color/strip_line_non_ws.txt6
-rw-r--r--tests/fixtures_test.rs45
-rw-r--r--tests/formatter.rs675
-rw-r--r--tests/snippet/mod.rs208
23 files changed, 1685 insertions, 0 deletions
diff --git a/tests/diff/mod.rs b/tests/diff/mod.rs
new file mode 100644
index 0000000..576c6c4
--- /dev/null
+++ b/tests/diff/mod.rs
@@ -0,0 +1,43 @@
+use difference::{Changeset, Difference};
+use yansi_term::Color::{Black, Green, Red};
+
+pub fn get_diff(left: &str, right: &str) -> String {
+ let mut output = String::new();
+
+ let Changeset { diffs, .. } = Changeset::new(left, right, "\n");
+
+ for i in 0..diffs.len() {
+ match diffs[i] {
+ Difference::Same(ref x) => {
+ output += &format!(" {}\n", x);
+ }
+ Difference::Add(ref x) => {
+ match diffs[i - 1] {
+ Difference::Rem(ref y) => {
+ output += &format!("{}", Green.paint("+"));
+ let Changeset { diffs, .. } = Changeset::new(y, x, " ");
+ for c in diffs {
+ match c {
+ Difference::Same(ref z) => {
+ output += &format!("{} ", Green.paint(z.as_str()));
+ }
+ Difference::Add(ref z) => {
+ output += &format!("{} ", Black.on(Green).paint(z.as_str()));
+ }
+ _ => (),
+ }
+ }
+ output += "\n";
+ }
+ _ => {
+ output += &format!("+{}\n", Green.paint(x.as_str()));
+ }
+ };
+ }
+ Difference::Rem(ref x) => {
+ output += &format!("-{}\n", Red.paint(x.as_str()));
+ }
+ }
+ }
+ output
+}
diff --git a/tests/dl_from_snippet.rs b/tests/dl_from_snippet.rs
new file mode 100644
index 0000000..0dcfcfa
--- /dev/null
+++ b/tests/dl_from_snippet.rs
@@ -0,0 +1,407 @@
+use annotate_snippets::display_list::DisplayList;
+use annotate_snippets::{display_list as dl, formatter::get_term_style, snippet};
+
+#[test]
+fn test_format_title() {
+ let input = snippet::Snippet {
+ title: Some(snippet::Annotation {
+ id: Some("E0001"),
+ label: Some("This is a title"),
+ annotation_type: snippet::AnnotationType::Error,
+ }),
+ footer: vec![],
+ slices: vec![],
+ opt: Default::default(),
+ };
+ let output = dl::DisplayList {
+ body: vec![dl::DisplayLine::Raw(dl::DisplayRawLine::Annotation {
+ annotation: dl::Annotation {
+ annotation_type: dl::DisplayAnnotationType::Error,
+ id: Some("E0001"),
+ label: vec![dl::DisplayTextFragment {
+ content: "This is a title",
+ style: dl::DisplayTextStyle::Emphasis,
+ }],
+ },
+ source_aligned: false,
+ continuation: false,
+ })],
+ stylesheet: get_term_style(input.opt.color),
+ anonymized_line_numbers: input.opt.anonymized_line_numbers,
+ margin: None,
+ };
+ assert_eq!(dl::DisplayList::from(input), output);
+}
+
+#[test]
+fn test_format_slice() {
+ let line_1 = "This is line 1";
+ let line_2 = "This is line 2";
+ let source = vec![line_1, line_2].join("\n");
+ let input = snippet::Snippet {
+ title: None,
+ footer: vec![],
+ slices: vec![snippet::Slice {
+ source: &source,
+ line_start: 5402,
+ origin: None,
+ annotations: vec![],
+ fold: false,
+ }],
+ opt: Default::default(),
+ };
+ let output = dl::DisplayList {
+ body: vec![
+ dl::DisplayLine::Source {
+ lineno: None,
+ inline_marks: vec![],
+ line: dl::DisplaySourceLine::Empty,
+ },
+ dl::DisplayLine::Source {
+ lineno: Some(5402),
+ inline_marks: vec![],
+ line: dl::DisplaySourceLine::Content {
+ text: line_1,
+ range: (0, line_1.len()),
+ },
+ },
+ dl::DisplayLine::Source {
+ lineno: Some(5403),
+ inline_marks: vec![],
+ line: dl::DisplaySourceLine::Content {
+ range: (line_1.len() + 1, source.len()),
+ text: line_2,
+ },
+ },
+ dl::DisplayLine::Source {
+ lineno: None,
+ inline_marks: vec![],
+ line: dl::DisplaySourceLine::Empty,
+ },
+ ],
+ stylesheet: get_term_style(input.opt.color),
+ anonymized_line_numbers: input.opt.anonymized_line_numbers,
+ margin: None,
+ };
+ assert_eq!(dl::DisplayList::from(input), output);
+}
+
+#[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::Snippet {
+ title: None,
+ footer: vec![],
+ slices: vec![
+ snippet::Slice {
+ source: src_0,
+ line_start: 5402,
+ origin: Some("file1.rs"),
+ annotations: vec![],
+ fold: false,
+ },
+ snippet::Slice {
+ source: src_1,
+ line_start: 2,
+ origin: Some("file2.rs"),
+ annotations: vec![],
+ fold: false,
+ },
+ ],
+ opt: Default::default(),
+ };
+ let output = dl::DisplayList {
+ body: vec![
+ dl::DisplayLine::Raw(dl::DisplayRawLine::Origin {
+ path: "file1.rs",
+ pos: None,
+ header_type: dl::DisplayHeaderType::Initial,
+ }),
+ dl::DisplayLine::Source {
+ lineno: None,
+ inline_marks: vec![],
+ line: dl::DisplaySourceLine::Empty,
+ },
+ dl::DisplayLine::Source {
+ lineno: Some(5402),
+ inline_marks: vec![],
+ line: dl::DisplaySourceLine::Content {
+ text: src_0,
+ range: (0, src_0_len),
+ },
+ },
+ dl::DisplayLine::Source {
+ lineno: None,
+ inline_marks: vec![],
+ line: dl::DisplaySourceLine::Empty,
+ },
+ dl::DisplayLine::Raw(dl::DisplayRawLine::Origin {
+ path: "file2.rs",
+ pos: None,
+ header_type: dl::DisplayHeaderType::Continuation,
+ }),
+ dl::DisplayLine::Source {
+ lineno: None,
+ inline_marks: vec![],
+ line: dl::DisplaySourceLine::Empty,
+ },
+ dl::DisplayLine::Source {
+ lineno: Some(2),
+ inline_marks: vec![],
+ line: dl::DisplaySourceLine::Content {
+ text: src_1,
+ range: (0, src_1_len),
+ },
+ },
+ dl::DisplayLine::Source {
+ lineno: None,
+ inline_marks: vec![],
+ line: dl::DisplaySourceLine::Empty,
+ },
+ ],
+ stylesheet: get_term_style(input.opt.color),
+ anonymized_line_numbers: input.opt.anonymized_line_numbers,
+ margin: None,
+ };
+ assert_eq!(dl::DisplayList::from(input), output);
+}
+
+#[test]
+fn test_format_slice_annotation_standalone() {
+ let line_1 = "This is line 1";
+ let line_2 = "This is line 2";
+ let source = vec![line_1, line_2].join("\n");
+ // In line 2
+ let range = (22, 24);
+ let input = snippet::Snippet {
+ title: None,
+ footer: vec![],
+ slices: vec![snippet::Slice {
+ source: &source,
+ line_start: 5402,
+ origin: None,
+ annotations: vec![snippet::SourceAnnotation {
+ range,
+ label: "Test annotation",
+ annotation_type: snippet::AnnotationType::Info,
+ }],
+ fold: false,
+ }],
+ opt: Default::default(),
+ };
+ let output = dl::DisplayList {
+ body: vec![
+ dl::DisplayLine::Source {
+ lineno: None,
+ inline_marks: vec![],
+ line: dl::DisplaySourceLine::Empty,
+ },
+ dl::DisplayLine::Source {
+ lineno: Some(5402),
+ inline_marks: vec![],
+ line: dl::DisplaySourceLine::Content {
+ range: (0, line_1.len()),
+ text: line_1,
+ },
+ },
+ dl::DisplayLine::Source {
+ lineno: Some(5403),
+ inline_marks: vec![],
+ line: dl::DisplaySourceLine::Content {
+ range: (line_1.len() + 1, source.len()),
+ text: line_2,
+ },
+ },
+ dl::DisplayLine::Source {
+ lineno: None,
+ inline_marks: vec![],
+ line: dl::DisplaySourceLine::Annotation {
+ annotation: dl::Annotation {
+ annotation_type: dl::DisplayAnnotationType::Info,
+ id: None,
+ label: vec![dl::DisplayTextFragment {
+ content: "Test annotation",
+ style: dl::DisplayTextStyle::Regular,
+ }],
+ },
+ range: (range.0 - (line_1.len() + 1), range.1 - (line_1.len() + 1)),
+ annotation_type: dl::DisplayAnnotationType::Info,
+ annotation_part: dl::DisplayAnnotationPart::Standalone,
+ },
+ },
+ dl::DisplayLine::Source {
+ lineno: None,
+ inline_marks: vec![],
+ line: dl::DisplaySourceLine::Empty,
+ },
+ ],
+ stylesheet: get_term_style(input.opt.color),
+ anonymized_line_numbers: input.opt.anonymized_line_numbers,
+ margin: None,
+ };
+ assert_eq!(dl::DisplayList::from(input), output);
+}
+
+#[test]
+fn test_format_label() {
+ let input = snippet::Snippet {
+ title: None,
+ footer: vec![snippet::Annotation {
+ id: None,
+ label: Some("This __is__ a title"),
+ annotation_type: snippet::AnnotationType::Error,
+ }],
+ slices: vec![],
+ opt: Default::default(),
+ };
+ let output = dl::DisplayList {
+ body: vec![dl::DisplayLine::Raw(dl::DisplayRawLine::Annotation {
+ annotation: dl::Annotation {
+ annotation_type: dl::DisplayAnnotationType::Error,
+ id: None,
+ label: vec![
+ dl::DisplayTextFragment {
+ content: "This ",
+ style: dl::DisplayTextStyle::Regular,
+ },
+ dl::DisplayTextFragment {
+ content: "is",
+ style: dl::DisplayTextStyle::Emphasis,
+ },
+ dl::DisplayTextFragment {
+ content: " a title",
+ style: dl::DisplayTextStyle::Regular,
+ },
+ ],
+ },
+ source_aligned: true,
+ continuation: false,
+ })],
+ stylesheet: get_term_style(input.opt.color),
+ anonymized_line_numbers: input.opt.anonymized_line_numbers,
+ margin: None,
+ };
+ assert_eq!(dl::DisplayList::from(input), output);
+}
+
+#[test]
+#[should_panic]
+fn test_i26() {
+ let source = "short";
+ let label = "label";
+ let input = snippet::Snippet {
+ title: None,
+ footer: vec![],
+ slices: vec![snippet::Slice {
+ annotations: vec![snippet::SourceAnnotation {
+ range: (0, source.len() + 1),
+ label,
+ annotation_type: snippet::AnnotationType::Error,
+ }],
+ source,
+ line_start: 0,
+ origin: None,
+ fold: false,
+ }],
+ opt: Default::default(),
+ };
+
+ let _ = dl::DisplayList::from(input);
+}
+
+#[test]
+fn test_i_29() {
+ let snippets = snippet::Snippet {
+ title: Some(snippet::Annotation {
+ id: None,
+ label: Some("oops"),
+ annotation_type: snippet::AnnotationType::Error,
+ }),
+ footer: vec![],
+ slices: vec![snippet::Slice {
+ source: "First line\r\nSecond oops line",
+ line_start: 1,
+ origin: Some("<current file>"),
+ annotations: vec![snippet::SourceAnnotation {
+ range: (19, 23),
+ label: "oops",
+ annotation_type: snippet::AnnotationType::Error,
+ }],
+ fold: true,
+ }],
+ opt: Default::default(),
+ };
+
+ let expected = DisplayList {
+ body: vec![
+ dl::DisplayLine::Raw(dl::DisplayRawLine::Annotation {
+ annotation: dl::Annotation {
+ annotation_type: dl::DisplayAnnotationType::Error,
+ id: None,
+ label: vec![dl::DisplayTextFragment {
+ content: "oops",
+ style: dl::DisplayTextStyle::Emphasis,
+ }],
+ },
+ source_aligned: false,
+ continuation: false,
+ }),
+ dl::DisplayLine::Raw(dl::DisplayRawLine::Origin {
+ path: "<current file>",
+ pos: Some((2, 8)),
+ header_type: dl::DisplayHeaderType::Initial,
+ }),
+ dl::DisplayLine::Source {
+ lineno: None,
+ inline_marks: vec![],
+ line: dl::DisplaySourceLine::Empty,
+ },
+ dl::DisplayLine::Source {
+ lineno: Some(1),
+ inline_marks: vec![],
+ line: dl::DisplaySourceLine::Content {
+ text: "First line",
+ range: (0, 10),
+ },
+ },
+ dl::DisplayLine::Source {
+ lineno: Some(2),
+ inline_marks: vec![],
+ line: dl::DisplaySourceLine::Content {
+ text: "Second oops line",
+ range: (12, 28),
+ },
+ },
+ dl::DisplayLine::Source {
+ lineno: None,
+ inline_marks: vec![],
+ line: dl::DisplaySourceLine::Annotation {
+ annotation: dl::Annotation {
+ annotation_type: dl::DisplayAnnotationType::None,
+ id: None,
+ label: vec![dl::DisplayTextFragment {
+ content: "oops",
+ style: dl::DisplayTextStyle::Regular,
+ }],
+ },
+ range: (7, 11),
+ annotation_type: dl::DisplayAnnotationType::Error,
+ annotation_part: dl::DisplayAnnotationPart::Standalone,
+ },
+ },
+ dl::DisplayLine::Source {
+ lineno: None,
+ inline_marks: vec![],
+ line: dl::DisplaySourceLine::Empty,
+ },
+ ],
+ stylesheet: get_term_style(false),
+ anonymized_line_numbers: false,
+ margin: None,
+ };
+
+ assert_eq!(DisplayList::from(snippets), expected);
+}
diff --git a/tests/fixtures/no-color/issue_9.toml b/tests/fixtures/no-color/issue_9.toml
new file mode 100644
index 0000000..a30563b
--- /dev/null
+++ b/tests/fixtures/no-color/issue_9.toml
@@ -0,0 +1,28 @@
+[title]
+label = "expected one of `.`, `;`, `?`, or an operator, found `for`"
+annotation_type = "Error"
+
+[[slices]]
+source = "let x = vec![1];"
+line_start = 4
+origin = "/code/rust/src/test/ui/annotate-snippet/suggestion.rs"
+[[slices.annotations]]
+label = "move occurs because `x` has type `std::vec::Vec<i32>`, which does not implement the `Copy` trait"
+annotation_type = "Warning"
+range = [4, 5]
+
+[[slices]]
+source = "let y = x;"
+line_start = 7
+[[slices.annotations]]
+label = "value moved here"
+annotation_type = "Warning"
+range = [8, 9]
+
+[[slices]]
+source = "x;"
+line_start = 9
+[[slices.annotations]]
+label = "value used here after move"
+annotation_type = "Error"
+range = [0, 1]
diff --git a/tests/fixtures/no-color/issue_9.txt b/tests/fixtures/no-color/issue_9.txt
new file mode 100644
index 0000000..affe6bc
--- /dev/null
+++ b/tests/fixtures/no-color/issue_9.txt
@@ -0,0 +1,12 @@
+error: expected one of `.`, `;`, `?`, or an operator, found `for`
+ --> /code/rust/src/test/ui/annotate-snippet/suggestion.rs:4:5
+ |
+4 | let x = vec![1];
+ | - move occurs because `x` has type `std::vec::Vec<i32>`, which does not implement the `Copy` trait
+ |
+7 | let y = x;
+ | - value moved here
+ |
+9 | x;
+ | ^ value used here after move
+ | \ No newline at end of file
diff --git a/tests/fixtures/no-color/multiline_annotation.toml b/tests/fixtures/no-color/multiline_annotation.toml
new file mode 100644
index 0000000..c3dc1e9
--- /dev/null
+++ b/tests/fixtures/no-color/multiline_annotation.toml
@@ -0,0 +1,40 @@
+[[slices]]
+source = """
+) -> Option<String> {
+ for ann in annotations {
+ match (ann.range.0, ann.range.1) {
+ (None, None) => continue,
+ (Some(start), Some(end)) if start > end_index || end < start_index => continue,
+ (Some(start), Some(end)) if start >= start_index && end <= end_index => {
+ let label = if let Some(ref label) = ann.label {
+ format!(" {}", label)
+ } else {
+ String::from("")
+ };
+
+ return Some(format!(
+ "{}{}{}",
+ " ".repeat(start - start_index),
+ "^".repeat(end - start),
+ label
+ ));
+ }
+ _ => continue,
+ }
+ }
+"""
+line_start = 51
+origin = "src/format.rs"
+fold = true
+[[slices.annotations]]
+label = "expected `std::option::Option<std::string::String>` because of return type"
+annotation_type = "Warning"
+range = [5, 19]
+[[slices.annotations]]
+label = "expected enum `std::option::Option`, found ()"
+annotation_type = "Error"
+range = [22, 766]
+[title]
+label = "mismatched types"
+id = "E0308"
+annotation_type = "Error"
diff --git a/tests/fixtures/no-color/multiline_annotation.txt b/tests/fixtures/no-color/multiline_annotation.txt
new file mode 100644
index 0000000..bacdec1
--- /dev/null
+++ b/tests/fixtures/no-color/multiline_annotation.txt
@@ -0,0 +1,14 @@
+error[E0308]: mismatched types
+ --> src/format.rs:51:6
+ |
+51 | ) -> Option<String> {
+ | -------------- expected `std::option::Option<std::string::String>` because of return type
+52 | / for ann in annotations {
+53 | | match (ann.range.0, ann.range.1) {
+54 | | (None, None) => continue,
+55 | | (Some(start), Some(end)) if start > end_index || end < start_index => continue,
+... |
+71 | | }
+72 | | }
+ | |_____^ expected enum `std::option::Option`, found ()
+ |
diff --git a/tests/fixtures/no-color/multiline_annotation2.toml b/tests/fixtures/no-color/multiline_annotation2.toml
new file mode 100644
index 0000000..845bf9f
--- /dev/null
+++ b/tests/fixtures/no-color/multiline_annotation2.toml
@@ -0,0 +1,18 @@
+[[slices]]
+source = """
+ if let DisplayLine::Source {
+ ref mut inline_marks,
+ } = body[body_idx]
+"""
+line_start = 139
+origin = "src/display_list.rs"
+fold = false
+[[slices.annotations]]
+label = "missing fields `lineno`, `content`"
+annotation_type = "Error"
+range = [31, 128]
+
+[title]
+label = "pattern does not mention fields `lineno`, `content`"
+id = "E0027"
+annotation_type = "Error"
diff --git a/tests/fixtures/no-color/multiline_annotation2.txt b/tests/fixtures/no-color/multiline_annotation2.txt
new file mode 100644
index 0000000..8a00bfa
--- /dev/null
+++ b/tests/fixtures/no-color/multiline_annotation2.txt
@@ -0,0 +1,9 @@
+error[E0027]: pattern does not mention fields `lineno`, `content`
+ --> src/display_list.rs:139:32
+ |
+139 | if let DisplayLine::Source {
+ | ________________________________^
+140 | | ref mut inline_marks,
+141 | | } = body[body_idx]
+ | |_________________________^ missing fields `lineno`, `content`
+ |
diff --git a/tests/fixtures/no-color/multiline_annotation3.toml b/tests/fixtures/no-color/multiline_annotation3.toml
new file mode 100644
index 0000000..21bbcd8
--- /dev/null
+++ b/tests/fixtures/no-color/multiline_annotation3.toml
@@ -0,0 +1,18 @@
+[[slices]]
+source = """
+This is an exampl
+e of an edge case of an annotation overflowing
+to exactly one character on next line.
+"""
+line_start = 26
+origin = "foo.txt"
+fold = false
+[[slices.annotations]]
+label = "this should not be on separate lines"
+annotation_type = "Error"
+range = [11, 18]
+
+[title]
+label = "spacing error found"
+id = "E####"
+annotation_type = "Error"
diff --git a/tests/fixtures/no-color/multiline_annotation3.txt b/tests/fixtures/no-color/multiline_annotation3.txt
new file mode 100644
index 0000000..12e174c
--- /dev/null
+++ b/tests/fixtures/no-color/multiline_annotation3.txt
@@ -0,0 +1,9 @@
+error[E####]: spacing error found
+ --> foo.txt:26:12
+ |
+26 | This is an exampl
+ | ____________^
+27 | | e of an edge case of an annotation overflowing
+ | |_^ this should not be on separate lines
+28 | to exactly one character on next line.
+ | \ No newline at end of file
diff --git a/tests/fixtures/no-color/multiple_annotations.toml b/tests/fixtures/no-color/multiple_annotations.toml
new file mode 100644
index 0000000..84efc5f
--- /dev/null
+++ b/tests/fixtures/no-color/multiple_annotations.toml
@@ -0,0 +1,25 @@
+[[slices]]
+source = """
+fn add_title_line(result: &mut Vec<String>, main_annotation: Option<&Annotation>) {
+ if let Some(annotation) = main_annotation {
+ result.push(format_title_line(
+ &annotation.annotation_type,
+ None,
+ &annotation.label,
+ ));
+ }
+}
+"""
+line_start = 96
+[[slices.annotations]]
+label = "Variable defined here"
+annotation_type = "Error"
+range = [100, 110]
+[[slices.annotations]]
+label = "Referenced here"
+annotation_type = "Error"
+range = [184, 194]
+[[slices.annotations]]
+label = "Referenced again here"
+annotation_type = "Error"
+range = [243, 253]
diff --git a/tests/fixtures/no-color/multiple_annotations.txt b/tests/fixtures/no-color/multiple_annotations.txt
new file mode 100644
index 0000000..26c677f
--- /dev/null
+++ b/tests/fixtures/no-color/multiple_annotations.txt
@@ -0,0 +1,14 @@
+ |
+ 96 | fn add_title_line(result: &mut Vec<String>, main_annotation: Option<&Annotation>) {
+ 97 | if let Some(annotation) = main_annotation {
+ | ^^^^^^^^^^ Variable defined here
+ 98 | result.push(format_title_line(
+ 99 | &annotation.annotation_type,
+ | ^^^^^^^^^^ Referenced here
+100 | None,
+101 | &annotation.label,
+ | ^^^^^^^^^^ Referenced again here
+102 | ));
+103 | }
+104 | }
+ |
diff --git a/tests/fixtures/no-color/simple.toml b/tests/fixtures/no-color/simple.toml
new file mode 100644
index 0000000..6c38674
--- /dev/null
+++ b/tests/fixtures/no-color/simple.toml
@@ -0,0 +1,18 @@
+[[slices]]
+source = """
+ })
+
+ for line in &self.body {"""
+line_start = 169
+origin = "src/format_color.rs"
+[[slices.annotations]]
+label = "unexpected token"
+annotation_type = "Error"
+range = [20, 23]
+[[slices.annotations]]
+label = "expected one of `.`, `;`, `?`, or an operator here"
+annotation_type = "Warning"
+range = [10, 11]
+[title]
+label = "expected one of `.`, `;`, `?`, or an operator, found `for`"
+annotation_type = "Error"
diff --git a/tests/fixtures/no-color/simple.txt b/tests/fixtures/no-color/simple.txt
new file mode 100644
index 0000000..752cc89
--- /dev/null
+++ b/tests/fixtures/no-color/simple.txt
@@ -0,0 +1,9 @@
+error: expected one of `.`, `;`, `?`, or an operator, found `for`
+ --> src/format_color.rs:171:9
+ |
+169 | })
+ | - expected one of `.`, `;`, `?`, or an operator here
+170 |
+171 | for line in &self.body {
+ | ^^^ unexpected token
+ |
diff --git a/tests/fixtures/no-color/strip_line.toml b/tests/fixtures/no-color/strip_line.toml
new file mode 100644
index 0000000..76d9519
--- /dev/null
+++ b/tests/fixtures/no-color/strip_line.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.txt b/tests/fixtures/no-color/strip_line.txt
new file mode 100644
index 0000000..65b0538
--- /dev/null
+++ b/tests/fixtures/no-color/strip_line.txt
@@ -0,0 +1,6 @@
+error[E0308]: mismatched types
+ --> $DIR/whitespace-trimming.rs:4:193
+ |
+LL | ... let _: () = 42;
+ | ^^ expected (), found integer
+ |
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
+ |
diff --git a/tests/fixtures/no-color/strip_line_non_ws.toml b/tests/fixtures/no-color/strip_line_non_ws.toml
new file mode 100644
index 0000000..5129f5c
--- /dev/null
+++ b/tests/fixtures/no-color/strip_line_non_ws.toml
@@ -0,0 +1,25 @@
+[title]
+id = "E0308"
+label = "mismatched types"
+annotation_type = "Error"
+
+[[slices]]
+source = " let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = 42; let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = ();"
+line_start = 4
+origin = "$DIR/non-whitespace-trimming.rs"
+
+[[slices.annotations]]
+label = "expected (), found integer"
+annotation_type = "Error"
+range = [240, 242]
+
+[opt]
+color = false
+anonymized_line_numbers = true
+[opt.margin]
+whitespace_left = 4
+span_left = 240
+span_right = 242
+label_right = 271
+column_width = 140
+max_line_len = 371
diff --git a/tests/fixtures/no-color/strip_line_non_ws.txt b/tests/fixtures/no-color/strip_line_non_ws.txt
new file mode 100644
index 0000000..850619a
--- /dev/null
+++ b/tests/fixtures/no-color/strip_line_non_ws.txt
@@ -0,0 +1,6 @@
+error[E0308]: mismatched types
+ --> $DIR/non-whitespace-trimming.rs:4:241
+ |
+LL | ... = (); let _: () = (); let _: () = (); let _: () = 42; let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = ();...
+ | ^^ expected (), found integer
+ |
diff --git a/tests/fixtures_test.rs b/tests/fixtures_test.rs
new file mode 100644
index 0000000..e471521
--- /dev/null
+++ b/tests/fixtures_test.rs
@@ -0,0 +1,45 @@
+mod diff;
+mod snippet;
+
+use crate::snippet::SnippetDef;
+use annotate_snippets::{display_list::DisplayList, snippet::Snippet};
+use glob::glob;
+use std::{error::Error, fs::File, io, io::prelude::*};
+
+fn read_file(path: &str) -> Result<String, io::Error> {
+ let mut f = File::open(path)?;
+ let mut s = String::new();
+ (f.read_to_string(&mut s))?;
+ Ok(s.trim_end().to_string())
+}
+
+fn read_fixture<'de>(src: &'de str) -> Result<Snippet<'de>, Box<dyn Error>> {
+ Ok(toml::from_str(src).map(|a: SnippetDef| a.into())?)
+}
+
+#[test]
+fn test_fixtures() {
+ for entry in glob("./tests/fixtures/no-color/**/*.toml").expect("Failed to read glob pattern") {
+ let p = entry.expect("Error while getting an entry");
+
+ let path_in = p.to_str().expect("Can't print path");
+ let path_out = path_in.replace(".toml", ".txt");
+
+ let src = read_file(&path_in).expect("Failed to read file");
+ let snippet = read_fixture(&src).expect("Failed to read file");
+ let expected_out = read_file(&path_out).expect("Failed to read file");
+
+ let dl = DisplayList::from(snippet);
+ let actual_out = dl.to_string();
+ println!("{}", expected_out);
+ println!("{}", actual_out.trim_end());
+
+ assert_eq!(
+ expected_out,
+ actual_out.trim_end(),
+ "\n\n\nWhile parsing: {}\nThe diff is:\n\n\n{}\n\n\n",
+ path_in,
+ diff::get_diff(expected_out.as_str(), actual_out.as_str())
+ );
+ }
+}
diff --git a/tests/formatter.rs b/tests/formatter.rs
new file mode 100644
index 0000000..b1392a1
--- /dev/null
+++ b/tests/formatter.rs
@@ -0,0 +1,675 @@
+use annotate_snippets::display_list::*;
+use annotate_snippets::snippet::{self, Snippet};
+
+#[test]
+fn test_source_empty() {
+ let dl = DisplayList::from(vec![DisplayLine::Source {
+ lineno: None,
+ inline_marks: vec![],
+ line: DisplaySourceLine::Empty,
+ }]);
+
+ assert_eq!(dl.to_string(), " |");
+}
+
+#[test]
+fn test_source_content() {
+ let dl = DisplayList::from(vec![
+ DisplayLine::Source {
+ lineno: Some(56),
+ inline_marks: vec![],
+ line: DisplaySourceLine::Content {
+ text: "This is an example",
+ range: (0, 19),
+ },
+ },
+ DisplayLine::Source {
+ lineno: Some(57),
+ inline_marks: vec![],
+ line: DisplaySourceLine::Content {
+ text: "of content lines",
+ range: (0, 19),
+ },
+ },
+ ]);
+
+ assert_eq!(
+ dl.to_string(),
+ "56 | This is an example\n57 | of content lines"
+ );
+}
+
+#[test]
+fn test_source_annotation_standalone_singleline() {
+ let dl = DisplayList::from(vec![DisplayLine::Source {
+ lineno: None,
+ inline_marks: vec![],
+ line: DisplaySourceLine::Annotation {
+ range: (0, 5),
+ annotation: Annotation {
+ annotation_type: DisplayAnnotationType::None,
+ id: None,
+ label: vec![DisplayTextFragment {
+ content: "Example string",
+ style: DisplayTextStyle::Regular,
+ }],
+ },
+ annotation_type: DisplayAnnotationType::Error,
+ annotation_part: DisplayAnnotationPart::Standalone,
+ },
+ }]);
+
+ assert_eq!(dl.to_string(), " | ^^^^^ Example string");
+}
+
+#[test]
+fn test_source_annotation_standalone_multiline() {
+ let dl = DisplayList::from(vec![
+ DisplayLine::Source {
+ lineno: None,
+ inline_marks: vec![],
+ line: DisplaySourceLine::Annotation {
+ range: (0, 5),
+ annotation: Annotation {
+ annotation_type: DisplayAnnotationType::Help,
+ id: None,
+ label: vec![DisplayTextFragment {
+ content: "Example string",
+ style: DisplayTextStyle::Regular,
+ }],
+ },
+ annotation_type: DisplayAnnotationType::Warning,
+ annotation_part: DisplayAnnotationPart::Standalone,
+ },
+ },
+ DisplayLine::Source {
+ lineno: None,
+ inline_marks: vec![],
+ line: DisplaySourceLine::Annotation {
+ range: (0, 5),
+ annotation: Annotation {
+ annotation_type: DisplayAnnotationType::Help,
+ id: None,
+ label: vec![DisplayTextFragment {
+ content: "Second line",
+ style: DisplayTextStyle::Regular,
+ }],
+ },
+ annotation_type: DisplayAnnotationType::Warning,
+ annotation_part: DisplayAnnotationPart::LabelContinuation,
+ },
+ },
+ ]);
+
+ assert_eq!(
+ dl.to_string(),
+ " | ----- help: Example string\n | Second line"
+ );
+}
+
+#[test]
+fn test_source_annotation_standalone_multi_annotation() {
+ let dl = DisplayList::from(vec![
+ DisplayLine::Source {
+ lineno: None,
+ inline_marks: vec![],
+ line: DisplaySourceLine::Annotation {
+ range: (0, 5),
+ annotation: Annotation {
+ annotation_type: DisplayAnnotationType::Info,
+ id: None,
+ label: vec![DisplayTextFragment {
+ content: "Example string",
+ style: DisplayTextStyle::Regular,
+ }],
+ },
+ annotation_type: DisplayAnnotationType::Note,
+ annotation_part: DisplayAnnotationPart::Standalone,
+ },
+ },
+ DisplayLine::Source {
+ lineno: None,
+ inline_marks: vec![],
+ line: DisplaySourceLine::Annotation {
+ range: (0, 5),
+ annotation: Annotation {
+ annotation_type: DisplayAnnotationType::Info,
+ id: None,
+ label: vec![DisplayTextFragment {
+ content: "Second line",
+ style: DisplayTextStyle::Regular,
+ }],
+ },
+ annotation_type: DisplayAnnotationType::Note,
+ annotation_part: DisplayAnnotationPart::LabelContinuation,
+ },
+ },
+ DisplayLine::Source {
+ lineno: None,
+ inline_marks: vec![],
+ line: DisplaySourceLine::Annotation {
+ range: (0, 5),
+ annotation: Annotation {
+ annotation_type: DisplayAnnotationType::Warning,
+ id: None,
+ label: vec![DisplayTextFragment {
+ content: "This is a note",
+ style: DisplayTextStyle::Regular,
+ }],
+ },
+ annotation_type: DisplayAnnotationType::Note,
+ annotation_part: DisplayAnnotationPart::Consequitive,
+ },
+ },
+ DisplayLine::Source {
+ lineno: None,
+ inline_marks: vec![],
+ line: DisplaySourceLine::Annotation {
+ range: (0, 5),
+ annotation: Annotation {
+ annotation_type: DisplayAnnotationType::Warning,
+ id: None,
+ label: vec![DisplayTextFragment {
+ content: "Second line of the warning",
+ style: DisplayTextStyle::Regular,
+ }],
+ },
+ annotation_type: DisplayAnnotationType::Note,
+ annotation_part: DisplayAnnotationPart::LabelContinuation,
+ },
+ },
+ DisplayLine::Source {
+ lineno: None,
+ inline_marks: vec![],
+ line: DisplaySourceLine::Annotation {
+ range: (0, 5),
+ annotation: Annotation {
+ annotation_type: DisplayAnnotationType::Info,
+ id: None,
+ label: vec![DisplayTextFragment {
+ content: "This is an info",
+ style: DisplayTextStyle::Regular,
+ }],
+ },
+ annotation_type: DisplayAnnotationType::Info,
+ annotation_part: DisplayAnnotationPart::Standalone,
+ },
+ },
+ DisplayLine::Source {
+ lineno: None,
+ inline_marks: vec![],
+ line: DisplaySourceLine::Annotation {
+ range: (0, 5),
+ annotation: Annotation {
+ annotation_type: DisplayAnnotationType::Help,
+ id: None,
+ label: vec![DisplayTextFragment {
+ content: "This is help",
+ style: DisplayTextStyle::Regular,
+ }],
+ },
+ annotation_type: DisplayAnnotationType::Help,
+ annotation_part: DisplayAnnotationPart::Standalone,
+ },
+ },
+ DisplayLine::Source {
+ lineno: None,
+ inline_marks: vec![],
+ line: DisplaySourceLine::Annotation {
+ range: (0, 0),
+ annotation: Annotation {
+ annotation_type: DisplayAnnotationType::None,
+ id: None,
+ label: vec![DisplayTextFragment {
+ content: "This is an annotation of type none",
+ style: DisplayTextStyle::Regular,
+ }],
+ },
+ annotation_type: DisplayAnnotationType::None,
+ annotation_part: DisplayAnnotationPart::Standalone,
+ },
+ },
+ ]);
+
+ assert_eq!(dl.to_string(), " | ----- info: Example string\n | Second line\n | warning: This is a note\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");
+}
+
+#[test]
+fn test_fold_line() {
+ let dl = DisplayList::from(vec![
+ DisplayLine::Source {
+ lineno: Some(5),
+ inline_marks: vec![],
+ line: DisplaySourceLine::Content {
+ text: "This is line 5",
+ range: (0, 19),
+ },
+ },
+ DisplayLine::Fold {
+ inline_marks: vec![],
+ },
+ DisplayLine::Source {
+ lineno: Some(10021),
+ inline_marks: vec![],
+ line: DisplaySourceLine::Content {
+ text: "... and now we're at line 10021",
+ range: (0, 19),
+ },
+ },
+ ]);
+
+ assert_eq!(
+ dl.to_string(),
+ " 5 | This is line 5\n...\n10021 | ... and now we're at line 10021"
+ );
+}
+
+#[test]
+fn test_raw_origin_initial_nopos() {
+ let dl = DisplayList::from(vec![DisplayLine::Raw(DisplayRawLine::Origin {
+ path: "src/test.rs",
+ pos: None,
+ header_type: DisplayHeaderType::Initial,
+ })]);
+
+ assert_eq!(dl.to_string(), "--> src/test.rs");
+}
+
+#[test]
+fn test_raw_origin_initial_pos() {
+ let dl = DisplayList::from(vec![DisplayLine::Raw(DisplayRawLine::Origin {
+ path: "src/test.rs",
+ pos: Some((23, 15)),
+ header_type: DisplayHeaderType::Initial,
+ })]);
+
+ assert_eq!(dl.to_string(), "--> src/test.rs:23:15");
+}
+
+#[test]
+fn test_raw_origin_continuation() {
+ let dl = DisplayList::from(vec![DisplayLine::Raw(DisplayRawLine::Origin {
+ path: "src/test.rs",
+ pos: Some((23, 15)),
+ header_type: DisplayHeaderType::Continuation,
+ })]);
+
+ assert_eq!(dl.to_string(), "::: src/test.rs:23:15");
+}
+
+#[test]
+fn test_raw_annotation_unaligned() {
+ let dl = DisplayList::from(vec![DisplayLine::Raw(DisplayRawLine::Annotation {
+ annotation: Annotation {
+ annotation_type: DisplayAnnotationType::Error,
+ id: Some("E0001"),
+ label: vec![DisplayTextFragment {
+ content: "This is an error",
+ style: DisplayTextStyle::Regular,
+ }],
+ },
+ source_aligned: false,
+ continuation: false,
+ })]);
+
+ assert_eq!(dl.to_string(), "error[E0001]: This is an error");
+}
+
+#[test]
+fn test_raw_annotation_unaligned_multiline() {
+ let dl = DisplayList::from(vec![
+ DisplayLine::Raw(DisplayRawLine::Annotation {
+ annotation: Annotation {
+ annotation_type: DisplayAnnotationType::Warning,
+ id: Some("E0001"),
+ label: vec![DisplayTextFragment {
+ content: "This is an error",
+ style: DisplayTextStyle::Regular,
+ }],
+ },
+ source_aligned: false,
+ continuation: false,
+ }),
+ DisplayLine::Raw(DisplayRawLine::Annotation {
+ annotation: Annotation {
+ annotation_type: DisplayAnnotationType::Warning,
+ id: Some("E0001"),
+ label: vec![DisplayTextFragment {
+ content: "Second line of the error",
+ style: DisplayTextStyle::Regular,
+ }],
+ },
+ source_aligned: false,
+ continuation: true,
+ }),
+ ]);
+
+ assert_eq!(
+ dl.to_string(),
+ "warning[E0001]: This is an error\n Second line of the error"
+ );
+}
+
+#[test]
+fn test_raw_annotation_aligned() {
+ let dl = DisplayList::from(vec![DisplayLine::Raw(DisplayRawLine::Annotation {
+ annotation: Annotation {
+ annotation_type: DisplayAnnotationType::Error,
+ id: Some("E0001"),
+ label: vec![DisplayTextFragment {
+ content: "This is an error",
+ style: DisplayTextStyle::Regular,
+ }],
+ },
+ source_aligned: true,
+ continuation: false,
+ })]);
+
+ assert_eq!(dl.to_string(), " = error[E0001]: This is an error");
+}
+
+#[test]
+fn test_raw_annotation_aligned_multiline() {
+ let dl = DisplayList::from(vec![
+ DisplayLine::Raw(DisplayRawLine::Annotation {
+ annotation: Annotation {
+ annotation_type: DisplayAnnotationType::Warning,
+ id: Some("E0001"),
+ label: vec![DisplayTextFragment {
+ content: "This is an error",
+ style: DisplayTextStyle::Regular,
+ }],
+ },
+ source_aligned: true,
+ continuation: false,
+ }),
+ DisplayLine::Raw(DisplayRawLine::Annotation {
+ annotation: Annotation {
+ annotation_type: DisplayAnnotationType::Warning,
+ id: Some("E0001"),
+ label: vec![DisplayTextFragment {
+ content: "Second line of the error",
+ style: DisplayTextStyle::Regular,
+ }],
+ },
+ source_aligned: true,
+ continuation: true,
+ }),
+ ]);
+
+ assert_eq!(
+ dl.to_string(),
+ " = warning[E0001]: This is an error\n Second line of the error"
+ );
+}
+
+#[test]
+fn test_different_annotation_types() {
+ let dl = DisplayList::from(vec![
+ DisplayLine::Raw(DisplayRawLine::Annotation {
+ annotation: Annotation {
+ annotation_type: DisplayAnnotationType::Note,
+ id: None,
+ label: vec![DisplayTextFragment {
+ content: "This is a note",
+ style: DisplayTextStyle::Regular,
+ }],
+ },
+ source_aligned: false,
+ continuation: false,
+ }),
+ DisplayLine::Raw(DisplayRawLine::Annotation {
+ annotation: Annotation {
+ annotation_type: DisplayAnnotationType::None,
+ id: None,
+ label: vec![DisplayTextFragment {
+ content: "This is just a string",
+ style: DisplayTextStyle::Regular,
+ }],
+ },
+ source_aligned: false,
+ continuation: false,
+ }),
+ DisplayLine::Raw(DisplayRawLine::Annotation {
+ annotation: Annotation {
+ annotation_type: DisplayAnnotationType::None,
+ id: None,
+ label: vec![DisplayTextFragment {
+ content: "Second line of none type annotation",
+ style: DisplayTextStyle::Regular,
+ }],
+ },
+ source_aligned: false,
+ continuation: true,
+ }),
+ ]);
+
+ assert_eq!(
+ dl.to_string(),
+ "note: This is a note\nThis is just a string\n Second line of none type annotation",
+ );
+}
+
+#[test]
+fn test_inline_marks_empty_line() {
+ let dl = DisplayList::from(vec![DisplayLine::Source {
+ lineno: None,
+ inline_marks: vec![DisplayMark {
+ mark_type: DisplayMarkType::AnnotationThrough,
+ annotation_type: DisplayAnnotationType::Error,
+ }],
+ line: DisplaySourceLine::Empty,
+ }]);
+
+ assert_eq!(dl.to_string(), " | |",);
+}
+
+#[test]
+fn test_anon_lines() {
+ let mut dl = DisplayList::from(vec![
+ DisplayLine::Source {
+ lineno: Some(56),
+ inline_marks: vec![],
+ line: DisplaySourceLine::Content {
+ text: "This is an example",
+ range: (0, 19),
+ },
+ },
+ DisplayLine::Source {
+ lineno: Some(57),
+ inline_marks: vec![],
+ line: DisplaySourceLine::Content {
+ text: "of content lines",
+ range: (0, 19),
+ },
+ },
+ DisplayLine::Source {
+ lineno: None,
+ inline_marks: vec![],
+ line: DisplaySourceLine::Empty,
+ },
+ DisplayLine::Source {
+ lineno: None,
+ inline_marks: vec![],
+ line: DisplaySourceLine::Content {
+ text: "abc",
+ range: (0, 19),
+ },
+ },
+ ]);
+
+ dl.anonymized_line_numbers = true;
+ assert_eq!(
+ dl.to_string(),
+ "LL | This is an example\nLL | of content lines\n |\n | abc"
+ );
+}
+
+#[test]
+fn test_raw_origin_initial_pos_anon_lines() {
+ let mut dl = DisplayList::from(vec![DisplayLine::Raw(DisplayRawLine::Origin {
+ path: "src/test.rs",
+ pos: Some((23, 15)),
+ header_type: DisplayHeaderType::Initial,
+ })]);
+
+ // Using anonymized_line_numbers should not affect the inital position
+ dl.anonymized_line_numbers = true;
+ assert_eq!(dl.to_string(), "--> src/test.rs:23:15");
+}
+
+#[test]
+fn test_i_29() {
+ let snippets = Snippet {
+ title: Some(snippet::Annotation {
+ id: None,
+ label: Some("oops"),
+ annotation_type: snippet::AnnotationType::Error,
+ }),
+ footer: vec![],
+ slices: vec![snippet::Slice {
+ source: "First line\r\nSecond oops line",
+ line_start: 1,
+ origin: Some("<current file>"),
+ annotations: vec![snippet::SourceAnnotation {
+ range: (19, 23),
+ label: "oops",
+ annotation_type: snippet::AnnotationType::Error,
+ }],
+ fold: true,
+ }],
+ opt: Default::default(),
+ };
+ let expected = r#"error: oops
+ --> <current file>:2:8
+ |
+1 | First line
+2 | Second oops line
+ | ^^^^ oops
+ |"#;
+
+ assert_eq!(DisplayList::from(snippets).to_string(), expected);
+}
+
+#[test]
+fn test_point_to_double_width_characters() {
+ let snippets = Snippet {
+ slices: vec![snippet::Slice {
+ source: "こんにちは、世界",
+ line_start: 1,
+ origin: Some("<current file>"),
+ annotations: vec![snippet::SourceAnnotation {
+ range: (6, 8),
+ label: "world",
+ annotation_type: snippet::AnnotationType::Error,
+ }],
+ fold: false,
+ }],
+ title: None,
+ footer: vec![],
+ opt: Default::default(),
+ };
+
+ let expected = r#" --> <current file>:1:7
+ |
+1 | こんにちは、世界
+ | ^^^^ world
+ |"#;
+
+ assert_eq!(DisplayList::from(snippets).to_string(), expected);
+}
+
+#[test]
+fn test_point_to_double_width_characters_across_lines() {
+ let snippets = Snippet {
+ slices: vec![snippet::Slice {
+ source: "おはよう\nございます",
+ line_start: 1,
+ origin: Some("<current file>"),
+ annotations: vec![snippet::SourceAnnotation {
+ range: (2, 8),
+ label: "Good morning",
+ annotation_type: snippet::AnnotationType::Error,
+ }],
+ fold: false,
+ }],
+ title: None,
+ footer: vec![],
+ opt: Default::default(),
+ };
+
+ let expected = r#" --> <current file>:1:3
+ |
+1 | おはよう
+ | _____^
+2 | | ございます
+ | |______^ Good morning
+ |"#;
+
+ assert_eq!(DisplayList::from(snippets).to_string(), expected);
+}
+
+#[test]
+fn test_point_to_double_width_characters_multiple() {
+ let snippets = Snippet {
+ slices: vec![snippet::Slice {
+ source: "お寿司\n食べたい🍣",
+ line_start: 1,
+ origin: Some("<current file>"),
+ annotations: vec![
+ snippet::SourceAnnotation {
+ range: (0, 3),
+ label: "Sushi1",
+ annotation_type: snippet::AnnotationType::Error,
+ },
+ snippet::SourceAnnotation {
+ range: (6, 8),
+ label: "Sushi2",
+ annotation_type: snippet::AnnotationType::Note,
+ },
+ ],
+ fold: false,
+ }],
+ title: None,
+ footer: vec![],
+ opt: Default::default(),
+ };
+
+ let expected = r#" --> <current file>:1:1
+ |
+1 | お寿司
+ | ^^^^^^ Sushi1
+2 | 食べたい🍣
+ | ---- note: Sushi2
+ |"#;
+
+ assert_eq!(DisplayList::from(snippets).to_string(), expected);
+}
+
+#[test]
+fn test_point_to_double_width_characters_mixed() {
+ let snippets = Snippet {
+ slices: vec![snippet::Slice {
+ source: "こんにちは、新しいWorld!",
+ line_start: 1,
+ origin: Some("<current file>"),
+ annotations: vec![snippet::SourceAnnotation {
+ range: (6, 14),
+ label: "New world",
+ annotation_type: snippet::AnnotationType::Error,
+ }],
+ fold: false,
+ }],
+ title: None,
+ footer: vec![],
+ opt: Default::default(),
+ };
+
+ let expected = r#" --> <current file>:1:7
+ |
+1 | こんにちは、新しいWorld!
+ | ^^^^^^^^^^^ New world
+ |"#;
+
+ assert_eq!(DisplayList::from(snippets).to_string(), expected);
+}
diff --git a/tests/snippet/mod.rs b/tests/snippet/mod.rs
new file mode 100644
index 0000000..40249f4
--- /dev/null
+++ b/tests/snippet/mod.rs
@@ -0,0 +1,208 @@
+use serde::{Deserialize, Deserializer, Serialize};
+
+use annotate_snippets::{
+ display_list::{FormatOptions, Margin},
+ snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation},
+};
+
+#[derive(Deserialize)]
+pub struct SnippetDef<'a> {
+ #[serde(deserialize_with = "deserialize_annotation")]
+ #[serde(default)]
+ #[serde(borrow)]
+ pub title: Option<Annotation<'a>>,
+ #[serde(deserialize_with = "deserialize_annotations")]
+ #[serde(default)]
+ #[serde(borrow)]
+ pub footer: Vec<Annotation<'a>>,
+ #[serde(deserialize_with = "deserialize_opt")]
+ #[serde(default)]
+ pub opt: FormatOptions,
+ #[serde(deserialize_with = "deserialize_slices")]
+ #[serde(borrow)]
+ pub slices: Vec<Slice<'a>>,
+}
+
+impl<'a> Into<Snippet<'a>> for SnippetDef<'a> {
+ fn into(self) -> Snippet<'a> {
+ let SnippetDef {
+ title,
+ footer,
+ opt,
+ slices,
+ } = self;
+ Snippet {
+ title,
+ footer,
+ slices,
+ opt,
+ }
+ }
+}
+
+fn deserialize_opt<'de, D>(deserializer: D) -> Result<FormatOptions, D::Error>
+where
+ D: Deserializer<'de>,
+{
+ #[derive(Deserialize)]
+ struct Wrapper(#[serde(with = "FormatOptionsDef")] FormatOptions);
+
+ Wrapper::deserialize(deserializer).map(|w| w.0)
+}
+
+#[derive(Deserialize)]
+#[serde(remote = "FormatOptions")]
+pub struct FormatOptionsDef {
+ #[serde(default)]
+ pub color: bool,
+ #[serde(default)]
+ pub anonymized_line_numbers: bool,
+ #[serde(deserialize_with = "deserialize_margin")]
+ #[serde(default)]
+ pub margin: Option<Margin>,
+}
+
+fn deserialize_margin<'de, D>(deserializer: D) -> Result<Option<Margin>, D::Error>
+where
+ D: Deserializer<'de>,
+{
+ #[derive(Deserialize)]
+ struct Wrapper {
+ whitespace_left: usize,
+ span_left: usize,
+ span_right: usize,
+ label_right: usize,
+ column_width: usize,
+ max_line_len: usize,
+ }
+
+ Option::<Wrapper>::deserialize(deserializer).map(|opt_wrapped: Option<Wrapper>| {
+ opt_wrapped.map(|wrapped: Wrapper| {
+ let Wrapper {
+ whitespace_left,
+ span_left,
+ span_right,
+ label_right,
+ column_width,
+ max_line_len,
+ } = wrapped;
+ Margin::new(
+ whitespace_left,
+ span_left,
+ span_right,
+ label_right,
+ column_width,
+ max_line_len,
+ )
+ })
+ })
+}
+
+fn deserialize_slices<'de, D>(deserializer: D) -> Result<Vec<Slice<'de>>, D::Error>
+where
+ D: Deserializer<'de>,
+{
+ #[derive(Deserialize)]
+ struct Wrapper<'a>(
+ #[serde(with = "SliceDef")]
+ #[serde(borrow)]
+ Slice<'a>,
+ );
+
+ let v = Vec::deserialize(deserializer)?;
+ Ok(v.into_iter().map(|Wrapper(a)| a).collect())
+}
+
+fn deserialize_annotation<'de, D>(deserializer: D) -> Result<Option<Annotation<'de>>, D::Error>
+where
+ D: Deserializer<'de>,
+{
+ #[derive(Deserialize)]
+ struct Wrapper<'a>(
+ #[serde(with = "AnnotationDef")]
+ #[serde(borrow)]
+ Annotation<'a>,
+ );
+
+ Option::<Wrapper>::deserialize(deserializer)
+ .map(|opt_wrapped: Option<Wrapper>| opt_wrapped.map(|wrapped: Wrapper| wrapped.0))
+}
+
+fn deserialize_annotations<'de, D>(deserializer: D) -> Result<Vec<Annotation<'de>>, D::Error>
+where
+ D: Deserializer<'de>,
+{
+ #[derive(Deserialize)]
+ struct Wrapper<'a>(
+ #[serde(with = "AnnotationDef")]
+ #[serde(borrow)]
+ Annotation<'a>,
+ );
+
+ let v = Vec::deserialize(deserializer)?;
+ Ok(v.into_iter().map(|Wrapper(a)| a).collect())
+}
+
+#[derive(Deserialize)]
+#[serde(remote = "Slice")]
+pub struct SliceDef<'a> {
+ #[serde(borrow)]
+ pub source: &'a str,
+ pub line_start: usize,
+ #[serde(borrow)]
+ pub origin: Option<&'a str>,
+ #[serde(deserialize_with = "deserialize_source_annotations")]
+ #[serde(borrow)]
+ pub annotations: Vec<SourceAnnotation<'a>>,
+ #[serde(default)]
+ pub fold: bool,
+}
+
+fn deserialize_source_annotations<'de, D>(
+ deserializer: D,
+) -> Result<Vec<SourceAnnotation<'de>>, D::Error>
+where
+ D: Deserializer<'de>,
+{
+ #[derive(Deserialize)]
+ struct Wrapper<'a>(
+ #[serde(with = "SourceAnnotationDef")]
+ #[serde(borrow)]
+ SourceAnnotation<'a>,
+ );
+
+ let v = Vec::deserialize(deserializer)?;
+ Ok(v.into_iter().map(|Wrapper(a)| a).collect())
+}
+
+#[derive(Serialize, Deserialize)]
+#[serde(remote = "SourceAnnotation")]
+pub struct SourceAnnotationDef<'a> {
+ pub range: (usize, usize),
+ #[serde(borrow)]
+ pub label: &'a str,
+ #[serde(with = "AnnotationTypeDef")]
+ pub annotation_type: AnnotationType,
+}
+
+#[derive(Serialize, Deserialize)]
+#[serde(remote = "Annotation")]
+pub struct AnnotationDef<'a> {
+ #[serde(borrow)]
+ pub id: Option<&'a str>,
+ #[serde(borrow)]
+ pub label: Option<&'a str>,
+ #[serde(with = "AnnotationTypeDef")]
+ pub annotation_type: AnnotationType,
+}
+
+#[allow(dead_code)]
+#[derive(Serialize, Deserialize)]
+#[serde(remote = "AnnotationType")]
+enum AnnotationTypeDef {
+ Error,
+ Warning,
+ Info,
+ Note,
+ Help,
+}