Initial import annotate-snippets 0.9.1

Test: TH
Bug: 288151817
Bug: 288129815
Change-Id: Ifea4948f004ee68598a2aeeee7aa080b446fa672
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
new file mode 100644
index 0000000..59e14f0
--- /dev/null
+++ b/.cargo_vcs_info.json
@@ -0,0 +1,5 @@
+{
+  "git": {
+    "sha1": "77bd1c8e03466ff290c15dc025eac13088c244ee"
+  }
+}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..6936990
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+/target
+**/*.rs.bk
+Cargo.lock
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..e186cc2
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,30 @@
+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
new file mode 100644
index 0000000..21e5f2f
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,39 @@
+# Changelog
+ 
+## Unreleased
+ 
+  - …
+
+## annotate-snippets 0.9.1 (September 4, 2021)
+
+  - Fix character split when strip code. (#37)
+  - Fix off by one error in multiline highlighting. (#42)
+  - Fix display of annotation for double width characters. (#46)
+
+## annotate-snippets 0.9.0 (June 28, 2020)
+
+  - Add strip code to the left and right of long lines. (#36)
+
+## annotate-snippets 0.8.0 (April 14, 2020)
+
+  - Replace `ansi_term` with `yansi-term` for improved performance. (#30)
+  - Turn `Snippet` and `Slice` to work on borrowed slices, rather than Strings. (#32)
+  - Fix `\r\n` end of lines. (#29)
+
+## annotate-snippets 0.7.0 (March 30, 2020)
+
+  - Refactor API to use `fmt::Display` (#27)
+  - Fix SourceAnnotation range (#27)
+  - Fix column numbers (#22)
+  - Derive `PartialEq` for `AnnotationType` (#19)
+  - Update `ansi_term` to 0.12.
+
+## 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
new file mode 100644
index 0000000..e2bccd9
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,65 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies
+#
+# If you believe there's an error in this file please file an
+# issue against the rust-lang/cargo repository. If you're
+# editing this file be aware that the upstream Cargo.toml
+# will likely look very different (and much more reasonable)
+
+[package]
+edition = "2018"
+name = "annotate-snippets"
+version = "0.9.1"
+authors = ["Zibi Braniecki <gandalf@mozilla.com>"]
+description = "Library for building code annotations"
+readme = "README.md"
+keywords = ["code", "analysis", "ascii", "errors", "debug"]
+license = "Apache-2.0/MIT"
+repository = "https://github.com/rust-lang/annotate-snippets-rs"
+
+[[bench]]
+name = "simple"
+harness = false
+[dependencies.unicode-width]
+version = "0.1"
+
+[dependencies.yansi-term]
+version = "0.1"
+optional = true
+[dev-dependencies.criterion]
+version = "0.3"
+
+[dev-dependencies.difference]
+version = "2.0"
+
+[dev-dependencies.glob]
+version = "0.3"
+
+[dev-dependencies.serde]
+version = "1.0"
+features = ["derive"]
+
+[dev-dependencies.toml]
+version = "0.5"
+
+[dev-dependencies.yansi-term]
+version = "0.1"
+
+[features]
+color = ["yansi-term"]
+default = []
+[badges.coveralls]
+branch = "master"
+repository = "rust-lang/annotate-snippets-rs"
+service = "github"
+
+[badges.maintenance]
+status = "actively-developed"
+
+[badges.travis-ci]
+branch = "master"
+repository = "rust-lang/annotate-snippets-rs"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
new file mode 100644
index 0000000..b7a97fd
--- /dev/null
+++ b/Cargo.toml.orig
@@ -0,0 +1,36 @@
+[package]
+name = "annotate-snippets"
+version = "0.9.1"
+edition = "2018"
+authors = ["Zibi Braniecki <gandalf@mozilla.com>"]
+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" }
+
+[dependencies]
+unicode-width = "0.1"
+yansi-term = { version = "0.1", optional = true }
+
+[dev-dependencies]
+glob = "0.3"
+toml = "0.5"
+serde = { version = "1.0", features = ["derive"] }
+difference = "2.0"
+yansi-term = "0.1"
+criterion = "0.3"
+
+[[bench]]
+name = "simple"
+harness = false
+
+[features]
+default = []
+color = ["yansi-term"]
diff --git a/LICENSE b/LICENSE
new file mode 120000
index 0000000..6b579aa
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1 @@
+LICENSE-APACHE
\ No newline at end of file
diff --git a/LICENSE-APACHE b/LICENSE-APACHE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/LICENSE-APACHE
@@ -0,0 +1,201 @@
+                                 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
new file mode 100644
index 0000000..5655fa3
--- /dev/null
+++ b/LICENSE-MIT
@@ -0,0 +1,19 @@
+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/METADATA b/METADATA
new file mode 100644
index 0000000..383f04e
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,20 @@
+name: "annotate-snippets"
+description: "Library for building code annotations"
+third_party {
+  url {
+    type: HOMEPAGE
+    value: "https://crates.io/crates/annotate-snippets"
+  }
+  url {
+    type: ARCHIVE
+    value: "https://static.crates.io/crates/annotate-snippets/annotate-snippets-0.9.1.crate"
+  }
+  version: "0.9.1"
+  # Dual-licensed, using the least restrictive per go/thirdpartylicenses#same.
+  license_type: NOTICE
+  last_upgrade_date {
+    year: 2023
+    month: 6
+    day: 12
+  }
+}
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_APACHE2
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..45dc4dd
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1 @@
+include platform/prebuilts/rust:master:/OWNERS
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..a3e1683
--- /dev/null
+++ b/README.md
@@ -0,0 +1,90 @@
+# annotate-snippets
+
+`annotate-snippets` is a Rust library for annotation of programming code slices.
+
+[![crates.io](https://img.shields.io/crates/v/annotate-snippets.svg)](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<String> {
+   |        -------------- expected `Option<String>` because of return type
+52 | /     for ann in annotations {
+53 | |         match (ann.range.0, ann.range.1) {
+54 | |             (None, None) => continue,
+55 | |             (Some(start), Some(end)) if start > end_index => continue,
+...  |
+71 | |         }
+72 | |     }
+   | |_____^ expected enum `std::option::Option`, found ()
+```
+
+[Documentation][]
+
+[Documentation]: https://docs.rs/annotate-snippets/
+
+Usage
+-----
+
+```rust
+use annotate_snippets::{
+    display_list::{DisplayList, FormatOptions},
+    snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation},
+};
+
+fn main() {
+    let snippet = Snippet {
+        title: Some(Annotation {
+            label: Some("expected type, found `22`"),
+            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"
+                    ,
+                range: <22, 25>,"#,
+            line_start: 26,
+            origin: Some("examples/footer.rs"),
+            fold: true,
+            annotations: vec![
+                SourceAnnotation {
+                    label: "",
+                    annotation_type: AnnotationType::Error,
+                    range: (187, 189),
+                },
+                SourceAnnotation {
+                    label: "while parsing this struct",
+                    annotation_type: AnnotationType::Info,
+                    range: (34, 50),
+                },
+            ],
+        }],
+        opt: FormatOptions {
+            color: true,
+            ..Default::default()
+        },
+    };
+
+    let dl = DisplayList::from(snippet);
+    println!("{}", 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
new file mode 100644
index 0000000..4c13a8f
--- /dev/null
+++ b/benches/simple.rs
@@ -0,0 +1,74 @@
+#![allow(clippy::unit_arg)]
+#[macro_use]
+extern crate criterion;
+
+use criterion::{black_box, Criterion};
+
+use annotate_snippets::{
+    display_list::{DisplayList, FormatOptions},
+    snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation},
+};
+
+fn create_snippet() {
+    let snippet = Snippet {
+        slices: vec![Slice {
+            source: r#") -> Option<String> {
+    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
+                ));
+            }
+            _ => continue,
+        }
+    }"#,
+            line_start: 51,
+            origin: Some("src/format.rs"),
+            fold: false,
+            annotations: vec![
+                SourceAnnotation {
+                    label: "expected `Option<String>` because of return type",
+                    annotation_type: AnnotationType::Warning,
+                    range: (5, 19),
+                },
+                SourceAnnotation {
+                    label: "expected enum `std::option::Option`",
+                    annotation_type: AnnotationType::Error,
+                    range: (26, 724),
+                },
+            ],
+        }],
+        title: Some(Annotation {
+            label: Some("mismatched types"),
+            id: Some("E0308"),
+            annotation_type: AnnotationType::Error,
+        }),
+        footer: vec![],
+        opt: FormatOptions {
+            color: true,
+            ..Default::default()
+        },
+    };
+
+    let dl = DisplayList::from(snippet);
+    let _result = dl.to_string();
+}
+
+pub fn criterion_benchmark(c: &mut Criterion) {
+    c.bench_function("format", |b| b.iter(|| black_box(create_snippet())));
+}
+
+criterion_group!(benches, criterion_benchmark);
+criterion_main!(benches);
diff --git a/examples/expected_type.rs b/examples/expected_type.rs
new file mode 100644
index 0000000..6f2a0d9
--- /dev/null
+++ b/examples/expected_type.rs
@@ -0,0 +1,43 @@
+use annotate_snippets::{
+    display_list::{DisplayList, FormatOptions},
+    snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation},
+};
+
+fn main() {
+    let snippet = Snippet {
+        title: Some(Annotation {
+            label: Some("expected type, found `22`"),
+            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"
+                    ,
+                range: <22, 25>,"#,
+            line_start: 26,
+            origin: Some("examples/footer.rs"),
+            fold: true,
+            annotations: vec![
+                SourceAnnotation {
+                    label: "",
+                    annotation_type: AnnotationType::Error,
+                    range: (193, 195),
+                },
+                SourceAnnotation {
+                    label: "while parsing this struct",
+                    annotation_type: AnnotationType::Info,
+                    range: (34, 50),
+                },
+            ],
+        }],
+        opt: FormatOptions {
+            color: true,
+            ..Default::default()
+        },
+    };
+
+    let dl = DisplayList::from(snippet);
+    println!("{}", dl);
+}
diff --git a/examples/footer.rs b/examples/footer.rs
new file mode 100644
index 0000000..f3c15c4
--- /dev/null
+++ b/examples/footer.rs
@@ -0,0 +1,39 @@
+use annotate_snippets::{
+    display_list::{DisplayList, FormatOptions},
+    snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation},
+};
+
+fn main() {
+    let snippet = Snippet {
+        title: Some(Annotation {
+            label: Some("mismatched types"),
+            id: Some("E0308"),
+            annotation_type: AnnotationType::Error,
+        }),
+        footer: vec![Annotation {
+            label: Some(
+                "expected type: `snippet::Annotation`\n   found type: `__&__snippet::Annotation`",
+            ),
+            id: None,
+            annotation_type: AnnotationType::Note,
+        }],
+        slices: vec![Slice {
+            source: "        slices: vec![\"A\",",
+            line_start: 13,
+            origin: Some("src/multislice.rs"),
+            fold: false,
+            annotations: vec![SourceAnnotation {
+                label: "expected struct `annotate_snippets::snippet::Slice`, found reference",
+                range: (21, 24),
+                annotation_type: AnnotationType::Error,
+            }],
+        }],
+        opt: FormatOptions {
+            color: true,
+            ..Default::default()
+        },
+    };
+
+    let dl = DisplayList::from(snippet);
+    println!("{}", dl);
+}
diff --git a/examples/format.rs b/examples/format.rs
new file mode 100644
index 0000000..98b77a1
--- /dev/null
+++ b/examples/format.rs
@@ -0,0 +1,61 @@
+use annotate_snippets::{
+    display_list::{DisplayList, FormatOptions},
+    snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation},
+};
+
+fn main() {
+    let snippet = Snippet {
+        slices: vec![Slice {
+            source: r#") -> Option<String> {
+    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
+                ));
+            }
+            _ => continue,
+        }
+    }"#,
+            line_start: 51,
+            origin: Some("src/format.rs"),
+            fold: false,
+            annotations: vec![
+                SourceAnnotation {
+                    label: "expected `Option<String>` because of return type",
+                    annotation_type: AnnotationType::Warning,
+                    range: (5, 19),
+                },
+                SourceAnnotation {
+                    label: "expected enum `std::option::Option`",
+                    annotation_type: AnnotationType::Error,
+                    range: (26, 724),
+                },
+            ],
+        }],
+        title: Some(Annotation {
+            label: Some("mismatched types"),
+            id: Some("E0308"),
+            annotation_type: AnnotationType::Error,
+        }),
+        footer: vec![],
+        opt: FormatOptions {
+            color: true,
+            ..Default::default()
+        },
+    };
+
+    let dl = DisplayList::from(snippet);
+    println!("{}", dl);
+}
diff --git a/examples/multislice.rs b/examples/multislice.rs
new file mode 100644
index 0000000..5675a07
--- /dev/null
+++ b/examples/multislice.rs
@@ -0,0 +1,38 @@
+use annotate_snippets::{
+    display_list::{DisplayList, FormatOptions},
+    snippet::{Annotation, AnnotationType, Slice, Snippet},
+};
+
+fn main() {
+    let snippet = Snippet {
+        title: Some(Annotation {
+            label: Some("mismatched types"),
+            id: None,
+            annotation_type: AnnotationType::Error,
+        }),
+        footer: vec![],
+        slices: vec![
+            Slice {
+                source: "Foo",
+                line_start: 51,
+                origin: Some("src/format.rs"),
+                fold: false,
+                annotations: vec![],
+            },
+            Slice {
+                source: "Faa",
+                line_start: 129,
+                origin: Some("src/display.rs"),
+                fold: false,
+                annotations: vec![],
+            },
+        ],
+        opt: FormatOptions {
+            color: true,
+            ..Default::default()
+        },
+    };
+
+    let dl = DisplayList::from(snippet);
+    println!("{}", dl);
+}
diff --git a/src/display_list/from_snippet.rs b/src/display_list/from_snippet.rs
new file mode 100644
index 0000000..faf48f2
--- /dev/null
+++ b/src/display_list/from_snippet.rs
@@ -0,0 +1,594 @@
+//! Trait for converting `Snippet` to `DisplayList`.
+use super::*;
+use crate::{formatter::get_term_style, snippet};
+
+struct CursorLines<'a>(&'a str);
+
+impl<'a> CursorLines<'a> {
+    fn new(src: &str) -> CursorLines<'_> {
+        CursorLines(src)
+    }
+}
+
+enum EndLine {
+    EOF = 0,
+    CRLF = 1,
+    LF = 2,
+}
+
+impl<'a> Iterator for CursorLines<'a> {
+    type Item = (&'a str, EndLine);
+
+    fn next(&mut self) -> Option<Self::Item> {
+        if self.0.is_empty() {
+            None
+        } else {
+            self.0
+                .find('\n')
+                .map(|x| {
+                    let ret = if 0 < x {
+                        if self.0.as_bytes()[x - 1] == b'\r' {
+                            (&self.0[..x - 1], EndLine::LF)
+                        } else {
+                            (&self.0[..x], EndLine::CRLF)
+                        }
+                    } else {
+                        ("", EndLine::CRLF)
+                    };
+                    self.0 = &self.0[x + 1..];
+                    ret
+                })
+                .or_else(|| {
+                    let ret = Some((self.0, EndLine::EOF));
+                    self.0 = "";
+                    ret
+                })
+        }
+    }
+}
+
+fn format_label(
+    label: Option<&str>,
+    style: Option<DisplayTextStyle>,
+) -> Vec<DisplayTextFragment<'_>> {
+    let mut result = vec![];
+    if let Some(label) = label {
+        for (idx, element) in label.split("__").enumerate() {
+            let element_style = match style {
+                Some(s) => s,
+                None => {
+                    if idx % 2 == 0 {
+                        DisplayTextStyle::Regular
+                    } else {
+                        DisplayTextStyle::Emphasis
+                    }
+                }
+            };
+            result.push(DisplayTextFragment {
+                content: element,
+                style: element_style,
+            });
+        }
+    }
+    result
+}
+
+fn format_title(annotation: snippet::Annotation<'_>) -> DisplayLine<'_> {
+    let label = annotation.label.unwrap_or_default();
+    DisplayLine::Raw(DisplayRawLine::Annotation {
+        annotation: Annotation {
+            annotation_type: DisplayAnnotationType::from(annotation.annotation_type),
+            id: annotation.id,
+            label: format_label(Some(label), Some(DisplayTextStyle::Emphasis)),
+        },
+        source_aligned: false,
+        continuation: false,
+    })
+}
+
+fn format_annotation(annotation: snippet::Annotation<'_>) -> Vec<DisplayLine<'_>> {
+    let mut result = vec![];
+    let label = annotation.label.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,
+    margin: Option<Margin>,
+) -> Vec<DisplayLine<'_>> {
+    let main_range = slice.annotations.get(0).map(|x| x.range.0);
+    let origin = slice.origin;
+    let line_start = slice.line_start;
+    let need_empty_header = origin.is_some() || is_first;
+    let mut body = format_body(slice, need_empty_header, has_footer, margin);
+    let header = format_header(origin, main_range, line_start, &body, is_first);
+    let mut result = vec![];
+
+    if let Some(header) = header {
+        result.push(header);
+    }
+    result.append(&mut body);
+    result
+}
+
+#[inline]
+// TODO: option_zip
+fn zip_opt<A, B>(a: Option<A>, b: Option<B>) -> Option<(A, B)> {
+    a.and_then(|a| b.map(|b| (a, b)))
+}
+
+fn format_header<'a>(
+    origin: Option<&'a str>,
+    main_range: Option<usize>,
+    mut row: usize,
+    body: &[DisplayLine<'_>],
+    is_first: bool,
+) -> Option<DisplayLine<'a>> {
+    let display_header = if is_first {
+        DisplayHeaderType::Initial
+    } else {
+        DisplayHeaderType::Continuation
+    };
+
+    if let Some((main_range, path)) = zip_opt(main_range, origin) {
+        let mut col = 1;
+
+        for item in body {
+            if let DisplayLine::Source {
+                line: DisplaySourceLine::Content { range, .. },
+                ..
+            } = item
+            {
+                if main_range >= range.0 && main_range <= range.1 {
+                    col = main_range - range.0 + 1;
+                    break;
+                }
+                row += 1;
+            }
+        }
+
+        return Some(DisplayLine::Raw(DisplayRawLine::Origin {
+            path,
+            pos: Some((row, col)),
+            header_type: display_header,
+        }));
+    }
+
+    if let Some(path) = origin {
+        return Some(DisplayLine::Raw(DisplayRawLine::Origin {
+            path,
+            pos: None,
+            header_type: display_header,
+        }));
+    }
+
+    None
+}
+
+fn fold_body(mut body: Vec<DisplayLine<'_>>) -> Vec<DisplayLine<'_>> {
+    enum Line {
+        Fold(usize),
+        Source(usize),
+    }
+
+    let mut lines = vec![];
+    let mut no_annotation_lines_counter = 0;
+
+    for (idx, line) in body.iter().enumerate() {
+        match line {
+            DisplayLine::Source {
+                line: DisplaySourceLine::Annotation { .. },
+                ..
+            } => {
+                let fold_start = idx - no_annotation_lines_counter;
+                if no_annotation_lines_counter > 2 {
+                    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 (i, _) in body
+                        .iter()
+                        .enumerate()
+                        .take(fold_start + pre_len)
+                        .skip(fold_start)
+                    {
+                        lines.push(Line::Source(i));
+                    }
+                    lines.push(Line::Fold(idx));
+                    for (i, _) in body
+                        .iter()
+                        .enumerate()
+                        .take(fold_end)
+                        .skip(fold_end - post_len)
+                    {
+                        lines.push(Line::Source(i));
+                    }
+                } else {
+                    for (i, _) in body.iter().enumerate().take(idx).skip(fold_start) {
+                        lines.push(Line::Source(i));
+                    }
+                }
+                no_annotation_lines_counter = 0;
+            }
+            DisplayLine::Source { .. } => {
+                no_annotation_lines_counter += 1;
+                continue;
+            }
+            _ => {
+                no_annotation_lines_counter += 1;
+            }
+        }
+        lines.push(Line::Source(idx));
+    }
+
+    let mut new_body = vec![];
+    let mut removed = 0;
+    for line in lines {
+        match line {
+            Line::Source(i) => {
+                new_body.push(body.remove(i - removed));
+                removed += 1;
+            }
+            Line::Fold(i) => {
+                if let DisplayLine::Source {
+                    line: DisplaySourceLine::Annotation { .. },
+                    ref inline_marks,
+                    ..
+                } = body.get(i - removed).unwrap()
+                {
+                    new_body.push(DisplayLine::Fold {
+                        inline_marks: inline_marks.clone(),
+                    })
+                } else {
+                    unreachable!()
+                }
+            }
+        }
+    }
+
+    new_body
+}
+
+fn format_body(
+    slice: snippet::Slice<'_>,
+    need_empty_header: bool,
+    has_footer: bool,
+    margin: Option<Margin>,
+) -> Vec<DisplayLine<'_>> {
+    let source_len = slice.source.chars().count();
+    if let Some(bigger) = slice.annotations.iter().find_map(|x| {
+        if source_len < x.range.1 {
+            Some(x.range)
+        } else {
+            None
+        }
+    }) {
+        panic!(
+            "SourceAnnotation range `{:?}` is bigger than source length `{}`",
+            bigger, source_len
+        )
+    }
+
+    let mut body = vec![];
+    let mut current_line = slice.line_start;
+    let mut current_index = 0;
+    let mut line_info = vec![];
+
+    struct LineInfo {
+        line_start_index: usize,
+        line_end_index: usize,
+        // How many spaces each character in the line take up when displayed
+        char_widths: Vec<usize>,
+    }
+
+    for (line, end_line) in CursorLines::new(slice.source) {
+        let line_length = line.chars().count();
+        let line_range = (current_index, current_index + line_length);
+        let char_widths = line
+            .chars()
+            .map(|c| unicode_width::UnicodeWidthChar::width(c).unwrap_or(0))
+            .chain(std::iter::once(1)) // treat the end of line as signle-width
+            .collect::<Vec<_>>();
+        body.push(DisplayLine::Source {
+            lineno: Some(current_line),
+            inline_marks: vec![],
+            line: DisplaySourceLine::Content {
+                text: line,
+                range: line_range,
+            },
+        });
+        line_info.push(LineInfo {
+            line_start_index: line_range.0,
+            line_end_index: line_range.1,
+            char_widths,
+        });
+        current_line += 1;
+        current_index += line_length + end_line as usize;
+    }
+
+    let mut annotation_line_count = 0;
+    let mut annotations = slice.annotations;
+    for (
+        idx,
+        LineInfo {
+            line_start_index,
+            line_end_index,
+            char_widths,
+        },
+    ) in line_info.into_iter().enumerate()
+    {
+        let margin_left = margin
+            .map(|m| m.left(line_end_index - line_start_index))
+            .unwrap_or_default();
+        // 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_index => true,
+                    (start, end)
+                        if start >= line_start_index && end <= line_end_index
+                            || start == line_end_index && end - start <= 1 =>
+                    {
+                        let annotation_start_col = char_widths
+                            .iter()
+                            .take(start - line_start_index)
+                            .sum::<usize>()
+                            - margin_left;
+                        let annotation_end_col = char_widths
+                            .iter()
+                            .take(end - line_start_index)
+                            .sum::<usize>()
+                            - margin_left;
+                        let range = (annotation_start_col, annotation_end_col);
+                        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_index
+                            && start <= line_end_index
+                            && end > line_end_index =>
+                    {
+                        if start - line_start_index == 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 annotation_start_col = char_widths
+                                .iter()
+                                .take(start - line_start_index)
+                                .sum::<usize>();
+                            let range = (annotation_start_col, annotation_start_col + 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_index && end > line_end_index => {
+                        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_index
+                            && end >= line_start_index
+                            && end <= line_end_index =>
+                    {
+                        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 end_mark = char_widths
+                            .iter()
+                            .take(end - line_start_index)
+                            .sum::<usize>()
+                            .saturating_sub(1);
+                        let range = (end_mark - margin_left, (end_mark + 1) - margin_left);
+                        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);
+    }
+
+    if need_empty_header {
+        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<'a> From<snippet::Snippet<'a>> for DisplayList<'a> {
+    fn from(
+        snippet::Snippet {
+            title,
+            footer,
+            slices,
+            opt,
+        }: snippet::Snippet<'a>,
+    ) -> DisplayList<'a> {
+        let mut body = vec![];
+        if let Some(annotation) = title {
+            body.push(format_title(annotation));
+        }
+
+        for (idx, slice) in slices.into_iter().enumerate() {
+            body.append(&mut format_slice(
+                slice,
+                idx == 0,
+                !footer.is_empty(),
+                opt.margin,
+            ));
+        }
+
+        for annotation in footer {
+            body.append(&mut format_annotation(annotation));
+        }
+
+        let FormatOptions {
+            color,
+            anonymized_line_numbers,
+            margin,
+        } = opt;
+
+        Self {
+            body,
+            stylesheet: get_term_style(color),
+            anonymized_line_numbers,
+            margin,
+        }
+    }
+}
+
+impl From<snippet::AnnotationType> 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/mod.rs b/src/display_list/mod.rs
new file mode 100644
index 0000000..224a9f5
--- /dev/null
+++ b/src/display_list/mod.rs
@@ -0,0 +1,37 @@
+//! 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 implementation of `std::fmt::Display` using information such as
+//! styling.
+//!
+//! The above snippet has been built out of the following structure:
+mod from_snippet;
+mod structs;
+
+pub use self::structs::*;
diff --git a/src/display_list/structs.rs b/src/display_list/structs.rs
new file mode 100644
index 0000000..7941d5f
--- /dev/null
+++ b/src/display_list/structs.rs
@@ -0,0 +1,308 @@
+use std::cmp::{max, min};
+use std::fmt;
+
+use crate::formatter::{get_term_style, style::Stylesheet};
+
+/// List of lines to be displayed.
+pub struct DisplayList<'a> {
+    pub body: Vec<DisplayLine<'a>>,
+    pub stylesheet: Box<dyn Stylesheet>,
+    pub anonymized_line_numbers: bool,
+    pub margin: Option<Margin>,
+}
+
+impl<'a> From<Vec<DisplayLine<'a>>> for DisplayList<'a> {
+    fn from(body: Vec<DisplayLine<'a>>) -> DisplayList<'a> {
+        Self {
+            body,
+            anonymized_line_numbers: false,
+            stylesheet: get_term_style(false),
+            margin: None,
+        }
+    }
+}
+
+impl<'a> PartialEq for DisplayList<'a> {
+    fn eq(&self, other: &Self) -> bool {
+        self.body == other.body && self.anonymized_line_numbers == other.anonymized_line_numbers
+    }
+}
+
+impl<'a> fmt::Debug for DisplayList<'a> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("DisplayList")
+            .field("body", &self.body)
+            .field("anonymized_line_numbers", &self.anonymized_line_numbers)
+            .finish()
+    }
+}
+
+#[derive(Debug, Default, Copy, Clone)]
+pub struct FormatOptions {
+    pub color: bool,
+    pub anonymized_line_numbers: bool,
+    pub margin: Option<Margin>,
+}
+
+#[derive(Clone, Copy, Debug)]
+pub struct Margin {
+    /// The available whitespace in the left that can be consumed when centering.
+    whitespace_left: usize,
+    /// The column of the beginning of left-most span.
+    span_left: usize,
+    /// The column of the end of right-most span.
+    span_right: usize,
+    /// The beginning of the line to be displayed.
+    computed_left: usize,
+    /// The end of the line to be displayed.
+    computed_right: usize,
+    /// The current width of the terminal. 140 by default and in tests.
+    column_width: usize,
+    /// The end column of a span label, including the span. Doesn't account for labels not in the
+    /// same line as the span.
+    label_right: usize,
+}
+
+impl Margin {
+    pub fn new(
+        whitespace_left: usize,
+        span_left: usize,
+        span_right: usize,
+        label_right: usize,
+        column_width: usize,
+        max_line_len: usize,
+    ) -> Self {
+        // The 6 is padding to give a bit of room for `...` when displaying:
+        // ```
+        // error: message
+        //   --> file.rs:16:58
+        //    |
+        // 16 | ... fn foo(self) -> Self::Bar {
+        //    |                     ^^^^^^^^^
+        // ```
+
+        let mut m = Margin {
+            whitespace_left: whitespace_left.saturating_sub(6),
+            span_left: span_left.saturating_sub(6),
+            span_right: span_right + 6,
+            computed_left: 0,
+            computed_right: 0,
+            column_width,
+            label_right: label_right + 6,
+        };
+        m.compute(max_line_len);
+        m
+    }
+
+    pub(crate) fn was_cut_left(&self) -> bool {
+        self.computed_left > 0
+    }
+
+    pub(crate) fn was_cut_right(&self, line_len: usize) -> bool {
+        let right =
+            if self.computed_right == self.span_right || self.computed_right == self.label_right {
+                // Account for the "..." padding given above. Otherwise we end up with code lines that
+                // do fit but end in "..." as if they were trimmed.
+                self.computed_right - 6
+            } else {
+                self.computed_right
+            };
+        right < line_len && self.computed_left + self.column_width < line_len
+    }
+
+    fn compute(&mut self, max_line_len: usize) {
+        // When there's a lot of whitespace (>20), we want to trim it as it is useless.
+        self.computed_left = if self.whitespace_left > 20 {
+            self.whitespace_left - 16 // We want some padding.
+        } else {
+            0
+        };
+        // We want to show as much as possible, max_line_len is the right-most boundary for the
+        // relevant code.
+        self.computed_right = max(max_line_len, self.computed_left);
+
+        if self.computed_right - self.computed_left > self.column_width {
+            // Trimming only whitespace isn't enough, let's get craftier.
+            if self.label_right - self.whitespace_left <= self.column_width {
+                // Attempt to fit the code window only trimming whitespace.
+                self.computed_left = self.whitespace_left;
+                self.computed_right = self.computed_left + self.column_width;
+            } else if self.label_right - self.span_left <= self.column_width {
+                // Attempt to fit the code window considering only the spans and labels.
+                let padding_left = (self.column_width - (self.label_right - self.span_left)) / 2;
+                self.computed_left = self.span_left.saturating_sub(padding_left);
+                self.computed_right = self.computed_left + self.column_width;
+            } else if self.span_right - self.span_left <= self.column_width {
+                // Attempt to fit the code window considering the spans and labels plus padding.
+                let padding_left = (self.column_width - (self.span_right - self.span_left)) / 5 * 2;
+                self.computed_left = self.span_left.saturating_sub(padding_left);
+                self.computed_right = self.computed_left + self.column_width;
+            } else {
+                // Mostly give up but still don't show the full line.
+                self.computed_left = self.span_left;
+                self.computed_right = self.span_right;
+            }
+        }
+    }
+
+    pub(crate) fn left(&self, line_len: usize) -> usize {
+        min(self.computed_left, line_len)
+    }
+
+    pub(crate) fn right(&self, line_len: usize) -> usize {
+        if line_len.saturating_sub(self.computed_left) <= self.column_width {
+            line_len
+        } else {
+            min(line_len, self.computed_right)
+        }
+    }
+}
+
+/// Inline annotation which can be used in either Raw or Source line.
+#[derive(Debug, PartialEq)]
+pub struct Annotation<'a> {
+    pub annotation_type: DisplayAnnotationType,
+    pub id: Option<&'a str>,
+    pub label: Vec<DisplayTextFragment<'a>>,
+}
+
+/// A single line used in `DisplayList`.
+#[derive(Debug, PartialEq)]
+pub enum DisplayLine<'a> {
+    /// A line with `lineno` portion of the slice.
+    Source {
+        lineno: Option<usize>,
+        inline_marks: Vec<DisplayMark>,
+        line: DisplaySourceLine<'a>,
+    },
+
+    /// A line indicating a folded part of the slice.
+    Fold { inline_marks: Vec<DisplayMark> },
+
+    /// A line which is displayed outside of slices.
+    Raw(DisplayRawLine<'a>),
+}
+
+/// A source line.
+#[derive(Debug, PartialEq)]
+pub enum DisplaySourceLine<'a> {
+    /// A line with the content of the Slice.
+    Content {
+        text: &'a str,
+        range: (usize, usize), // meta information for annotation placement.
+    },
+
+    /// An annotation line which is displayed in context of the slice.
+    Annotation {
+        annotation: Annotation<'a>,
+        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, PartialEq)]
+pub enum DisplayRawLine<'a> {
+    /// A line which provides information about the location of the given
+    /// slice in the project structure.
+    Origin {
+        path: &'a str,
+        pos: Option<(usize, usize)>,
+        header_type: DisplayHeaderType,
+    },
+
+    /// An annotation line which is not part of any snippet.
+    Annotation {
+        annotation: Annotation<'a>,
+
+        /// 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, PartialEq)]
+pub struct DisplayTextFragment<'a> {
+    pub content: &'a str,
+    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.
+    AnnotationThrough,
+    /// A mark indicating a multiline annotation starting on the given line.
+    AnnotationStart,
+}
+
+/// A type of the `Annotation` which may impact the sigils, style or text displayed.
+///
+/// There are several ways to 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.
+// TODO: private
+#[derive(Debug, Clone, PartialEq)]
+pub enum DisplayHeaderType {
+    /// Initial header is the first header in the snippet.
+    Initial,
+
+    /// Continuation marks all headers of following slices in the snippet.
+    Continuation,
+}
diff --git a/src/formatter/mod.rs b/src/formatter/mod.rs
new file mode 100644
index 0000000..16889ba
--- /dev/null
+++ b/src/formatter/mod.rs
@@ -0,0 +1,456 @@
+use std::{
+    cmp,
+    fmt::{self, Display, Write},
+    iter::once,
+};
+
+pub mod style;
+
+use self::style::{Style, StyleClass, Stylesheet};
+
+#[cfg(feature = "color")]
+use crate::stylesheets::color::AnsiTermStylesheet;
+use crate::{display_list::*, stylesheets::no_color::NoColorStylesheet};
+
+fn format_repeat_char(c: char, n: usize, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+    for _ in 0..n {
+        f.write_char(c)?;
+    }
+    Ok(())
+}
+
+#[inline]
+fn is_annotation_empty(annotation: &Annotation<'_>) -> bool {
+    annotation
+        .label
+        .iter()
+        .all(|fragment| fragment.content.is_empty())
+}
+
+#[cfg(feature = "color")]
+#[inline]
+pub fn get_term_style(color: bool) -> Box<dyn Stylesheet> {
+    if color {
+        Box::new(AnsiTermStylesheet)
+    } else {
+        Box::new(NoColorStylesheet)
+    }
+}
+
+#[cfg(not(feature = "color"))]
+#[inline]
+pub fn get_term_style(_color: bool) -> Box<dyn Stylesheet> {
+    Box::new(NoColorStylesheet)
+}
+
+impl<'a> fmt::Display for DisplayList<'a> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let lineno_width = self.body.iter().fold(0, |max, line| match line {
+            DisplayLine::Source {
+                lineno: Some(lineno),
+                ..
+            } => {
+                // The largest line is the largest width.
+                cmp::max(*lineno, max)
+            }
+            _ => max,
+        });
+        let lineno_width = if lineno_width == 0 {
+            lineno_width
+        } else if self.anonymized_line_numbers {
+            Self::ANONYMIZED_LINE_NUM.len()
+        } else {
+            ((lineno_width as f64).log10().floor() as usize) + 1
+        };
+        let inline_marks_width = self.body.iter().fold(0, |max, line| match line {
+            DisplayLine::Source { inline_marks, .. } => cmp::max(inline_marks.len(), max),
+            _ => max,
+        });
+
+        for (i, line) in self.body.iter().enumerate() {
+            self.format_line(line, lineno_width, inline_marks_width, f)?;
+            if i + 1 < self.body.len() {
+                f.write_char('\n')?;
+            }
+        }
+        Ok(())
+    }
+}
+
+impl<'a> DisplayList<'a> {
+    const ANONYMIZED_LINE_NUM: &'static str = "LL";
+    const ERROR_TXT: &'static str = "error";
+    const HELP_TXT: &'static str = "help";
+    const INFO_TXT: &'static str = "info";
+    const NOTE_TXT: &'static str = "note";
+    const WARNING_TXT: &'static str = "warning";
+
+    #[inline]
+    fn format_annotation_type(
+        annotation_type: &DisplayAnnotationType,
+        f: &mut fmt::Formatter<'_>,
+    ) -> fmt::Result {
+        match annotation_type {
+            DisplayAnnotationType::Error => f.write_str(Self::ERROR_TXT),
+            DisplayAnnotationType::Help => f.write_str(Self::HELP_TXT),
+            DisplayAnnotationType::Info => f.write_str(Self::INFO_TXT),
+            DisplayAnnotationType::Note => f.write_str(Self::NOTE_TXT),
+            DisplayAnnotationType::Warning => f.write_str(Self::WARNING_TXT),
+            DisplayAnnotationType::None => Ok(()),
+        }
+    }
+
+    fn annotation_type_len(annotation_type: &DisplayAnnotationType) -> usize {
+        match annotation_type {
+            DisplayAnnotationType::Error => Self::ERROR_TXT.len(),
+            DisplayAnnotationType::Help => Self::HELP_TXT.len(),
+            DisplayAnnotationType::Info => Self::INFO_TXT.len(),
+            DisplayAnnotationType::Note => Self::NOTE_TXT.len(),
+            DisplayAnnotationType::Warning => Self::WARNING_TXT.len(),
+            DisplayAnnotationType::None => 0,
+        }
+    }
+
+    fn get_annotation_style(&self, annotation_type: &DisplayAnnotationType) -> Box<dyn Style> {
+        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<'_>],
+        f: &mut fmt::Formatter<'_>,
+    ) -> fmt::Result {
+        let emphasis_style = self.stylesheet.get_style(StyleClass::Emphasis);
+
+        for fragment in label {
+            match fragment.style {
+                DisplayTextStyle::Regular => fragment.content.fmt(f)?,
+                DisplayTextStyle::Emphasis => emphasis_style.paint(fragment.content, f)?,
+            }
+        }
+        Ok(())
+    }
+
+    fn format_annotation(
+        &self,
+        annotation: &Annotation<'_>,
+        continuation: bool,
+        in_source: bool,
+        f: &mut fmt::Formatter<'_>,
+    ) -> fmt::Result {
+        let color = self.get_annotation_style(&annotation.annotation_type);
+        let formatted_len = if let Some(id) = &annotation.id {
+            2 + id.len() + Self::annotation_type_len(&annotation.annotation_type)
+        } else {
+            Self::annotation_type_len(&annotation.annotation_type)
+        };
+
+        if continuation {
+            format_repeat_char(' ', formatted_len + 2, f)?;
+            return self.format_label(&annotation.label, f);
+        }
+        if formatted_len == 0 {
+            self.format_label(&annotation.label, f)
+        } else {
+            color.paint_fn(
+                Box::new(|f| {
+                    Self::format_annotation_type(&annotation.annotation_type, f)?;
+                    if let Some(id) = &annotation.id {
+                        f.write_char('[')?;
+                        f.write_str(id)?;
+                        f.write_char(']')?;
+                    }
+                    Ok(())
+                }),
+                f,
+            )?;
+            if !is_annotation_empty(annotation) {
+                if in_source {
+                    color.paint_fn(
+                        Box::new(|f| {
+                            f.write_str(": ")?;
+                            self.format_label(&annotation.label, f)
+                        }),
+                        f,
+                    )?;
+                } else {
+                    f.write_str(": ")?;
+                    self.format_label(&annotation.label, f)?;
+                }
+            }
+            Ok(())
+        }
+    }
+
+    #[inline]
+    fn format_source_line(
+        &self,
+        line: &DisplaySourceLine<'_>,
+        f: &mut fmt::Formatter<'_>,
+    ) -> fmt::Result {
+        match line {
+            DisplaySourceLine::Empty => Ok(()),
+            DisplaySourceLine::Content { text, .. } => {
+                f.write_char(' ')?;
+                if let Some(margin) = self.margin {
+                    let line_len = text.chars().count();
+                    let mut left = margin.left(line_len);
+                    let right = margin.right(line_len);
+
+                    if margin.was_cut_left() {
+                        // We have stripped some code/whitespace from the beginning, make it clear.
+                        "...".fmt(f)?;
+                        left += 3;
+                    }
+
+                    // On long lines, we strip the source line, accounting for unicode.
+                    let mut taken = 0;
+                    let cut_right = if margin.was_cut_right(line_len) {
+                        taken += 3;
+                        true
+                    } else {
+                        false
+                    };
+                    // Specifies that it will end on the next character, so it will return
+                    // until the next one to the final condition.
+                    let mut ended = false;
+                    let range = text
+                        .char_indices()
+                        .skip(left)
+                        // Complete char iterator with final character
+                        .chain(once((text.len(), '\0')))
+                        // Take until the next one to the final condition
+                        .take_while(|(_, ch)| {
+                            // Fast return to iterate over final byte position
+                            if ended {
+                                return false;
+                            }
+                            // Make sure that the trimming on the right will fall within the terminal width.
+                            // FIXME: `unicode_width` sometimes disagrees with terminals on how wide a `char` is.
+                            // For now, just accept that sometimes the code line will be longer than desired.
+                            taken += unicode_width::UnicodeWidthChar::width(*ch).unwrap_or(1);
+                            if taken > right - left {
+                                ended = true;
+                            }
+                            true
+                        })
+                        // Reduce to start and end byte position
+                        .fold((None, 0), |acc, (i, _)| {
+                            if acc.0.is_some() {
+                                (acc.0, i)
+                            } else {
+                                (Some(i), i)
+                            }
+                        });
+
+                    // Format text with margins
+                    text[range.0.expect("One character at line")..range.1].fmt(f)?;
+
+                    if cut_right {
+                        // We have stripped some code after the right-most span end, make it clear we did so.
+                        "...".fmt(f)?;
+                    }
+                    Ok(())
+                } else {
+                    text.fmt(f)
+                }
+            }
+            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,
+                };
+
+                color.paint_fn(
+                    Box::new(|f| {
+                        format_repeat_char(indent_char, indent_length + 1, f)?;
+                        format_repeat_char(mark, range.1 - indent_length, f)
+                    }),
+                    f,
+                )?;
+
+                if !is_annotation_empty(annotation) {
+                    f.write_char(' ')?;
+                    color.paint_fn(
+                        Box::new(|f| {
+                            self.format_annotation(
+                                annotation,
+                                annotation_part == &DisplayAnnotationPart::LabelContinuation,
+                                true,
+                                f,
+                            )
+                        }),
+                        f,
+                    )?;
+                }
+
+                Ok(())
+            }
+        }
+    }
+
+    #[inline]
+    fn format_raw_line(
+        &self,
+        line: &DisplayRawLine<'_>,
+        lineno_width: usize,
+        f: &mut fmt::Formatter<'_>,
+    ) -> fmt::Result {
+        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, f)?;
+                    lineno_color.paint(header_sigil, f)?;
+                    f.write_char(' ')?;
+                    path.fmt(f)?;
+                    f.write_char(':')?;
+                    col.fmt(f)?;
+                    f.write_char(':')?;
+                    row.fmt(f)
+                } else {
+                    format_repeat_char(' ', lineno_width, f)?;
+                    lineno_color.paint(header_sigil, f)?;
+                    f.write_char(' ')?;
+                    path.fmt(f)
+                }
+            }
+            DisplayRawLine::Annotation {
+                annotation,
+                source_aligned,
+                continuation,
+            } => {
+                if *source_aligned {
+                    if *continuation {
+                        format_repeat_char(' ', lineno_width + 3, f)?;
+                    } else {
+                        let lineno_color = self.stylesheet.get_style(StyleClass::LineNo);
+                        format_repeat_char(' ', lineno_width, f)?;
+                        f.write_char(' ')?;
+                        lineno_color.paint("=", f)?;
+                        f.write_char(' ')?;
+                    }
+                }
+                self.format_annotation(annotation, *continuation, false, f)
+            }
+        }
+    }
+
+    #[inline]
+    fn format_line(
+        &self,
+        dl: &DisplayLine<'_>,
+        lineno_width: usize,
+        inline_marks_width: usize,
+        f: &mut fmt::Formatter<'_>,
+    ) -> fmt::Result {
+        match dl {
+            DisplayLine::Source {
+                lineno,
+                inline_marks,
+                line,
+            } => {
+                let lineno_color = self.stylesheet.get_style(StyleClass::LineNo);
+                if self.anonymized_line_numbers && lineno.is_some() {
+                    lineno_color.paint_fn(
+                        Box::new(|f| {
+                            f.write_str(Self::ANONYMIZED_LINE_NUM)?;
+                            f.write_str(" |")
+                        }),
+                        f,
+                    )?;
+                } else {
+                    lineno_color.paint_fn(
+                        Box::new(|f| {
+                            match lineno {
+                                Some(n) => write!(f, "{:>width$}", n, width = lineno_width),
+                                None => format_repeat_char(' ', lineno_width, f),
+                            }?;
+                            f.write_str(" |")
+                        }),
+                        f,
+                    )?;
+                }
+                if *line != DisplaySourceLine::Empty {
+                    if !inline_marks.is_empty() || 0 < inline_marks_width {
+                        f.write_char(' ')?;
+                        self.format_inline_marks(inline_marks, inline_marks_width, f)?;
+                    }
+                    self.format_source_line(line, f)?;
+                } else if !inline_marks.is_empty() {
+                    f.write_char(' ')?;
+                    self.format_inline_marks(inline_marks, inline_marks_width, f)?;
+                }
+                Ok(())
+            }
+            DisplayLine::Fold { inline_marks } => {
+                f.write_str("...")?;
+                if !inline_marks.is_empty() || 0 < inline_marks_width {
+                    format_repeat_char(' ', lineno_width, f)?;
+                    self.format_inline_marks(inline_marks, inline_marks_width, f)?;
+                }
+                Ok(())
+            }
+            DisplayLine::Raw(line) => self.format_raw_line(line, lineno_width, f),
+        }
+    }
+
+    fn format_inline_marks(
+        &self,
+        inline_marks: &[DisplayMark],
+        inline_marks_width: usize,
+        f: &mut fmt::Formatter<'_>,
+    ) -> fmt::Result {
+        format_repeat_char(' ', inline_marks_width - inline_marks.len(), f)?;
+        for mark in inline_marks {
+            self.get_annotation_style(&mark.annotation_type).paint_fn(
+                Box::new(|f| {
+                    f.write_char(match mark.mark_type {
+                        DisplayMarkType::AnnotationThrough => '|',
+                        DisplayMarkType::AnnotationStart => '/',
+                    })
+                }),
+                f,
+            )?;
+        }
+        Ok(())
+    }
+}
diff --git a/src/formatter/style.rs b/src/formatter/style.rs
new file mode 100644
index 0000000..3fc01c1
--- /dev/null
+++ b/src/formatter/style.rs
@@ -0,0 +1,51 @@
+//! Set of structures required to implement a stylesheet
+//!
+//! In order to provide additional styling information for the
+//! formatter, a structs can implement `Stylesheet` and `Style`
+//! traits.
+//!
+use std::fmt;
+
+/// StyleClass is a collection of named variants of style classes
+pub enum StyleClass {
+    /// Message indicating an error.
+    Error,
+    /// Message indicating a warning.
+    Warning,
+    /// Message indicating an information.
+    Info,
+    /// Message indicating a note.
+    Note,
+    /// Message indicating a help.
+    Help,
+
+    /// Style for line numbers.
+    LineNo,
+
+    /// Parts of the text that are to be emphasised.
+    Emphasis,
+
+    /// Parts of the text that are regular. Usually a no-op.
+    None,
+}
+
+/// This trait implements a return value for the `Stylesheet::get_style`.
+pub trait Style {
+    /// The method used to write text with formatter
+    fn paint(&self, text: &str, f: &mut fmt::Formatter<'_>) -> fmt::Result;
+    /// The method used to write display function with formatter
+    fn paint_fn<'a>(
+        &self,
+        c: Box<dyn FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result + 'a>,
+        f: &mut fmt::Formatter<'_>,
+    ) -> fmt::Result;
+    /// The method used by the `Formatter` to display the message in bold font.
+    fn bold(&self) -> Box<dyn Style>;
+}
+
+/// Trait to annotate structs that can provide `Style` implementations for
+/// every `StyleClass` variant.
+pub trait Stylesheet {
+    /// Returns a `Style` implementer based on the requested `StyleClass` variant.
+    fn get_style(&self, class: StyleClass) -> Box<dyn Style>;
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..d581367
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,54 @@
+#![deny(rust_2018_idioms)]
+
+//! A library for formatting of text or programming code snippets.
+//!
+//! It's primary purpose is to build an ASCII-graphical representation of the snippet
+//! with annotations.
+//!
+//! # Example
+//!
+//! ```text
+//! error[E0308]: mismatched types
+//!   --> src/format.rs:52:1
+//!    |
+//! 51 |   ) -> Option<String> {
+//!    |        -------------- expected `Option<String>` because of return type
+//! 52 | /     for ann in annotations {
+//! 53 | |         match (ann.range.0, ann.range.1) {
+//! 54 | |             (None, None) => continue,
+//! 55 | |             (Some(start), Some(end)) if start > end_index => continue,
+//! ...  |
+//! 71 | |         }
+//! 72 | |     }
+//!    | |_____^ expected enum `std::option::Option`, found ()
+//! ```
+//!
+//! The crate uses a three stage process with two conversions between states:
+//!
+//! ```text
+//! Snippet --> DisplayList --> String
+//! ```
+//!
+//! The input type - [Snippet](self::snippet) is a structure designed
+//! to align with likely output from any parser whose code snippet is to be
+//! annotated.
+//!
+//! The middle structure - [DisplayList](self::display_list) is a
+//! structure designed to store the snippet data converted into a vector
+//! of lines containing semantic information about each line.
+//! This structure is the easiest to manipulate and organize.
+//!
+//! Finally, `impl Display` into a final `String` output.
+//!
+//! A user of the crate may choose to provide their own equivalent of the input
+//! structure with an `Into<DisplayList>` trait.
+//!
+//! A user of the crate may also choose to provide their own formatter logic,
+//! to convert a `DisplayList` into a `String`, or just a `Stylesheet` to
+//! use the crate's formatting logic, but with a custom stylesheet.
+// TODO: check documentation
+
+pub mod display_list;
+pub mod formatter;
+pub mod snippet;
+pub mod stylesheets;
diff --git a/src/snippet.rs b/src/snippet.rs
new file mode 100644
index 0000000..bc7ba00
--- /dev/null
+++ b/src/snippet.rs
@@ -0,0 +1,88 @@
+//! Structures used as an input for the library.
+//!
+//! Example:
+//!
+//! ```
+//! use annotate_snippets::snippet::*;
+//!
+//! Snippet {
+//!     title: Some(Annotation {
+//!         label: Some("mismatched types"),
+//!         id: None,
+//!         annotation_type: AnnotationType::Error,
+//!     }),
+//!     footer: vec![],
+//!     slices: vec![
+//!         Slice {
+//!             source: "Foo",
+//!             line_start: 51,
+//!             origin: Some("src/format.rs"),
+//!             fold: false,
+//!             annotations: vec![],
+//!         },
+//!         Slice {
+//!             source: "Faa",
+//!             line_start: 129,
+//!             origin: Some("src/display.rs"),
+//!             fold: false,
+//!             annotations: vec![],
+//!         },
+//!     ],
+//!     opt: Default::default(),
+//! };
+//! ```
+use crate::display_list::FormatOptions;
+
+/// Primary structure provided for formatting
+#[derive(Debug, Default)]
+pub struct Snippet<'a> {
+    pub title: Option<Annotation<'a>>,
+    pub footer: Vec<Annotation<'a>>,
+    pub slices: Vec<Slice<'a>>,
+    pub opt: FormatOptions,
+}
+
+/// Structure containing the slice of text to be annotated and
+/// basic information about the location of the slice.
+///
+/// One `Slice` is meant to represent a single, continuous,
+/// slice of source code that you want to annotate.
+#[derive(Debug)]
+pub struct Slice<'a> {
+    pub source: &'a str,
+    pub line_start: usize,
+    pub origin: Option<&'a str>,
+    pub annotations: Vec<SourceAnnotation<'a>>,
+    /// 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, PartialEq)]
+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)]
+pub struct SourceAnnotation<'a> {
+    pub range: (usize, usize),
+    pub label: &'a str,
+    pub annotation_type: AnnotationType,
+}
+
+/// An annotation for a `Snippet`.
+#[derive(Debug)]
+pub struct Annotation<'a> {
+    /// Identifier of the annotation. Usually error code like "E0308".
+    pub id: Option<&'a str>,
+    pub label: Option<&'a str>,
+    pub annotation_type: AnnotationType,
+}
diff --git a/src/stylesheets/color.rs b/src/stylesheets/color.rs
new file mode 100644
index 0000000..024dd06
--- /dev/null
+++ b/src/stylesheets/color.rs
@@ -0,0 +1,50 @@
+use std::fmt::{self, Display};
+
+use yansi_term::{Color::Fixed, Style as AnsiTermStyle};
+
+use crate::formatter::style::{Style, StyleClass, Stylesheet};
+
+struct AnsiTermStyleWrapper {
+    style: AnsiTermStyle,
+}
+
+impl Style for AnsiTermStyleWrapper {
+    fn paint(&self, text: &str, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.style.paint(text).fmt(f)
+    }
+
+    fn paint_fn<'a>(
+        &self,
+        c: Box<dyn FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result + 'a>,
+        f: &mut fmt::Formatter<'_>,
+    ) -> fmt::Result {
+        self.style.paint_fn(c).fmt(f)
+    }
+
+    fn bold(&self) -> Box<dyn Style> {
+        Box::new(AnsiTermStyleWrapper { style: self.style })
+    }
+}
+
+pub struct AnsiTermStylesheet;
+
+impl Stylesheet for AnsiTermStylesheet {
+    fn get_style(&self, class: StyleClass) -> Box<dyn Style> {
+        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
new file mode 100644
index 0000000..4648852
--- /dev/null
+++ b/src/stylesheets/mod.rs
@@ -0,0 +1,11 @@
+//! List of stylesheets
+//!
+//! 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 = "color")]
+pub mod color;
+pub mod no_color;
diff --git a/src/stylesheets/no_color.rs b/src/stylesheets/no_color.rs
new file mode 100644
index 0000000..21cb269
--- /dev/null
+++ b/src/stylesheets/no_color.rs
@@ -0,0 +1,31 @@
+use std::fmt;
+
+use crate::formatter::style::{Style, StyleClass, Stylesheet};
+
+pub struct NoOpStyle {}
+
+impl Style for NoOpStyle {
+    fn paint(&self, text: &str, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str(text)
+    }
+
+    fn paint_fn<'a>(
+        &self,
+        c: Box<dyn FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result + 'a>,
+        f: &mut fmt::Formatter<'_>,
+    ) -> fmt::Result {
+        c(f)
+    }
+
+    fn bold(&self) -> Box<dyn Style> {
+        Box::new(NoOpStyle {})
+    }
+}
+
+pub struct NoColorStylesheet;
+
+impl Stylesheet for NoColorStylesheet {
+    fn get_style(&self, _class: StyleClass) -> Box<dyn Style> {
+        Box::new(NoOpStyle {})
+    }
+}
diff --git a/tests/diff/mod.rs b/tests/diff/mod.rs
new file mode 100644
index 0000000..576c6c4
--- /dev/null
+++ b/tests/diff/mod.rs
@@ -0,0 +1,43 @@
+use difference::{Changeset, Difference};
+use yansi_term::Color::{Black, Green, Red};
+
+pub fn get_diff(left: &str, right: &str) -> String {
+    let mut output = String::new();
+
+    let Changeset { diffs, .. } = Changeset::new(left, right, "\n");
+
+    for i in 0..diffs.len() {
+        match diffs[i] {
+            Difference::Same(ref x) => {
+                output += &format!(" {}\n", x);
+            }
+            Difference::Add(ref x) => {
+                match diffs[i - 1] {
+                    Difference::Rem(ref y) => {
+                        output += &format!("{}", Green.paint("+"));
+                        let Changeset { diffs, .. } = Changeset::new(y, x, " ");
+                        for c in diffs {
+                            match c {
+                                Difference::Same(ref z) => {
+                                    output += &format!("{} ", Green.paint(z.as_str()));
+                                }
+                                Difference::Add(ref z) => {
+                                    output += &format!("{} ", Black.on(Green).paint(z.as_str()));
+                                }
+                                _ => (),
+                            }
+                        }
+                        output += "\n";
+                    }
+                    _ => {
+                        output += &format!("+{}\n", Green.paint(x.as_str()));
+                    }
+                };
+            }
+            Difference::Rem(ref x) => {
+                output += &format!("-{}\n", Red.paint(x.as_str()));
+            }
+        }
+    }
+    output
+}
diff --git a/tests/dl_from_snippet.rs b/tests/dl_from_snippet.rs
new file mode 100644
index 0000000..0dcfcfa
--- /dev/null
+++ b/tests/dl_from_snippet.rs
@@ -0,0 +1,407 @@
+use annotate_snippets::display_list::DisplayList;
+use annotate_snippets::{display_list as dl, formatter::get_term_style, snippet};
+
+#[test]
+fn test_format_title() {
+    let input = snippet::Snippet {
+        title: Some(snippet::Annotation {
+            id: Some("E0001"),
+            label: Some("This is a title"),
+            annotation_type: snippet::AnnotationType::Error,
+        }),
+        footer: vec![],
+        slices: vec![],
+        opt: Default::default(),
+    };
+    let output = dl::DisplayList {
+        body: vec![dl::DisplayLine::Raw(dl::DisplayRawLine::Annotation {
+            annotation: dl::Annotation {
+                annotation_type: dl::DisplayAnnotationType::Error,
+                id: Some("E0001"),
+                label: vec![dl::DisplayTextFragment {
+                    content: "This is a title",
+                    style: dl::DisplayTextStyle::Emphasis,
+                }],
+            },
+            source_aligned: false,
+            continuation: false,
+        })],
+        stylesheet: get_term_style(input.opt.color),
+        anonymized_line_numbers: input.opt.anonymized_line_numbers,
+        margin: None,
+    };
+    assert_eq!(dl::DisplayList::from(input), output);
+}
+
+#[test]
+fn test_format_slice() {
+    let line_1 = "This is line 1";
+    let line_2 = "This is line 2";
+    let source = vec![line_1, line_2].join("\n");
+    let input = snippet::Snippet {
+        title: None,
+        footer: vec![],
+        slices: vec![snippet::Slice {
+            source: &source,
+            line_start: 5402,
+            origin: None,
+            annotations: vec![],
+            fold: false,
+        }],
+        opt: Default::default(),
+    };
+    let output = dl::DisplayList {
+        body: vec![
+            dl::DisplayLine::Source {
+                lineno: None,
+                inline_marks: vec![],
+                line: dl::DisplaySourceLine::Empty,
+            },
+            dl::DisplayLine::Source {
+                lineno: Some(5402),
+                inline_marks: vec![],
+                line: dl::DisplaySourceLine::Content {
+                    text: line_1,
+                    range: (0, line_1.len()),
+                },
+            },
+            dl::DisplayLine::Source {
+                lineno: Some(5403),
+                inline_marks: vec![],
+                line: dl::DisplaySourceLine::Content {
+                    range: (line_1.len() + 1, source.len()),
+                    text: line_2,
+                },
+            },
+            dl::DisplayLine::Source {
+                lineno: None,
+                inline_marks: vec![],
+                line: dl::DisplaySourceLine::Empty,
+            },
+        ],
+        stylesheet: get_term_style(input.opt.color),
+        anonymized_line_numbers: input.opt.anonymized_line_numbers,
+        margin: None,
+    };
+    assert_eq!(dl::DisplayList::from(input), output);
+}
+
+#[test]
+fn test_format_slices_continuation() {
+    let src_0 = "This is slice 1";
+    let src_0_len = src_0.len();
+    let src_1 = "This is slice 2";
+    let src_1_len = src_1.len();
+    let input = snippet::Snippet {
+        title: None,
+        footer: vec![],
+        slices: vec![
+            snippet::Slice {
+                source: src_0,
+                line_start: 5402,
+                origin: Some("file1.rs"),
+                annotations: vec![],
+                fold: false,
+            },
+            snippet::Slice {
+                source: src_1,
+                line_start: 2,
+                origin: Some("file2.rs"),
+                annotations: vec![],
+                fold: false,
+            },
+        ],
+        opt: Default::default(),
+    };
+    let output = dl::DisplayList {
+        body: vec![
+            dl::DisplayLine::Raw(dl::DisplayRawLine::Origin {
+                path: "file1.rs",
+                pos: None,
+                header_type: dl::DisplayHeaderType::Initial,
+            }),
+            dl::DisplayLine::Source {
+                lineno: None,
+                inline_marks: vec![],
+                line: dl::DisplaySourceLine::Empty,
+            },
+            dl::DisplayLine::Source {
+                lineno: Some(5402),
+                inline_marks: vec![],
+                line: dl::DisplaySourceLine::Content {
+                    text: src_0,
+                    range: (0, src_0_len),
+                },
+            },
+            dl::DisplayLine::Source {
+                lineno: None,
+                inline_marks: vec![],
+                line: dl::DisplaySourceLine::Empty,
+            },
+            dl::DisplayLine::Raw(dl::DisplayRawLine::Origin {
+                path: "file2.rs",
+                pos: None,
+                header_type: dl::DisplayHeaderType::Continuation,
+            }),
+            dl::DisplayLine::Source {
+                lineno: None,
+                inline_marks: vec![],
+                line: dl::DisplaySourceLine::Empty,
+            },
+            dl::DisplayLine::Source {
+                lineno: Some(2),
+                inline_marks: vec![],
+                line: dl::DisplaySourceLine::Content {
+                    text: src_1,
+                    range: (0, src_1_len),
+                },
+            },
+            dl::DisplayLine::Source {
+                lineno: None,
+                inline_marks: vec![],
+                line: dl::DisplaySourceLine::Empty,
+            },
+        ],
+        stylesheet: get_term_style(input.opt.color),
+        anonymized_line_numbers: input.opt.anonymized_line_numbers,
+        margin: None,
+    };
+    assert_eq!(dl::DisplayList::from(input), output);
+}
+
+#[test]
+fn test_format_slice_annotation_standalone() {
+    let line_1 = "This is line 1";
+    let line_2 = "This is line 2";
+    let source = vec![line_1, line_2].join("\n");
+    // In line 2
+    let range = (22, 24);
+    let input = snippet::Snippet {
+        title: None,
+        footer: vec![],
+        slices: vec![snippet::Slice {
+            source: &source,
+            line_start: 5402,
+            origin: None,
+            annotations: vec![snippet::SourceAnnotation {
+                range,
+                label: "Test annotation",
+                annotation_type: snippet::AnnotationType::Info,
+            }],
+            fold: false,
+        }],
+        opt: Default::default(),
+    };
+    let output = dl::DisplayList {
+        body: vec![
+            dl::DisplayLine::Source {
+                lineno: None,
+                inline_marks: vec![],
+                line: dl::DisplaySourceLine::Empty,
+            },
+            dl::DisplayLine::Source {
+                lineno: Some(5402),
+                inline_marks: vec![],
+                line: dl::DisplaySourceLine::Content {
+                    range: (0, line_1.len()),
+                    text: line_1,
+                },
+            },
+            dl::DisplayLine::Source {
+                lineno: Some(5403),
+                inline_marks: vec![],
+                line: dl::DisplaySourceLine::Content {
+                    range: (line_1.len() + 1, source.len()),
+                    text: line_2,
+                },
+            },
+            dl::DisplayLine::Source {
+                lineno: None,
+                inline_marks: vec![],
+                line: dl::DisplaySourceLine::Annotation {
+                    annotation: dl::Annotation {
+                        annotation_type: dl::DisplayAnnotationType::Info,
+                        id: None,
+                        label: vec![dl::DisplayTextFragment {
+                            content: "Test annotation",
+                            style: dl::DisplayTextStyle::Regular,
+                        }],
+                    },
+                    range: (range.0 - (line_1.len() + 1), range.1 - (line_1.len() + 1)),
+                    annotation_type: dl::DisplayAnnotationType::Info,
+                    annotation_part: dl::DisplayAnnotationPart::Standalone,
+                },
+            },
+            dl::DisplayLine::Source {
+                lineno: None,
+                inline_marks: vec![],
+                line: dl::DisplaySourceLine::Empty,
+            },
+        ],
+        stylesheet: get_term_style(input.opt.color),
+        anonymized_line_numbers: input.opt.anonymized_line_numbers,
+        margin: None,
+    };
+    assert_eq!(dl::DisplayList::from(input), output);
+}
+
+#[test]
+fn test_format_label() {
+    let input = snippet::Snippet {
+        title: None,
+        footer: vec![snippet::Annotation {
+            id: None,
+            label: Some("This __is__ a title"),
+            annotation_type: snippet::AnnotationType::Error,
+        }],
+        slices: vec![],
+        opt: Default::default(),
+    };
+    let output = dl::DisplayList {
+        body: vec![dl::DisplayLine::Raw(dl::DisplayRawLine::Annotation {
+            annotation: dl::Annotation {
+                annotation_type: dl::DisplayAnnotationType::Error,
+                id: None,
+                label: vec![
+                    dl::DisplayTextFragment {
+                        content: "This ",
+                        style: dl::DisplayTextStyle::Regular,
+                    },
+                    dl::DisplayTextFragment {
+                        content: "is",
+                        style: dl::DisplayTextStyle::Emphasis,
+                    },
+                    dl::DisplayTextFragment {
+                        content: " a title",
+                        style: dl::DisplayTextStyle::Regular,
+                    },
+                ],
+            },
+            source_aligned: true,
+            continuation: false,
+        })],
+        stylesheet: get_term_style(input.opt.color),
+        anonymized_line_numbers: input.opt.anonymized_line_numbers,
+        margin: None,
+    };
+    assert_eq!(dl::DisplayList::from(input), output);
+}
+
+#[test]
+#[should_panic]
+fn test_i26() {
+    let source = "short";
+    let label = "label";
+    let input = snippet::Snippet {
+        title: None,
+        footer: vec![],
+        slices: vec![snippet::Slice {
+            annotations: vec![snippet::SourceAnnotation {
+                range: (0, source.len() + 1),
+                label,
+                annotation_type: snippet::AnnotationType::Error,
+            }],
+            source,
+            line_start: 0,
+            origin: None,
+            fold: false,
+        }],
+        opt: Default::default(),
+    };
+
+    let _ = dl::DisplayList::from(input);
+}
+
+#[test]
+fn test_i_29() {
+    let snippets = snippet::Snippet {
+        title: Some(snippet::Annotation {
+            id: None,
+            label: Some("oops"),
+            annotation_type: snippet::AnnotationType::Error,
+        }),
+        footer: vec![],
+        slices: vec![snippet::Slice {
+            source: "First line\r\nSecond oops line",
+            line_start: 1,
+            origin: Some("<current file>"),
+            annotations: vec![snippet::SourceAnnotation {
+                range: (19, 23),
+                label: "oops",
+                annotation_type: snippet::AnnotationType::Error,
+            }],
+            fold: true,
+        }],
+        opt: Default::default(),
+    };
+
+    let expected = DisplayList {
+        body: vec![
+            dl::DisplayLine::Raw(dl::DisplayRawLine::Annotation {
+                annotation: dl::Annotation {
+                    annotation_type: dl::DisplayAnnotationType::Error,
+                    id: None,
+                    label: vec![dl::DisplayTextFragment {
+                        content: "oops",
+                        style: dl::DisplayTextStyle::Emphasis,
+                    }],
+                },
+                source_aligned: false,
+                continuation: false,
+            }),
+            dl::DisplayLine::Raw(dl::DisplayRawLine::Origin {
+                path: "<current file>",
+                pos: Some((2, 8)),
+                header_type: dl::DisplayHeaderType::Initial,
+            }),
+            dl::DisplayLine::Source {
+                lineno: None,
+                inline_marks: vec![],
+                line: dl::DisplaySourceLine::Empty,
+            },
+            dl::DisplayLine::Source {
+                lineno: Some(1),
+                inline_marks: vec![],
+                line: dl::DisplaySourceLine::Content {
+                    text: "First line",
+                    range: (0, 10),
+                },
+            },
+            dl::DisplayLine::Source {
+                lineno: Some(2),
+                inline_marks: vec![],
+                line: dl::DisplaySourceLine::Content {
+                    text: "Second oops line",
+                    range: (12, 28),
+                },
+            },
+            dl::DisplayLine::Source {
+                lineno: None,
+                inline_marks: vec![],
+                line: dl::DisplaySourceLine::Annotation {
+                    annotation: dl::Annotation {
+                        annotation_type: dl::DisplayAnnotationType::None,
+                        id: None,
+                        label: vec![dl::DisplayTextFragment {
+                            content: "oops",
+                            style: dl::DisplayTextStyle::Regular,
+                        }],
+                    },
+                    range: (7, 11),
+                    annotation_type: dl::DisplayAnnotationType::Error,
+                    annotation_part: dl::DisplayAnnotationPart::Standalone,
+                },
+            },
+            dl::DisplayLine::Source {
+                lineno: None,
+                inline_marks: vec![],
+                line: dl::DisplaySourceLine::Empty,
+            },
+        ],
+        stylesheet: get_term_style(false),
+        anonymized_line_numbers: false,
+        margin: None,
+    };
+
+    assert_eq!(DisplayList::from(snippets), expected);
+}
diff --git a/tests/fixtures/no-color/issue_9.toml b/tests/fixtures/no-color/issue_9.toml
new file mode 100644
index 0000000..a30563b
--- /dev/null
+++ b/tests/fixtures/no-color/issue_9.toml
@@ -0,0 +1,28 @@
+[title]
+label = "expected one of `.`, `;`, `?`, or an operator, found `for`"
+annotation_type = "Error"
+
+[[slices]]
+source = "let x = vec![1];"
+line_start = 4
+origin = "/code/rust/src/test/ui/annotate-snippet/suggestion.rs"
+[[slices.annotations]]
+label = "move occurs because `x` has type `std::vec::Vec<i32>`, which does not implement the `Copy` trait"
+annotation_type = "Warning"
+range = [4, 5]
+
+[[slices]]
+source = "let y = x;"
+line_start = 7
+[[slices.annotations]]
+label = "value moved here"
+annotation_type = "Warning"
+range = [8, 9]
+
+[[slices]]
+source = "x;"
+line_start = 9
+[[slices.annotations]]
+label = "value used here after move"
+annotation_type = "Error"
+range = [0, 1]
diff --git a/tests/fixtures/no-color/issue_9.txt b/tests/fixtures/no-color/issue_9.txt
new file mode 100644
index 0000000..affe6bc
--- /dev/null
+++ b/tests/fixtures/no-color/issue_9.txt
@@ -0,0 +1,12 @@
+error: expected one of `.`, `;`, `?`, or an operator, found `for`
+ --> /code/rust/src/test/ui/annotate-snippet/suggestion.rs:4:5
+  |
+4 | let x = vec![1];
+  |     - move occurs because `x` has type `std::vec::Vec<i32>`, which does not implement the `Copy` trait
+  |
+7 | let y = x;
+  |         - value moved here
+  |
+9 | x;
+  | ^ value used here after move
+  |
\ No newline at end of file
diff --git a/tests/fixtures/no-color/multiline_annotation.toml b/tests/fixtures/no-color/multiline_annotation.toml
new file mode 100644
index 0000000..c3dc1e9
--- /dev/null
+++ b/tests/fixtures/no-color/multiline_annotation.toml
@@ -0,0 +1,40 @@
+[[slices]]
+source = """
+) -> Option<String> {
+    for ann in annotations {
+        match (ann.range.0, ann.range.1) {
+            (None, None) => continue,
+            (Some(start), Some(end)) if start > end_index || end < start_index => continue,
+            (Some(start), Some(end)) if start >= start_index && end <= end_index => {
+                let label = if let Some(ref label) = ann.label {
+                    format!(" {}", label)
+                } else {
+                    String::from("")
+                };
+
+                return Some(format!(
+                    "{}{}{}",
+                    " ".repeat(start - start_index),
+                    "^".repeat(end - start),
+                    label
+                ));
+            }
+            _ => continue,
+        }
+    }
+"""
+line_start = 51
+origin = "src/format.rs"
+fold = true
+[[slices.annotations]]
+label = "expected `std::option::Option<std::string::String>` because of return type"
+annotation_type = "Warning"
+range = [5, 19]
+[[slices.annotations]]
+label = "expected enum `std::option::Option`, found ()"
+annotation_type = "Error"
+range = [22, 766]
+[title]
+label = "mismatched types"
+id = "E0308"
+annotation_type =  "Error"
diff --git a/tests/fixtures/no-color/multiline_annotation.txt b/tests/fixtures/no-color/multiline_annotation.txt
new file mode 100644
index 0000000..bacdec1
--- /dev/null
+++ b/tests/fixtures/no-color/multiline_annotation.txt
@@ -0,0 +1,14 @@
+error[E0308]: mismatched types
+  --> src/format.rs:51:6
+   |
+51 |   ) -> Option<String> {
+   |        -------------- expected `std::option::Option<std::string::String>` because of return type
+52 | /     for ann in annotations {
+53 | |         match (ann.range.0, ann.range.1) {
+54 | |             (None, None) => continue,
+55 | |             (Some(start), Some(end)) if start > end_index || end < start_index => continue,
+...  |
+71 | |         }
+72 | |     }
+   | |_____^ expected enum `std::option::Option`, found ()
+   |
diff --git a/tests/fixtures/no-color/multiline_annotation2.toml b/tests/fixtures/no-color/multiline_annotation2.toml
new file mode 100644
index 0000000..845bf9f
--- /dev/null
+++ b/tests/fixtures/no-color/multiline_annotation2.toml
@@ -0,0 +1,18 @@
+[[slices]]
+source = """
+                        if let DisplayLine::Source {
+                            ref mut inline_marks,
+                        } = body[body_idx]
+"""
+line_start = 139
+origin = "src/display_list.rs"
+fold = false
+[[slices.annotations]]
+label = "missing fields `lineno`, `content`"
+annotation_type = "Error"
+range = [31, 128]
+
+[title]
+label = "pattern does not mention fields `lineno`, `content`"
+id = "E0027"
+annotation_type = "Error"
diff --git a/tests/fixtures/no-color/multiline_annotation2.txt b/tests/fixtures/no-color/multiline_annotation2.txt
new file mode 100644
index 0000000..8a00bfa
--- /dev/null
+++ b/tests/fixtures/no-color/multiline_annotation2.txt
@@ -0,0 +1,9 @@
+error[E0027]: pattern does not mention fields `lineno`, `content`
+   --> src/display_list.rs:139:32
+    |
+139 |                           if let DisplayLine::Source {
+    |  ________________________________^
+140 | |                             ref mut inline_marks,
+141 | |                         } = body[body_idx]
+    | |_________________________^ missing fields `lineno`, `content`
+    |
diff --git a/tests/fixtures/no-color/multiline_annotation3.toml b/tests/fixtures/no-color/multiline_annotation3.toml
new file mode 100644
index 0000000..21bbcd8
--- /dev/null
+++ b/tests/fixtures/no-color/multiline_annotation3.toml
@@ -0,0 +1,18 @@
+[[slices]]
+source = """
+This is an exampl
+e of an edge case of an annotation overflowing
+to exactly one character on next line.
+"""
+line_start = 26
+origin = "foo.txt"
+fold = false
+[[slices.annotations]]
+label = "this should not be on separate lines"
+annotation_type = "Error"
+range = [11, 18]
+
+[title]
+label = "spacing error found"
+id = "E####"
+annotation_type = "Error"
diff --git a/tests/fixtures/no-color/multiline_annotation3.txt b/tests/fixtures/no-color/multiline_annotation3.txt
new file mode 100644
index 0000000..12e174c
--- /dev/null
+++ b/tests/fixtures/no-color/multiline_annotation3.txt
@@ -0,0 +1,9 @@
+error[E####]: spacing error found
+  --> foo.txt:26:12
+   |
+26 |   This is an exampl
+   |  ____________^
+27 | | e of an edge case of an annotation overflowing
+   | |_^ this should not be on separate lines
+28 |   to exactly one character on next line.
+   |
\ No newline at end of file
diff --git a/tests/fixtures/no-color/multiple_annotations.toml b/tests/fixtures/no-color/multiple_annotations.toml
new file mode 100644
index 0000000..84efc5f
--- /dev/null
+++ b/tests/fixtures/no-color/multiple_annotations.toml
@@ -0,0 +1,25 @@
+[[slices]]
+source = """
+fn add_title_line(result: &mut Vec<String>, main_annotation: Option<&Annotation>) {
+    if let Some(annotation) = main_annotation {
+        result.push(format_title_line(
+            &annotation.annotation_type,
+            None,
+            &annotation.label,
+        ));
+    }
+}
+"""
+line_start = 96
+[[slices.annotations]]
+label = "Variable defined here"
+annotation_type = "Error"
+range = [100, 110]
+[[slices.annotations]]
+label = "Referenced here"
+annotation_type = "Error"
+range = [184, 194]
+[[slices.annotations]]
+label = "Referenced again here"
+annotation_type = "Error"
+range = [243, 253]
diff --git a/tests/fixtures/no-color/multiple_annotations.txt b/tests/fixtures/no-color/multiple_annotations.txt
new file mode 100644
index 0000000..26c677f
--- /dev/null
+++ b/tests/fixtures/no-color/multiple_annotations.txt
@@ -0,0 +1,14 @@
+    |
+ 96 | fn add_title_line(result: &mut Vec<String>, main_annotation: Option<&Annotation>) {
+ 97 |     if let Some(annotation) = main_annotation {
+    |                 ^^^^^^^^^^ Variable defined here
+ 98 |         result.push(format_title_line(
+ 99 |             &annotation.annotation_type,
+    |              ^^^^^^^^^^ Referenced here
+100 |             None,
+101 |             &annotation.label,
+    |              ^^^^^^^^^^ Referenced again here
+102 |         ));
+103 |     }
+104 | }
+    |
diff --git a/tests/fixtures/no-color/simple.toml b/tests/fixtures/no-color/simple.toml
new file mode 100644
index 0000000..6c38674
--- /dev/null
+++ b/tests/fixtures/no-color/simple.toml
@@ -0,0 +1,18 @@
+[[slices]]
+source = """
+        })
+
+        for line in &self.body {"""
+line_start = 169
+origin = "src/format_color.rs"
+[[slices.annotations]]
+label = "unexpected token"
+annotation_type = "Error"
+range = [20, 23]
+[[slices.annotations]]
+label = "expected one of `.`, `;`, `?`, or an operator here"
+annotation_type = "Warning"
+range = [10, 11]
+[title]
+label = "expected one of `.`, `;`, `?`, or an operator, found `for`"
+annotation_type = "Error"
diff --git a/tests/fixtures/no-color/simple.txt b/tests/fixtures/no-color/simple.txt
new file mode 100644
index 0000000..752cc89
--- /dev/null
+++ b/tests/fixtures/no-color/simple.txt
@@ -0,0 +1,9 @@
+error: expected one of `.`, `;`, `?`, or an operator, found `for`
+   --> src/format_color.rs:171:9
+    |
+169 |         })
+    |           - expected one of `.`, `;`, `?`, or an operator here
+170 | 
+171 |         for line in &self.body {
+    |         ^^^ unexpected token
+    |
diff --git a/tests/fixtures/no-color/strip_line.toml b/tests/fixtures/no-color/strip_line.toml
new file mode 100644
index 0000000..76d9519
--- /dev/null
+++ b/tests/fixtures/no-color/strip_line.toml
@@ -0,0 +1,25 @@
+[title]
+id = "E0308"
+label = "mismatched types"
+annotation_type = "Error"
+
+[[slices]]
+source = "                                                                                                                                                                                    let _: () = 42;"
+line_start = 4
+origin = "$DIR/whitespace-trimming.rs"
+
+[[slices.annotations]]
+label = "expected (), found integer"
+annotation_type = "Error"
+range = [192, 194]
+
+[opt]
+color = false
+anonymized_line_numbers = true
+[opt.margin]
+whitespace_left = 180
+span_left = 192
+span_right = 194
+label_right = 221
+column_width = 140
+max_line_len = 195
diff --git a/tests/fixtures/no-color/strip_line.txt b/tests/fixtures/no-color/strip_line.txt
new file mode 100644
index 0000000..65b0538
--- /dev/null
+++ b/tests/fixtures/no-color/strip_line.txt
@@ -0,0 +1,6 @@
+error[E0308]: mismatched types
+  --> $DIR/whitespace-trimming.rs:4:193
+   |
+LL | ...                   let _: () = 42;
+   |                                   ^^ expected (), found integer
+   |
diff --git a/tests/fixtures/no-color/strip_line_char.toml b/tests/fixtures/no-color/strip_line_char.toml
new file mode 100644
index 0000000..5b432be
--- /dev/null
+++ b/tests/fixtures/no-color/strip_line_char.toml
@@ -0,0 +1,25 @@
+[title]
+id = "E0308"
+label = "mismatched types"
+annotation_type = "Error"
+
+[[slices]]
+source = "                                                                                                                                                                                    let _: () = 42ñ"
+line_start = 4
+origin = "$DIR/whitespace-trimming.rs"
+
+[[slices.annotations]]
+label = "expected (), found integer"
+annotation_type = "Error"
+range = [192, 194]
+
+[opt]
+color = false
+anonymized_line_numbers = true
+[opt.margin]
+whitespace_left = 180
+span_left = 192
+span_right = 194
+label_right = 221
+column_width = 140
+max_line_len = 195
diff --git a/tests/fixtures/no-color/strip_line_char.txt b/tests/fixtures/no-color/strip_line_char.txt
new file mode 100644
index 0000000..3d4b700
--- /dev/null
+++ b/tests/fixtures/no-color/strip_line_char.txt
@@ -0,0 +1,6 @@
+error[E0308]: mismatched types
+  --> $DIR/whitespace-trimming.rs:4:193
+   |
+LL | ...                   let _: () = 42ñ
+   |                                   ^^ expected (), found integer
+   |
diff --git a/tests/fixtures/no-color/strip_line_non_ws.toml b/tests/fixtures/no-color/strip_line_non_ws.toml
new file mode 100644
index 0000000..5129f5c
--- /dev/null
+++ b/tests/fixtures/no-color/strip_line_non_ws.toml
@@ -0,0 +1,25 @@
+[title]
+id = "E0308"
+label = "mismatched types"
+annotation_type = "Error"
+
+[[slices]]
+source = "    let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = 42; let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = ();"
+line_start = 4
+origin = "$DIR/non-whitespace-trimming.rs"
+
+[[slices.annotations]]
+label = "expected (), found integer"
+annotation_type = "Error"
+range = [240, 242]
+
+[opt]
+color = false
+anonymized_line_numbers = true
+[opt.margin]
+whitespace_left = 4
+span_left = 240
+span_right = 242
+label_right = 271
+column_width = 140
+max_line_len = 371
diff --git a/tests/fixtures/no-color/strip_line_non_ws.txt b/tests/fixtures/no-color/strip_line_non_ws.txt
new file mode 100644
index 0000000..850619a
--- /dev/null
+++ b/tests/fixtures/no-color/strip_line_non_ws.txt
@@ -0,0 +1,6 @@
+error[E0308]: mismatched types
+  --> $DIR/non-whitespace-trimming.rs:4:241
+   |
+LL | ... = (); let _: () = (); let _: () = (); let _: () = 42; let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = ();...
+   |                                                       ^^ expected (), found integer
+   |
diff --git a/tests/fixtures_test.rs b/tests/fixtures_test.rs
new file mode 100644
index 0000000..e471521
--- /dev/null
+++ b/tests/fixtures_test.rs
@@ -0,0 +1,45 @@
+mod diff;
+mod snippet;
+
+use crate::snippet::SnippetDef;
+use annotate_snippets::{display_list::DisplayList, snippet::Snippet};
+use glob::glob;
+use std::{error::Error, fs::File, io, io::prelude::*};
+
+fn read_file(path: &str) -> Result<String, io::Error> {
+    let mut f = File::open(path)?;
+    let mut s = String::new();
+    (f.read_to_string(&mut s))?;
+    Ok(s.trim_end().to_string())
+}
+
+fn read_fixture<'de>(src: &'de str) -> Result<Snippet<'de>, Box<dyn Error>> {
+    Ok(toml::from_str(src).map(|a: SnippetDef| a.into())?)
+}
+
+#[test]
+fn test_fixtures() {
+    for entry in glob("./tests/fixtures/no-color/**/*.toml").expect("Failed to read glob pattern") {
+        let p = entry.expect("Error while getting an entry");
+
+        let path_in = p.to_str().expect("Can't print path");
+        let path_out = path_in.replace(".toml", ".txt");
+
+        let src = read_file(&path_in).expect("Failed to read file");
+        let snippet = read_fixture(&src).expect("Failed to read file");
+        let expected_out = read_file(&path_out).expect("Failed to read file");
+
+        let dl = DisplayList::from(snippet);
+        let actual_out = dl.to_string();
+        println!("{}", expected_out);
+        println!("{}", actual_out.trim_end());
+
+        assert_eq!(
+            expected_out,
+            actual_out.trim_end(),
+            "\n\n\nWhile parsing: {}\nThe diff is:\n\n\n{}\n\n\n",
+            path_in,
+            diff::get_diff(expected_out.as_str(), actual_out.as_str())
+        );
+    }
+}
diff --git a/tests/formatter.rs b/tests/formatter.rs
new file mode 100644
index 0000000..b1392a1
--- /dev/null
+++ b/tests/formatter.rs
@@ -0,0 +1,675 @@
+use annotate_snippets::display_list::*;
+use annotate_snippets::snippet::{self, Snippet};
+
+#[test]
+fn test_source_empty() {
+    let dl = DisplayList::from(vec![DisplayLine::Source {
+        lineno: None,
+        inline_marks: vec![],
+        line: DisplaySourceLine::Empty,
+    }]);
+
+    assert_eq!(dl.to_string(), " |");
+}
+
+#[test]
+fn test_source_content() {
+    let dl = DisplayList::from(vec![
+        DisplayLine::Source {
+            lineno: Some(56),
+            inline_marks: vec![],
+            line: DisplaySourceLine::Content {
+                text: "This is an example",
+                range: (0, 19),
+            },
+        },
+        DisplayLine::Source {
+            lineno: Some(57),
+            inline_marks: vec![],
+            line: DisplaySourceLine::Content {
+                text: "of content lines",
+                range: (0, 19),
+            },
+        },
+    ]);
+
+    assert_eq!(
+        dl.to_string(),
+        "56 | This is an example\n57 | of content lines"
+    );
+}
+
+#[test]
+fn test_source_annotation_standalone_singleline() {
+    let dl = DisplayList::from(vec![DisplayLine::Source {
+        lineno: None,
+        inline_marks: vec![],
+        line: DisplaySourceLine::Annotation {
+            range: (0, 5),
+            annotation: Annotation {
+                annotation_type: DisplayAnnotationType::None,
+                id: None,
+                label: vec![DisplayTextFragment {
+                    content: "Example string",
+                    style: DisplayTextStyle::Regular,
+                }],
+            },
+            annotation_type: DisplayAnnotationType::Error,
+            annotation_part: DisplayAnnotationPart::Standalone,
+        },
+    }]);
+
+    assert_eq!(dl.to_string(), " | ^^^^^ Example string");
+}
+
+#[test]
+fn test_source_annotation_standalone_multiline() {
+    let dl = DisplayList::from(vec![
+        DisplayLine::Source {
+            lineno: None,
+            inline_marks: vec![],
+            line: DisplaySourceLine::Annotation {
+                range: (0, 5),
+                annotation: Annotation {
+                    annotation_type: DisplayAnnotationType::Help,
+                    id: None,
+                    label: vec![DisplayTextFragment {
+                        content: "Example string",
+                        style: DisplayTextStyle::Regular,
+                    }],
+                },
+                annotation_type: DisplayAnnotationType::Warning,
+                annotation_part: DisplayAnnotationPart::Standalone,
+            },
+        },
+        DisplayLine::Source {
+            lineno: None,
+            inline_marks: vec![],
+            line: DisplaySourceLine::Annotation {
+                range: (0, 5),
+                annotation: Annotation {
+                    annotation_type: DisplayAnnotationType::Help,
+                    id: None,
+                    label: vec![DisplayTextFragment {
+                        content: "Second line",
+                        style: DisplayTextStyle::Regular,
+                    }],
+                },
+                annotation_type: DisplayAnnotationType::Warning,
+                annotation_part: DisplayAnnotationPart::LabelContinuation,
+            },
+        },
+    ]);
+
+    assert_eq!(
+        dl.to_string(),
+        " | ----- help: Example string\n |             Second line"
+    );
+}
+
+#[test]
+fn test_source_annotation_standalone_multi_annotation() {
+    let dl = DisplayList::from(vec![
+        DisplayLine::Source {
+            lineno: None,
+            inline_marks: vec![],
+            line: DisplaySourceLine::Annotation {
+                range: (0, 5),
+                annotation: Annotation {
+                    annotation_type: DisplayAnnotationType::Info,
+                    id: None,
+                    label: vec![DisplayTextFragment {
+                        content: "Example string",
+                        style: DisplayTextStyle::Regular,
+                    }],
+                },
+                annotation_type: DisplayAnnotationType::Note,
+                annotation_part: DisplayAnnotationPart::Standalone,
+            },
+        },
+        DisplayLine::Source {
+            lineno: None,
+            inline_marks: vec![],
+            line: DisplaySourceLine::Annotation {
+                range: (0, 5),
+                annotation: Annotation {
+                    annotation_type: DisplayAnnotationType::Info,
+                    id: None,
+                    label: vec![DisplayTextFragment {
+                        content: "Second line",
+                        style: DisplayTextStyle::Regular,
+                    }],
+                },
+                annotation_type: DisplayAnnotationType::Note,
+                annotation_part: DisplayAnnotationPart::LabelContinuation,
+            },
+        },
+        DisplayLine::Source {
+            lineno: None,
+            inline_marks: vec![],
+            line: DisplaySourceLine::Annotation {
+                range: (0, 5),
+                annotation: Annotation {
+                    annotation_type: DisplayAnnotationType::Warning,
+                    id: None,
+                    label: vec![DisplayTextFragment {
+                        content: "This is a note",
+                        style: DisplayTextStyle::Regular,
+                    }],
+                },
+                annotation_type: DisplayAnnotationType::Note,
+                annotation_part: DisplayAnnotationPart::Consequitive,
+            },
+        },
+        DisplayLine::Source {
+            lineno: None,
+            inline_marks: vec![],
+            line: DisplaySourceLine::Annotation {
+                range: (0, 5),
+                annotation: Annotation {
+                    annotation_type: DisplayAnnotationType::Warning,
+                    id: None,
+                    label: vec![DisplayTextFragment {
+                        content: "Second line of the warning",
+                        style: DisplayTextStyle::Regular,
+                    }],
+                },
+                annotation_type: DisplayAnnotationType::Note,
+                annotation_part: DisplayAnnotationPart::LabelContinuation,
+            },
+        },
+        DisplayLine::Source {
+            lineno: None,
+            inline_marks: vec![],
+            line: DisplaySourceLine::Annotation {
+                range: (0, 5),
+                annotation: Annotation {
+                    annotation_type: DisplayAnnotationType::Info,
+                    id: None,
+                    label: vec![DisplayTextFragment {
+                        content: "This is an info",
+                        style: DisplayTextStyle::Regular,
+                    }],
+                },
+                annotation_type: DisplayAnnotationType::Info,
+                annotation_part: DisplayAnnotationPart::Standalone,
+            },
+        },
+        DisplayLine::Source {
+            lineno: None,
+            inline_marks: vec![],
+            line: DisplaySourceLine::Annotation {
+                range: (0, 5),
+                annotation: Annotation {
+                    annotation_type: DisplayAnnotationType::Help,
+                    id: None,
+                    label: vec![DisplayTextFragment {
+                        content: "This is help",
+                        style: DisplayTextStyle::Regular,
+                    }],
+                },
+                annotation_type: DisplayAnnotationType::Help,
+                annotation_part: DisplayAnnotationPart::Standalone,
+            },
+        },
+        DisplayLine::Source {
+            lineno: None,
+            inline_marks: vec![],
+            line: DisplaySourceLine::Annotation {
+                range: (0, 0),
+                annotation: Annotation {
+                    annotation_type: DisplayAnnotationType::None,
+                    id: None,
+                    label: vec![DisplayTextFragment {
+                        content: "This is an annotation of type none",
+                        style: DisplayTextStyle::Regular,
+                    }],
+                },
+                annotation_type: DisplayAnnotationType::None,
+                annotation_part: DisplayAnnotationPart::Standalone,
+            },
+        },
+    ]);
+
+    assert_eq!(dl.to_string(), " | ----- info: Example string\n |             Second line\n |       warning: This is a note\n |                Second line of the warning\n | ----- info: This is an info\n | ----- help: This is help\n |  This is an annotation of type none");
+}
+
+#[test]
+fn test_fold_line() {
+    let dl = DisplayList::from(vec![
+        DisplayLine::Source {
+            lineno: Some(5),
+            inline_marks: vec![],
+            line: DisplaySourceLine::Content {
+                text: "This is line 5",
+                range: (0, 19),
+            },
+        },
+        DisplayLine::Fold {
+            inline_marks: vec![],
+        },
+        DisplayLine::Source {
+            lineno: Some(10021),
+            inline_marks: vec![],
+            line: DisplaySourceLine::Content {
+                text: "... and now we're at line 10021",
+                range: (0, 19),
+            },
+        },
+    ]);
+
+    assert_eq!(
+        dl.to_string(),
+        "    5 | This is line 5\n...\n10021 | ... and now we're at line 10021"
+    );
+}
+
+#[test]
+fn test_raw_origin_initial_nopos() {
+    let dl = DisplayList::from(vec![DisplayLine::Raw(DisplayRawLine::Origin {
+        path: "src/test.rs",
+        pos: None,
+        header_type: DisplayHeaderType::Initial,
+    })]);
+
+    assert_eq!(dl.to_string(), "--> src/test.rs");
+}
+
+#[test]
+fn test_raw_origin_initial_pos() {
+    let dl = DisplayList::from(vec![DisplayLine::Raw(DisplayRawLine::Origin {
+        path: "src/test.rs",
+        pos: Some((23, 15)),
+        header_type: DisplayHeaderType::Initial,
+    })]);
+
+    assert_eq!(dl.to_string(), "--> src/test.rs:23:15");
+}
+
+#[test]
+fn test_raw_origin_continuation() {
+    let dl = DisplayList::from(vec![DisplayLine::Raw(DisplayRawLine::Origin {
+        path: "src/test.rs",
+        pos: Some((23, 15)),
+        header_type: DisplayHeaderType::Continuation,
+    })]);
+
+    assert_eq!(dl.to_string(), "::: src/test.rs:23:15");
+}
+
+#[test]
+fn test_raw_annotation_unaligned() {
+    let dl = DisplayList::from(vec![DisplayLine::Raw(DisplayRawLine::Annotation {
+        annotation: Annotation {
+            annotation_type: DisplayAnnotationType::Error,
+            id: Some("E0001"),
+            label: vec![DisplayTextFragment {
+                content: "This is an error",
+                style: DisplayTextStyle::Regular,
+            }],
+        },
+        source_aligned: false,
+        continuation: false,
+    })]);
+
+    assert_eq!(dl.to_string(), "error[E0001]: This is an error");
+}
+
+#[test]
+fn test_raw_annotation_unaligned_multiline() {
+    let dl = DisplayList::from(vec![
+        DisplayLine::Raw(DisplayRawLine::Annotation {
+            annotation: Annotation {
+                annotation_type: DisplayAnnotationType::Warning,
+                id: Some("E0001"),
+                label: vec![DisplayTextFragment {
+                    content: "This is an error",
+                    style: DisplayTextStyle::Regular,
+                }],
+            },
+            source_aligned: false,
+            continuation: false,
+        }),
+        DisplayLine::Raw(DisplayRawLine::Annotation {
+            annotation: Annotation {
+                annotation_type: DisplayAnnotationType::Warning,
+                id: Some("E0001"),
+                label: vec![DisplayTextFragment {
+                    content: "Second line of the error",
+                    style: DisplayTextStyle::Regular,
+                }],
+            },
+            source_aligned: false,
+            continuation: true,
+        }),
+    ]);
+
+    assert_eq!(
+        dl.to_string(),
+        "warning[E0001]: This is an error\n                Second line of the error"
+    );
+}
+
+#[test]
+fn test_raw_annotation_aligned() {
+    let dl = DisplayList::from(vec![DisplayLine::Raw(DisplayRawLine::Annotation {
+        annotation: Annotation {
+            annotation_type: DisplayAnnotationType::Error,
+            id: Some("E0001"),
+            label: vec![DisplayTextFragment {
+                content: "This is an error",
+                style: DisplayTextStyle::Regular,
+            }],
+        },
+        source_aligned: true,
+        continuation: false,
+    })]);
+
+    assert_eq!(dl.to_string(), " = error[E0001]: This is an error");
+}
+
+#[test]
+fn test_raw_annotation_aligned_multiline() {
+    let dl = DisplayList::from(vec![
+        DisplayLine::Raw(DisplayRawLine::Annotation {
+            annotation: Annotation {
+                annotation_type: DisplayAnnotationType::Warning,
+                id: Some("E0001"),
+                label: vec![DisplayTextFragment {
+                    content: "This is an error",
+                    style: DisplayTextStyle::Regular,
+                }],
+            },
+            source_aligned: true,
+            continuation: false,
+        }),
+        DisplayLine::Raw(DisplayRawLine::Annotation {
+            annotation: Annotation {
+                annotation_type: DisplayAnnotationType::Warning,
+                id: Some("E0001"),
+                label: vec![DisplayTextFragment {
+                    content: "Second line of the error",
+                    style: DisplayTextStyle::Regular,
+                }],
+            },
+            source_aligned: true,
+            continuation: true,
+        }),
+    ]);
+
+    assert_eq!(
+        dl.to_string(),
+        " = warning[E0001]: This is an error\n                   Second line of the error"
+    );
+}
+
+#[test]
+fn test_different_annotation_types() {
+    let dl = DisplayList::from(vec![
+        DisplayLine::Raw(DisplayRawLine::Annotation {
+            annotation: Annotation {
+                annotation_type: DisplayAnnotationType::Note,
+                id: None,
+                label: vec![DisplayTextFragment {
+                    content: "This is a note",
+                    style: DisplayTextStyle::Regular,
+                }],
+            },
+            source_aligned: false,
+            continuation: false,
+        }),
+        DisplayLine::Raw(DisplayRawLine::Annotation {
+            annotation: Annotation {
+                annotation_type: DisplayAnnotationType::None,
+                id: None,
+                label: vec![DisplayTextFragment {
+                    content: "This is just a string",
+                    style: DisplayTextStyle::Regular,
+                }],
+            },
+            source_aligned: false,
+            continuation: false,
+        }),
+        DisplayLine::Raw(DisplayRawLine::Annotation {
+            annotation: Annotation {
+                annotation_type: DisplayAnnotationType::None,
+                id: None,
+                label: vec![DisplayTextFragment {
+                    content: "Second line of none type annotation",
+                    style: DisplayTextStyle::Regular,
+                }],
+            },
+            source_aligned: false,
+            continuation: true,
+        }),
+    ]);
+
+    assert_eq!(
+        dl.to_string(),
+        "note: This is a note\nThis is just a string\n  Second line of none type annotation",
+    );
+}
+
+#[test]
+fn test_inline_marks_empty_line() {
+    let dl = DisplayList::from(vec![DisplayLine::Source {
+        lineno: None,
+        inline_marks: vec![DisplayMark {
+            mark_type: DisplayMarkType::AnnotationThrough,
+            annotation_type: DisplayAnnotationType::Error,
+        }],
+        line: DisplaySourceLine::Empty,
+    }]);
+
+    assert_eq!(dl.to_string(), " | |",);
+}
+
+#[test]
+fn test_anon_lines() {
+    let mut dl = DisplayList::from(vec![
+        DisplayLine::Source {
+            lineno: Some(56),
+            inline_marks: vec![],
+            line: DisplaySourceLine::Content {
+                text: "This is an example",
+                range: (0, 19),
+            },
+        },
+        DisplayLine::Source {
+            lineno: Some(57),
+            inline_marks: vec![],
+            line: DisplaySourceLine::Content {
+                text: "of content lines",
+                range: (0, 19),
+            },
+        },
+        DisplayLine::Source {
+            lineno: None,
+            inline_marks: vec![],
+            line: DisplaySourceLine::Empty,
+        },
+        DisplayLine::Source {
+            lineno: None,
+            inline_marks: vec![],
+            line: DisplaySourceLine::Content {
+                text: "abc",
+                range: (0, 19),
+            },
+        },
+    ]);
+
+    dl.anonymized_line_numbers = true;
+    assert_eq!(
+        dl.to_string(),
+        "LL | This is an example\nLL | of content lines\n   |\n   | abc"
+    );
+}
+
+#[test]
+fn test_raw_origin_initial_pos_anon_lines() {
+    let mut dl = DisplayList::from(vec![DisplayLine::Raw(DisplayRawLine::Origin {
+        path: "src/test.rs",
+        pos: Some((23, 15)),
+        header_type: DisplayHeaderType::Initial,
+    })]);
+
+    // Using anonymized_line_numbers should not affect the inital position
+    dl.anonymized_line_numbers = true;
+    assert_eq!(dl.to_string(), "--> src/test.rs:23:15");
+}
+
+#[test]
+fn test_i_29() {
+    let snippets = Snippet {
+        title: Some(snippet::Annotation {
+            id: None,
+            label: Some("oops"),
+            annotation_type: snippet::AnnotationType::Error,
+        }),
+        footer: vec![],
+        slices: vec![snippet::Slice {
+            source: "First line\r\nSecond oops line",
+            line_start: 1,
+            origin: Some("<current file>"),
+            annotations: vec![snippet::SourceAnnotation {
+                range: (19, 23),
+                label: "oops",
+                annotation_type: snippet::AnnotationType::Error,
+            }],
+            fold: true,
+        }],
+        opt: Default::default(),
+    };
+    let expected = r#"error: oops
+ --> <current file>:2:8
+  |
+1 | First line
+2 | Second oops line
+  |        ^^^^ oops
+  |"#;
+
+    assert_eq!(DisplayList::from(snippets).to_string(), expected);
+}
+
+#[test]
+fn test_point_to_double_width_characters() {
+    let snippets = Snippet {
+        slices: vec![snippet::Slice {
+            source: "こんにちは、世界",
+            line_start: 1,
+            origin: Some("<current file>"),
+            annotations: vec![snippet::SourceAnnotation {
+                range: (6, 8),
+                label: "world",
+                annotation_type: snippet::AnnotationType::Error,
+            }],
+            fold: false,
+        }],
+        title: None,
+        footer: vec![],
+        opt: Default::default(),
+    };
+
+    let expected = r#" --> <current file>:1:7
+  |
+1 | こんにちは、世界
+  |             ^^^^ world
+  |"#;
+
+    assert_eq!(DisplayList::from(snippets).to_string(), expected);
+}
+
+#[test]
+fn test_point_to_double_width_characters_across_lines() {
+    let snippets = Snippet {
+        slices: vec![snippet::Slice {
+            source: "おはよう\nございます",
+            line_start: 1,
+            origin: Some("<current file>"),
+            annotations: vec![snippet::SourceAnnotation {
+                range: (2, 8),
+                label: "Good morning",
+                annotation_type: snippet::AnnotationType::Error,
+            }],
+            fold: false,
+        }],
+        title: None,
+        footer: vec![],
+        opt: Default::default(),
+    };
+
+    let expected = r#" --> <current file>:1:3
+  |
+1 |   おはよう
+  |  _____^
+2 | | ございます
+  | |______^ Good morning
+  |"#;
+
+    assert_eq!(DisplayList::from(snippets).to_string(), expected);
+}
+
+#[test]
+fn test_point_to_double_width_characters_multiple() {
+    let snippets = Snippet {
+        slices: vec![snippet::Slice {
+            source: "お寿司\n食べたい🍣",
+            line_start: 1,
+            origin: Some("<current file>"),
+            annotations: vec![
+                snippet::SourceAnnotation {
+                    range: (0, 3),
+                    label: "Sushi1",
+                    annotation_type: snippet::AnnotationType::Error,
+                },
+                snippet::SourceAnnotation {
+                    range: (6, 8),
+                    label: "Sushi2",
+                    annotation_type: snippet::AnnotationType::Note,
+                },
+            ],
+            fold: false,
+        }],
+        title: None,
+        footer: vec![],
+        opt: Default::default(),
+    };
+
+    let expected = r#" --> <current file>:1:1
+  |
+1 | お寿司
+  | ^^^^^^ Sushi1
+2 | 食べたい🍣
+  |     ---- note: Sushi2
+  |"#;
+
+    assert_eq!(DisplayList::from(snippets).to_string(), expected);
+}
+
+#[test]
+fn test_point_to_double_width_characters_mixed() {
+    let snippets = Snippet {
+        slices: vec![snippet::Slice {
+            source: "こんにちは、新しいWorld!",
+            line_start: 1,
+            origin: Some("<current file>"),
+            annotations: vec![snippet::SourceAnnotation {
+                range: (6, 14),
+                label: "New world",
+                annotation_type: snippet::AnnotationType::Error,
+            }],
+            fold: false,
+        }],
+        title: None,
+        footer: vec![],
+        opt: Default::default(),
+    };
+
+    let expected = r#" --> <current file>:1:7
+  |
+1 | こんにちは、新しいWorld!
+  |             ^^^^^^^^^^^ New world
+  |"#;
+
+    assert_eq!(DisplayList::from(snippets).to_string(), expected);
+}
diff --git a/tests/snippet/mod.rs b/tests/snippet/mod.rs
new file mode 100644
index 0000000..40249f4
--- /dev/null
+++ b/tests/snippet/mod.rs
@@ -0,0 +1,208 @@
+use serde::{Deserialize, Deserializer, Serialize};
+
+use annotate_snippets::{
+    display_list::{FormatOptions, Margin},
+    snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation},
+};
+
+#[derive(Deserialize)]
+pub struct SnippetDef<'a> {
+    #[serde(deserialize_with = "deserialize_annotation")]
+    #[serde(default)]
+    #[serde(borrow)]
+    pub title: Option<Annotation<'a>>,
+    #[serde(deserialize_with = "deserialize_annotations")]
+    #[serde(default)]
+    #[serde(borrow)]
+    pub footer: Vec<Annotation<'a>>,
+    #[serde(deserialize_with = "deserialize_opt")]
+    #[serde(default)]
+    pub opt: FormatOptions,
+    #[serde(deserialize_with = "deserialize_slices")]
+    #[serde(borrow)]
+    pub slices: Vec<Slice<'a>>,
+}
+
+impl<'a> Into<Snippet<'a>> for SnippetDef<'a> {
+    fn into(self) -> Snippet<'a> {
+        let SnippetDef {
+            title,
+            footer,
+            opt,
+            slices,
+        } = self;
+        Snippet {
+            title,
+            footer,
+            slices,
+            opt,
+        }
+    }
+}
+
+fn deserialize_opt<'de, D>(deserializer: D) -> Result<FormatOptions, D::Error>
+where
+    D: Deserializer<'de>,
+{
+    #[derive(Deserialize)]
+    struct Wrapper(#[serde(with = "FormatOptionsDef")] FormatOptions);
+
+    Wrapper::deserialize(deserializer).map(|w| w.0)
+}
+
+#[derive(Deserialize)]
+#[serde(remote = "FormatOptions")]
+pub struct FormatOptionsDef {
+    #[serde(default)]
+    pub color: bool,
+    #[serde(default)]
+    pub anonymized_line_numbers: bool,
+    #[serde(deserialize_with = "deserialize_margin")]
+    #[serde(default)]
+    pub margin: Option<Margin>,
+}
+
+fn deserialize_margin<'de, D>(deserializer: D) -> Result<Option<Margin>, D::Error>
+where
+    D: Deserializer<'de>,
+{
+    #[derive(Deserialize)]
+    struct Wrapper {
+        whitespace_left: usize,
+        span_left: usize,
+        span_right: usize,
+        label_right: usize,
+        column_width: usize,
+        max_line_len: usize,
+    }
+
+    Option::<Wrapper>::deserialize(deserializer).map(|opt_wrapped: Option<Wrapper>| {
+        opt_wrapped.map(|wrapped: Wrapper| {
+            let Wrapper {
+                whitespace_left,
+                span_left,
+                span_right,
+                label_right,
+                column_width,
+                max_line_len,
+            } = wrapped;
+            Margin::new(
+                whitespace_left,
+                span_left,
+                span_right,
+                label_right,
+                column_width,
+                max_line_len,
+            )
+        })
+    })
+}
+
+fn deserialize_slices<'de, D>(deserializer: D) -> Result<Vec<Slice<'de>>, D::Error>
+where
+    D: Deserializer<'de>,
+{
+    #[derive(Deserialize)]
+    struct Wrapper<'a>(
+        #[serde(with = "SliceDef")]
+        #[serde(borrow)]
+        Slice<'a>,
+    );
+
+    let v = Vec::deserialize(deserializer)?;
+    Ok(v.into_iter().map(|Wrapper(a)| a).collect())
+}
+
+fn deserialize_annotation<'de, D>(deserializer: D) -> Result<Option<Annotation<'de>>, D::Error>
+where
+    D: Deserializer<'de>,
+{
+    #[derive(Deserialize)]
+    struct Wrapper<'a>(
+        #[serde(with = "AnnotationDef")]
+        #[serde(borrow)]
+        Annotation<'a>,
+    );
+
+    Option::<Wrapper>::deserialize(deserializer)
+        .map(|opt_wrapped: Option<Wrapper>| opt_wrapped.map(|wrapped: Wrapper| wrapped.0))
+}
+
+fn deserialize_annotations<'de, D>(deserializer: D) -> Result<Vec<Annotation<'de>>, D::Error>
+where
+    D: Deserializer<'de>,
+{
+    #[derive(Deserialize)]
+    struct Wrapper<'a>(
+        #[serde(with = "AnnotationDef")]
+        #[serde(borrow)]
+        Annotation<'a>,
+    );
+
+    let v = Vec::deserialize(deserializer)?;
+    Ok(v.into_iter().map(|Wrapper(a)| a).collect())
+}
+
+#[derive(Deserialize)]
+#[serde(remote = "Slice")]
+pub struct SliceDef<'a> {
+    #[serde(borrow)]
+    pub source: &'a str,
+    pub line_start: usize,
+    #[serde(borrow)]
+    pub origin: Option<&'a str>,
+    #[serde(deserialize_with = "deserialize_source_annotations")]
+    #[serde(borrow)]
+    pub annotations: Vec<SourceAnnotation<'a>>,
+    #[serde(default)]
+    pub fold: bool,
+}
+
+fn deserialize_source_annotations<'de, D>(
+    deserializer: D,
+) -> Result<Vec<SourceAnnotation<'de>>, D::Error>
+where
+    D: Deserializer<'de>,
+{
+    #[derive(Deserialize)]
+    struct Wrapper<'a>(
+        #[serde(with = "SourceAnnotationDef")]
+        #[serde(borrow)]
+        SourceAnnotation<'a>,
+    );
+
+    let v = Vec::deserialize(deserializer)?;
+    Ok(v.into_iter().map(|Wrapper(a)| a).collect())
+}
+
+#[derive(Serialize, Deserialize)]
+#[serde(remote = "SourceAnnotation")]
+pub struct SourceAnnotationDef<'a> {
+    pub range: (usize, usize),
+    #[serde(borrow)]
+    pub label: &'a str,
+    #[serde(with = "AnnotationTypeDef")]
+    pub annotation_type: AnnotationType,
+}
+
+#[derive(Serialize, Deserialize)]
+#[serde(remote = "Annotation")]
+pub struct AnnotationDef<'a> {
+    #[serde(borrow)]
+    pub id: Option<&'a str>,
+    #[serde(borrow)]
+    pub label: Option<&'a str>,
+    #[serde(with = "AnnotationTypeDef")]
+    pub annotation_type: AnnotationType,
+}
+
+#[allow(dead_code)]
+#[derive(Serialize, Deserialize)]
+#[serde(remote = "AnnotationType")]
+enum AnnotationTypeDef {
+    Error,
+    Warning,
+    Info,
+    Note,
+    Help,
+}