diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index e186cc2..0000000 --- a/.travis.yml +++ /dev/null @@ -1,30 +0,0 @@ -language: rust -sudo: required -dist: trusty -addons: - apt: - packages: - - libssl-dev -cache: cargo -rust: - - stable - - beta - - nightly -matrix: - allow_failures: - - rust: nightly - -before_cache: | - if [[ "$TRAVIS_RUST_VERSION" == nightly ]]; then - RUSTFLAGS="--cfg procmacro2_semver_exempt" cargo install cargo-tarpaulin -f - fi - -script: -- cargo clean -- cargo build -- cargo test - -after_success: | - if [[ "$TRAVIS_RUST_VERSION" == nightly ]]; then - cargo tarpaulin --ciserver travis-ci --coveralls $TRAVIS_JOB_ID - fi diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index a125e8f..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,15 +0,0 @@ -# Changelog - -## Unreleased - - - … - -## annotate-snippets 0.6.1 (July 23, 2019) - - - Fix too many anonymized line numbers (#5) - -## annotate-snippets 0.6.0 (June 26, 2019) - - - Add an option to anonymize line numbers (#3) - - Transition the crate to rust-lang org. - - Update the syntax to Rust 2018 idioms. (#4) diff --git a/Cargo.toml b/Cargo.toml index 7980bc9..4aff434 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,35 +1,25 @@ [package] name = "annotate-snippets" -version = "0.6.1" +version = "0.1.0" +authors = ["Zibi Braniecki "] edition = "2018" -authors = ["Zibi Braniecki "] -description = "Library for building code annotations" -license = "Apache-2.0/MIT" -repository = "https://github.com/rust-lang/annotate-snippets-rs" -readme = "README.md" -keywords = ["code", "analysis", "ascii", "errors", "debug"] -[badges] -travis-ci = { repository = "rust-lang/annotate-snippets-rs", branch = "master" } -coveralls = { repository = "rust-lang/annotate-snippets-rs", branch = "master", service = "github" } - -maintenance = { status = "actively-developed" } +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ansi_term = { version = "^0.12", optional = true } +ansi_term = { version = "0.12", optional = true } +termcolor = { version = "1", optional = true } [dev-dependencies] -glob = "^0.3" -serde_yaml = "^0.8" -serde = { version = "^1.0", features = ["derive"] } -difference = "^2.0" -ansi_term = "^0.12" criterion = "0.3" -[[bench]] -name = "simple" -harness = false - [features] default = [] color = ["ansi_term"] +color2 = ["termcolor"] +unicode = [] +html = [] + +[[bench]] +name = "simple" +harness = false diff --git a/LICENSE-APACHE b/LICENSE-APACHE deleted file mode 100644 index 261eeb9..0000000 --- a/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/LICENSE-MIT b/LICENSE-MIT deleted file mode 100644 index 5655fa3..0000000 --- a/LICENSE-MIT +++ /dev/null @@ -1,19 +0,0 @@ -Copyright 2017 Mozilla - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md deleted file mode 100644 index 26a54a6..0000000 --- a/README.md +++ /dev/null @@ -1,92 +0,0 @@ -# annotate-snippets - -`annotate-snippets` is a Rust library for annotation of programming code slices. - -[![crates.io](https://meritbadge.herokuapp.com/annotate-snippets)](https://crates.io/crates/annotate-snippets) -[![Build Status](https://travis-ci.com/rust-lang/annotate-snippets-rs.svg?branch=master)](https://travis-ci.com/rust-lang/annotate-snippets-rs) -[![Coverage Status](https://coveralls.io/repos/github/rust-lang/annotate-snippets-rs/badge.svg?branch=master)](https://coveralls.io/github/rust-lang/annotate-snippets-rs?branch=master) - -The library helps visualize meta information annotating source code slices. -It takes a data structure called `Snippet` on the input and produces a `String` -which may look like this: - -```text -error[E0308]: mismatched types - --> src/format.rs:52:1 - | -51 | ) -> Option { - | -------------- expected `Option` 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 => continue, -... | -71 | | } -72 | | } - | |_____^ expected enum `std::option::Option`, found () -``` - -[Documentation][] - -[Documentation]: https://docs.rs/annotate-snippets/ - -Usage ------ - -```rust -use annotate_snippets::{ - display_list::DisplayList, - formatter::DisplayListFormatter, - snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation}, -}; - -fn main() { - let snippet = Snippet { - title: Some(Annotation { - label: Some("expected type, found `22`".to_string()), - id: None, - annotation_type: AnnotationType::Error, - }), - footer: vec![], - slices: vec![ - Slice { - source: r#" -This is an example -content of the slice -which will be annotated -with the list of annotations below. - "#.to_string(), - line_start: 26, - origin: Some("examples/example.txt".to_string()), - fold: false, - annotations: vec![ - SourceAnnotation { - label: "Example error annotation".to_string(), - annotation_type: AnnotationType::Error, - range: (13, 18), - }, - SourceAnnotation { - label: "and here's a warning".to_string(), - annotation_type: AnnotationType::Warning, - range: (34, 50), - }, - ], - }, - ], - }; - - let dl = DisplayList::from(snippet); - let dlf = DisplayListFormatter::new(true, false); - dlf.format(&dl); -} -``` - -Local Development ------------------ - - cargo build - cargo test - -When submitting a PR please use [`cargo fmt`][] (nightly). - -[`cargo fmt`]: https://github.com/rust-lang/rustfmt diff --git a/benches/simple.rs b/benches/simple.rs index 9633260..35da8d6 100644 --- a/benches/simple.rs +++ b/benches/simple.rs @@ -4,63 +4,66 @@ extern crate criterion; use criterion::black_box; use criterion::Criterion; -use annotate_snippets::display_list::DisplayList; -use annotate_snippets::formatter::DisplayListFormatter; -use annotate_snippets::snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation}; +use annotate_snippets::DisplayList; +use annotate_snippets::{Annotation, AnnotationType, SourceAnnotation}; +use annotate_snippets::{Slice, Snippet}; -fn create_snippet() { - let snippet = Snippet { - slices: vec![Slice { - source: r#") -> Option { - for ann in annotations { - match (ann.range.0, ann.range.1) { - (None, None) => continue, - (Some(start), Some(end)) if start > end_index => continue, - (Some(start), Some(end)) if start >= start_index => { - let label = if let Some(ref label) = ann.label { - format!(" {}", label) - } else { - String::from("") - }; +use annotate_snippets::renderers::ascii_default::get_renderer; +use annotate_snippets::renderers::Renderer; - return Some(format!( - "{}{}{}", - " ".repeat(start - start_index), - "^".repeat(end - start), - label - )); - } - _ => continue, +const SOURCE: &'static str = r#") -> Option { +for ann in annotations { + match (ann.range.0, ann.range.1) { + (None, None) => continue, + (Some(start), Some(end)) if start > end_index => continue, + (Some(start), Some(end)) if start >= start_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 + )); } - }"# - .to_string(), - line_start: 51, - origin: Some("src/format.rs".to_string()), - fold: false, - annotations: vec![ + _ => continue, + } +}"#; + +fn create_snippet() { + let snippet = Snippet { + title: Some(Annotation { + id: Some("E0308"), + label: Some("mismatched types"), + annotation_type: AnnotationType::Error, + }), + footer: &[], + slices: &[Slice { + source: SOURCE, + line_start: Some(51), + origin: Some("src/format.rs"), + annotations: &[ SourceAnnotation { - label: "expected `Option` because of return type".to_string(), + label: "expected `Option` because of return type", annotation_type: AnnotationType::Warning, - range: (5, 19), + range: 5..19, }, SourceAnnotation { - label: "expected enum `std::option::Option`".to_string(), + label: "expected enum `std::option::Option`", annotation_type: AnnotationType::Error, - range: (23, 745), + range: 23..725, }, ], }], - title: Some(Annotation { - label: Some("mismatched types".to_string()), - id: Some("E0308".to_string()), - annotation_type: AnnotationType::Error, - }), - footer: vec![], }; - - let dl = DisplayList::from(snippet); - let dlf = DisplayListFormatter::new(true, false); - let _result = dlf.format(&dl); + let r = get_renderer(); + let dl: DisplayList = (&snippet).into(); + let mut result: Vec = Vec::new(); + r.fmt(&mut result, &dl).unwrap(); } pub fn criterion_benchmark(c: &mut Criterion) { diff --git a/examples/expected_type.rs b/examples/expected_type.rs deleted file mode 100644 index 20cda66..0000000 --- a/examples/expected_type.rs +++ /dev/null @@ -1,40 +0,0 @@ -use annotate_snippets::display_list::DisplayList; -use annotate_snippets::formatter::DisplayListFormatter; -use annotate_snippets::snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation}; - -fn main() { - let snippet = Snippet { - title: Some(Annotation { - label: Some("expected type, found `22`".to_string()), - id: None, - annotation_type: AnnotationType::Error, - }), - footer: vec![], - slices: vec![Slice { - source: r#" annotations: vec![SourceAnnotation { - label: "expected struct `annotate_snippets::snippet::Slice`, found reference" - .to_string(), - range: <22, 25>,"# - .to_string(), - line_start: 26, - origin: Some("examples/footer.rs".to_string()), - fold: true, - annotations: vec![ - SourceAnnotation { - label: "".to_string(), - annotation_type: AnnotationType::Error, - range: (208, 210), - }, - SourceAnnotation { - label: "while parsing this struct".to_string(), - annotation_type: AnnotationType::Info, - range: (34, 50), - }, - ], - }], - }; - - let dl = DisplayList::from(snippet); - let dlf = DisplayListFormatter::new(true, false); - println!("{}", dlf.format(&dl)); -} diff --git a/examples/footer.rs b/examples/footer.rs deleted file mode 100644 index 1b03910..0000000 --- a/examples/footer.rs +++ /dev/null @@ -1,37 +0,0 @@ -use annotate_snippets::display_list::DisplayList; -use annotate_snippets::formatter::DisplayListFormatter; -use annotate_snippets::snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation}; - -fn main() { - let snippet = Snippet { - title: Some(Annotation { - label: Some("mismatched types".to_string()), - id: Some("E0308".to_string()), - annotation_type: AnnotationType::Error, - }), - footer: vec![Annotation { - label: Some( - "expected type: `snippet::Annotation`\n found type: `__&__snippet::Annotation`" - .to_string(), - ), - id: None, - annotation_type: AnnotationType::Note, - }], - slices: vec![Slice { - source: " slices: vec![\"A\",".to_string(), - line_start: 13, - origin: Some("src/multislice.rs".to_string()), - fold: false, - annotations: vec![SourceAnnotation { - label: "expected struct `annotate_snippets::snippet::Slice`, found reference" - .to_string(), - range: (21, 24), - annotation_type: AnnotationType::Error, - }], - }], - }; - - let dl = DisplayList::from(snippet); - let dlf = DisplayListFormatter::new(true, false); - println!("{}", dlf.format(&dl)); -} diff --git a/examples/format.rs b/examples/format.rs index 7e41802..c59bb09 100644 --- a/examples/format.rs +++ b/examples/format.rs @@ -1,11 +1,12 @@ -use annotate_snippets::display_list::DisplayList; -use annotate_snippets::formatter::DisplayListFormatter; -use annotate_snippets::snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation}; +use annotate_snippets::DisplayList; +use annotate_snippets::{Annotation, AnnotationType, SourceAnnotation}; +use annotate_snippets::{Slice, Snippet}; + +use annotate_snippets::renderers::get_renderer; +use annotate_snippets::renderers::Renderer; fn main() { - let snippet = Snippet { - slices: vec![Slice { - source: r#") -> Option { + let source = r#") -> Option { for ann in annotations { match (ann.range.0, ann.range.1) { (None, None) => continue, @@ -26,33 +27,37 @@ fn main() { } _ => continue, } - }"# - .to_string(), - line_start: 51, - origin: Some("src/format.rs".to_string()), - fold: false, - annotations: vec![ + }"#; + + let snippet = Snippet { + title: Some(Annotation { + id: Some("E0308"), + label: Some("mismatched types"), + annotation_type: AnnotationType::Error, + }), + footer: &[], + slices: &[Slice { + source, + line_start: Some(51), + origin: Some("src/format.rs"), + annotations: &[ SourceAnnotation { - label: "expected `Option` because of return type".to_string(), + label: "expected `Option` because of return type", annotation_type: AnnotationType::Warning, - range: (5, 19), + range: 5..19, }, SourceAnnotation { - label: "expected enum `std::option::Option`".to_string(), + label: "expected enum `std::option::Option`", annotation_type: AnnotationType::Error, - range: (23, 745), + range: 23..725, }, ], }], - title: Some(Annotation { - label: Some("mismatched types".to_string()), - id: Some("E0308".to_string()), - annotation_type: AnnotationType::Error, - }), - footer: vec![], }; + let dl = DisplayList::from(&snippet); + let r = get_renderer(); - let dl = DisplayList::from(snippet); - let dlf = DisplayListFormatter::new(true, false); - println!("{}", dlf.format(&dl)); + let mut s: Vec = Vec::new(); + r.fmt(&mut s, &dl).unwrap(); + println!("{}", std::str::from_utf8(&s).unwrap()); } diff --git a/examples/multislice.rs b/examples/multislice.rs deleted file mode 100644 index af1cb15..0000000 --- a/examples/multislice.rs +++ /dev/null @@ -1,34 +0,0 @@ -use annotate_snippets::display_list::DisplayList; -use annotate_snippets::formatter::DisplayListFormatter; -use annotate_snippets::snippet::{Annotation, AnnotationType, Slice, Snippet}; - -fn main() { - let snippet = Snippet { - title: Some(Annotation { - label: Some("mismatched types".to_string()), - id: None, - annotation_type: AnnotationType::Error, - }), - footer: vec![], - slices: vec![ - Slice { - source: "Foo".to_string(), - line_start: 51, - origin: Some("src/format.rs".to_string()), - fold: false, - annotations: vec![], - }, - Slice { - source: "Faa".to_string(), - line_start: 129, - origin: Some("src/display.rs".to_string()), - fold: false, - annotations: vec![], - }, - ], - }; - - let dl = DisplayList::from(snippet); - let dlf = DisplayListFormatter::new(true, false); - println!("{}", dlf.format(&dl)); -} diff --git a/src/annotation.rs b/src/annotation.rs new file mode 100644 index 0000000..b1ac52d --- /dev/null +++ b/src/annotation.rs @@ -0,0 +1,25 @@ +use std::ops::Range; + +#[derive(Debug, Clone)] +pub struct Annotation<'s> { + pub id: Option<&'s str>, + pub label: Option<&'s str>, + pub annotation_type: AnnotationType, +} + +#[derive(Debug, Clone)] +pub enum AnnotationType { + None, + Error, + Warning, + Info, + Note, + Help, +} + +#[derive(Debug, Clone)] +pub struct SourceAnnotation<'s> { + pub range: Range, + pub label: &'s str, + pub annotation_type: AnnotationType, +} diff --git a/src/display_list/annotation.rs b/src/display_list/annotation.rs new file mode 100644 index 0000000..a9da08c --- /dev/null +++ b/src/display_list/annotation.rs @@ -0,0 +1,8 @@ +use crate::annotation::AnnotationType; + +#[derive(Debug, Clone)] +pub struct Annotation<'d> { + pub annotation_type: AnnotationType, + pub id: Option<&'d str>, + pub label: &'d str, +} diff --git a/src/display_list/from_snippet.rs b/src/display_list/from_snippet.rs deleted file mode 100644 index e3d79ed..0000000 --- a/src/display_list/from_snippet.rs +++ /dev/null @@ -1,403 +0,0 @@ -//! Trait for converting `Snippet` to `DisplayList`. -use super::*; -use crate::snippet; - -fn format_label(label: Option<&str>, style: Option) -> Vec { - let mut result = vec![]; - if let Some(label) = label { - let elements: Vec<&str> = label.split("__").collect(); - for (idx, element) in elements.iter().enumerate() { - let element_style = match style { - Some(s) => s, - None => { - if idx % 2 == 0 { - DisplayTextStyle::Regular - } else { - DisplayTextStyle::Emphasis - } - } - }; - result.push(DisplayTextFragment { - content: element.to_string(), - style: element_style, - }); - } - } - result -} - -fn format_title(annotation: &snippet::Annotation) -> DisplayLine { - let label = annotation.label.clone().unwrap_or_default(); - DisplayLine::Raw(DisplayRawLine::Annotation { - annotation: Annotation { - annotation_type: DisplayAnnotationType::from(annotation.annotation_type), - id: annotation.id.clone(), - label: format_label(Some(&label), Some(DisplayTextStyle::Emphasis)), - }, - source_aligned: false, - continuation: false, - }) -} - -fn format_annotation(annotation: &snippet::Annotation) -> Vec { - let mut result = vec![]; - let label = annotation.label.clone().unwrap_or_default(); - for (i, line) in label.lines().enumerate() { - result.push(DisplayLine::Raw(DisplayRawLine::Annotation { - annotation: Annotation { - annotation_type: DisplayAnnotationType::from(annotation.annotation_type), - id: None, - label: format_label(Some(line), None), - }, - source_aligned: true, - continuation: i != 0, - })); - } - result -} - -fn format_slice(slice: &snippet::Slice, is_first: bool, has_footer: bool) -> Vec { - let mut body = format_body(slice, has_footer); - let mut result = vec![]; - - let header = format_header(slice, &body, is_first); - if let Some(header) = header { - result.push(header); - } - result.append(&mut body); - result -} - -fn format_header( - slice: &snippet::Slice, - body: &[DisplayLine], - is_first: bool, -) -> Option { - let main_annotation = slice.annotations.get(0); - - let display_header = if is_first { - DisplayHeaderType::Initial - } else { - DisplayHeaderType::Continuation - }; - - if let Some(annotation) = main_annotation { - let mut col = 1; - let mut row = slice.line_start; - - for item in body.iter() { - if let DisplayLine::Source { - line: DisplaySourceLine::Content { range, .. }, - .. - } = item - { - if annotation.range.0 >= range.0 && annotation.range.0 <= range.1 { - col = annotation.range.0 - range.0; - break; - } - row += 1; - } - } - if let Some(ref path) = slice.origin { - return Some(DisplayLine::Raw(DisplayRawLine::Origin { - path: path.to_string(), - pos: Some((row, col)), - header_type: display_header, - })); - } - } - if let Some(ref path) = slice.origin { - return Some(DisplayLine::Raw(DisplayRawLine::Origin { - path: path.to_string(), - pos: None, - header_type: display_header, - })); - } - None -} - -fn fold_body(body: &[DisplayLine]) -> Vec { - let mut new_body = vec![]; - - let mut no_annotation_lines_counter = 0; - let mut idx = 0; - - while idx < body.len() { - match body[idx] { - DisplayLine::Source { - line: DisplaySourceLine::Annotation { .. }, - ref inline_marks, - .. - } => { - if no_annotation_lines_counter > 2 { - let fold_start = idx - no_annotation_lines_counter; - let fold_end = idx; - let pre_len = if no_annotation_lines_counter > 8 { - 4 - } else { - 0 - }; - let post_len = if no_annotation_lines_counter > 8 { - 2 - } else { - 1 - }; - for item in body.iter().take(fold_start + pre_len).skip(fold_start) { - new_body.push(item.clone()); - } - new_body.push(DisplayLine::Fold { - inline_marks: inline_marks.clone(), - }); - for item in body.iter().take(fold_end).skip(fold_end - post_len) { - new_body.push(item.clone()); - } - } else { - let start = idx - no_annotation_lines_counter; - for item in body.iter().take(idx).skip(start) { - new_body.push(item.clone()); - } - } - no_annotation_lines_counter = 0; - } - DisplayLine::Source { .. } => { - no_annotation_lines_counter += 1; - idx += 1; - continue; - } - _ => { - no_annotation_lines_counter += 1; - } - } - new_body.push(body[idx].clone()); - idx += 1; - } - - new_body -} - -fn format_body(slice: &snippet::Slice, has_footer: bool) -> Vec { - let mut body = vec![]; - - let mut current_line = slice.line_start; - let mut current_index = 0; - let mut line_index_ranges = vec![]; - - for line in slice.source.lines() { - let line_length = line.chars().count() + 1; - let line_range = (current_index, current_index + line_length); - body.push(DisplayLine::Source { - lineno: Some(current_line), - inline_marks: vec![], - line: DisplaySourceLine::Content { - text: line.to_string(), - range: line_range, - }, - }); - line_index_ranges.push(line_range); - current_line += 1; - current_index += line_length + 1; - } - - let mut annotation_line_count = 0; - let mut annotations = slice.annotations.clone(); - for idx in 0..body.len() { - let (line_start, line_end) = line_index_ranges[idx]; - // It would be nice to use filter_drain here once it's stable. - annotations = annotations - .into_iter() - .filter(|annotation| { - let body_idx = idx + annotation_line_count; - let annotation_type = match annotation.annotation_type { - snippet::AnnotationType::Error => DisplayAnnotationType::None, - snippet::AnnotationType::Warning => DisplayAnnotationType::None, - _ => DisplayAnnotationType::from(annotation.annotation_type), - }; - match annotation.range { - (start, _) if start > line_end => true, - (start, end) if start >= line_start && end <= line_end + 1 => { - let range = (start - line_start, end - line_start); - body.insert( - body_idx + 1, - DisplayLine::Source { - lineno: None, - inline_marks: vec![], - line: DisplaySourceLine::Annotation { - annotation: Annotation { - annotation_type, - id: None, - label: format_label(Some(&annotation.label), None), - }, - range, - annotation_type: DisplayAnnotationType::from( - annotation.annotation_type, - ), - annotation_part: DisplayAnnotationPart::Standalone, - }, - }, - ); - annotation_line_count += 1; - false - } - (start, end) if start >= line_start && start <= line_end && end > line_end => { - if start - line_start == 0 { - if let DisplayLine::Source { - ref mut inline_marks, - .. - } = body[body_idx] - { - inline_marks.push(DisplayMark { - mark_type: DisplayMarkType::AnnotationStart, - annotation_type: DisplayAnnotationType::from( - annotation.annotation_type, - ), - }); - } - } else { - let range = (start - line_start, start - line_start + 1); - body.insert( - body_idx + 1, - DisplayLine::Source { - lineno: None, - inline_marks: vec![], - line: DisplaySourceLine::Annotation { - annotation: Annotation { - annotation_type: DisplayAnnotationType::None, - id: None, - label: vec![], - }, - range, - annotation_type: DisplayAnnotationType::from( - annotation.annotation_type, - ), - annotation_part: DisplayAnnotationPart::MultilineStart, - }, - }, - ); - annotation_line_count += 1; - } - true - } - (start, end) if start < line_start && end > line_end => { - if let DisplayLine::Source { - ref mut inline_marks, - .. - } = body[body_idx] - { - inline_marks.push(DisplayMark { - mark_type: DisplayMarkType::AnnotationThrough, - annotation_type: DisplayAnnotationType::from( - annotation.annotation_type, - ), - }); - } - true - } - (start, end) if start < line_start && end >= line_start && end <= line_end => { - if let DisplayLine::Source { - ref mut inline_marks, - .. - } = body[body_idx] - { - inline_marks.push(DisplayMark { - mark_type: DisplayMarkType::AnnotationThrough, - annotation_type: DisplayAnnotationType::from( - annotation.annotation_type, - ), - }); - } - let range = (end - line_start, end - line_start + 1); - body.insert( - body_idx + 1, - DisplayLine::Source { - lineno: None, - inline_marks: vec![DisplayMark { - mark_type: DisplayMarkType::AnnotationThrough, - annotation_type: DisplayAnnotationType::from( - annotation.annotation_type, - ), - }], - line: DisplaySourceLine::Annotation { - annotation: Annotation { - annotation_type, - id: None, - label: format_label(Some(&annotation.label), None), - }, - range, - annotation_type: DisplayAnnotationType::from( - annotation.annotation_type, - ), - annotation_part: DisplayAnnotationPart::MultilineEnd, - }, - }, - ); - annotation_line_count += 1; - false - } - _ => true, - } - }) - .collect(); - } - - if slice.fold { - body = fold_body(&body); - } - - body.insert( - 0, - DisplayLine::Source { - lineno: None, - inline_marks: vec![], - line: DisplaySourceLine::Empty, - }, - ); - if has_footer { - body.push(DisplayLine::Source { - lineno: None, - inline_marks: vec![], - line: DisplaySourceLine::Empty, - }); - } else if let Some(DisplayLine::Source { .. }) = body.last() { - body.push(DisplayLine::Source { - lineno: None, - inline_marks: vec![], - line: DisplaySourceLine::Empty, - }); - } - body -} - -impl From for DisplayList { - fn from(snippet: snippet::Snippet) -> Self { - let mut body = vec![]; - if let Some(annotation) = snippet.title { - body.push(format_title(&annotation)); - } - - for (idx, slice) in snippet.slices.iter().enumerate() { - body.append(&mut format_slice( - &slice, - idx == 0, - !snippet.footer.is_empty(), - )); - } - - for annotation in snippet.footer { - body.append(&mut format_annotation(&annotation)); - } - - Self { body } - } -} - -impl From for DisplayAnnotationType { - fn from(at: snippet::AnnotationType) -> Self { - match at { - snippet::AnnotationType::Error => DisplayAnnotationType::Error, - snippet::AnnotationType::Warning => DisplayAnnotationType::Warning, - snippet::AnnotationType::Info => DisplayAnnotationType::Info, - snippet::AnnotationType::Note => DisplayAnnotationType::Note, - snippet::AnnotationType::Help => DisplayAnnotationType::Help, - } - } -} diff --git a/src/display_list/line.rs b/src/display_list/line.rs new file mode 100644 index 0000000..f0774b7 --- /dev/null +++ b/src/display_list/line.rs @@ -0,0 +1,51 @@ +use super::annotation::Annotation; +use crate::annotation::AnnotationType; +use std::ops::Range; + +#[derive(Debug, Clone)] +pub enum DisplayLine<'d> { + Source { + lineno: Option, + inline_marks: Vec, + line: DisplaySourceLine<'d>, + }, + Raw(DisplayRawLine<'d>), +} + +#[derive(Debug, Clone)] +pub enum DisplaySourceLine<'d> { + Content { + text: &'d str, + }, + Annotation { + annotation: Annotation<'d>, + range: Range, + }, + Empty, +} + +#[derive(Debug, Clone)] +pub enum DisplayRawLine<'d> { + Origin { + path: &'d str, + pos: (Option, Option), + }, + Annotation { + annotation: Annotation<'d>, + source_aligned: bool, + continuation: bool, + }, +} + +#[derive(Debug, Clone)] +pub struct DisplayMark { + pub mark_type: DisplayMarkType, + pub annotation_type: AnnotationType, +} + +#[derive(Debug, Clone)] +pub enum DisplayMarkType { + AnnotationThrough, + AnnotationStart, + AnnotationEnd, +} diff --git a/src/display_list/list.rs b/src/display_list/list.rs new file mode 100644 index 0000000..3b18248 --- /dev/null +++ b/src/display_list/list.rs @@ -0,0 +1,147 @@ +use super::annotation::Annotation; +use super::line::{DisplayLine, DisplayMark, DisplayMarkType, DisplayRawLine, DisplaySourceLine}; +use crate::{Slice, Snippet, SourceAnnotation}; + +#[derive(Debug, Clone)] +pub struct DisplayList<'d> { + pub body: Vec>, +} + +fn get_header_pos(slice: &Slice) -> (Option, Option) { + let line = slice.line_start; + (line, None) +} + +impl<'d> From<&Snippet<'d>> for DisplayList<'d> { + fn from(snippet: &Snippet<'d>) -> Self { + let mut body = vec![]; + + if let Some(annotation) = &snippet.title { + let label = annotation.label.unwrap_or_default(); + body.push(DisplayLine::Raw(DisplayRawLine::Annotation { + annotation: Annotation { + annotation_type: annotation.annotation_type.clone(), + id: annotation.id, + label: &label, + }, + source_aligned: false, + continuation: false, + })); + } + + for slice in snippet.slices { + let slice_dl: DisplayList = slice.into(); + body.extend(slice_dl.body); + } + DisplayList { body } + } +} + +impl<'d> From<&Slice<'d>> for DisplayList<'d> { + fn from(slice: &Slice<'d>) -> Self { + let mut body = vec![]; + + if let Some(path) = slice.origin { + body.push(DisplayLine::Raw(DisplayRawLine::Origin { + path, + pos: get_header_pos(slice), + })); + } + + body.push(DisplayLine::Source { + lineno: None, + inline_marks: vec![], + line: DisplaySourceLine::Empty, + }); + + let mut annotations: Vec<&SourceAnnotation> = slice.annotations.iter().collect(); + + // let mut current_annotation = annotations.next(); + let mut line_start_pos = 0; + + let mut i = slice.line_start.unwrap_or(1); + for line in slice.source.lines() { + let line_range = line_start_pos..(line_start_pos + line.chars().count() + 1); + + let mut current_annotations = vec![]; + let mut inline_marks = vec![]; + + annotations.retain(|ann| { + if line_range.contains(&ann.range.start) && line_range.contains(&ann.range.end) { + // Annotation in this line + current_annotations.push(*ann); + false + } else if line_range.contains(&ann.range.start) + && !line_range.contains(&ann.range.end) + { + // Annotation starts in this line + inline_marks.push(DisplayMark { + mark_type: DisplayMarkType::AnnotationStart, + annotation_type: ann.annotation_type.clone(), + }); + true + } else if ann.range.start < line_range.start && ann.range.end > line_range.end { + // Annotation goes through this line + inline_marks.push(DisplayMark { + mark_type: DisplayMarkType::AnnotationThrough, + annotation_type: ann.annotation_type.clone(), + }); + true + } else if line_range.contains(&ann.range.end) { + // Annotation ends on this line + inline_marks.push(DisplayMark { + mark_type: DisplayMarkType::AnnotationThrough, + annotation_type: ann.annotation_type.clone(), + }); + current_annotations.push(*ann); + false + } else { + true + } + }); + + body.push(DisplayLine::Source { + lineno: Some(i), + inline_marks, + line: DisplaySourceLine::Content { text: line }, + }); + for ann in current_annotations { + let start = if ann.range.start >= line_start_pos { + ann.range.start - line_start_pos + } else { + 0 + }; + let inline_marks = if ann.range.start < line_start_pos { + vec![DisplayMark { + mark_type: DisplayMarkType::AnnotationEnd, + annotation_type: ann.annotation_type.clone(), + }] + } else { + vec![] + }; + body.push(DisplayLine::Source { + lineno: None, + inline_marks, + line: DisplaySourceLine::Annotation { + annotation: Annotation { + annotation_type: ann.annotation_type.clone(), + id: None, + label: ann.label, + }, + range: start..(ann.range.end - line_start_pos), + }, + }); + } + line_start_pos += line_range.len(); + i += 1; + } + + body.push(DisplayLine::Source { + lineno: None, + inline_marks: vec![], + line: DisplaySourceLine::Empty, + }); + + DisplayList { body } + } +} diff --git a/src/display_list/mod.rs b/src/display_list/mod.rs index 5e0b393..49ff81a 100644 --- a/src/display_list/mod.rs +++ b/src/display_list/mod.rs @@ -1,124 +1,5 @@ -//! display_list module stores the output model for the snippet. -//! -//! `DisplayList` is a central structure in the crate, which contains -//! the structured list of lines to be displayed. -//! -//! It is made of two types of lines: `Source` and `Raw`. All `Source` lines -//! are structured using four columns: -//! -//! ```text -//! /------------ (1) Line number column. -//! | /--------- (2) Line number column delimiter. -//! | | /------- (3) Inline marks column. -//! | | | /--- (4) Content column with the source and annotations for slices. -//! | | | | -//! ============================================================================= -//! error[E0308]: mismatched types -//! --> src/format.rs:51:5 -//! | -//! 151 | / fn test() -> String { -//! 152 | | return "test"; -//! 153 | | } -//! | |___^ error: expected `String`, for `&str`. -//! | -//! ``` -//! -//! The first two lines of the example above are `Raw` lines, while the rest -//! are `Source` lines. -//! -//! `DisplayList` does not store column alignment information, and those are -//! only calculated by the `DisplayListFormatter` using information such as -//! styling. -//! -//! The above snippet has been built out of the following structure: -//! -//! ``` -//! use annotate_snippets::display_list::*; -//! -//! let dl = DisplayList { -//! body: vec![ -//! DisplayLine::Raw(DisplayRawLine::Annotation { -//! annotation: Annotation { -//! annotation_type: DisplayAnnotationType::Error, -//! id: Some("E0308".to_string()), -//! label: vec![ -//! DisplayTextFragment { -//! content: "mismatched types".to_string(), -//! style: DisplayTextStyle::Regular, -//! } -//! ] -//! }, -//! source_aligned: false, -//! continuation: false, -//! }), -//! DisplayLine::Raw(DisplayRawLine::Origin { -//! path: "src/format.rs".to_string(), -//! pos: Some((51, 5)), -//! header_type: DisplayHeaderType::Initial, -//! }), -//! DisplayLine::Source { -//! lineno: Some(151), -//! inline_marks: vec![ -//! DisplayMark { -//! mark_type: DisplayMarkType::AnnotationStart, -//! annotation_type: DisplayAnnotationType::Error, -//! } -//! ], -//! line: DisplaySourceLine::Content { -//! text: " fn test() -> String {".to_string(), -//! range: (0, 24) -//! } -//! }, -//! DisplayLine::Source { -//! lineno: Some(152), -//! inline_marks: vec![ -//! DisplayMark { -//! mark_type: DisplayMarkType::AnnotationThrough, -//! annotation_type: DisplayAnnotationType::Error, -//! } -//! ], -//! line: DisplaySourceLine::Content { -//! text: " return \"test\";".to_string(), -//! range: (25, 46) -//! } -//! }, -//! DisplayLine::Source { -//! lineno: Some(153), -//! inline_marks: vec![ -//! DisplayMark { -//! mark_type: DisplayMarkType::AnnotationThrough, -//! annotation_type: DisplayAnnotationType::Error, -//! } -//! ], -//! line: DisplaySourceLine::Content { -//! text: " }".to_string(), -//! range: (47, 51) -//! } -//! }, -//! DisplayLine::Source { -//! lineno: None, -//! inline_marks: vec![], -//! line: DisplaySourceLine::Annotation { -//! annotation: Annotation { -//! annotation_type: DisplayAnnotationType::Error, -//! id: None, -//! label: vec![ -//! DisplayTextFragment { -//! content: "expected `String`, for `&str`.".to_string(), -//! style: DisplayTextStyle::Regular, -//! } -//! ] -//! }, -//! range: (3, 4), -//! annotation_type: DisplayAnnotationType::Error, -//! annotation_part: DisplayAnnotationPart::MultilineEnd, -//! } -//! -//! } -//! ] -//! }; -//! ``` -mod from_snippet; -mod structs; +pub mod annotation; +pub mod line; +pub mod list; -pub use self::structs::*; +pub use list::DisplayList; diff --git a/src/display_list/structs.rs b/src/display_list/structs.rs deleted file mode 100644 index 0d3e0bc..0000000 --- a/src/display_list/structs.rs +++ /dev/null @@ -1,253 +0,0 @@ -/// List of lines to be displayed. -#[derive(Debug, Clone, PartialEq)] -pub struct DisplayList { - pub body: Vec, -} - -impl From> for DisplayList { - fn from(body: Vec) -> Self { - Self { body } - } -} - -/// Inline annotation which can be used in either Raw or Source line. -#[derive(Debug, Clone, PartialEq)] -pub struct Annotation { - pub annotation_type: DisplayAnnotationType, - pub id: Option, - pub label: Vec, -} - -/// A single line used in `DisplayList`. -#[derive(Debug, Clone, PartialEq)] -pub enum DisplayLine { - /// A line with `lineno` portion of the slice. - Source { - lineno: Option, - inline_marks: Vec, - line: DisplaySourceLine, - }, - - /// A line indicating a folded part of the slice. - Fold { inline_marks: Vec }, - - /// A line which is displayed outside of slices. - Raw(DisplayRawLine), -} - -/// A source line. -#[derive(Debug, Clone, PartialEq)] -pub enum DisplaySourceLine { - /// A line with the content of the Slice. - Content { - text: String, - range: (usize, usize), // meta information for annotation placement. - }, - - /// An annotation line which is displayed in context of the slice. - Annotation { - annotation: Annotation, - range: (usize, usize), - annotation_type: DisplayAnnotationType, - annotation_part: DisplayAnnotationPart, - }, - - /// An empty source line. - Empty, -} - -/// Raw line - a line which does not have the `lineno` part and is not considered -/// a part of the snippet. -#[derive(Debug, Clone, PartialEq)] -pub enum DisplayRawLine { - /// A line which provides information about the location of the given - /// slice in the project structure. - Origin { - path: String, - pos: Option<(usize, usize)>, - header_type: DisplayHeaderType, - }, - - /// An annotation line which is not part of any snippet. - Annotation { - annotation: Annotation, - - /// If set to `true`, the annotation will be aligned to the - /// lineno delimiter of the snippet. - source_aligned: bool, - /// If set to `true`, only the label of the `Annotation` will be - /// displayed. It allows for a multiline annotation to be aligned - /// without displaing the meta information (`type` and `id`) to be - /// displayed on each line. - continuation: bool, - }, -} - -/// An inline text fragment which any label is composed of. -#[derive(Debug, Clone, PartialEq)] -pub struct DisplayTextFragment { - pub content: String, - pub style: DisplayTextStyle, -} - -/// A style for the `DisplayTextFragment` which can be visually formatted. -/// -/// This information may be used to emphasis parts of the label. -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum DisplayTextStyle { - Regular, - Emphasis, -} - -/// An indicator of what part of the annotation a given `Annotation` is. -#[derive(Debug, Clone, PartialEq)] -pub enum DisplayAnnotationPart { - /// A standalone, single-line annotation. - Standalone, - /// A continuation of a multi-line label of an annotation. - LabelContinuation, - /// A consequitive annotation in case multiple annotations annotate a single line. - Consequitive, - /// A line starting a multiline annotation. - MultilineStart, - /// A line ending a multiline annotation. - MultilineEnd, -} - -/// A visual mark used in `inline_marks` field of the `DisplaySourceLine`. -#[derive(Debug, Clone, PartialEq)] -pub struct DisplayMark { - pub mark_type: DisplayMarkType, - pub annotation_type: DisplayAnnotationType, -} - -/// A type of the `DisplayMark`. -#[derive(Debug, Clone, PartialEq)] -pub enum DisplayMarkType { - /// A mark indicating a multiline annotation going through the current line. - /// - /// Example: - /// ``` - /// use annotate_snippets::display_list::*; - /// use annotate_snippets::formatter::DisplayListFormatter; - /// - /// let dlf = DisplayListFormatter::new(false, false); // Don't use colors - /// - /// let dl = DisplayList { - /// body: vec![ - /// DisplayLine::Source { - /// lineno: Some(51), - /// inline_marks: vec![ - /// DisplayMark { - /// mark_type: DisplayMarkType::AnnotationThrough, - /// annotation_type: DisplayAnnotationType::Error, - /// } - /// ], - /// line: DisplaySourceLine::Content { - /// text: "Example".to_string(), - /// range: (0, 7), - /// } - /// } - /// ] - /// }; - /// assert_eq!(dlf.format(&dl), "51 | | Example"); - /// ``` - AnnotationThrough, - - /// A mark indicating a multiline annotation starting on the given line. - /// - /// Example: - /// ``` - /// use annotate_snippets::display_list::*; - /// use annotate_snippets::formatter::DisplayListFormatter; - /// - /// let dlf = DisplayListFormatter::new(false, false); // Don't use colors - /// - /// let dl = DisplayList { - /// body: vec![ - /// DisplayLine::Source { - /// lineno: Some(51), - /// inline_marks: vec![ - /// DisplayMark { - /// mark_type: DisplayMarkType::AnnotationStart, - /// annotation_type: DisplayAnnotationType::Error, - /// } - /// ], - /// line: DisplaySourceLine::Content { - /// text: "Example".to_string(), - /// range: (0, 7), - /// } - /// } - /// ] - /// }; - /// assert_eq!(dlf.format(&dl), "51 | / Example"); - /// ``` - AnnotationStart, -} - -/// A type of the `Annotation` which may impact the sigils, style or text displayed. -/// -/// There are several ways in which the `DisplayListFormatter` uses this information -/// when formatting the `DisplayList`: -/// -/// * An annotation may display the name of the type like `error` or `info`. -/// * An underline for `Error` may be `^^^` while for `Warning` it coule be `---`. -/// * `ColorStylesheet` may use different colors for different annotations. -#[derive(Debug, Clone, PartialEq)] -pub enum DisplayAnnotationType { - None, - Error, - Warning, - Info, - Note, - Help, -} - -/// Information whether the header is the initial one or a consequitive one -/// for multi-slice cases. -#[derive(Debug, Clone, PartialEq)] -pub enum DisplayHeaderType { - /// Initial header is the first header in the snippet. - /// - /// Example: - /// ``` - /// use annotate_snippets::display_list::*; - /// use annotate_snippets::formatter::DisplayListFormatter; - /// - /// let dlf = DisplayListFormatter::new(false, false); // Don't use colors - /// - /// let dl = DisplayList { - /// body: vec![ - /// DisplayLine::Raw(DisplayRawLine::Origin { - /// path: "file1.rs".to_string(), - /// pos: Some((51, 5)), - /// header_type: DisplayHeaderType::Initial, - /// }) - /// ] - /// }; - /// assert_eq!(dlf.format(&dl), "--> file1.rs:51:5"); - /// ``` - Initial, - - /// Continuation marks all headers of following slices in the snippet. - /// - /// Example: - /// ``` - /// use annotate_snippets::display_list::*; - /// use annotate_snippets::formatter::DisplayListFormatter; - /// - /// let dlf = DisplayListFormatter::new(false, false); // Don't use colors - /// - /// let dl = DisplayList { - /// body: vec![ - /// DisplayLine::Raw(DisplayRawLine::Origin { - /// path: "file1.rs".to_string(), - /// pos: Some((51, 5)), - /// header_type: DisplayHeaderType::Continuation, - /// }) - /// ] - /// }; - /// assert_eq!(dlf.format(&dl), "::: file1.rs:51:5"); - /// ``` - Continuation, -} diff --git a/src/formatter/mod.rs b/src/formatter/mod.rs deleted file mode 100644 index b10a5e6..0000000 --- a/src/formatter/mod.rs +++ /dev/null @@ -1,367 +0,0 @@ -//! DisplayListFormatter is a module handling the formatting of a -//! `DisplayList` into a formatted string. -//! -//! Besides formatting into a string it also uses a `style::Stylesheet` to -//! provide additional styling like colors and emphasis to the text. - -pub mod style; - -use self::style::{Style, StyleClass, Stylesheet}; -use crate::display_list::*; -use std::cmp; - -#[cfg(feature = "ansi_term")] -use crate::stylesheets::color::AnsiTermStylesheet; -use crate::stylesheets::no_color::NoColorStylesheet; - -fn repeat_char(c: char, n: usize) -> String { - let mut s = String::with_capacity(c.len_utf8()); - s.push(c); - s.repeat(n) -} - -/// DisplayListFormatter' constructor accepts two arguments: -/// -/// * `color` allows the formatter to optionally apply colors and emphasis -/// using the `ansi_term` crate. -/// * `anonymized_line_numbers` will replace line numbers in the left column with the text `LL`. -/// -/// Example: -/// -/// ``` -/// use annotate_snippets::formatter::DisplayListFormatter; -/// use annotate_snippets::display_list::{DisplayList, DisplayLine, DisplaySourceLine}; -/// -/// let dlf = DisplayListFormatter::new(false, false); // Don't use colors, Don't anonymize line numbers -/// -/// let dl = DisplayList { -/// body: vec![ -/// DisplayLine::Source { -/// lineno: Some(192), -/// inline_marks: vec![], -/// line: DisplaySourceLine::Content { -/// text: "Example line of text".into(), -/// range: (0, 21) -/// } -/// } -/// ] -/// }; -/// assert_eq!(dlf.format(&dl), "192 | Example line of text"); -/// ``` -pub struct DisplayListFormatter { - stylesheet: Box, - anonymized_line_numbers: bool, -} - -impl DisplayListFormatter { - const ANONYMIZED_LINE_NUM: &'static str = "LL"; - - /// Constructor for the struct. - /// - /// The argument `color` selects the stylesheet depending on the user preferences and - /// `ansi_term` crate availability. - /// - /// The argument `anonymized_line_numbers` will replace line numbers in the left column with - /// the text `LL`. This can be useful to enable when running UI tests, such as in the Rust - /// test suite. - pub fn new(color: bool, anonymized_line_numbers: bool) -> Self { - if color { - Self { - #[cfg(feature = "ansi_term")] - stylesheet: Box::new(AnsiTermStylesheet {}), - #[cfg(not(feature = "ansi_term"))] - stylesheet: Box::new(NoColorStylesheet {}), - anonymized_line_numbers, - } - } else { - Self { - stylesheet: Box::new(NoColorStylesheet {}), - anonymized_line_numbers, - } - } - } - - /// Formats a `DisplayList` into a String. - pub fn format(&self, dl: &DisplayList) -> String { - let lineno_width = dl.body.iter().fold(0, |max, line| match line { - DisplayLine::Source { - lineno: Some(lineno), - .. - } => { - if self.anonymized_line_numbers { - Self::ANONYMIZED_LINE_NUM.len() - } else { - cmp::max(lineno.to_string().len(), max) - } - } - _ => max, - }); - let inline_marks_width = dl.body.iter().fold(0, |max, line| match line { - DisplayLine::Source { inline_marks, .. } => cmp::max(inline_marks.len(), max), - _ => max, - }); - - dl.body - .iter() - .map(|line| self.format_line(line, lineno_width, inline_marks_width)) - .collect::>() - .join("\n") - } - - fn format_annotation_type(&self, annotation_type: &DisplayAnnotationType) -> &'static str { - match annotation_type { - DisplayAnnotationType::Error => "error", - DisplayAnnotationType::Warning => "warning", - DisplayAnnotationType::Info => "info", - DisplayAnnotationType::Note => "note", - DisplayAnnotationType::Help => "help", - DisplayAnnotationType::None => "", - } - } - - fn get_annotation_style(&self, annotation_type: &DisplayAnnotationType) -> Box { - self.stylesheet.get_style(match annotation_type { - DisplayAnnotationType::Error => StyleClass::Error, - DisplayAnnotationType::Warning => StyleClass::Warning, - DisplayAnnotationType::Info => StyleClass::Info, - DisplayAnnotationType::Note => StyleClass::Note, - DisplayAnnotationType::Help => StyleClass::Help, - DisplayAnnotationType::None => StyleClass::None, - }) - } - - fn format_label(&self, label: &[DisplayTextFragment]) -> String { - let emphasis_style = self.stylesheet.get_style(StyleClass::Emphasis); - label - .iter() - .map(|fragment| match fragment.style { - DisplayTextStyle::Regular => fragment.content.clone(), - DisplayTextStyle::Emphasis => emphasis_style.paint(&fragment.content), - }) - .collect::>() - .join("") - } - - fn format_annotation( - &self, - annotation: &Annotation, - continuation: bool, - in_source: bool, - ) -> String { - let color = self.get_annotation_style(&annotation.annotation_type); - let formatted_type = if let Some(ref id) = annotation.id { - format!( - "{}[{}]", - self.format_annotation_type(&annotation.annotation_type), - id - ) - } else { - self.format_annotation_type(&annotation.annotation_type) - .to_string() - }; - let label = self.format_label(&annotation.label); - - let label_part = if label.is_empty() { - "".to_string() - } else if in_source { - color.paint(&format!(": {}", self.format_label(&annotation.label))) - } else { - format!(": {}", self.format_label(&annotation.label)) - }; - if continuation { - let indent = formatted_type.len() + 2; - return format!("{}{}", repeat_char(' ', indent), label); - } - if !formatted_type.is_empty() { - format!("{}{}", color.paint(&formatted_type), label_part) - } else { - label - } - } - - fn format_source_line(&self, line: &DisplaySourceLine) -> Option { - match line { - DisplaySourceLine::Empty => None, - DisplaySourceLine::Content { text, .. } => Some(format!(" {}", text)), - DisplaySourceLine::Annotation { - range, - annotation, - annotation_type, - annotation_part, - } => { - let indent_char = match annotation_part { - DisplayAnnotationPart::Standalone => ' ', - DisplayAnnotationPart::LabelContinuation => ' ', - DisplayAnnotationPart::Consequitive => ' ', - DisplayAnnotationPart::MultilineStart => '_', - DisplayAnnotationPart::MultilineEnd => '_', - }; - let mark = match annotation_type { - DisplayAnnotationType::Error => '^', - DisplayAnnotationType::Warning => '-', - DisplayAnnotationType::Info => '-', - DisplayAnnotationType::Note => '-', - DisplayAnnotationType::Help => '-', - DisplayAnnotationType::None => ' ', - }; - let color = self.get_annotation_style(annotation_type); - let indent_length = match annotation_part { - DisplayAnnotationPart::LabelContinuation => range.1, - DisplayAnnotationPart::Consequitive => range.1, - _ => range.0, - }; - let indent = color.paint(&repeat_char(indent_char, indent_length + 1)); - let marks = color.paint(&repeat_char(mark, range.1 - indent_length)); - let annotation = self.format_annotation( - annotation, - annotation_part == &DisplayAnnotationPart::LabelContinuation, - true, - ); - if annotation.is_empty() { - return Some(format!("{}{}", indent, marks)); - } - Some(format!("{}{} {}", indent, marks, color.paint(&annotation))) - } - } - } - - fn format_lineno(&self, lineno: Option, lineno_width: usize) -> String { - match lineno { - Some(n) => format!("{:>width$}", n, width = lineno_width), - None => repeat_char(' ', lineno_width), - } - } - - fn format_raw_line(&self, line: &DisplayRawLine, lineno_width: usize) -> String { - match line { - DisplayRawLine::Origin { - path, - pos, - header_type, - } => { - let header_sigil = match header_type { - DisplayHeaderType::Initial => "-->", - DisplayHeaderType::Continuation => ":::", - }; - let lineno_color = self.stylesheet.get_style(StyleClass::LineNo); - - if let Some((col, row)) = pos { - format!( - "{}{} {}:{}:{}", - repeat_char(' ', lineno_width), - lineno_color.paint(header_sigil), - path, - col, - row - ) - } else { - format!( - "{}{} {}", - repeat_char(' ', lineno_width), - lineno_color.paint(header_sigil), - path - ) - } - } - DisplayRawLine::Annotation { - annotation, - source_aligned, - continuation, - } => { - if *source_aligned { - if *continuation { - format!( - "{}{}", - repeat_char(' ', lineno_width + 3), - self.format_annotation(annotation, *continuation, false) - ) - } else { - let lineno_color = self.stylesheet.get_style(StyleClass::LineNo); - format!( - "{} {} {}", - repeat_char(' ', lineno_width), - lineno_color.paint("="), - self.format_annotation(annotation, *continuation, false) - ) - } - } else { - self.format_annotation(annotation, *continuation, false) - } - } - } - } - - fn format_line( - &self, - dl: &DisplayLine, - lineno_width: usize, - inline_marks_width: usize, - ) -> String { - match dl { - DisplayLine::Source { - lineno, - inline_marks, - line, - } => { - let lineno = if self.anonymized_line_numbers && lineno.is_some() { - Self::ANONYMIZED_LINE_NUM.to_string() - } else { - self.format_lineno(*lineno, lineno_width) - }; - let marks = self.format_inline_marks(inline_marks, inline_marks_width); - let lf = self.format_source_line(line); - let lineno_color = self.stylesheet.get_style(StyleClass::LineNo); - - let mut prefix = lineno_color.paint(&format!("{} |", lineno)); - - match lf { - Some(lf) => { - if !marks.is_empty() { - prefix.push_str(&format!(" {}", marks)); - } - format!("{}{}", prefix, lf) - } - None => { - if !marks.trim().is_empty() { - prefix.push_str(&format!(" {}", marks)); - } - prefix - } - } - } - DisplayLine::Fold { inline_marks } => { - let marks = self.format_inline_marks(inline_marks, inline_marks_width); - let indent = lineno_width; - if marks.trim().is_empty() { - String::from("...") - } else { - format!("...{}{}", repeat_char(' ', indent), marks) - } - } - DisplayLine::Raw(line) => self.format_raw_line(line, lineno_width), - } - } - - fn format_inline_marks( - &self, - inline_marks: &[DisplayMark], - inline_marks_width: usize, - ) -> String { - format!( - "{}{}", - " ".repeat(inline_marks_width - inline_marks.len()), - inline_marks - .iter() - .map(|mark| { - let sigil = match mark.mark_type { - DisplayMarkType::AnnotationThrough => "|", - DisplayMarkType::AnnotationStart => "/", - }; - let color = self.get_annotation_style(&mark.annotation_type); - color.paint(sigil) - }) - .collect::>() - .join(""), - ) - } -} diff --git a/src/formatter/style.rs b/src/formatter/style.rs deleted file mode 100644 index c1b9046..0000000 --- a/src/formatter/style.rs +++ /dev/null @@ -1,98 +0,0 @@ -//! Set of structures required to implement a stylesheet for -//! [DisplayListFormatter](super::DisplayListFormatter). -//! -//! In order to provide additional styling information for the -//! formatter, a structs can implement `Stylesheet` and `Style` -//! traits. -//! -//! Example: -//! -//! ``` -//! use annotate_snippets::formatter::style::{Stylesheet, StyleClass, Style}; -//! -//! struct HTMLStyle { -//! prefix: String, -//! postfix: String, -//! }; -//! -//! impl HTMLStyle { -//! fn new(prefix: &str, postfix: &str) -> Self { -//! HTMLStyle { -//! prefix: prefix.into(), -//! postfix: postfix.into() -//! } -//! } -//! }; -//! -//! impl Style for HTMLStyle { -//! fn paint(&self, text: &str) -> String { -//! format!("{}{}{}", self.prefix, text, self.postfix) -//! } -//! -//! fn bold(&self) -> Box
")
+    }
+
+    fn fmt_footer(&self, w: &mut impl std::io::Write) -> std::io::Result<()> {
+        write!(w, "
") + } +} + +impl RendererTrait for Renderer { + fn fmt(&self, w: &mut impl Write, dl: &DisplayList) -> std::io::Result<()> { + Renderer::fmt(self, w, dl) + } +} diff --git a/src/renderers/mod.rs b/src/renderers/mod.rs new file mode 100644 index 0000000..b37cd72 --- /dev/null +++ b/src/renderers/mod.rs @@ -0,0 +1,21 @@ +#[cfg(not(feature = "html"))] +pub mod ascii_default; +#[cfg(feature = "html")] +pub mod html; + +#[cfg(feature = "html")] +use html::get_renderer as get_type_renderer; + +#[cfg(not(feature = "html"))] +use ascii_default::get_renderer as get_type_renderer; + +use crate::DisplayList; +use std::io::Write; + +pub trait Renderer { + fn fmt(&self, w: &mut impl Write, dl: &DisplayList) -> std::io::Result<()>; +} + +pub fn get_renderer() -> impl Renderer { + get_type_renderer() +} diff --git a/src/slice.rs b/src/slice.rs new file mode 100644 index 0000000..23a58d0 --- /dev/null +++ b/src/slice.rs @@ -0,0 +1,9 @@ +use crate::annotation::SourceAnnotation; + +#[derive(Debug, Clone, Default)] +pub struct Slice<'s> { + pub source: &'s str, + pub line_start: Option, + pub origin: Option<&'s str>, + pub annotations: &'s [SourceAnnotation<'s>], +} diff --git a/src/snippet.rs b/src/snippet.rs index 6566b0f..3be7c34 100644 --- a/src/snippet.rs +++ b/src/snippet.rs @@ -1,81 +1,9 @@ -//! Structures used as an input for the library. -//! -//! Example: -//! -//! ``` -//! use annotate_snippets::snippet::*; -//! -//! Snippet { -//! title: Some(Annotation { -//! label: Some("mismatched types".to_string()), -//! id: None, -//! annotation_type: AnnotationType::Error, -//! }), -//! footer: vec![], -//! slices: vec![ -//! Slice { -//! source: "Foo".to_string(), -//! line_start: 51, -//! origin: Some("src/format.rs".to_string()), -//! fold: false, -//! annotations: vec![], -//! }, -//! Slice { -//! source: "Faa".to_string(), -//! line_start: 129, -//! origin: Some("src/display.rs".to_string()), -//! fold: false, -//! annotations: vec![], -//! }, -//! ], -//! }; -//! ``` -/// Primary structure provided for formatting -#[derive(Debug, Clone)] -pub struct Snippet { - pub title: Option, - pub footer: Vec, - pub slices: Vec, -} - -/// Structure containing the slice of text to be annotated and -/// basic information about the location of the slice. -#[derive(Debug, Clone)] -pub struct Slice { - pub source: String, - pub line_start: usize, - pub origin: Option, - pub annotations: Vec, - /// If set explicitly to `true`, the snippet will fold - /// parts of the slice that don't contain any annotations. - pub fold: bool, -} - -/// Types of annotations. -#[derive(Debug, Clone, Copy)] -pub enum AnnotationType { - /// Error annotations are displayed using red color and "^" character. - Error, - /// Warning annotations are displayed using blue color and "-" character. - Warning, - Info, - Note, - Help, -} - -/// An annotation for a `Slice`. -#[derive(Debug, Clone)] -pub struct SourceAnnotation { - pub range: (usize, usize), - pub label: String, - pub annotation_type: AnnotationType, -} +use crate::annotation::Annotation; +use crate::slice::Slice; -/// An annotation for a `Snippet`. #[derive(Debug, Clone)] -pub struct Annotation { - /// Identifier of the annotation. Usually error code like "E0308". - pub id: Option, - pub label: Option, - pub annotation_type: AnnotationType, +pub struct Snippet<'s> { + pub title: Option>, + pub footer: &'s [Annotation<'s>], + pub slices: &'s [Slice<'s>], } diff --git a/src/stylesheets/color.rs b/src/stylesheets/color.rs deleted file mode 100644 index f0a5d9c..0000000 --- a/src/stylesheets/color.rs +++ /dev/null @@ -1,43 +0,0 @@ -use crate::formatter::style::{Style, StyleClass, Stylesheet}; - -use ansi_term::Color::Fixed; -use ansi_term::Style as AnsiTermStyle; - -struct AnsiTermStyleWrapper { - style: AnsiTermStyle, -} - -impl Style for AnsiTermStyleWrapper { - fn paint(&self, text: &str) -> String { - format!("{}", self.style.paint(text)) - } - - fn bold(&self) -> Box { - Box::new(AnsiTermStyleWrapper { - style: self.style.clone(), - }) - } -} - -pub struct AnsiTermStylesheet {} - -impl Stylesheet for AnsiTermStylesheet { - fn get_style(&self, class: StyleClass) -> Box { - let ansi_term_style = match class { - StyleClass::Error => Fixed(9).bold(), - StyleClass::Warning => Fixed(11).bold(), - StyleClass::Info => Fixed(12).bold(), - StyleClass::Note => AnsiTermStyle::new().bold(), - StyleClass::Help => Fixed(14).bold(), - - StyleClass::LineNo => Fixed(12).bold(), - - StyleClass::Emphasis => AnsiTermStyle::new().bold(), - - StyleClass::None => AnsiTermStyle::new(), - }; - Box::new(AnsiTermStyleWrapper { - style: ansi_term_style, - }) - } -} diff --git a/src/stylesheets/mod.rs b/src/stylesheets/mod.rs deleted file mode 100644 index 40aef52..0000000 --- a/src/stylesheets/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -//! List of stylesheets that can be used by the `DisplayListFormatter`. -//! -//! The list depends on what optional dependencies the crate has been -//! compiled with. -//! -//! By default the `no_color` is available. If the crate gets compiled -//! with `ansi_term`, the `color` stylesheet is added. - -#[cfg(feature = "ansi_term")] -pub mod color; -pub mod no_color; diff --git a/src/stylesheets/no_color.rs b/src/stylesheets/no_color.rs deleted file mode 100644 index 608e4e5..0000000 --- a/src/stylesheets/no_color.rs +++ /dev/null @@ -1,21 +0,0 @@ -use crate::formatter::style::{Style, StyleClass, Stylesheet}; - -pub struct NoOpStyle {} - -impl Style for NoOpStyle { - fn paint(&self, text: &str) -> String { - text.to_string() - } - - fn bold(&self) -> Box { - Box::new(NoOpStyle {}) - } -} - -pub struct NoColorStylesheet {} - -impl Stylesheet for NoColorStylesheet { - fn get_style(&self, _class: StyleClass) -> Box { - Box::new(NoOpStyle {}) - } -} diff --git a/tests/diff/mod.rs b/tests/diff/mod.rs deleted file mode 100644 index 7032e71..0000000 --- a/tests/diff/mod.rs +++ /dev/null @@ -1,43 +0,0 @@ -use ansi_term::Color::{Black, Green, Red}; -use difference::{Changeset, Difference}; - -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())); - } - } - } - return output; -} diff --git a/tests/dl_from_snippet.rs b/tests/dl_from_snippet.rs deleted file mode 100644 index 2eb0313..0000000 --- a/tests/dl_from_snippet.rs +++ /dev/null @@ -1,256 +0,0 @@ -use annotate_snippets::display_list as dl; -use annotate_snippets::snippet; - -#[test] -fn test_format_title() { - let input = snippet::Snippet { - title: Some(snippet::Annotation { - id: Some("E0001".to_string()), - label: Some("This is a title".to_string()), - annotation_type: snippet::AnnotationType::Error, - }), - footer: vec![], - slices: vec![], - }; - let output = dl::DisplayList { - body: vec![dl::DisplayLine::Raw(dl::DisplayRawLine::Annotation { - annotation: dl::Annotation { - annotation_type: dl::DisplayAnnotationType::Error, - id: Some("E0001".to_string()), - label: vec![dl::DisplayTextFragment { - content: "This is a title".to_string(), - style: dl::DisplayTextStyle::Emphasis, - }], - }, - source_aligned: false, - continuation: false, - })], - }; - assert_eq!(dl::DisplayList::from(input), output); -} - -#[test] -fn test_format_slice() { - let input = snippet::Snippet { - title: None, - footer: vec![], - slices: vec![snippet::Slice { - source: "This is line 1\nThis is line 2".to_string(), - line_start: 5402, - origin: None, - annotations: vec![], - fold: false, - }], - }; - 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: "This is line 1".to_string(), - range: (0, 15), - }, - }, - dl::DisplayLine::Source { - lineno: Some(5403), - inline_marks: vec![], - line: dl::DisplaySourceLine::Content { - text: "This is line 2".to_string(), - range: (16, 31), - }, - }, - dl::DisplayLine::Source { - lineno: None, - inline_marks: vec![], - line: dl::DisplaySourceLine::Empty, - }, - ], - }; - assert_eq!(dl::DisplayList::from(input), output); -} - -#[test] -fn test_format_slices_continuation() { - let input = snippet::Snippet { - title: None, - footer: vec![], - slices: vec![ - snippet::Slice { - source: "This is slice 1".to_string(), - line_start: 5402, - origin: Some("file1.rs".to_string()), - annotations: vec![], - fold: false, - }, - snippet::Slice { - source: "This is slice 2".to_string(), - line_start: 2, - origin: Some("file2.rs".to_string()), - annotations: vec![], - fold: false, - }, - ], - }; - let output = dl::DisplayList { - body: vec![ - dl::DisplayLine::Raw(dl::DisplayRawLine::Origin { - path: "file1.rs".to_string(), - 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: "This is slice 1".to_string(), - range: (0, 16), - }, - }, - dl::DisplayLine::Source { - lineno: None, - inline_marks: vec![], - line: dl::DisplaySourceLine::Empty, - }, - dl::DisplayLine::Raw(dl::DisplayRawLine::Origin { - path: "file2.rs".to_string(), - 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: "This is slice 2".to_string(), - range: (0, 16), - }, - }, - dl::DisplayLine::Source { - lineno: None, - inline_marks: vec![], - line: dl::DisplaySourceLine::Empty, - }, - ], - }; - assert_eq!(dl::DisplayList::from(input), output); -} - -#[test] -fn test_format_slice_annotation_standalone() { - let input = snippet::Snippet { - title: None, - footer: vec![], - slices: vec![snippet::Slice { - source: "This is line 1\nThis is line 2".to_string(), - line_start: 5402, - origin: None, - annotations: vec![snippet::SourceAnnotation { - range: (22, 24), - label: "Test annotation".to_string(), - annotation_type: snippet::AnnotationType::Info, - }], - fold: false, - }], - }; - 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: "This is line 1".to_string(), - range: (0, 15), - }, - }, - dl::DisplayLine::Source { - lineno: Some(5403), - inline_marks: vec![], - line: dl::DisplaySourceLine::Content { - text: "This is line 2".to_string(), - range: (16, 31), - }, - }, - 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".to_string(), - style: dl::DisplayTextStyle::Regular, - }], - }, - range: (6, 8), - annotation_type: dl::DisplayAnnotationType::Info, - annotation_part: dl::DisplayAnnotationPart::Standalone, - }, - }, - dl::DisplayLine::Source { - lineno: None, - inline_marks: vec![], - line: dl::DisplaySourceLine::Empty, - }, - ], - }; - 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".to_string()), - annotation_type: snippet::AnnotationType::Error, - }], - slices: vec![], - }; - 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 ".to_string(), - style: dl::DisplayTextStyle::Regular, - }, - dl::DisplayTextFragment { - content: "is".to_string(), - style: dl::DisplayTextStyle::Emphasis, - }, - dl::DisplayTextFragment { - content: " a title".to_string(), - style: dl::DisplayTextStyle::Regular, - }, - ], - }, - source_aligned: true, - continuation: false, - })], - }; - assert_eq!(dl::DisplayList::from(input), output); -} diff --git a/tests/fixtures/no-color/multiline_annotation.txt b/tests/fixtures/no-color/multiline_annotation.txt deleted file mode 100644 index b900784..0000000 --- a/tests/fixtures/no-color/multiline_annotation.txt +++ /dev/null @@ -1,14 +0,0 @@ -error[E0308]: mismatched types - --> src/format.rs:51:5 - | -51 | ) -> Option { - | -------------- expected `std::option::Option` 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_annotation.yaml b/tests/fixtures/no-color/multiline_annotation.yaml deleted file mode 100644 index 6048e38..0000000 --- a/tests/fixtures/no-color/multiline_annotation.yaml +++ /dev/null @@ -1,38 +0,0 @@ -slices: - - source: |- - ) -> Option { - 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 - annotations: - - label: expected `std::option::Option` because of return type - annotation_type: Warning - range: [5, 19] - - label: expected enum `std::option::Option`, found () - annotation_type: Error - range: [23, 786] -title: - label: mismatched types - id: E0308 - annotation_type: Error diff --git a/tests/fixtures/no-color/multiline_annotation2.txt b/tests/fixtures/no-color/multiline_annotation2.txt deleted file mode 100644 index 5234ee8..0000000 --- a/tests/fixtures/no-color/multiline_annotation2.txt +++ /dev/null @@ -1,9 +0,0 @@ -error[E0027]: pattern does not mention fields `lineno`, `content` - --> src/display_list.rs:139:31 - | -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_annotation2.yaml b/tests/fixtures/no-color/multiline_annotation2.yaml deleted file mode 100644 index bdbdb4c..0000000 --- a/tests/fixtures/no-color/multiline_annotation2.yaml +++ /dev/null @@ -1,16 +0,0 @@ -slices: - - source: |1 - if let DisplayLine::Source { - ref mut inline_marks, - } = body[body_idx] - line_start: 139 - origin: "src/display_list.rs" - fold: false - annotations: - - label: missing fields `lineno`, `content` - annotation_type: Error - range: [31, 129] -title: - label: pattern does not mention fields `lineno`, `content` - id: E0027 - annotation_type: Error diff --git a/tests/fixtures/no-color/multiple_annotations.txt b/tests/fixtures/no-color/multiple_annotations.txt deleted file mode 100644 index 26c677f..0000000 --- a/tests/fixtures/no-color/multiple_annotations.txt +++ /dev/null @@ -1,14 +0,0 @@ - | - 96 | fn add_title_line(result: &mut Vec, 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/multiple_annotations.yaml b/tests/fixtures/no-color/multiple_annotations.yaml deleted file mode 100644 index 5bc71bb..0000000 --- a/tests/fixtures/no-color/multiple_annotations.yaml +++ /dev/null @@ -1,23 +0,0 @@ -slices: - - source: |- - fn add_title_line(result: &mut Vec, main_annotation: Option<&Annotation>) { - if let Some(annotation) = main_annotation { - result.push(format_title_line( - &annotation.annotation_type, - None, - &annotation.label, - )); - } - } - line_start: 96 - annotations: - - label: Variable defined here - annotation_type: Error - range: [101, 111] - - label: Referenced here - annotation_type: Error - range: [187, 197] - - label: Referenced again here - annotation_type: Error - range: [248, 258] -title: null diff --git a/tests/fixtures/no-color/simple.txt b/tests/fixtures/no-color/simple.txt deleted file mode 100644 index a5a3136..0000000 --- a/tests/fixtures/no-color/simple.txt +++ /dev/null @@ -1,9 +0,0 @@ -error: expected one of `.`, `;`, `?`, or an operator, found `for` - --> src/format_color.rs:171:8 - | -169 | }) - | - expected one of `.`, `;`, `?`, or an operator here -170 | -171 | for line in &self.body { - | ^^^ unexpected token - | diff --git a/tests/fixtures/no-color/simple.yaml b/tests/fixtures/no-color/simple.yaml deleted file mode 100644 index db3862c..0000000 --- a/tests/fixtures/no-color/simple.yaml +++ /dev/null @@ -1,17 +0,0 @@ -slices: - - source: |1 - }) - - for line in &self.body { - line_start: 169 - origin: src/format_color.rs - annotations: - - label: unexpected token - annotation_type: Error - range: [22, 25] - - 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_test.rs b/tests/fixtures_test.rs deleted file mode 100644 index 8f49242..0000000 --- a/tests/fixtures_test.rs +++ /dev/null @@ -1,57 +0,0 @@ -mod diff; -mod snippet; - -use crate::snippet::SnippetDef; -use annotate_snippets::display_list::DisplayList; -use annotate_snippets::formatter::DisplayListFormatter; -use annotate_snippets::snippet::Snippet; -use glob::glob; -use serde::Deserialize; -use std::error::Error; -use std::fs::File; -use std::io; -use std::io::prelude::*; -use std::path::Path; - -fn read_file(path: &str) -> Result { - 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>(path: P) -> Result> { - #[derive(Deserialize)] - struct Wrapper(#[serde(with = "SnippetDef")] Snippet); - - let file = File::open(path)?; - let u = serde_yaml::from_reader(file).map(|Wrapper(a)| a)?; - Ok(u) -} - -#[test] -fn test_fixtures() { - for entry in glob("./tests/fixtures/no-color/**/*.yaml").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(".yaml", ".txt"); - - let snippet = read_fixture(path_in).expect("Failed to read file"); - let expected_out = read_file(&path_out).expect("Failed to read file"); - - let dl = DisplayList::from(snippet); - let dlf = DisplayListFormatter::new(true, false); - let actual_out = dlf.format(&dl); - 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 deleted file mode 100644 index 9d224e9..0000000 --- a/tests/formatter.rs +++ /dev/null @@ -1,551 +0,0 @@ -use annotate_snippets::display_list::*; -use annotate_snippets::formatter::DisplayListFormatter; - -#[test] -fn test_source_empty() { - let dl = DisplayList::from(vec![DisplayLine::Source { - lineno: None, - inline_marks: vec![], - line: DisplaySourceLine::Empty, - }]); - - let dlf = DisplayListFormatter::new(false, false); - - assert_eq!(dlf.format(&dl), " |"); -} - -#[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".to_string(), - range: (0, 19), - }, - }, - DisplayLine::Source { - lineno: Some(57), - inline_marks: vec![], - line: DisplaySourceLine::Content { - text: "of content lines".to_string(), - range: (0, 19), - }, - }, - ]); - - let dlf = DisplayListFormatter::new(false, false); - - assert_eq!( - dlf.format(&dl), - "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: String::from("Example string"), - style: DisplayTextStyle::Regular, - }], - }, - annotation_type: DisplayAnnotationType::Error, - annotation_part: DisplayAnnotationPart::Standalone, - }, - }]); - - let dlf = DisplayListFormatter::new(false, false); - - assert_eq!(dlf.format(&dl), " | ^^^^^ 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: String::from("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: String::from("Second line"), - style: DisplayTextStyle::Regular, - }], - }, - annotation_type: DisplayAnnotationType::Warning, - annotation_part: DisplayAnnotationPart::LabelContinuation, - }, - }, - ]); - - let dlf = DisplayListFormatter::new(false, false); - - assert_eq!( - dlf.format(&dl), - " | ----- 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: String::from("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: String::from("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: String::from("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: String::from("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: String::from("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: String::from("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: String::from("This is an annotation of type none"), - style: DisplayTextStyle::Regular, - }], - }, - annotation_type: DisplayAnnotationType::None, - annotation_part: DisplayAnnotationPart::Standalone, - }, - }, - ]); - - let dlf = DisplayListFormatter::new(false, false); - - assert_eq!(dlf.format(&dl), " | ----- 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".to_string(), - 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".to_string(), - range: (0, 19), - }, - }, - ]); - - let dlf = DisplayListFormatter::new(false, false); - - assert_eq!( - dlf.format(&dl), - " 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".to_string(), - pos: None, - header_type: DisplayHeaderType::Initial, - })]); - - let dlf = DisplayListFormatter::new(false, false); - - assert_eq!(dlf.format(&dl), "--> src/test.rs"); -} - -#[test] -fn test_raw_origin_initial_pos() { - let dl = DisplayList::from(vec![DisplayLine::Raw(DisplayRawLine::Origin { - path: "src/test.rs".to_string(), - pos: Some((23, 15)), - header_type: DisplayHeaderType::Initial, - })]); - - let dlf = DisplayListFormatter::new(false, false); - - assert_eq!(dlf.format(&dl), "--> src/test.rs:23:15"); -} - -#[test] -fn test_raw_origin_continuation() { - let dl = DisplayList::from(vec![DisplayLine::Raw(DisplayRawLine::Origin { - path: "src/test.rs".to_string(), - pos: Some((23, 15)), - header_type: DisplayHeaderType::Continuation, - })]); - - let dlf = DisplayListFormatter::new(false, false); - - assert_eq!(dlf.format(&dl), "::: 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".to_string()), - label: vec![DisplayTextFragment { - content: String::from("This is an error"), - style: DisplayTextStyle::Regular, - }], - }, - source_aligned: false, - continuation: false, - })]); - - let dlf = DisplayListFormatter::new(false, false); - - assert_eq!(dlf.format(&dl), "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".to_string()), - label: vec![DisplayTextFragment { - content: String::from("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".to_string()), - label: vec![DisplayTextFragment { - content: String::from("Second line of the error"), - style: DisplayTextStyle::Regular, - }], - }, - source_aligned: false, - continuation: true, - }), - ]); - - let dlf = DisplayListFormatter::new(false, false); - - assert_eq!( - dlf.format(&dl), - "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".to_string()), - label: vec![DisplayTextFragment { - content: String::from("This is an error"), - style: DisplayTextStyle::Regular, - }], - }, - source_aligned: true, - continuation: false, - })]); - - let dlf = DisplayListFormatter::new(false, false); - - assert_eq!(dlf.format(&dl), " = 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".to_string()), - label: vec![DisplayTextFragment { - content: String::from("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".to_string()), - label: vec![DisplayTextFragment { - content: String::from("Second line of the error"), - style: DisplayTextStyle::Regular, - }], - }, - source_aligned: true, - continuation: true, - }), - ]); - - let dlf = DisplayListFormatter::new(false, false); - - assert_eq!( - dlf.format(&dl), - " = 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: String::from("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: String::from("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: String::from("Second line of none type annotation"), - style: DisplayTextStyle::Regular, - }], - }, - source_aligned: false, - continuation: true, - }), - ]); - - let dlf = DisplayListFormatter::new(false, false); - - assert_eq!( - dlf.format(&dl), - "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, - }]); - - let dlf = DisplayListFormatter::new(false, false); - - assert_eq!(dlf.format(&dl), " | |",); -} - -#[test] -fn test_anon_lines() { - let dl = DisplayList::from(vec![ - DisplayLine::Source { - lineno: Some(56), - inline_marks: vec![], - line: DisplaySourceLine::Content { - text: "This is an example".to_string(), - range: (0, 19), - }, - }, - DisplayLine::Source { - lineno: Some(57), - inline_marks: vec![], - line: DisplaySourceLine::Content { - text: "of content lines".to_string(), - 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".to_string(), - range: (0, 19), - }, - }, - ]); - - let dlf = DisplayListFormatter::new(false, true); - - assert_eq!( - dlf.format(&dl), - "LL | This is an example\nLL | of content lines\n |\n | abc" - ); -} - -#[test] -fn test_raw_origin_initial_pos_anon_lines() { - let dl = DisplayList::from(vec![DisplayLine::Raw(DisplayRawLine::Origin { - path: "src/test.rs".to_string(), - pos: Some((23, 15)), - header_type: DisplayHeaderType::Initial, - })]); - - let dlf = DisplayListFormatter::new(false, true); - - // Using anonymized_line_numbers should not affect the inital position - assert_eq!(dlf.format(&dl), "--> src/test.rs:23:15"); -} diff --git a/tests/snippet/mod.rs b/tests/snippet/mod.rs deleted file mode 100644 index 9ef8fdc..0000000 --- a/tests/snippet/mod.rs +++ /dev/null @@ -1,103 +0,0 @@ -use serde::{Deserialize, Deserializer, Serialize}; - -use annotate_snippets::snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation}; - -#[derive(Deserialize)] -#[serde(remote = "Snippet")] -pub struct SnippetDef { - #[serde(deserialize_with = "deserialize_annotation")] - #[serde(default)] - pub title: Option, - #[serde(deserialize_with = "deserialize_annotations")] - #[serde(default)] - pub footer: Vec, - #[serde(deserialize_with = "deserialize_slices")] - pub slices: Vec, -} - -fn deserialize_slices<'de, D>(deserializer: D) -> Result, D::Error> -where - D: Deserializer<'de>, -{ - #[derive(Deserialize)] - struct Wrapper(#[serde(with = "SliceDef")] Slice); - - let v = Vec::deserialize(deserializer)?; - Ok(v.into_iter().map(|Wrapper(a)| a).collect()) -} - -fn deserialize_annotation<'de, D>(deserializer: D) -> Result, D::Error> -where - D: Deserializer<'de>, -{ - #[derive(Deserialize)] - struct Wrapper(#[serde(with = "AnnotationDef")] Annotation); - - Option::::deserialize(deserializer) - .map(|opt_wrapped: Option| opt_wrapped.map(|wrapped: Wrapper| wrapped.0)) -} - -fn deserialize_annotations<'de, D>(deserializer: D) -> Result, D::Error> -where - D: Deserializer<'de>, -{ - #[derive(Deserialize)] - struct Wrapper(#[serde(with = "AnnotationDef")] Annotation); - - let v = Vec::deserialize(deserializer)?; - Ok(v.into_iter().map(|Wrapper(a)| a).collect()) -} - -#[derive(Deserialize)] -#[serde(remote = "Slice")] -pub struct SliceDef { - pub source: String, - pub line_start: usize, - pub origin: Option, - #[serde(deserialize_with = "deserialize_source_annotations")] - pub annotations: Vec, - #[serde(default)] - pub fold: bool, -} - -fn deserialize_source_annotations<'de, D>( - deserializer: D, -) -> Result, D::Error> -where - D: Deserializer<'de>, -{ - #[derive(Deserialize)] - struct Wrapper(#[serde(with = "SourceAnnotationDef")] SourceAnnotation); - - let v = Vec::deserialize(deserializer)?; - Ok(v.into_iter().map(|Wrapper(a)| a).collect()) -} - -#[derive(Serialize, Deserialize)] -#[serde(remote = "SourceAnnotation")] -pub struct SourceAnnotationDef { - pub range: (usize, usize), - pub label: String, - #[serde(with = "AnnotationTypeDef")] - pub annotation_type: AnnotationType, -} - -#[derive(Serialize, Deserialize)] -#[serde(remote = "Annotation")] -pub struct AnnotationDef { - pub id: Option, - pub label: Option, - #[serde(with = "AnnotationTypeDef")] - pub annotation_type: AnnotationType, -} - -#[allow(dead_code)] -#[derive(Serialize, Deserialize)] -#[serde(remote = "AnnotationType")] -enum AnnotationTypeDef { - Error, - Warning, - Info, - Note, - Help, -}