use annotate_snippets::{Level, Renderer, Snippet}; use snapbox::{assert_data_eq, str}; #[test] fn test_i_29() { let snippets = Level::Error.title("oops").snippet( Snippet::source("First line\r\nSecond oops line") .origin("") .annotation(Level::Error.span(19..23).label("oops")) .fold(true), ); let expected = str![[r#" error: oops --> :2:8 | 2 | Second oops line | ^^^^ oops | "#]]; let renderer = Renderer::plain(); assert_data_eq!(renderer.render(snippets).to_string(), expected); } #[test] fn test_point_to_double_width_characters() { let snippets = Level::Error.title("").snippet( Snippet::source("こんにちは、世界") .origin("") .annotation(Level::Error.span(18..24).label("world")), ); let expected = str![[r#" error --> :1:7 | 1 | こんにちは、世界 | ^^^^ world | "#]]; let renderer = Renderer::plain(); assert_data_eq!(renderer.render(snippets).to_string(), expected); } #[test] fn test_point_to_double_width_characters_across_lines() { let snippets = Level::Error.title("").snippet( Snippet::source("おはよう\nございます") .origin("") .annotation(Level::Error.span(6..22).label("Good morning")), ); let expected = str![[r#" error --> :1:3 | 1 | おはよう | _____^ 2 | | ございます | |______^ Good morning | "#]]; let renderer = Renderer::plain(); assert_data_eq!(renderer.render(snippets).to_string(), expected); } #[test] fn test_point_to_double_width_characters_multiple() { let snippets = Level::Error.title("").snippet( Snippet::source("お寿司\n食べたい🍣") .origin("") .annotation(Level::Error.span(0..9).label("Sushi1")) .annotation(Level::Note.span(16..22).label("Sushi2")), ); let expected = str![[r#" error --> :1:1 | 1 | お寿司 | ^^^^^^ Sushi1 2 | 食べたい🍣 | ---- note: Sushi2 | "#]]; let renderer = Renderer::plain(); assert_data_eq!(renderer.render(snippets).to_string(), expected); } #[test] fn test_point_to_double_width_characters_mixed() { let snippets = Level::Error.title("").snippet( Snippet::source("こんにちは、新しいWorld!") .origin("") .annotation(Level::Error.span(18..32).label("New world")), ); let expected = str![[r#" error --> :1:7 | 1 | こんにちは、新しいWorld! | ^^^^^^^^^^^ New world | "#]]; let renderer = Renderer::plain(); assert_data_eq!(renderer.render(snippets).to_string(), expected); } #[test] fn test_format_title() { let input = Level::Error.title("This is a title").id("E0001"); let expected = str![r#"error[E0001]: This is a title"#]; let renderer = Renderer::plain(); assert_data_eq!(renderer.render(input).to_string(), expected); } #[test] fn test_format_snippet_only() { let source = "This is line 1\nThis is line 2"; let input = Level::Error .title("") .snippet(Snippet::source(source).line_start(5402)); let expected = str![[r#" error | 5402 | This is line 1 5403 | This is line 2 | "#]]; let renderer = Renderer::plain(); assert_data_eq!(renderer.render(input).to_string(), expected); } #[test] fn test_format_snippets_continuation() { let src_0 = "This is slice 1"; let src_1 = "This is slice 2"; let input = Level::Error .title("") .snippet(Snippet::source(src_0).line_start(5402).origin("file1.rs")) .snippet(Snippet::source(src_1).line_start(2).origin("file2.rs")); let expected = str![[r#" error --> file1.rs | 5402 | This is slice 1 | ::: file2.rs | 2 | This is slice 2 | "#]]; let renderer = Renderer::plain(); assert_data_eq!(renderer.render(input).to_string(), expected); } #[test] fn test_format_snippet_annotation_standalone() { let line_1 = "This is line 1"; let line_2 = "This is line 2"; let source = [line_1, line_2].join("\n"); // In line 2 let range = 22..24; let input = Level::Error.title("").snippet( Snippet::source(&source) .line_start(5402) .annotation(Level::Info.span(range.clone()).label("Test annotation")), ); let expected = str![[r#" error | 5402 | This is line 1 5403 | This is line 2 | -- info: Test annotation | "#]]; let renderer = Renderer::plain(); assert_data_eq!(renderer.render(input).to_string(), expected); } #[test] fn test_format_footer_title() { let input = Level::Error .title("") .footer(Level::Error.title("This __is__ a title")); let expected = str![[r#" error = error: This __is__ a title "#]]; let renderer = Renderer::plain(); assert_data_eq!(renderer.render(input).to_string(), expected); } #[test] #[should_panic] fn test_i26() { let source = "short"; let label = "label"; let input = Level::Error.title("").snippet( Snippet::source(source) .line_start(0) .annotation(Level::Error.span(0..source.len() + 2).label(label)), ); let renderer = Renderer::plain(); let _ = renderer.render(input).to_string(); } #[test] fn test_source_content() { let source = "This is an example\nof content lines"; let input = Level::Error .title("") .snippet(Snippet::source(source).line_start(56)); let expected = str![[r#" error | 56 | This is an example 57 | of content lines | "#]]; let renderer = Renderer::plain(); assert_data_eq!(renderer.render(input).to_string(), expected); } #[test] fn test_source_annotation_standalone_singleline() { let source = "tests"; let input = Level::Error.title("").snippet( Snippet::source(source) .line_start(1) .annotation(Level::Help.span(0..5).label("Example string")), ); let expected = str![[r#" error | 1 | tests | ----- help: Example string | "#]]; let renderer = Renderer::plain(); assert_data_eq!(renderer.render(input).to_string(), expected); } #[test] fn test_source_annotation_standalone_multiline() { let source = "tests"; let input = Level::Error.title("").snippet( Snippet::source(source) .line_start(1) .annotation(Level::Help.span(0..5).label("Example string")) .annotation(Level::Help.span(0..5).label("Second line")), ); let expected = str![[r#" error | 1 | tests | ----- help: Example string | ----- help: Second line | "#]]; let renderer = Renderer::plain(); assert_data_eq!(renderer.render(input).to_string(), expected); } #[test] fn test_only_source() { let input = Level::Error .title("") .snippet(Snippet::source("").origin("file.rs")); let expected = str![[r#" error --> file.rs | | "#]]; let renderer = Renderer::plain(); assert_data_eq!(renderer.render(input).to_string(), expected); } #[test] fn test_anon_lines() { let source = "This is an example\nof content lines\n\nabc"; let input = Level::Error .title("") .snippet(Snippet::source(source).line_start(56)); let expected = str![[r#" error | LL | This is an example LL | of content lines LL | LL | abc | "#]]; let renderer = Renderer::plain().anonymized_line_numbers(true); assert_data_eq!(renderer.render(input).to_string(), expected); } #[test] fn issue_130() { let input = Level::Error.title("dummy").snippet( Snippet::source("foo\nbar\nbaz") .origin("file/path") .line_start(3) .fold(true) .annotation(Level::Error.span(4..11)), // bar\nbaz ); let expected = str![[r#" error: dummy --> file/path:4:1 | 4 | / bar 5 | | baz | |___^ | "#]]; let renderer = Renderer::plain(); assert_data_eq!(renderer.render(input).to_string(), expected); } #[test] fn unterminated_string_multiline() { let source = "\ a\" // ... "; let input = Level::Error.title("").snippet( Snippet::source(source) .origin("file/path") .line_start(3) .fold(true) .annotation(Level::Error.span(0..10)), // 1..10 works ); let expected = str![[r#" error --> file/path:3:1 | 3 | / a" 4 | | // ... | |_______^ | "#]]; let renderer = Renderer::plain().anonymized_line_numbers(false); assert_data_eq!(renderer.render(input).to_string(), expected); } #[test] fn char_and_nl_annotate_char() { let source = "a\r\nb"; let input = Level::Error.title("").snippet( Snippet::source(source) .origin("file/path") .line_start(3) .annotation(Level::Error.span(0..2)), // a\r ); let expected = str![[r#" error --> file/path:3:1 | 3 | a | ^ 4 | b |"#]]; let renderer = Renderer::plain().anonymized_line_numbers(false); assert_data_eq!(renderer.render(input).to_string(), expected); } #[test] fn char_eol_annotate_char() { let source = "a\r\nb"; let input = Level::Error.title("").snippet( Snippet::source(source) .origin("file/path") .line_start(3) .annotation(Level::Error.span(0..3)), // a\r\n ); let expected = str![[r#" error --> file/path:3:1 | 3 | a | ^ 4 | b |"#]]; let renderer = Renderer::plain().anonymized_line_numbers(false); assert_data_eq!(renderer.render(input).to_string(), expected); } #[test] fn char_eol_annotate_char_double_width() { let snippets = Level::Error.title("").snippet( Snippet::source("こん\r\nにちは\r\n世界") .origin("") .annotation(Level::Error.span(3..8)), // ん\r\n ); let expected = str![[r#" error --> :1:2 | 1 | こん | ^^ 2 | にちは 3 | 世界 | "#]]; let renderer = Renderer::plain(); assert_data_eq!(renderer.render(snippets).to_string(), expected); } #[test] fn annotate_eol() { let source = "a\r\nb"; let input = Level::Error.title("").snippet( Snippet::source(source) .origin("file/path") .line_start(3) .annotation(Level::Error.span(1..2)), // \r ); let expected = str![[r#" error --> file/path:3:2 | 3 | a | ^ 4 | b |"#]]; let renderer = Renderer::plain().anonymized_line_numbers(false); assert_data_eq!(renderer.render(input).to_string(), expected); } #[test] fn annotate_eol2() { let source = "a\r\nb"; let input = Level::Error.title("").snippet( Snippet::source(source) .origin("file/path") .line_start(3) .annotation(Level::Error.span(1..3)), // \r\n ); let expected = str![[r#" error --> file/path:3:2 | 3 | a | ^ 4 | b |"#]]; let renderer = Renderer::plain().anonymized_line_numbers(false); assert_data_eq!(renderer.render(input).to_string(), expected); } #[test] fn annotate_eol3() { let source = "a\r\nb"; let input = Level::Error.title("").snippet( Snippet::source(source) .origin("file/path") .line_start(3) .annotation(Level::Error.span(2..3)), // \n ); let expected = str![[r#" error --> file/path:3:2 | 3 | a | ^ 4 | b |"#]]; let renderer = Renderer::plain().anonymized_line_numbers(false); assert_data_eq!(renderer.render(input).to_string(), expected); } #[test] fn annotate_eol4() { let source = "a\r\nb"; let input = Level::Error.title("").snippet( Snippet::source(source) .origin("file/path") .line_start(3) .annotation(Level::Error.span(2..2)), // \n ); let expected = str![[r#" error --> file/path:3:2 | 3 | a | ^ 4 | b |"#]]; let renderer = Renderer::plain().anonymized_line_numbers(false); assert_data_eq!(renderer.render(input).to_string(), expected); } #[test] fn annotate_eol_double_width() { let snippets = Level::Error.title("").snippet( Snippet::source("こん\r\nにちは\r\n世界") .origin("") .annotation(Level::Error.span(7..8)), // \n ); let expected = str![[r#" error --> :1:3 | 1 | こん | ^ 2 | にちは 3 | 世界 | "#]]; let renderer = Renderer::plain(); assert_data_eq!(renderer.render(snippets).to_string(), expected); } #[test] fn multiline_eol_start() { let source = "a\r\nb"; let input = Level::Error.title("").snippet( Snippet::source(source) .origin("file/path") .line_start(3) .annotation(Level::Error.span(1..4)), // \r\nb ); let expected = str![[r#" error --> file/path:3:2 | 3 | a | __^ 4 | | b | |_^ |"#]]; let renderer = Renderer::plain().anonymized_line_numbers(false); assert_data_eq!(renderer.render(input).to_string(), expected); } #[test] fn multiline_eol_start2() { let source = "a\r\nb"; let input = Level::Error.title("").snippet( Snippet::source(source) .origin("file/path") .line_start(3) .annotation(Level::Error.span(2..4)), // \nb ); let expected = str![[r#" error --> file/path:3:2 | 3 | a | __^ 4 | | b | |_^ |"#]]; let renderer = Renderer::plain().anonymized_line_numbers(false); assert_data_eq!(renderer.render(input).to_string(), expected); } #[test] fn multiline_eol_start3() { let source = "a\nb"; let input = Level::Error.title("").snippet( Snippet::source(source) .origin("file/path") .line_start(3) .annotation(Level::Error.span(1..3)), // \nb ); let expected = str![[r#" error --> file/path:3:2 | 3 | a | __^ 4 | | b | |_^ |"#]]; let renderer = Renderer::plain().anonymized_line_numbers(false); assert_data_eq!(renderer.render(input).to_string(), expected); } #[test] fn multiline_eol_start_double_width() { let snippets = Level::Error.title("").snippet( Snippet::source("こん\r\nにちは\r\n世界") .origin("") .annotation(Level::Error.span(7..11)), // \r\nに ); let expected = str![[r#" error --> :1:3 | 1 | こん | _____^ 2 | | にちは | |__^ 3 | 世界 | "#]]; let renderer = Renderer::plain(); assert_data_eq!(renderer.render(snippets).to_string(), expected); } #[test] fn multiline_eol_start_eol_end() { let source = "a\nb\nc"; let input = Level::Error.title("").snippet( Snippet::source(source) .origin("file/path") .line_start(3) .annotation(Level::Error.span(1..4)), // \nb\n ); let expected = str![[r#" error --> file/path:3:2 | 3 | a | __^ 4 | | b | |__^ 5 | c | "#]]; let renderer = Renderer::plain().anonymized_line_numbers(false); assert_data_eq!(renderer.render(input).to_string(), expected); } #[test] fn multiline_eol_start_eol_end2() { let source = "a\r\nb\r\nc"; let input = Level::Error.title("").snippet( Snippet::source(source) .origin("file/path") .line_start(3) .annotation(Level::Error.span(2..5)), // \nb\r ); let expected = str![[r#" error --> file/path:3:2 | 3 | a | __^ 4 | | b | |__^ 5 | c | "#]]; let renderer = Renderer::plain().anonymized_line_numbers(false); assert_data_eq!(renderer.render(input).to_string(), expected); } #[test] fn multiline_eol_start_eol_end3() { let source = "a\r\nb\r\nc"; let input = Level::Error.title("").snippet( Snippet::source(source) .origin("file/path") .line_start(3) .annotation(Level::Error.span(2..6)), // \nb\r\n ); let expected = str![[r#" error --> file/path:3:2 | 3 | a | __^ 4 | | b | |__^ 5 | c | "#]]; let renderer = Renderer::plain().anonymized_line_numbers(false); assert_data_eq!(renderer.render(input).to_string(), expected); } #[test] fn multiline_eol_start_eof_end() { let source = "a\r\nb"; let input = Level::Error.title("").snippet( Snippet::source(source) .origin("file/path") .line_start(3) .annotation(Level::Error.span(1..5)), // \r\nb(EOF) ); let expected = str![[r#" error --> file/path:3:2 | 3 | a | __^ 4 | | b | |__^ | "#]]; let renderer = Renderer::plain().anonymized_line_numbers(false); assert_data_eq!(renderer.render(input).to_string(), expected); } #[test] fn multiline_eol_start_eof_end_double_width() { let source = "ん\r\nに"; let input = Level::Error.title("").snippet( Snippet::source(source) .origin("file/path") .line_start(3) .annotation(Level::Error.span(3..9)), // \r\nに(EOF) ); let expected = str![[r#" error --> file/path:3:2 | 3 | ん | ___^ 4 | | に | |___^ | "#]]; let renderer = Renderer::plain().anonymized_line_numbers(false); assert_data_eq!(renderer.render(input).to_string(), expected); }