diff --git a/.clippy.toml b/.clippy.toml
new file mode 100644
index 00000000..2e52a640
--- /dev/null
+++ b/.clippy.toml
@@ -0,0 +1,4 @@
+allow-print-in-tests = true
+allow-expect-in-tests = true
+allow-unwrap-in-tests = true
+allow-dbg-in-tests = true
diff --git a/.github/renovate.json5 b/.github/renovate.json5
new file mode 100644
index 00000000..7ab13b9f
--- /dev/null
+++ b/.github/renovate.json5
@@ -0,0 +1,109 @@
+{
+  schedule: [
+    'before 5am on the first day of the month',
+  ],
+  semanticCommits: 'enabled',
+  commitMessageLowerCase: 'never',
+  configMigration: true,
+  dependencyDashboard: true,
+  customManagers: [
+    {
+      customType: 'regex',
+      fileMatch: [
+        '^rust-toolchain\\.toml$',
+        'Cargo.toml$',
+        'clippy.toml$',
+        '\\.clippy.toml$',
+        '^\\.github/workflows/ci.yml$',
+        '^\\.github/workflows/rust-next.yml$',
+      ],
+      matchStrings: [
+        'STABLE.*?(?<currentValue>\\d+\\.\\d+(\\.\\d+)?)',
+        '(?<currentValue>\\d+\\.\\d+(\\.\\d+)?).*?STABLE',
+      ],
+      depNameTemplate: 'STABLE',
+      packageNameTemplate: 'rust-lang/rust',
+      datasourceTemplate: 'github-releases',
+    },
+  ],
+  packageRules: [
+    {
+      commitMessageTopic: 'Rust Stable',
+      matchManagers: [
+        'custom.regex',
+      ],
+      matchDepNames: [
+        'STABLE',
+      ],
+      extractVersion: '^(?<version>\\d+\\.\\d+)',  // Drop the patch version
+      schedule: [
+        '* * * * *',
+      ],
+      automerge: true,
+    },
+    // Goals:
+    // - Keep version reqs low, ignoring compatible normal/build dependencies
+    // - Take advantage of latest dev-dependencies
+    // - Rollup safe upgrades to reduce CI runner load
+    // - Help keep number of versions down by always using latest breaking change
+    // - Have lockfile and manifest in-sync
+    {
+      matchManagers: [
+        'cargo',
+      ],
+      matchDepTypes: [
+        'build-dependencies',
+        'dependencies',
+      ],
+      matchCurrentVersion: '>=0.1.0',
+      matchUpdateTypes: [
+        'patch',
+      ],
+      enabled: false,
+    },
+    {
+      matchManagers: [
+        'cargo',
+      ],
+      matchDepTypes: [
+        'build-dependencies',
+        'dependencies',
+      ],
+      matchCurrentVersion: '>=1.0.0',
+      matchUpdateTypes: [
+        'minor',
+        'patch',
+      ],
+      enabled: false,
+    },
+    {
+      matchManagers: [
+        'cargo',
+      ],
+      matchDepTypes: [
+        'dev-dependencies',
+      ],
+      matchCurrentVersion: '>=0.1.0',
+      matchUpdateTypes: [
+        'patch',
+      ],
+      automerge: true,
+      groupName: 'compatible (dev)',
+    },
+    {
+      matchManagers: [
+        'cargo',
+      ],
+      matchDepTypes: [
+        'dev-dependencies',
+      ],
+      matchCurrentVersion: '>=1.0.0',
+      matchUpdateTypes: [
+        'minor',
+        'patch',
+      ],
+      automerge: true,
+      groupName: 'compatible (dev)',
+    },
+  ],
+}
diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml
new file mode 100644
index 00000000..2da233df
--- /dev/null
+++ b/.github/workflows/audit.yml
@@ -0,0 +1,53 @@
+name: Security audit
+
+permissions:
+  contents: read
+
+on:
+  pull_request:
+    paths:
+      - '**/Cargo.toml'
+      - '**/Cargo.lock'
+  push:
+    branches:
+    - master
+
+env:
+  RUST_BACKTRACE: 1
+  CARGO_TERM_COLOR: always
+  CLICOLOR: 1
+
+concurrency:
+  group: "${{ github.workflow }}-${{ github.ref }}"
+  cancel-in-progress: true
+
+jobs:
+  security_audit:
+    permissions:
+      issues: write # to create issues (actions-rs/audit-check)
+      checks: write # to create check (actions-rs/audit-check)
+    runs-on: ubuntu-latest
+    # Prevent sudden announcement of a new advisory from failing ci:
+    continue-on-error: true
+    steps:
+    - name: Checkout repository
+      uses: actions/checkout@v4
+    - uses: actions-rs/audit-check@v1
+      with:
+        token: ${{ secrets.GITHUB_TOKEN }}
+
+  cargo_deny:
+    permissions:
+      issues: write # to create issues (actions-rs/audit-check)
+      checks: write # to create check (actions-rs/audit-check)
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        checks:
+          - bans licenses sources
+    steps:
+    - uses: actions/checkout@v4
+    - uses: EmbarkStudios/cargo-deny-action@v2
+      with:
+        command: check ${{ matrix.checks }}
+        rust-version: stable
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 00000000..aa6d8bbd
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,177 @@
+name: CI
+
+permissions:
+  contents: read
+
+on:
+  pull_request:
+  push:
+    branches:
+    - master
+
+env:
+  RUST_BACKTRACE: 1
+  CARGO_TERM_COLOR: always
+  CLICOLOR: 1
+
+concurrency:
+  group: "${{ github.workflow }}-${{ github.ref }}"
+  cancel-in-progress: true
+
+jobs:
+  ci:
+    permissions:
+      contents: none
+    name: CI
+    needs: [test, msrv, lockfile, docs, rustfmt, clippy, minimal-versions]
+    runs-on: ubuntu-latest
+    if: "always()"
+    steps:
+      - name: Failed
+        run: exit 1
+        if: "contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') || contains(needs.*.result, 'skipped')"
+  test:
+    name: Test
+    strategy:
+      matrix:
+        os: ["ubuntu-latest", "windows-latest", "macos-latest"]
+        rust: ["stable"]
+    continue-on-error: ${{ matrix.rust != 'stable' }}
+    runs-on: ${{ matrix.os }}
+    steps:
+    - name: Checkout repository
+      uses: actions/checkout@v4
+    - name: Install Rust
+      uses: dtolnay/rust-toolchain@stable
+      with:
+        toolchain: ${{ matrix.rust }}
+    - uses: Swatinem/rust-cache@v2
+    - uses: taiki-e/install-action@cargo-hack
+    - name: Build
+      run: cargo test --workspace --no-run
+    - name: Test
+      run: cargo hack test --feature-powerset --workspace
+  msrv:
+    name: "Check MSRV"
+    runs-on: ubuntu-latest
+    steps:
+    - name: Checkout repository
+      uses: actions/checkout@v4
+    - name: Install Rust
+      uses: dtolnay/rust-toolchain@stable
+      with:
+        toolchain: stable
+    - uses: Swatinem/rust-cache@v2
+    - uses: taiki-e/install-action@cargo-hack
+    - name: Default features
+      run: cargo hack check --feature-powerset --locked --rust-version --ignore-private --workspace --lib --bins
+  minimal-versions:
+    name: Minimal versions
+    runs-on: ubuntu-latest
+    steps:
+    - name: Checkout repository
+      uses: actions/checkout@v4
+    - name: Install stable Rust
+      uses: dtolnay/rust-toolchain@stable
+      with:
+        toolchain: stable
+    - name: Install nightly Rust
+      uses: dtolnay/rust-toolchain@stable
+      with:
+        toolchain: nightly
+    - name: Downgrade dependencies to minimal versions
+      run: cargo +nightly generate-lockfile -Z minimal-versions
+    - name: Compile with minimal versions
+      run: cargo +stable check --workspace --all-features --locked
+  lockfile:
+    runs-on: ubuntu-latest
+    steps:
+    - name: Checkout repository
+      uses: actions/checkout@v4
+    - name: Install Rust
+      uses: dtolnay/rust-toolchain@stable
+      with:
+        toolchain: stable
+    - uses: Swatinem/rust-cache@v2
+    - name: "Is lockfile updated?"
+      run: cargo update --workspace --locked
+  docs:
+    name: Docs
+    runs-on: ubuntu-latest
+    steps:
+    - name: Checkout repository
+      uses: actions/checkout@v4
+    - name: Install Rust
+      uses: dtolnay/rust-toolchain@stable
+      with:
+        toolchain: "1.86"  # STABLE
+    - uses: Swatinem/rust-cache@v2
+    - name: Check documentation
+      env:
+        RUSTDOCFLAGS: -D warnings
+      run: cargo doc --workspace --all-features --no-deps --document-private-items
+  rustfmt:
+    name: rustfmt
+    runs-on: ubuntu-latest
+    steps:
+    - name: Checkout repository
+      uses: actions/checkout@v4
+    - name: Install Rust
+      uses: dtolnay/rust-toolchain@stable
+      with:
+        toolchain: "1.86"  # STABLE
+        components: rustfmt
+    - uses: Swatinem/rust-cache@v2
+    - name: Check formatting
+      run: cargo fmt --all -- --check
+  clippy:
+    name: clippy
+    runs-on: ubuntu-latest
+    permissions:
+      security-events: write # to upload sarif results
+    steps:
+    - name: Checkout repository
+      uses: actions/checkout@v4
+    - name: Install Rust
+      uses: dtolnay/rust-toolchain@stable
+      with:
+        toolchain: "1.86"  # STABLE
+        components: clippy
+    - uses: Swatinem/rust-cache@v2
+    - name: Install SARIF tools
+      run: cargo install clippy-sarif --locked
+    - name: Install SARIF tools
+      run: cargo install sarif-fmt --locked
+    - name: Check
+      run: >
+        cargo clippy --workspace --all-features --all-targets --message-format=json
+        | clippy-sarif
+        | tee clippy-results.sarif
+        | sarif-fmt
+      continue-on-error: true
+    - name: Upload
+      uses: github/codeql-action/upload-sarif@v3
+      with:
+        sarif_file: clippy-results.sarif
+        wait-for-processing: true
+    - name: Report status
+      run: cargo clippy --workspace --all-features --all-targets -- -D warnings --allow deprecated
+  coverage:
+    name: Coverage
+    runs-on: ubuntu-latest
+    steps:
+    - name: Checkout repository
+      uses: actions/checkout@v4
+    - name: Install Rust
+      uses: dtolnay/rust-toolchain@stable
+      with:
+        toolchain: stable
+    - uses: Swatinem/rust-cache@v2
+    - name: Install cargo-tarpaulin
+      run: cargo install cargo-tarpaulin
+    - name: Gather coverage
+      run: cargo tarpaulin --output-dir coverage --out lcov --timeout 120
+    - name: Publish to Coveralls
+      uses: coverallsapp/github-action@master
+      with:
+        github-token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/committed.yml b/.github/workflows/committed.yml
new file mode 100644
index 00000000..e7a50fbb
--- /dev/null
+++ b/.github/workflows/committed.yml
@@ -0,0 +1,28 @@
+# Not run as part of pre-commit checks because they don't handle sending the correct commit
+# range to `committed`
+name: Lint Commits
+on: [pull_request]
+
+permissions:
+  contents: read
+
+env:
+  RUST_BACKTRACE: 1
+  CARGO_TERM_COLOR: always
+  CLICOLOR: 1
+
+concurrency:
+  group: "${{ github.workflow }}-${{ github.ref }}"
+  cancel-in-progress: true
+
+jobs:
+  committed:
+    name: Lint Commits
+    runs-on: ubuntu-latest
+    steps:
+    - name: Checkout Actions Repository
+      uses: actions/checkout@v4
+      with:
+        fetch-depth: 0
+    - name: Lint Commits
+      uses: crate-ci/committed@master
diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml
new file mode 100644
index 00000000..491030a1
--- /dev/null
+++ b/.github/workflows/pre-commit.yml
@@ -0,0 +1,29 @@
+name: pre-commit
+
+permissions: {} # none
+
+on:
+  pull_request:
+  push:
+    branches: [master]
+
+env:
+  RUST_BACKTRACE: 1
+  CARGO_TERM_COLOR: always
+  CLICOLOR: 1
+
+concurrency:
+  group: "${{ github.workflow }}-${{ github.ref }}"
+  cancel-in-progress: true
+
+jobs:
+  pre-commit:
+    permissions:
+      contents: read
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v4
+    - uses: actions/setup-python@v5
+      with:
+        python-version: '3.x'
+    - uses: pre-commit/action@v3.0.1
diff --git a/.github/workflows/rust-next.yml b/.github/workflows/rust-next.yml
new file mode 100644
index 00000000..e98386c4
--- /dev/null
+++ b/.github/workflows/rust-next.yml
@@ -0,0 +1,61 @@
+name: rust-next
+
+permissions:
+  contents: read
+
+on:
+  schedule:
+  - cron: '1 1 1 * *'
+
+env:
+  RUST_BACKTRACE: 1
+  CARGO_TERM_COLOR: always
+  CLICOLOR: 1
+
+concurrency:
+  group: "${{ github.workflow }}-${{ github.ref }}"
+  cancel-in-progress: true
+
+jobs:
+  test:
+    name: Test
+    strategy:
+      matrix:
+        os: ["ubuntu-latest", "windows-latest", "macos-latest"]
+        rust: ["stable", "beta"]
+        include:
+        - os: ubuntu-latest
+          rust: "nightly"
+    continue-on-error: ${{ matrix.rust != 'stable' }}
+    runs-on: ${{ matrix.os }}
+    steps:
+    - name: Checkout repository
+      uses: actions/checkout@v4
+    - name: Install Rust
+      uses: dtolnay/rust-toolchain@stable
+      with:
+        toolchain: ${{ matrix.rust }}
+    - uses: Swatinem/rust-cache@v2
+    - uses: taiki-e/install-action@cargo-hack
+    - name: Build
+      run: cargo test --workspace --no-run
+    - name: Test
+      run: cargo hack test --feature-powerset --workspace
+  latest:
+    name: "Check latest dependencies"
+    runs-on: ubuntu-latest
+    steps:
+    - name: Checkout repository
+      uses: actions/checkout@v4
+    - name: Install Rust
+      uses: dtolnay/rust-toolchain@stable
+      with:
+        toolchain: stable
+    - uses: Swatinem/rust-cache@v2
+    - uses: taiki-e/install-action@cargo-hack
+    - name: Update dependencies
+      run: cargo update
+    - name: Build
+      run: cargo test --workspace --no-run
+    - name: Test
+      run: cargo hack test --feature-powerset --workspace
diff --git a/.github/workflows/spelling.yml b/.github/workflows/spelling.yml
new file mode 100644
index 00000000..8e58d9ec
--- /dev/null
+++ b/.github/workflows/spelling.yml
@@ -0,0 +1,25 @@
+name: Spelling
+
+permissions:
+  contents: read
+
+on: [pull_request]
+
+env:
+  RUST_BACKTRACE: 1
+  CARGO_TERM_COLOR: always
+  CLICOLOR: 1
+
+concurrency:
+  group: "${{ github.workflow }}-${{ github.ref }}"
+  cancel-in-progress: true
+
+jobs:
+  spelling:
+    name: Spell Check with Typos
+    runs-on: ubuntu-latest
+    steps:
+    - name: Checkout Actions Repository
+      uses: actions/checkout@v4
+    - name: Spell Check Repo
+      uses: crate-ci/typos@master
diff --git a/.gitignore b/.gitignore
index 69369904..eb5a316c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1 @@
-/target
-**/*.rs.bk
-Cargo.lock
+target
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 00000000..68db968e
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,26 @@
+repos:
+  - repo: https://github.com/pre-commit/pre-commit-hooks
+    rev: v4.5.0
+    hooks:
+    - id: check-yaml
+      stages: [commit]
+    - id: check-json
+      stages: [commit]
+    - id: check-toml
+      stages: [commit]
+    - id: check-merge-conflict
+      stages: [commit]
+    - id: check-case-conflict
+      stages: [commit]
+    - id: detect-private-key
+      stages: [commit]
+  - repo: https://github.com/crate-ci/typos
+    rev: v1.16.20
+    hooks:
+    - id: typos
+      stages: [commit]
+  - repo: https://github.com/crate-ci/committed
+    rev: v1.0.20
+    hooks:
+    - id: committed
+      stages: [commit-msg]
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index e186cc24..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,30 +0,0 @@
-language: rust
-sudo: required
-dist: trusty
-addons:
-    apt:
-        packages:
-            - libssl-dev
-cache: cargo
-rust:
-  - stable
-  - beta
-  - nightly
-matrix:
-  allow_failures:
-    - rust: nightly
-
-before_cache: |
-  if [[ "$TRAVIS_RUST_VERSION" == nightly ]]; then
-    RUSTFLAGS="--cfg procmacro2_semver_exempt" cargo install cargo-tarpaulin -f
-  fi
-
-script:
-- cargo clean
-- cargo build
-- cargo test
-
-after_success: |
-  if [[ "$TRAVIS_RUST_VERSION" == nightly ]]; then
-    cargo tarpaulin --ciserver travis-ci --coveralls $TRAVIS_JOB_ID
-  fi
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 21e5f2fe..ed078cb1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,39 +1,185 @@
-# Changelog
- 
-## Unreleased
- 
-  - …
+# Change Log
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](http://keepachangelog.com/)
+and this project adheres to [Semantic Versioning](http://semver.org/).
+
+<!-- next-header -->
+## [Unreleased] - ReleaseDate
+
+## [0.11.5] - 2024-12-09
+
+### Added
+
+- `rustc`'s multiline annotation special case [#133](https://github.com/rust-lang/annotate-snippets-rs/pull/133)
+  - This special case happens when:
+    - The start of a multiline annotation is at the start of the line disregarding any leading whitespace
+    - No other multiline annotations overlap it
+- `simd` feature for faster folding [#146](https://github.com/rust-lang/annotate-snippets-rs/pull/146)
+
+### Changed
+
+- Multiline annotations with matching spans get merged [#133](https://github.com/rust-lang/annotate-snippets-rs/pull/133)
+- Multiple annotations on one line are no longer rendered on separate lines [#133](https://github.com/rust-lang/annotate-snippets-rs/pull/133)
+
+### Fixed
+
+- Overlapping multiline annotations are now correctly rendered [#133](https://github.com/rust-lang/annotate-snippets-rs/pull/133)
+- Origin position is now correctly calculated when an annotation starts at the beginning of the line [#154](https://github.com/rust-lang/annotate-snippets-rs/pull/154)
+
+## [0.11.4] - 2024-06-15
+
+### Fixes
+
+- Annotations for `\r\n` are now correctly handled [#131](https://github.com/rust-lang/annotate-snippets-rs/pull/131)
+
+## [0.11.3] - 2024-06-06
+
+### Fixes
+
+- Dropped MSRV to 1.65
+
+## [0.11.2] - 2024-04-27
+
+### Added
+
+- All public types now implement `Debug` [#119](https://github.com/rust-lang/annotate-snippets-rs/pull/119)
+
+## [0.11.1] - 2024-03-21
+
+### Fixes
+
+- Switch `fold` to use rustc's logic: always show first and last line of folded section and detect if its worth folding
+- When `fold`ing the start of a `source`, don't show anything, like we do for the end of the `source`
+- Render an underline for an empty span on `Annotation`s
+
+## [0.11.0] - 2024-03-15
+
+### Breaking Changes
 
-## annotate-snippets 0.9.1 (September 4, 2021)
+- Switched from char spans to byte spans [#90](https://github.com/rust-lang/annotate-snippets-rs/pull/90/commits/b65b8cabcd34da9fed88490a7a1cd8085777706a)
+- Renamed `AnnotationType` to `Level` [#94](https://github.com/rust-lang/annotate-snippets-rs/pull/94/commits/b49f9471d920c7f561fa61970039b0ba44e448ac)
+- Renamed `SourceAnnotation` to `Annotation` [#94](https://github.com/rust-lang/annotate-snippets-rs/pull/94/commits/bbf9c5fe27e83652433151cbfc7d6cafc02a8c47)
+- Renamed `Snippet` to `Message` [#94](https://github.com/rust-lang/annotate-snippets-rs/pull/94/commits/105da760b6e1bd4cfce4c642ac679ecf6011f511)
+- Renamed `Slice` to `Snippet` [#94](https://github.com/rust-lang/annotate-snippets-rs/pull/94/commits/1c18950300cf8b93d92d89e9797ed0bae02c0a37)
+- `Message`, `Snippet`, `Annotation` and `Level` can only be built with a builder pattern [#91](https://github.com/rust-lang/annotate-snippets-rs/pull/91) and [#94](https://github.com/rust-lang/annotate-snippets-rs/pull/94)
+- `Annotation` labels are now optional [#94](https://github.com/rust-lang/annotate-snippets-rs/pull/94/commits/c821084068a1acd2688b6c8d0b3423e143d359e2)
+- `Annotation` now takes in `Range<usize>` instead of `(usize, usize)` [#90](https://github.com/rust-lang/annotate-snippets-rs/pull/90/commits/c3bd0c3a63f983f5f2b4793a099972b1f6e97a9f)
+- `Margin` is now an internal detail, only `term_width` is exposed [#105](https://github.com/rust-lang/annotate-snippets-rs/pull/105)
+- `footer` was generalized to be a `Message` [#98](https://github.com/rust-lang/annotate-snippets-rs/pull/98)
 
-  - 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)
+### Added
+- `term_width` was added to `Renderer` to control the rendering width [#105](https://github.com/rust-lang/annotate-snippets-rs/pull/105)
+  - defaults to 140 when not set
 
-## annotate-snippets 0.9.0 (June 28, 2020)
+### Fixed
+- `Margin`s are now calculated per `Snippet`, rather than for the entire `Message` [#105](https://github.com/rust-lang/annotate-snippets-rs/pull/105)
+- `Annotation`s can be created without labels
 
-  - Add strip code to the left and right of long lines. (#36)
+### Features
+- `footer` was expanded to allow annotating sources by accepting `Message` [#98](https://github.com/rust-lang/annotate-snippets-rs/pull/98)
 
-## annotate-snippets 0.8.0 (April 14, 2020)
+## [0.10.2] - 2024-02-29
 
-  - 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)
+### Added
 
-## annotate-snippets 0.7.0 (March 30, 2020)
+- Added `testing-colors` feature to remove platform-specific colors when testing
+  [#82](https://github.com/rust-lang/annotate-snippets-rs/pull/82)
 
-  - 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.
+## [0.10.1] - 2024-01-04
 
-## annotate-snippets 0.6.1 (July 23, 2019)
+### Fixed
 
-  - Fix too many anonymized line numbers (#5)
+- Match `rustc`'s colors [#73](https://github.com/rust-lang/annotate-snippets-rs/pull/73)
+- Allow highlighting one past the end of `source` [#74](https://github.com/rust-lang/annotate-snippets-rs/pull/74)
+
+### Compatibility
+
+- Set the minimum supported Rust version to `1.73.0` [#71](https://github.com/rust-lang/annotate-snippets-rs/pull/71)
+
+## [0.10.0] - December 12, 2023
+
+### Added
+
+- `Renderer` is now used for displaying a `Snippet` [#67](https://github.com/rust-lang/annotate-snippets-rs/pull/67/commits/9076cbf66336e5137b47dc7a52df2999b6c82598)
+  - `Renderer` also controls the color scheme and formatting of the snippet
+
+### Changed
+
+- Moved everything in the `snippet` to be in the crate root [#67](https://github.com/rust-lang/annotate-snippets-rs/pull/67/commits/a1007ddf2fc6f76e960a4fc01207228e64e9fae7)
+
+### Breaking Changes
+
+- `Renderer` now controls the color scheme and formatting of `Snippet`s [#67](https://github.com/rust-lang/annotate-snippets-rs/pull/67/commits/d0c65b26493d60f86a82c5919ef736b35808c23a)
+- Removed the `Style` and `Stylesheet` traits, as color is controlled by `Renderer` [#67](https://github.com/rust-lang/annotate-snippets-rs/pull/67/commits/4affdfb50ea0670d85e52737c082c03f89ae8ada)
+- Replaced [`yansi-term`](https://crates.io/crates/yansi-term) with [`anstyle`](https://crates.io/crates/anstyle) [#67](https://github.com/rust-lang/annotate-snippets-rs/pull/67/commits/dfd4e87d6f31ec50d29af26d7310cff5e66ca978)
+  - `anstyle` is designed primarily to exist in public APIs for interoperability 
+  - `anstyle` is re-exported under `annotate_snippets::renderer`
+- Removed the `color` feature in favor of `Renderer::plain()` [#67](https://github.com/rust-lang/annotate-snippets-rs/pull/67/commits/dfd4e87d6f31ec50d29af26d7310cff5e66ca978)
+- Moved `Margin` to `renderer` module [#67](https://github.com/rust-lang/annotate-snippets-rs/pull/67/commits/79f657ea252c3c0ce55fa69894ee520f8820b4bf)
+- Made the `display_list` module private [#67](https://github.com/rust-lang/annotate-snippets-rs/pull/67/commits/da45f4858af3ec4c0d792ecc40225e27fdd2bac8)
+
+### Compatibility
+
+- Changed the edition to `2021` [#61](https://github.com/rust-lang/annotate-snippets-rs/pull/61)
+- Set the minimum supported Rust version to `1.70.0` [#61](https://github.com/rust-lang/annotate-snippets-rs/pull/61)
+
+## [0.9.2] - October 30, 2023
+
+- Remove parsing of __ in title strings, fixes (#53)
+- Origin line number is not correct when using a slice with fold: true (#52)
+
+## [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)
+
+## [0.9.0] - June 28, 2020
+
+- Add strip code to the left and right of long lines. (#36)
+
+## [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)
+
+## [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.
+
+## [0.6.1] - July 23, 2019
+
+- Fix too many anonymized line numbers (#5)
  
-## annotate-snippets 0.6.0 (June 26, 2019)
+## [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)
+- Add an option to anonymize line numbers (#3)
+- Transition the crate to rust-lang org.
+- Update the syntax to Rust 2018 idioms. (#4)
+
+<!-- next-url -->
+[Unreleased]: https://github.com/rust-lang/annotate-snippets-rs/compare/0.11.5...HEAD
+[0.11.5]: https://github.com/rust-lang/annotate-snippets-rs/compare/0.11.4...0.11.5
+[0.11.4]: https://github.com/rust-lang/annotate-snippets-rs/compare/0.11.3...0.11.4
+[0.11.3]: https://github.com/rust-lang/annotate-snippets-rs/compare/0.11.2...0.11.3
+[0.11.2]: https://github.com/rust-lang/annotate-snippets-rs/compare/0.11.1...0.11.2
+[0.11.1]: https://github.com/rust-lang/annotate-snippets-rs/compare/0.11.0...0.11.1
+[0.11.0]: https://github.com/rust-lang/annotate-snippets-rs/compare/0.10.2...0.11.0
+[0.10.2]: https://github.com/rust-lang/annotate-snippets-rs/compare/0.10.1...0.10.2
+[0.10.1]: https://github.com/rust-lang/annotate-snippets-rs/compare/0.10.0...0.10.1
+[0.10.0]: https://github.com/rust-lang/annotate-snippets-rs/compare/0.9.2...0.10.0
+[0.9.2]: https://github.com/rust-lang/annotate-snippets-rs/compare/0.9.1...0.9.2
+[0.9.1]: https://github.com/rust-lang/annotate-snippets-rs/compare/0.9.0...0.9.1
+[0.9.0]: https://github.com/rust-lang/annotate-snippets-rs/compare/0.8.0...0.9.0
+[0.8.0]: https://github.com/rust-lang/annotate-snippets-rs/compare/0.7.0...0.8.0
+[0.7.0]: https://github.com/rust-lang/annotate-snippets-rs/compare/0.6.1...0.7.0
+[0.6.1]: https://github.com/rust-lang/annotate-snippets-rs/compare/0.6.0...0.6.1
+[0.6.0]: https://github.com/rust-lang/annotate-snippets-rs/compare/0.5.0...0.6.0
+[0.5.0]: https://github.com/rust-lang/annotate-snippets-rs/compare/0.1.0...0.5.0
+[0.1.0]: https://github.com/rust-lang/annotate-snippets-rs/compare/6015d08d7d10151c126c6a70c14f234c0c01b50e...0.1.0
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 00000000..532bb57a
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,600 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "annotate-snippets"
+version = "0.11.5"
+dependencies = [
+ "annotate-snippets",
+ "anstream",
+ "anstyle",
+ "divan",
+ "memchr",
+ "snapbox",
+ "unicode-width 0.2.0",
+]
+
+[[package]]
+name = "anstream"
+version = "0.6.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "is_terminal_polyfill",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
+
+[[package]]
+name = "anstyle-lossy"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fcff6599f06e21b0165c85052ccd6e67dc388ddd1c516a9dc5f55dc8cacf004"
+dependencies = [
+ "anstyle",
+]
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391"
+dependencies = [
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "anstyle-svg"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbbf0bf947d663010f0b4132f28ca08da9151f3b9035fa7578a38de521c1d1aa"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "anstyle-lossy",
+ "html-escape",
+ "unicode-width 0.1.13",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
+dependencies = [
+ "anstyle",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "clap"
+version = "4.3.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb690e81c7840c0d7aade59f242ea3b41b9bc27bcd5997890e7702ae4b32e487"
+dependencies = [
+ "clap_builder",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.3.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ed2e96bc16d8d740f6f48d663eddf4b8a0983e79210fd55479b7bcd0a69860e"
+dependencies = [
+ "anstyle",
+ "clap_lex",
+ "terminal_size",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
+
+[[package]]
+name = "colorchoice"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
+
+[[package]]
+name = "condtype"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf0a07a401f374238ab8e2f11a104d2851bf9ce711ec69804834de8af45c7af"
+
+[[package]]
+name = "divan"
+version = "0.1.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a405457ec78b8fe08b0e32b4a3570ab5dff6dd16eb9e76a5ee0a9d9cbd898933"
+dependencies = [
+ "cfg-if",
+ "clap",
+ "condtype",
+ "divan-macros",
+ "libc",
+ "regex-lite",
+]
+
+[[package]]
+name = "divan-macros"
+version = "0.1.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9556bc800956545d6420a640173e5ba7dfa82f38d3ea5a167eb555bc69ac3323"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "errno"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
+dependencies = [
+ "libc",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "escargot"
+version = "0.5.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05a3ac187a16b5382fef8c69fd1bad123c67b7cf3932240a2d43dcdd32cded88"
+dependencies = [
+ "log",
+ "once_cell",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
+
+[[package]]
+name = "html-escape"
+version = "0.2.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d1ad449764d627e22bfd7cd5e8868264fc9236e07c752972b4080cd351cb476"
+dependencies = [
+ "utf8-width",
+]
+
+[[package]]
+name = "io-lifetimes"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "is-terminal"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "is_terminal_polyfill"
+version = "1.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b52b2de84ed0341893ce61ca1af04fa54eea0a764ecc38c6855cc5db84dc1927"
+dependencies = [
+ "is-terminal",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
+
+[[package]]
+name = "libc"
+version = "0.2.155"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
+
+[[package]]
+name = "log"
+version = "0.4.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
+
+[[package]]
+name = "memchr"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+[[package]]
+name = "normalize-line-endings"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
+
+[[package]]
+name = "once_cell"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+
+[[package]]
+name = "os_pipe"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29d73ba8daf8fac13b0501d1abeddcfe21ba7401ada61a819144b6c2a4f32209"
+dependencies = [
+ "libc",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.85"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "regex-lite"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a"
+
+[[package]]
+name = "rustix"
+version = "0.37.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2"
+dependencies = [
+ "bitflags",
+ "errno",
+ "io-lifetimes",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
+
+[[package]]
+name = "serde"
+version = "1.0.219"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.219"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.117"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "similar"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640"
+
+[[package]]
+name = "snapbox"
+version = "0.6.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96dcfc4581e3355d70ac2ee14cfdf81dce3d85c85f1ed9e2c1d3013f53b3436b"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "anstyle-svg",
+ "escargot",
+ "libc",
+ "normalize-line-endings",
+ "os_pipe",
+ "serde_json",
+ "similar",
+ "snapbox-macros",
+ "wait-timeout",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "snapbox-macros"
+version = "0.3.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16569f53ca23a41bb6f62e0a5084aa1661f4814a67fa33696a79073e03a664af"
+dependencies = [
+ "anstream",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e89275301d38033efb81a6e60e3497e734dfcc62571f2854bf4b16690398824c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "terminal_size"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e6bf6f19e9f8ed8d4048dc22981458ebcf406d67e94cd422e5ecd73d63b3237"
+dependencies = [
+ "rustix",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
+
+[[package]]
+name = "unicode-width"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
+
+[[package]]
+name = "utf8-width"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3"
+
+[[package]]
+name = "utf8parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
+
+[[package]]
+name = "wait-timeout"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
diff --git a/Cargo.toml b/Cargo.toml
index b7a97fd0..84345673 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,36 +1,139 @@
+[workspace]
+resolver = "2"
+
+[workspace.package]
+repository = "https://github.com/rust-lang/annotate-snippets-rs"
+license = "MIT OR Apache-2.0"
+edition = "2021"
+rust-version = "1.66.0"  # MSRV
+include = [
+  "build.rs",
+  "src/**/*",
+  "Cargo.toml",
+  "Cargo.lock",
+  "LICENSE*",
+  "README.md",
+  "benches/**/*",
+  "examples/**/*"
+]
+
+[workspace.lints.rust]
+rust_2018_idioms = { level = "warn", priority = -1 }
+unreachable_pub = "warn"
+unsafe_op_in_unsafe_fn = "warn"
+unused_lifetimes = "warn"
+unused_macro_rules = "warn"
+unused_qualifications = "warn"
+
+[workspace.lints.clippy]
+bool_assert_comparison = "allow"
+branches_sharing_code = "allow"
+checked_conversions = "warn"
+collapsible_else_if = "allow"
+create_dir = "warn"
+dbg_macro = "warn"
+debug_assert_with_mut_call = "warn"
+doc_markdown = "warn"
+empty_enum = "warn"
+enum_glob_use = "warn"
+expl_impl_clone_on_copy = "warn"
+explicit_deref_methods = "warn"
+explicit_into_iter_loop = "warn"
+fallible_impl_from = "warn"
+filter_map_next = "warn"
+flat_map_option = "warn"
+float_cmp_const = "warn"
+fn_params_excessive_bools = "warn"
+from_iter_instead_of_collect = "warn"
+if_same_then_else = "allow"
+implicit_clone = "warn"
+imprecise_flops = "warn"
+inconsistent_struct_constructor = "warn"
+inefficient_to_string = "warn"
+infinite_loop = "warn"
+invalid_upcast_comparisons = "warn"
+large_digit_groups = "warn"
+large_stack_arrays = "warn"
+large_types_passed_by_value = "warn"
+let_and_return = "allow"  # sometimes good to name what you are returning
+linkedlist = "warn"
+lossy_float_literal = "warn"
+macro_use_imports = "warn"
+mem_forget = "warn"
+mutex_integer = "warn"
+needless_continue = "warn"
+needless_for_each = "warn"
+negative_feature_names = "warn"
+path_buf_push_overwrite = "warn"
+ptr_as_ptr = "warn"
+rc_mutex = "warn"
+redundant_feature_names = "warn"
+ref_option_ref = "warn"
+rest_pat_in_fully_bound_structs = "warn"
+result_large_err = "allow"
+same_functions_in_if_condition = "warn"
+self_named_module_files = "warn"
+semicolon_if_nothing_returned = "warn"
+str_to_string = "warn"
+string_add = "warn"
+string_add_assign = "warn"
+string_lit_as_bytes = "warn"
+string_to_string = "warn"
+todo = "warn"
+trait_duplication_in_bounds = "warn"
+uninlined_format_args = "warn"
+verbose_file_reads = "warn"
+wildcard_imports = "warn"
+zero_sized_map_values = "warn"
+
 [package]
 name = "annotate-snippets"
-version = "0.9.1"
-edition = "2018"
-authors = ["Zibi Braniecki <gandalf@mozilla.com>"]
+version = "0.11.5"
 description = "Library for building code annotations"
-license = "Apache-2.0/MIT"
-repository = "https://github.com/rust-lang/annotate-snippets-rs"
-readme = "README.md"
+categories = []
 keywords = ["code", "analysis", "ascii", "errors", "debug"]
+repository.workspace = true
+license.workspace = true
+edition.workspace = true
+rust-version.workspace = true
+include.workspace = true
 
-[badges]
-travis-ci = { repository = "rust-lang/annotate-snippets-rs", branch = "master" }
-coveralls = { repository = "rust-lang/annotate-snippets-rs", branch = "master", service = "github" }
+[package.metadata.docs.rs]
+all-features = true
+rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"]
+
+[package.metadata.release]
+tag-name = "{{version}}"
+pre-release-replacements = [
+  {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1},
+  {file="CHANGELOG.md", search="\\.\\.\\.HEAD", replace="...{{tag_name}}", exactly=1},
+  {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1},
+  {file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## [Unreleased] - ReleaseDate\n", exactly=1},
+  {file="CHANGELOG.md", search="<!-- next-url -->", replace="<!-- next-url -->\n[Unreleased]: https://github.com/rust-lang/annotate-snippets-rs/compare/{{tag_name}}...HEAD", exactly=1},
+]
 
+[badges]
 maintenance = { status = "actively-developed" }
 
 [dependencies]
-unicode-width = "0.1"
-yansi-term = { version = "0.1", optional = true }
+anstyle = "1.0.4"
+memchr = { version = "2.7.4", optional = true }
+unicode-width = "0.2.0"
 
 [dev-dependencies]
-glob = "0.3"
-toml = "0.5"
-serde = { version = "1.0", features = ["derive"] }
-difference = "2.0"
-yansi-term = "0.1"
-criterion = "0.3"
+annotate-snippets = { path = ".", features = ["testing-colors"] }
+anstream = "0.6.13"
+divan = "0.1.14"
+snapbox = { version = "0.6.0", features = ["diff", "term-svg", "cmd", "examples"] }
 
 [[bench]]
-name = "simple"
+name = "bench"
 harness = false
 
 [features]
 default = []
-color = ["yansi-term"]
+simd = ["memchr"]
+testing-colors = []
+
+[lints]
+workspace = true
diff --git a/LICENSE-APACHE b/LICENSE-APACHE
index 261eeb9e..8f71f43f 100644
--- a/LICENSE-APACHE
+++ b/LICENSE-APACHE
@@ -178,7 +178,7 @@
    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 "[]"
+      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
@@ -186,7 +186,7 @@
       same "printed page" as the copyright notice for easier
       identification within third-party archives.
 
-   Copyright [yyyy] [name of copyright owner]
+   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.
@@ -199,3 +199,4 @@
    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
index 5655fa31..a2d01088 100644
--- a/LICENSE-MIT
+++ b/LICENSE-MIT
@@ -1,11 +1,11 @@
-Copyright 2017 Mozilla
+Copyright (c) Individual contributors
 
-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:
+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.
diff --git a/README.md b/README.md
index a3e1683f..21f95c4b 100644
--- a/README.md
+++ b/README.md
@@ -3,81 +3,14 @@
 `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)
+[![documentation](https://img.shields.io/badge/docs-master-blue.svg)][Documentation]
+![build status](https://github.com/rust-lang/annotate-snippets-rs/actions/workflows/ci.yml/badge.svg)
 
 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);
-}
-```
+![Screenshot](./examples/expected_type.svg)
 
 Local Development
 -----------------
@@ -88,3 +21,5 @@ Local Development
 When submitting a PR please use  [`cargo fmt`][] (nightly).
 
 [`cargo fmt`]: https://github.com/rust-lang/rustfmt
+
+[Documentation]: https://docs.rs/annotate-snippets/
diff --git a/benches/bench.rs b/benches/bench.rs
new file mode 100644
index 00000000..c3799fbd
--- /dev/null
+++ b/benches/bench.rs
@@ -0,0 +1,93 @@
+use annotate_snippets::{AnnotationKind, Group, Level, Renderer, Snippet};
+
+#[divan::bench]
+fn simple() -> String {
+    let 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,
+        }
+    }"#;
+    let message = Level::ERROR.header("mismatched types").id("E0308").group(
+        Group::new().element(
+            Snippet::source(source)
+                .line_start(51)
+                .origin("src/format.rs")
+                .annotation(
+                    AnnotationKind::Context
+                        .span(5..19)
+                        .label("expected `Option<String>` because of return type"),
+                )
+                .annotation(
+                    AnnotationKind::Primary
+                        .span(26..724)
+                        .label("expected enum `std::option::Option`"),
+                ),
+        ),
+    );
+
+    let renderer = Renderer::plain();
+    let rendered = renderer.render(message);
+    rendered
+}
+
+#[divan::bench(args=[0, 1, 10, 100, 1_000, 10_000, 100_000])]
+fn fold(bencher: divan::Bencher<'_, '_>, context: usize) {
+    bencher
+        .with_inputs(|| {
+            let line = "012345678901234567890123456789";
+            let mut input = String::new();
+            for _ in 1..=context {
+                input.push_str(line);
+                input.push('\n');
+            }
+            let span_start = input.len() + line.len();
+            let span = span_start..span_start;
+
+            input.push_str(line);
+            input.push('\n');
+            for _ in 1..=context {
+                input.push_str(line);
+                input.push('\n');
+            }
+            (input, span)
+        })
+        .bench_values(|(input, span)| {
+            let message = Level::ERROR.header("mismatched types").id("E0308").group(
+                Group::new().element(
+                    Snippet::source(&input)
+                        .fold(true)
+                        .origin("src/format.rs")
+                        .annotation(
+                            AnnotationKind::Context
+                                .span(span)
+                                .label("expected `Option<String>` because of return type"),
+                        ),
+                ),
+            );
+
+            let renderer = Renderer::plain();
+            let rendered = renderer.render(message);
+            rendered
+        });
+}
+
+fn main() {
+    divan::main();
+}
diff --git a/benches/simple.rs b/benches/simple.rs
deleted file mode 100644
index 4c13a8f0..00000000
--- a/benches/simple.rs
+++ /dev/null
@@ -1,74 +0,0 @@
-#![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/committed.toml b/committed.toml
new file mode 100644
index 00000000..4211ae38
--- /dev/null
+++ b/committed.toml
@@ -0,0 +1,3 @@
+style="conventional"
+ignore_author_re="(dependabot|renovate)"
+merge_commit = false
diff --git a/deny.toml b/deny.toml
new file mode 100644
index 00000000..b6ecbe9c
--- /dev/null
+++ b/deny.toml
@@ -0,0 +1,236 @@
+# Note that all fields that take a lint level have these possible values:
+# * deny - An error will be produced and the check will fail
+# * warn - A warning will be produced, but the check will not fail
+# * allow - No warning or error will be produced, though in some cases a note
+# will be
+
+# Root options
+
+# The graph table configures how the dependency graph is constructed and thus
+# which crates the checks are performed against
+[graph]
+# If 1 or more target triples (and optionally, target_features) are specified,
+# only the specified targets will be checked when running `cargo deny check`.
+# This means, if a particular package is only ever used as a target specific
+# dependency, such as, for example, the `nix` crate only being used via the
+# `target_family = "unix"` configuration, that only having windows targets in
+# this list would mean the nix crate, as well as any of its exclusive
+# dependencies not shared by any other crates, would be ignored, as the target
+# list here is effectively saying which targets you are building for.
+targets = [
+    # The triple can be any string, but only the target triples built in to
+    # rustc (as of 1.40) can be checked against actual config expressions
+    #"x86_64-unknown-linux-musl",
+    # You can also specify which target_features you promise are enabled for a
+    # particular target. target_features are currently not validated against
+    # the actual valid features supported by the target architecture.
+    #{ triple = "wasm32-unknown-unknown", features = ["atomics"] },
+]
+# When creating the dependency graph used as the source of truth when checks are
+# executed, this field can be used to prune crates from the graph, removing them
+# from the view of cargo-deny. This is an extremely heavy hammer, as if a crate
+# is pruned from the graph, all of its dependencies will also be pruned unless
+# they are connected to another crate in the graph that hasn't been pruned,
+# so it should be used with care. The identifiers are [Package ID Specifications]
+# (https://doc.rust-lang.org/cargo/reference/pkgid-spec.html)
+#exclude = []
+# If true, metadata will be collected with `--all-features`. Note that this can't
+# be toggled off if true, if you want to conditionally enable `--all-features` it
+# is recommended to pass `--all-features` on the cmd line instead
+all-features = false
+# If true, metadata will be collected with `--no-default-features`. The same
+# caveat with `all-features` applies
+no-default-features = false
+# If set, these feature will be enabled when collecting metadata. If `--features`
+# is specified on the cmd line they will take precedence over this option.
+#features = []
+
+# The output table provides options for how/if diagnostics are outputted
+[output]
+# When outputting inclusion graphs in diagnostics that include features, this
+# option can be used to specify the depth at which feature edges will be added.
+# This option is included since the graphs can be quite large and the addition
+# of features from the crate(s) to all of the graph roots can be far too verbose.
+# This option can be overridden via `--feature-depth` on the cmd line
+feature-depth = 1
+
+# This section is considered when running `cargo deny check advisories`
+# More documentation for the advisories section can be found here:
+# https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html
+[advisories]
+# The path where the advisory databases are cloned/fetched into
+#db-path = "$CARGO_HOME/advisory-dbs"
+# The url(s) of the advisory databases to use
+#db-urls = ["https://github.com/rustsec/advisory-db"]
+# A list of advisory IDs to ignore. Note that ignored advisories will still
+# output a note when they are encountered.
+ignore = [
+    #"RUSTSEC-0000-0000",
+    #{ id = "RUSTSEC-0000-0000", reason = "you can specify a reason the advisory is ignored" },
+    #"a-crate-that-is-yanked@0.1.1", # you can also ignore yanked crate versions if you wish
+    #{ crate = "a-crate-that-is-yanked@0.1.1", reason = "you can specify why you are ignoring the yanked crate" },
+]
+# If this is true, then cargo deny will use the git executable to fetch advisory database.
+# If this is false, then it uses a built-in git library.
+# Setting this to true can be helpful if you have special authentication requirements that cargo-deny does not support.
+# See Git Authentication for more information about setting up git authentication.
+#git-fetch-with-cli = true
+
+# This section is considered when running `cargo deny check licenses`
+# More documentation for the licenses section can be found here:
+# https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html
+[licenses]
+# List of explicitly allowed licenses
+# See https://spdx.org/licenses/ for list of possible licenses
+# [possible values: any SPDX 3.11 short identifier (+ optional exception)].
+allow = [
+    "MIT",
+    "MIT-0",
+    "Apache-2.0",
+    "BSD-3-Clause",
+    "MPL-2.0",
+    "Unicode-DFS-2016",
+    "CC0-1.0",
+    "ISC",
+    "OpenSSL",
+]
+# The confidence threshold for detecting a license from license text.
+# The higher the value, the more closely the license text must be to the
+# canonical license text of a valid SPDX license file.
+# [possible values: any between 0.0 and 1.0].
+confidence-threshold = 0.8
+# Allow 1 or more licenses on a per-crate basis, so that particular licenses
+# aren't accepted for every possible crate as with the normal allow list
+exceptions = [
+    # Each entry is the crate and version constraint, and its specific allow
+    # list
+    #{ allow = ["Zlib"], crate = "adler32" },
+]
+
+# Some crates don't have (easily) machine readable licensing information,
+# adding a clarification entry for it allows you to manually specify the
+# licensing information
+[[licenses.clarify]]
+# The package spec the clarification applies to
+crate = "ring"
+# The SPDX expression for the license requirements of the crate
+expression = "MIT AND ISC AND OpenSSL"
+# One or more files in the crate's source used as the "source of truth" for
+# the license expression. If the contents match, the clarification will be used
+# when running the license check, otherwise the clarification will be ignored
+# and the crate will be checked normally, which may produce warnings or errors
+# depending on the rest of your configuration
+license-files = [
+# Each entry is a crate relative path, and the (opaque) hash of its contents
+{ path = "LICENSE", hash = 0xbd0eed23 }
+]
+
+[licenses.private]
+# If true, ignores workspace crates that aren't published, or are only
+# published to private registries.
+# To see how to mark a crate as unpublished (to the official registry),
+# visit https://doc.rust-lang.org/cargo/reference/manifest.html#the-publish-field.
+ignore = true
+# One or more private registries that you might publish crates to, if a crate
+# is only published to private registries, and ignore is true, the crate will
+# not have its license(s) checked
+registries = [
+    #"https://sekretz.com/registry
+]
+
+# This section is considered when running `cargo deny check bans`.
+# More documentation about the 'bans' section can be found here:
+# https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html
+[bans]
+# Lint level for when multiple versions of the same crate are detected
+multiple-versions = "warn"
+# Lint level for when a crate version requirement is `*`
+wildcards = "allow"
+# The graph highlighting used when creating dotgraphs for crates
+# with multiple versions
+# * lowest-version - The path to the lowest versioned duplicate is highlighted
+# * simplest-path - The path to the version with the fewest edges is highlighted
+# * all - Both lowest-version and simplest-path are used
+highlight = "all"
+# The default lint level for `default` features for crates that are members of
+# the workspace that is being checked. This can be overridden by allowing/denying
+# `default` on a crate-by-crate basis if desired.
+workspace-default-features = "allow"
+# The default lint level for `default` features for external crates that are not
+# members of the workspace. This can be overridden by allowing/denying `default`
+# on a crate-by-crate basis if desired.
+external-default-features = "allow"
+# List of crates that are allowed. Use with care!
+allow = [
+    #"ansi_term@0.11.0",
+    #{ crate = "ansi_term@0.11.0", reason = "you can specify a reason it is allowed" },
+]
+# List of crates to deny
+deny = [
+    #"ansi_term@0.11.0",
+    #{ crate = "ansi_term@0.11.0", reason = "you can specify a reason it is banned" },
+    # Wrapper crates can optionally be specified to allow the crate when it
+    # is a direct dependency of the otherwise banned crate
+    #{ crate = "ansi_term@0.11.0", wrappers = ["this-crate-directly-depends-on-ansi_term"] },
+]
+
+# List of features to allow/deny
+# Each entry the name of a crate and a version range. If version is
+# not specified, all versions will be matched.
+#[[bans.features]]
+#crate = "reqwest"
+# Features to not allow
+#deny = ["json"]
+# Features to allow
+#allow = [
+#    "rustls",
+#    "__rustls",
+#    "__tls",
+#    "hyper-rustls",
+#    "rustls",
+#    "rustls-pemfile",
+#    "rustls-tls-webpki-roots",
+#    "tokio-rustls",
+#    "webpki-roots",
+#]
+# If true, the allowed features must exactly match the enabled feature set. If
+# this is set there is no point setting `deny`
+#exact = true
+
+# Certain crates/versions that will be skipped when doing duplicate detection.
+skip = [
+    #"ansi_term@0.11.0",
+    #{ crate = "ansi_term@0.11.0", reason = "you can specify a reason why it can't be updated/removed" },
+]
+# Similarly to `skip` allows you to skip certain crates during duplicate
+# detection. Unlike skip, it also includes the entire tree of transitive
+# dependencies starting at the specified crate, up to a certain depth, which is
+# by default infinite.
+skip-tree = [
+    #"ansi_term@0.11.0", # will be skipped along with _all_ of its direct and transitive dependencies
+    #{ crate = "ansi_term@0.11.0", depth = 20 },
+]
+
+# This section is considered when running `cargo deny check sources`.
+# More documentation about the 'sources' section can be found here:
+# https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html
+[sources]
+# Lint level for what to happen when a crate from a crate registry that is not
+# in the allow list is encountered
+unknown-registry = "deny"
+# Lint level for what to happen when a crate from a git repository that is not
+# in the allow list is encountered
+unknown-git = "deny"
+# List of URLs for allowed crate registries. Defaults to the crates.io index
+# if not specified. If it is specified but empty, no registries are allowed.
+allow-registry = ["https://github.com/rust-lang/crates.io-index"]
+# List of URLs for allowed Git repositories
+allow-git = []
+
+[sources.allow-org]
+# 1 or more github.com organizations to allow git sources for
+github = []
+# 1 or more gitlab.com organizations to allow git sources for
+gitlab = []
+# 1 or more bitbucket.org organizations to allow git sources for
+bitbucket = []
diff --git a/examples/custom_error.rs b/examples/custom_error.rs
new file mode 100644
index 00000000..b9e27b31
--- /dev/null
+++ b/examples/custom_error.rs
@@ -0,0 +1,37 @@
+use annotate_snippets::renderer::OutputTheme;
+use annotate_snippets::{AnnotationKind, Group, Level, Renderer, Snippet};
+
+fn main() {
+    let source = r#"//@ compile-flags: -Ztreat-err-as-bug
+//@ failure-status: 101
+//@ error-pattern: aborting due to `-Z treat-err-as-bug=1`
+//@ error-pattern: [eval_static_initializer] evaluating initializer of static `C`
+//@ normalize-stderr: "note: .*\n\n" -> ""
+//@ normalize-stderr: "thread 'rustc' panicked.*:\n.*\n" -> ""
+//@ rustc-env:RUST_BACKTRACE=0
+
+#![crate_type = "rlib"]
+
+pub static C: u32 = 0 - 1;
+//~^ ERROR could not evaluate static initializer
+"#;
+    let message = Level::ERROR
+        .text(Some("error: internal compiler error"))
+        .header("could not evaluate static initializer")
+        .id("E0080")
+        .group(
+            Group::new().element(
+                Snippet::source(source)
+                    .origin("$DIR/err.rs")
+                    .fold(true)
+                    .annotation(
+                        AnnotationKind::Primary
+                            .span(386..391)
+                            .label("attempt to compute `0_u32 - 1_u32`, which would overflow"),
+                    ),
+            ),
+        );
+
+    let renderer = Renderer::styled().theme(OutputTheme::Unicode);
+    anstream::println!("{}", renderer.render(message));
+}
diff --git a/examples/custom_error.svg b/examples/custom_error.svg
new file mode 100644
index 00000000..af3611a9
--- /dev/null
+++ b/examples/custom_error.svg
@@ -0,0 +1,36 @@
+<svg width="751px" height="128px" xmlns="http://www.w3.org/2000/svg">
+  <style>
+    .fg { fill: #AAAAAA }
+    .bg { background: #000000 }
+    .fg-bright-blue { fill: #5555FF }
+    .fg-bright-red { fill: #FF5555 }
+    .container {
+      padding: 0 10px;
+      line-height: 18px;
+    }
+    .bold { font-weight: bold; }
+    tspan {
+      font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
+      white-space: pre;
+      line-height: 18px;
+    }
+  </style>
+
+  <rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
+
+  <text xml:space="preserve" class="container fg">
+    <tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error: internal compiler error[E0080]</tspan><tspan class="bold">: could not evaluate static initializer</tspan>
+</tspan>
+    <tspan x="10px" y="46px"><tspan>  </tspan><tspan class="fg-bright-blue bold"> ╭▸ </tspan><tspan>$DIR/err.rs:11:21</tspan>
+</tspan>
+    <tspan x="10px" y="64px"><tspan>   </tspan><tspan class="fg-bright-blue bold">│</tspan>
+</tspan>
+    <tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">11</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> pub static C: u32 = 0 - 1;</tspan>
+</tspan>
+    <tspan x="10px" y="100px"><tspan>   </tspan><tspan class="fg-bright-blue bold">╰╴</tspan><tspan>                    </tspan><tspan class="fg-bright-red bold">━━━━━</tspan><tspan> </tspan><tspan class="fg-bright-red bold">attempt to compute `0_u32 - 1_u32`, which would overflow</tspan>
+</tspan>
+    <tspan x="10px" y="118px">
+</tspan>
+  </text>
+
+</svg>
diff --git a/examples/custom_level.rs b/examples/custom_level.rs
new file mode 100644
index 00000000..b2af361a
--- /dev/null
+++ b/examples/custom_level.rs
@@ -0,0 +1,71 @@
+use annotate_snippets::renderer::OutputTheme;
+use annotate_snippets::{AnnotationKind, Group, Level, Patch, Renderer, Snippet};
+
+fn main() {
+    let source = r#"// Regression test for issue #114529
+// Tests that we do not ICE during const eval for a
+// break-with-value in contexts where it is illegal
+
+#[allow(while_true)]
+fn main() {
+    [(); {
+        while true {
+            break 9; //~ ERROR `break` with value from a `while` loop
+        };
+        51
+    }];
+
+    [(); {
+        while let Some(v) = Some(9) {
+            break v; //~ ERROR `break` with value from a `while` loop
+        };
+        51
+    }];
+
+    while true {
+        break (|| { //~ ERROR `break` with value from a `while` loop
+            let local = 9;
+        });
+    }
+}
+"#;
+    let message = Level::ERROR
+        .header("`break` with value from a `while` loop")
+        .id("E0571")
+        .group(
+            Group::new().element(
+                Snippet::source(source)
+                    .line_start(1)
+                    .origin("$DIR/issue-114529-illegal-break-with-value.rs")
+                    .fold(true)
+                    .annotation(
+                        AnnotationKind::Primary
+                            .span(483..581)
+                            .label("can only break with a value inside `loop` or breakable block"),
+                    )
+                    .annotation(
+                        AnnotationKind::Context
+                            .span(462..472)
+                            .label("you can't `break` with a value in a `while` loop"),
+                    ),
+            ),
+        )
+        .group(
+            Group::new()
+                .element(
+                    Level::HELP
+                        .text(Some("suggestion"))
+                        .title("use `break` on its own without a value inside this `while` loop"),
+                )
+                .element(
+                    Snippet::source(source)
+                        .line_start(1)
+                        .origin("$DIR/issue-114529-illegal-break-with-value.rs")
+                        .fold(true)
+                        .patch(Patch::new(483..581, "break")),
+                ),
+        );
+
+    let renderer = Renderer::styled().theme(OutputTheme::Unicode);
+    anstream::println!("{}", renderer.render(message));
+}
diff --git a/examples/custom_level.svg b/examples/custom_level.svg
new file mode 100644
index 00000000..eebff280
--- /dev/null
+++ b/examples/custom_level.svg
@@ -0,0 +1,62 @@
+<svg width="740px" height="344px" xmlns="http://www.w3.org/2000/svg">
+  <style>
+    .fg { fill: #AAAAAA }
+    .bg { background: #000000 }
+    .fg-bright-blue { fill: #5555FF }
+    .fg-bright-cyan { fill: #55FFFF }
+    .fg-bright-green { fill: #55FF55 }
+    .fg-bright-red { fill: #FF5555 }
+    .container {
+      padding: 0 10px;
+      line-height: 18px;
+    }
+    .bold { font-weight: bold; }
+    tspan {
+      font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
+      white-space: pre;
+      line-height: 18px;
+    }
+  </style>
+
+  <rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
+
+  <text xml:space="preserve" class="container fg">
+    <tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0571]</tspan><tspan class="bold">: `break` with value from a `while` loop</tspan>
+</tspan>
+    <tspan x="10px" y="46px"><tspan>  </tspan><tspan class="fg-bright-blue bold"> ╭▸ </tspan><tspan>$DIR/issue-114529-illegal-break-with-value.rs:22:9</tspan>
+</tspan>
+    <tspan x="10px" y="64px"><tspan>   </tspan><tspan class="fg-bright-blue bold">│</tspan>
+</tspan>
+    <tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">21</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan>       while true {</tspan>
+</tspan>
+    <tspan x="10px" y="100px"><tspan>   </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan>       </tspan><tspan class="fg-bright-blue bold">──────────</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">you can't `break` with a value in a `while` loop</tspan>
+</tspan>
+    <tspan x="10px" y="118px"><tspan class="fg-bright-blue bold">22</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">┏</tspan><tspan>         break (|| { //~ ERROR `break` with value from a `while` loop</tspan>
+</tspan>
+    <tspan x="10px" y="136px"><tspan class="fg-bright-blue bold">23</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">┃</tspan><tspan>             let local = 9;</tspan>
+</tspan>
+    <tspan x="10px" y="154px"><tspan class="fg-bright-blue bold">24</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">┃</tspan><tspan>         });</tspan>
+</tspan>
+    <tspan x="10px" y="172px"><tspan>   </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">┗━━━━━━━━━━┛</tspan><tspan> </tspan><tspan class="fg-bright-red bold">can only break with a value inside `loop` or breakable block</tspan>
+</tspan>
+    <tspan x="10px" y="190px"><tspan>   </tspan><tspan class="fg-bright-blue bold">╰╴</tspan>
+</tspan>
+    <tspan x="10px" y="208px"><tspan class="fg-bright-cyan bold">suggestion</tspan><tspan class="bold">: use `break` on its own without a value inside this `while` loop</tspan>
+</tspan>
+    <tspan x="10px" y="226px"><tspan>   </tspan><tspan class="fg-bright-blue bold">╭╴</tspan>
+</tspan>
+    <tspan x="10px" y="244px"><tspan class="fg-bright-blue bold">22</tspan><tspan> </tspan><tspan class="fg-bright-red">- </tspan><tspan>        break (|| { //~ ERROR `break` with value from a `while` loop</tspan>
+</tspan>
+    <tspan x="10px" y="262px"><tspan class="fg-bright-blue bold">23</tspan><tspan> </tspan><tspan class="fg-bright-red">- </tspan><tspan>            let local = 9;</tspan>
+</tspan>
+    <tspan x="10px" y="280px"><tspan class="fg-bright-blue bold">24</tspan><tspan> </tspan><tspan class="fg-bright-red">- </tspan><tspan>        </tspan><tspan class="fg-bright-red">})</tspan><tspan>;</tspan>
+</tspan>
+    <tspan x="10px" y="298px"><tspan class="fg-bright-blue bold">22</tspan><tspan> </tspan><tspan class="fg-bright-green">+ </tspan><tspan>        </tspan><tspan class="fg-bright-green">break</tspan><tspan>;</tspan>
+</tspan>
+    <tspan x="10px" y="316px"><tspan>   </tspan><tspan class="fg-bright-blue bold">╰╴</tspan>
+</tspan>
+    <tspan x="10px" y="334px">
+</tspan>
+  </text>
+
+</svg>
diff --git a/examples/expected_type.rs b/examples/expected_type.rs
index 6f2a0d9a..02abdecf 100644
--- a/examples/expected_type.rs
+++ b/examples/expected_type.rs
@@ -1,43 +1,28 @@
-use annotate_snippets::{
-    display_list::{DisplayList, FormatOptions},
-    snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation},
-};
+use annotate_snippets::{AnnotationKind, Group, Level, Renderer, Snippet};
 
 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 {
+    let 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()
-        },
-    };
+                range: <22, 25>,"#;
+    let message =
+        Level::ERROR.header("expected type, found `22`").group(
+            Group::new().element(
+                Snippet::source(source)
+                    .line_start(26)
+                    .origin("examples/footer.rs")
+                    .fold(true)
+                    .annotation(AnnotationKind::Primary.span(193..195).label(
+                        "expected struct `annotate_snippets::snippet::Slice`, found reference",
+                    ))
+                    .annotation(
+                        AnnotationKind::Context
+                            .span(34..50)
+                            .label("while parsing this struct"),
+                    ),
+            ),
+        );
 
-    let dl = DisplayList::from(snippet);
-    println!("{}", dl);
+    let renderer = Renderer::styled();
+    anstream::println!("{}", renderer.render(message));
 }
diff --git a/examples/expected_type.svg b/examples/expected_type.svg
new file mode 100644
index 00000000..7c1b073d
--- /dev/null
+++ b/examples/expected_type.svg
@@ -0,0 +1,42 @@
+<svg width="860px" height="182px" xmlns="http://www.w3.org/2000/svg">
+  <style>
+    .fg { fill: #AAAAAA }
+    .bg { background: #000000 }
+    .fg-bright-blue { fill: #5555FF }
+    .fg-bright-red { fill: #FF5555 }
+    .container {
+      padding: 0 10px;
+      line-height: 18px;
+    }
+    .bold { font-weight: bold; }
+    tspan {
+      font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
+      white-space: pre;
+      line-height: 18px;
+    }
+  </style>
+
+  <rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
+
+  <text xml:space="preserve" class="container fg">
+    <tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error</tspan><tspan class="bold">: expected type, found `22`</tspan>
+</tspan>
+    <tspan x="10px" y="46px"><tspan>  </tspan><tspan class="fg-bright-blue bold">--&gt; </tspan><tspan>examples/footer.rs:29:25</tspan>
+</tspan>
+    <tspan x="10px" y="64px"><tspan>   </tspan><tspan class="fg-bright-blue bold">|</tspan>
+</tspan>
+    <tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">26</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>                 annotations: vec![SourceAnnotation {</tspan>
+</tspan>
+    <tspan x="10px" y="100px"><tspan>   </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>                                   </tspan><tspan class="fg-bright-blue bold">----------------</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">while parsing this struct</tspan>
+</tspan>
+    <tspan x="10px" y="118px"><tspan class="fg-bright-blue bold">...</tspan>
+</tspan>
+    <tspan x="10px" y="136px"><tspan class="fg-bright-blue bold">29</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>                 range: &lt;22, 25&gt;,</tspan>
+</tspan>
+    <tspan x="10px" y="154px"><tspan>   </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>                         </tspan><tspan class="fg-bright-red bold">^^</tspan><tspan> </tspan><tspan class="fg-bright-red bold">expected struct `annotate_snippets::snippet::Slice`, found reference</tspan>
+</tspan>
+    <tspan x="10px" y="172px">
+</tspan>
+  </text>
+
+</svg>
diff --git a/examples/footer.rs b/examples/footer.rs
index f3c15c41..ca6f1dc7 100644
--- a/examples/footer.rs
+++ b/examples/footer.rs
@@ -1,39 +1,23 @@
-use annotate_snippets::{
-    display_list::{DisplayList, FormatOptions},
-    snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation},
-};
+use annotate_snippets::{AnnotationKind, Group, Level, Renderer, Snippet};
 
 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`",
+    let message = Level::ERROR
+        .header("mismatched types")
+        .id("E0308")
+        .group(
+            Group::new().element(
+                Snippet::source("        slices: vec![\"A\",")
+                    .line_start(13)
+                    .origin("src/multislice.rs")
+                    .annotation(AnnotationKind::Primary.span(21..24).label(
+                        "expected struct `annotate_snippets::snippet::Slice`, found reference",
+                    )),
             ),
-            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()
-        },
-    };
+        )
+        .group(Group::new().element(Level::NOTE.title(
+            "expected type: `snippet::Annotation`\n   found type: `__&__snippet::Annotation`",
+        )));
 
-    let dl = DisplayList::from(snippet);
-    println!("{}", dl);
+    let renderer = Renderer::styled();
+    anstream::println!("{}", renderer.render(message));
 }
diff --git a/examples/footer.svg b/examples/footer.svg
new file mode 100644
index 00000000..e24ba5f5
--- /dev/null
+++ b/examples/footer.svg
@@ -0,0 +1,43 @@
+<svg width="844px" height="182px" xmlns="http://www.w3.org/2000/svg">
+  <style>
+    .fg { fill: #AAAAAA }
+    .bg { background: #000000 }
+    .fg-bright-blue { fill: #5555FF }
+    .fg-bright-green { fill: #55FF55 }
+    .fg-bright-red { fill: #FF5555 }
+    .container {
+      padding: 0 10px;
+      line-height: 18px;
+    }
+    .bold { font-weight: bold; }
+    tspan {
+      font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
+      white-space: pre;
+      line-height: 18px;
+    }
+  </style>
+
+  <rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
+
+  <text xml:space="preserve" class="container fg">
+    <tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0308]</tspan><tspan class="bold">: mismatched types</tspan>
+</tspan>
+    <tspan x="10px" y="46px"><tspan>  </tspan><tspan class="fg-bright-blue bold">--&gt; </tspan><tspan>src/multislice.rs:13:22</tspan>
+</tspan>
+    <tspan x="10px" y="64px"><tspan>   </tspan><tspan class="fg-bright-blue bold">|</tspan>
+</tspan>
+    <tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">13</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>         slices: vec!["A",</tspan>
+</tspan>
+    <tspan x="10px" y="100px"><tspan>   </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>                      </tspan><tspan class="fg-bright-red bold">^^^</tspan><tspan> </tspan><tspan class="fg-bright-red bold">expected struct `annotate_snippets::snippet::Slice`, found reference</tspan>
+</tspan>
+    <tspan x="10px" y="118px"><tspan>   </tspan><tspan class="fg-bright-blue bold">|</tspan>
+</tspan>
+    <tspan x="10px" y="136px"><tspan class="fg-bright-green bold">note</tspan><tspan class="bold">: expected type: `snippet::Annotation`</tspan>
+</tspan>
+    <tspan x="10px" y="154px"><tspan class="bold">         found type: `__&amp;__snippet::Annotation`</tspan>
+</tspan>
+    <tspan x="10px" y="172px">
+</tspan>
+  </text>
+
+</svg>
diff --git a/examples/format.rs b/examples/format.rs
index 98b77a14..4b688d4b 100644
--- a/examples/format.rs
+++ b/examples/format.rs
@@ -1,12 +1,7 @@
-use annotate_snippets::{
-    display_list::{DisplayList, FormatOptions},
-    snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation},
-};
+use annotate_snippets::{AnnotationKind, Group, Level, Renderer, Snippet};
 
 fn main() {
-    let snippet = Snippet {
-        slices: vec![Slice {
-            source: r#") -> Option<String> {
+    let source = r#") -> Option<String> {
     for ann in annotations {
         match (ann.range.0, ann.range.1) {
             (None, None) => continue,
@@ -27,35 +22,25 @@ fn main() {
             }
             _ => 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 message = Level::ERROR.header("mismatched types").id("E0308").group(
+        Group::new().element(
+            Snippet::source(source)
+                .line_start(51)
+                .origin("src/format.rs")
+                .annotation(
+                    AnnotationKind::Context
+                        .span(5..19)
+                        .label("expected `Option<String>` because of return type"),
+                )
+                .annotation(
+                    AnnotationKind::Primary
+                        .span(26..724)
+                        .label("expected enum `std::option::Option`"),
+                ),
+        ),
+    );
 
-    let dl = DisplayList::from(snippet);
-    println!("{}", dl);
+    let renderer = Renderer::styled();
+    anstream::println!("{}", renderer.render(message));
 }
diff --git a/examples/format.svg b/examples/format.svg
new file mode 100644
index 00000000..e4a4042c
--- /dev/null
+++ b/examples/format.svg
@@ -0,0 +1,80 @@
+<svg width="740px" height="524px" xmlns="http://www.w3.org/2000/svg">
+  <style>
+    .fg { fill: #AAAAAA }
+    .bg { background: #000000 }
+    .fg-bright-blue { fill: #5555FF }
+    .fg-bright-red { fill: #FF5555 }
+    .container {
+      padding: 0 10px;
+      line-height: 18px;
+    }
+    .bold { font-weight: bold; }
+    tspan {
+      font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
+      white-space: pre;
+      line-height: 18px;
+    }
+  </style>
+
+  <rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
+
+  <text xml:space="preserve" class="container fg">
+    <tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0308]</tspan><tspan class="bold">: mismatched types</tspan>
+</tspan>
+    <tspan x="10px" y="46px"><tspan>  </tspan><tspan class="fg-bright-blue bold">--&gt; </tspan><tspan>src/format.rs:52:5</tspan>
+</tspan>
+    <tspan x="10px" y="64px"><tspan>   </tspan><tspan class="fg-bright-blue bold">|</tspan>
+</tspan>
+    <tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">51</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>   ) -&gt; Option&lt;String&gt; {</tspan>
+</tspan>
+    <tspan x="10px" y="100px"><tspan>   </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>        </tspan><tspan class="fg-bright-blue bold">--------------</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">expected `Option&lt;String&gt;` because of return type</tspan>
+</tspan>
+    <tspan x="10px" y="118px"><tspan class="fg-bright-blue bold">52</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">/</tspan><tspan>     for ann in annotations {</tspan>
+</tspan>
+    <tspan x="10px" y="136px"><tspan class="fg-bright-blue bold">53</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan>         match (ann.range.0, ann.range.1) {</tspan>
+</tspan>
+    <tspan x="10px" y="154px"><tspan class="fg-bright-blue bold">54</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan>             (None, None) =&gt; continue,</tspan>
+</tspan>
+    <tspan x="10px" y="172px"><tspan class="fg-bright-blue bold">55</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan>             (Some(start), Some(end)) if start &gt; end_index =&gt; continue,</tspan>
+</tspan>
+    <tspan x="10px" y="190px"><tspan class="fg-bright-blue bold">56</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan>             (Some(start), Some(end)) if start &gt;= start_index =&gt; {</tspan>
+</tspan>
+    <tspan x="10px" y="208px"><tspan class="fg-bright-blue bold">57</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan>                 let label = if let Some(ref label) = ann.label {</tspan>
+</tspan>
+    <tspan x="10px" y="226px"><tspan class="fg-bright-blue bold">58</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan>                     format!(" {}", label)</tspan>
+</tspan>
+    <tspan x="10px" y="244px"><tspan class="fg-bright-blue bold">59</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan>                 } else {</tspan>
+</tspan>
+    <tspan x="10px" y="262px"><tspan class="fg-bright-blue bold">60</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan>                     String::from("")</tspan>
+</tspan>
+    <tspan x="10px" y="280px"><tspan class="fg-bright-blue bold">61</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan>                 };</tspan>
+</tspan>
+    <tspan x="10px" y="298px"><tspan class="fg-bright-blue bold">62</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan>
+</tspan>
+    <tspan x="10px" y="316px"><tspan class="fg-bright-blue bold">63</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan>                 return Some(format!(</tspan>
+</tspan>
+    <tspan x="10px" y="334px"><tspan class="fg-bright-blue bold">64</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan>                     "{}{}{}",</tspan>
+</tspan>
+    <tspan x="10px" y="352px"><tspan class="fg-bright-blue bold">65</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan>                     " ".repeat(start - start_index),</tspan>
+</tspan>
+    <tspan x="10px" y="370px"><tspan class="fg-bright-blue bold">66</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan>                     "^".repeat(end - start),</tspan>
+</tspan>
+    <tspan x="10px" y="388px"><tspan class="fg-bright-blue bold">67</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan>                     label</tspan>
+</tspan>
+    <tspan x="10px" y="406px"><tspan class="fg-bright-blue bold">68</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan>                 ));</tspan>
+</tspan>
+    <tspan x="10px" y="424px"><tspan class="fg-bright-blue bold">69</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan>             }</tspan>
+</tspan>
+    <tspan x="10px" y="442px"><tspan class="fg-bright-blue bold">70</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan>             _ =&gt; continue,</tspan>
+</tspan>
+    <tspan x="10px" y="460px"><tspan class="fg-bright-blue bold">71</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan>         }</tspan>
+</tspan>
+    <tspan x="10px" y="478px"><tspan class="fg-bright-blue bold">72</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan>     }</tspan>
+</tspan>
+    <tspan x="10px" y="496px"><tspan>   </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|____^</tspan><tspan> </tspan><tspan class="fg-bright-red bold">expected enum `std::option::Option`</tspan>
+</tspan>
+    <tspan x="10px" y="514px">
+</tspan>
+  </text>
+
+</svg>
diff --git a/examples/highlight_source.rs b/examples/highlight_source.rs
new file mode 100644
index 00000000..92d8114f
--- /dev/null
+++ b/examples/highlight_source.rs
@@ -0,0 +1,35 @@
+use annotate_snippets::{AnnotationKind, Group, Level, Renderer, Snippet};
+
+fn main() {
+    let source = r#"//@ compile-flags: -Z teach
+
+#![allow(warnings)]
+
+const CON: Vec<i32> = vec![1, 2, 3]; //~ ERROR E0010
+//~| ERROR cannot call non-const method
+fn main() {}
+"#;
+    let message = Level::ERROR
+        .header("allocations are not allowed in constants")
+        .id("E0010")
+        .group(
+            Group::new()
+                .element(
+                    Snippet::source(source)
+                        .fold(true)
+                        .origin("$DIR/E0010-teach.rs")
+                        .annotation(
+                            AnnotationKind::Primary
+                                .span(72..85)
+                                .label("allocation not allowed in constants")
+                                .highlight_source(true),
+                        ),
+                )
+                .element(
+                    Level::NOTE.title("The runtime heap is not yet available at compile-time, so no runtime heap allocations can be created."),
+                ),
+        );
+
+    let renderer = Renderer::styled().anonymized_line_numbers(true);
+    anstream::println!("{}", renderer.render(message));
+}
diff --git a/examples/highlight_source.svg b/examples/highlight_source.svg
new file mode 100644
index 00000000..391a097d
--- /dev/null
+++ b/examples/highlight_source.svg
@@ -0,0 +1,38 @@
+<svg width="961px" height="146px" xmlns="http://www.w3.org/2000/svg">
+  <style>
+    .fg { fill: #AAAAAA }
+    .bg { background: #000000 }
+    .fg-bright-blue { fill: #5555FF }
+    .fg-bright-red { fill: #FF5555 }
+    .container {
+      padding: 0 10px;
+      line-height: 18px;
+    }
+    .bold { font-weight: bold; }
+    tspan {
+      font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
+      white-space: pre;
+      line-height: 18px;
+    }
+  </style>
+
+  <rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
+
+  <text xml:space="preserve" class="container fg">
+    <tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0010]</tspan><tspan class="bold">: allocations are not allowed in constants</tspan>
+</tspan>
+    <tspan x="10px" y="46px"><tspan>  </tspan><tspan class="fg-bright-blue bold">--&gt; </tspan><tspan>$DIR/E0010-teach.rs:5:23</tspan>
+</tspan>
+    <tspan x="10px" y="64px"><tspan>   </tspan><tspan class="fg-bright-blue bold">|</tspan>
+</tspan>
+    <tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">LL</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> const CON: Vec&lt;i32&gt; = </tspan><tspan class="fg-bright-red bold">vec![1, 2, 3]</tspan><tspan>; //~ ERROR E0010</tspan>
+</tspan>
+    <tspan x="10px" y="100px"><tspan>   </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>                       </tspan><tspan class="fg-bright-red bold">^^^^^^^^^^^^^</tspan><tspan> </tspan><tspan class="fg-bright-red bold">allocation not allowed in constants</tspan>
+</tspan>
+    <tspan x="10px" y="118px"><tspan>   </tspan><tspan class="fg-bright-blue bold">= </tspan><tspan class="bold">note</tspan><tspan>: The runtime heap is not yet available at compile-time, so no runtime heap allocations can be created.</tspan>
+</tspan>
+    <tspan x="10px" y="136px">
+</tspan>
+  </text>
+
+</svg>
diff --git a/examples/highlight_title.rs b/examples/highlight_title.rs
new file mode 100644
index 00000000..12c106a6
--- /dev/null
+++ b/examples/highlight_title.rs
@@ -0,0 +1,68 @@
+use annotate_snippets::{AnnotationKind, Group, Level, Renderer, Snippet};
+use anstyle::Effects;
+
+fn main() {
+    let source = r#"// Make sure "highlighted" code is colored purple
+
+//@ compile-flags: --error-format=human --color=always
+//@ error-pattern:for<'a> 
+//@ edition:2018
+
+use core::pin::Pin;
+use core::future::Future;
+use core::any::Any;
+
+fn query(_: fn(Box<(dyn Any + Send + '_)>) -> Pin<Box<(
+    dyn Future<Output = Result<Box<(dyn Any + 'static)>, String>> + Send + 'static
+)>>) {}
+
+fn wrapped_fn<'a>(_: Box<(dyn Any + Send)>) -> Pin<Box<(
+    dyn Future<Output = Result<Box<(dyn Any + 'static)>, String>> + Send + 'static
+)>> {
+    Box::pin(async { Err("nope".into()) })
+}
+
+fn main() {
+    query(wrapped_fn);
+}
+"#;
+
+    let magenta = annotate_snippets::renderer::AnsiColor::Magenta
+        .on_default()
+        .effects(Effects::BOLD);
+    let title = format!(
+        "expected fn pointer `{}for<'a>{} fn(Box<{}(dyn Any + Send + 'a){}>) -> Pin<_>`
+      found fn item `fn(Box<{}(dyn Any + Send + 'static){}>) -> Pin<_> {}{{wrapped_fn}}{}`",
+        magenta.render(),
+        magenta.render_reset(),
+        magenta.render(),
+        magenta.render_reset(),
+        magenta.render(),
+        magenta.render_reset(),
+        magenta.render(),
+        magenta.render_reset()
+    );
+
+    let message = Level::ERROR.header("mismatched types").id("E0308").group(
+        Group::new()
+            .element(
+                Snippet::source(source)
+                    .fold(true)
+                    .origin("$DIR/highlighting.rs")
+                    .annotation(
+                        AnnotationKind::Primary
+                            .span(589..599)
+                            .label("one type is more general than the other"),
+                    )
+                    .annotation(
+                        AnnotationKind::Context
+                            .span(583..588)
+                            .label("arguments to this function are incorrect"),
+                    ),
+            )
+            .element(Level::NOTE.title(&title)),
+    );
+
+    let renderer = Renderer::styled().anonymized_line_numbers(true);
+    anstream::println!("{}", renderer.render(message));
+}
diff --git a/examples/highlight_title.svg b/examples/highlight_title.svg
new file mode 100644
index 00000000..ad358761
--- /dev/null
+++ b/examples/highlight_title.svg
@@ -0,0 +1,45 @@
+<svg width="785px" height="200px" xmlns="http://www.w3.org/2000/svg">
+  <style>
+    .fg { fill: #AAAAAA }
+    .bg { background: #000000 }
+    .fg-bright-blue { fill: #5555FF }
+    .fg-bright-red { fill: #FF5555 }
+    .fg-magenta { fill: #AA00AA }
+    .container {
+      padding: 0 10px;
+      line-height: 18px;
+    }
+    .bold { font-weight: bold; }
+    tspan {
+      font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
+      white-space: pre;
+      line-height: 18px;
+    }
+  </style>
+
+  <rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
+
+  <text xml:space="preserve" class="container fg">
+    <tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0308]</tspan><tspan class="bold">: mismatched types</tspan>
+</tspan>
+    <tspan x="10px" y="46px"><tspan>  </tspan><tspan class="fg-bright-blue bold">--&gt; </tspan><tspan>$DIR/highlighting.rs:22:11</tspan>
+</tspan>
+    <tspan x="10px" y="64px"><tspan>   </tspan><tspan class="fg-bright-blue bold">|</tspan>
+</tspan>
+    <tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">LL</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>     query(wrapped_fn);</tspan>
+</tspan>
+    <tspan x="10px" y="100px"><tspan>   </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>     </tspan><tspan class="fg-bright-blue bold">-----</tspan><tspan> </tspan><tspan class="fg-bright-red bold">^^^^^^^^^^</tspan><tspan> </tspan><tspan class="fg-bright-red bold">one type is more general than the other</tspan>
+</tspan>
+    <tspan x="10px" y="118px"><tspan>   </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>     </tspan><tspan class="fg-bright-blue bold">|</tspan>
+</tspan>
+    <tspan x="10px" y="136px"><tspan>   </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>     </tspan><tspan class="fg-bright-blue bold">arguments to this function are incorrect</tspan>
+</tspan>
+    <tspan x="10px" y="154px"><tspan>   </tspan><tspan class="fg-bright-blue bold">= </tspan><tspan class="bold">note</tspan><tspan>: expected fn pointer `</tspan><tspan class="fg-magenta bold">for&lt;'a&gt;</tspan><tspan> fn(Box&lt;</tspan><tspan class="fg-magenta bold">(dyn Any + Send + 'a)</tspan><tspan>&gt;) -&gt; Pin&lt;_&gt;`</tspan>
+</tspan>
+    <tspan x="10px" y="172px"><tspan>                 found fn item `fn(Box&lt;</tspan><tspan class="fg-magenta bold">(dyn Any + Send + 'static)</tspan><tspan>&gt;) -&gt; Pin&lt;_&gt; </tspan><tspan class="fg-magenta bold">{wrapped_fn}</tspan><tspan>`</tspan>
+</tspan>
+    <tspan x="10px" y="190px">
+</tspan>
+  </text>
+
+</svg>
diff --git a/examples/multislice.rs b/examples/multislice.rs
index 5675a07d..a7d340ad 100644
--- a/examples/multislice.rs
+++ b/examples/multislice.rs
@@ -1,38 +1,20 @@
-use annotate_snippets::{
-    display_list::{DisplayList, FormatOptions},
-    snippet::{Annotation, AnnotationType, Slice, Snippet},
-};
+use annotate_snippets::{Annotation, Group, Level, Renderer, 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 message = Level::ERROR.header("mismatched types").group(
+        Group::new()
+            .element(
+                Snippet::<Annotation<'_>>::source("Foo")
+                    .line_start(51)
+                    .origin("src/format.rs"),
+            )
+            .element(
+                Snippet::<Annotation<'_>>::source("Faa")
+                    .line_start(129)
+                    .origin("src/display.rs"),
+            ),
+    );
 
-    let dl = DisplayList::from(snippet);
-    println!("{}", dl);
+    let renderer = Renderer::styled();
+    anstream::println!("{}", renderer.render(message));
 }
diff --git a/examples/multislice.svg b/examples/multislice.svg
new file mode 100644
index 00000000..5bc01454
--- /dev/null
+++ b/examples/multislice.svg
@@ -0,0 +1,42 @@
+<svg width="740px" height="182px" xmlns="http://www.w3.org/2000/svg">
+  <style>
+    .fg { fill: #AAAAAA }
+    .bg { background: #000000 }
+    .fg-bright-blue { fill: #5555FF }
+    .fg-bright-red { fill: #FF5555 }
+    .container {
+      padding: 0 10px;
+      line-height: 18px;
+    }
+    .bold { font-weight: bold; }
+    tspan {
+      font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
+      white-space: pre;
+      line-height: 18px;
+    }
+  </style>
+
+  <rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
+
+  <text xml:space="preserve" class="container fg">
+    <tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error</tspan><tspan class="bold">: mismatched types</tspan>
+</tspan>
+    <tspan x="10px" y="46px"><tspan>   </tspan><tspan class="fg-bright-blue bold">--&gt; </tspan><tspan>src/format.rs</tspan>
+</tspan>
+    <tspan x="10px" y="64px"><tspan>    </tspan><tspan class="fg-bright-blue bold">|</tspan>
+</tspan>
+    <tspan x="10px" y="82px"><tspan class="fg-bright-blue bold"> 51</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> Foo</tspan>
+</tspan>
+    <tspan x="10px" y="100px"><tspan>    </tspan><tspan class="fg-bright-blue bold">|</tspan>
+</tspan>
+    <tspan x="10px" y="118px"><tspan>   </tspan><tspan class="fg-bright-blue bold">::: </tspan><tspan>src/display.rs:129</tspan>
+</tspan>
+    <tspan x="10px" y="136px"><tspan>    </tspan><tspan class="fg-bright-blue bold">|</tspan>
+</tspan>
+    <tspan x="10px" y="154px"><tspan class="fg-bright-blue bold">129</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> Faa</tspan>
+</tspan>
+    <tspan x="10px" y="172px">
+</tspan>
+  </text>
+
+</svg>
diff --git a/release.toml b/release.toml
new file mode 100644
index 00000000..f74b710a
--- /dev/null
+++ b/release.toml
@@ -0,0 +1,2 @@
+dependent-version = "fix"
+allow-branch = ["master"]
diff --git a/src/display_list/from_snippet.rs b/src/display_list/from_snippet.rs
deleted file mode 100644
index faf48f2a..00000000
--- a/src/display_list/from_snippet.rs
+++ /dev/null
@@ -1,594 +0,0 @@
-//! 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
deleted file mode 100644
index 224a9f58..00000000
--- a/src/display_list/mod.rs
+++ /dev/null
@@ -1,37 +0,0 @@
-//! 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
deleted file mode 100644
index 7941d5fc..00000000
--- a/src/display_list/structs.rs
+++ /dev/null
@@ -1,308 +0,0 @@
-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
deleted file mode 100644
index 16889baa..00000000
--- a/src/formatter/mod.rs
+++ /dev/null
@@ -1,456 +0,0 @@
-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
deleted file mode 100644
index 3fc01c19..00000000
--- a/src/formatter/style.rs
+++ /dev/null
@@ -1,51 +0,0 @@
-//! 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/level.rs b/src/level.rs
new file mode 100644
index 00000000..87d1a9f6
--- /dev/null
+++ b/src/level.rs
@@ -0,0 +1,138 @@
+//! [`Level`] constants for easy importing
+
+use crate::renderer::stylesheet::Stylesheet;
+use crate::snippet::{ERROR_TXT, HELP_TXT, INFO_TXT, NOTE_TXT, WARNING_TXT};
+use crate::{Element, Group, Message, Title};
+use anstyle::Style;
+
+/// Default `error:` [`Level`]
+pub const ERROR: Level<'_> = Level {
+    name: None,
+    level: LevelInner::Error,
+};
+
+/// Default `warning:` [`Level`]
+pub const WARNING: Level<'_> = Level {
+    name: None,
+    level: LevelInner::Warning,
+};
+
+/// Default `info:` [`Level`]
+pub const INFO: Level<'_> = Level {
+    name: None,
+    level: LevelInner::Info,
+};
+
+/// Default `note:` [`Level`]
+pub const NOTE: Level<'_> = Level {
+    name: None,
+    level: LevelInner::Note,
+};
+
+/// Default `help:` [`Level`]
+pub const HELP: Level<'_> = Level {
+    name: None,
+    level: LevelInner::Help,
+};
+
+/// [`Message`] or [`Title`] severity level
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Level<'a> {
+    pub(crate) name: Option<Option<&'a str>>,
+    pub(crate) level: LevelInner,
+}
+
+impl<'a> Level<'a> {
+    pub const ERROR: Level<'a> = ERROR;
+    pub const WARNING: Level<'a> = WARNING;
+    pub const INFO: Level<'a> = INFO;
+    pub const NOTE: Level<'a> = NOTE;
+    pub const HELP: Level<'a> = HELP;
+
+    /// <div class="warning">
+    ///
+    /// Text passed to this function is considered "untrusted input", as such
+    /// all text is passed through a normalization function. Pre-styled text is
+    /// not allowed to be passed to this function.
+    ///
+    /// </div>
+    pub fn text(self, text: Option<&'a str>) -> Level<'a> {
+        Level {
+            name: Some(text),
+            level: self.level,
+        }
+    }
+}
+
+impl<'a> Level<'a> {
+    /// <div class="warning">
+    ///
+    /// Text passed to this function is considered "untrusted input", as such
+    /// all text is passed through a normalization function. Pre-styled text is
+    /// not allowed to be passed to this function.
+    ///
+    /// </div>
+    pub fn header(self, header: &'a str) -> Message<'a> {
+        Message {
+            id: None,
+            groups: vec![Group::new().element(Element::Title(Title {
+                level: self,
+                title: header,
+                primary: true,
+            }))],
+        }
+    }
+
+    /// <div class="warning">
+    ///
+    /// Text passed to this function is allowed to be pre-styled, as such all
+    /// text is considered "trusted input" and has no normalizations applied to
+    /// it. [`normalize_untrusted_str`](crate::normalize_untrusted_str) can be
+    /// used to normalize untrusted text before it is passed to this function.
+    ///
+    /// </div>
+    pub fn title(self, title: &'a str) -> Title<'a> {
+        Title {
+            level: self,
+            title,
+            primary: false,
+        }
+    }
+
+    pub(crate) fn as_str(&self) -> &'a str {
+        match (self.name, self.level) {
+            (Some(Some(name)), _) => name,
+            (Some(None), _) => "",
+            (None, LevelInner::Error) => ERROR_TXT,
+            (None, LevelInner::Warning) => WARNING_TXT,
+            (None, LevelInner::Info) => INFO_TXT,
+            (None, LevelInner::Note) => NOTE_TXT,
+            (None, LevelInner::Help) => HELP_TXT,
+        }
+    }
+
+    pub(crate) fn style(&self, stylesheet: &Stylesheet) -> Style {
+        self.level.style(stylesheet)
+    }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub(crate) enum LevelInner {
+    Error,
+    Warning,
+    Info,
+    Note,
+    Help,
+}
+
+impl LevelInner {
+    pub(crate) fn style(self, stylesheet: &Stylesheet) -> Style {
+        match self {
+            LevelInner::Error => stylesheet.error,
+            LevelInner::Warning => stylesheet.warning,
+            LevelInner::Info => stylesheet.info,
+            LevelInner::Note => stylesheet.note,
+            LevelInner::Help => stylesheet.help,
+        }
+    }
+}
diff --git a/src/lib.rs b/src/lib.rs
index d5813672..76836d0c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,5 +1,3 @@
-#![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
@@ -7,48 +5,56 @@
 //!
 //! # 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 ()
+//! ```rust
+#![doc = include_str!("../examples/expected_type.rs")]
 //! ```
 //!
+#![doc = include_str!("../examples/expected_type.svg")]
+//!
 //! The crate uses a three stage process with two conversions between states:
 //!
 //! ```text
-//! Snippet --> DisplayList --> String
+//! Message --> Renderer --> impl Display
 //! ```
 //!
-//! The input type - [Snippet](self::snippet) is a structure designed
+//! The input type - [Message] 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.
+//! The middle structure - [Renderer] is a structure designed
+//! to convert a snippet into an internal structure that is designed to store
+//! the snippet data in a way that is easy to format.
+//! [Renderer] also handles the user-configurable formatting
+//! options, such as color, or margins.
 //!
 //! 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
+//! # features
+//! - `testing-colors` - Makes [Renderer::styled] colors OS independent, which
+//! allows for easier testing when testing colored output. It should be added as
+//! a feature in `[dev-dependencies]`, which can be done with the following command:
+//! ```text
+//! cargo add annotate-snippets --dev --feature testing-colors
+//! ```
+
+#![cfg_attr(docsrs, feature(doc_auto_cfg))]
+#![warn(clippy::print_stderr)]
+#![warn(clippy::print_stdout)]
+#![warn(missing_debug_implementations)]
+
+pub mod level;
+pub mod renderer;
+mod snippet;
+
+/// Normalize the string to avoid any unicode control characters.
+/// This is important for untrusted input, as it can contain
+/// invalid unicode sequences.
+pub fn normalize_untrusted_str(s: &str) -> String {
+    renderer::normalize_whitespace(s)
+}
 
-pub mod display_list;
-pub mod formatter;
-pub mod snippet;
-pub mod stylesheets;
+#[doc(inline)]
+pub use level::Level;
+#[doc(inline)]
+pub use renderer::Renderer;
+pub use snippet::*;
diff --git a/src/renderer/margin.rs b/src/renderer/margin.rs
new file mode 100644
index 00000000..c4844166
--- /dev/null
+++ b/src/renderer/margin.rs
@@ -0,0 +1,119 @@
+use std::cmp::{max, min};
+
+const ELLIPSIS_PASSING: usize = 6;
+const LONG_WHITESPACE: usize = 20;
+const LONG_WHITESPACE_PADDING: usize = 4;
+
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub(crate) 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.
+    term_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(crate) fn new(
+        whitespace_left: usize,
+        span_left: usize,
+        span_right: usize,
+        label_right: usize,
+        term_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(ELLIPSIS_PASSING),
+            span_left: span_left.saturating_sub(ELLIPSIS_PASSING),
+            span_right: span_right + ELLIPSIS_PASSING,
+            computed_left: 0,
+            computed_right: 0,
+            term_width,
+            label_right: label_right + ELLIPSIS_PASSING,
+        };
+        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 - ELLIPSIS_PASSING
+            } else {
+                self.computed_right
+            };
+        right < line_len && self.computed_left + self.term_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 > LONG_WHITESPACE {
+            self.whitespace_left - (LONG_WHITESPACE - LONG_WHITESPACE_PADDING) // 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.term_width {
+            // Trimming only whitespace isn't enough, let's get craftier.
+            if self.label_right - self.whitespace_left <= self.term_width {
+                // Attempt to fit the code window only trimming whitespace.
+                self.computed_left = self.whitespace_left;
+                self.computed_right = self.computed_left + self.term_width;
+            } else if self.label_right - self.span_left <= self.term_width {
+                // Attempt to fit the code window considering only the spans and labels.
+                let padding_left = (self.term_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.term_width;
+            } else if self.span_right - self.span_left <= self.term_width {
+                // Attempt to fit the code window considering the spans and labels plus padding.
+                let padding_left = (self.term_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.term_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.term_width {
+            line_len
+        } else {
+            min(line_len, self.computed_right)
+        }
+    }
+}
diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs
new file mode 100644
index 00000000..be2abab4
--- /dev/null
+++ b/src/renderer/mod.rs
@@ -0,0 +1,2736 @@
+// Most of this file is adapted from https://github.com/rust-lang/rust/blob/160905b6253f42967ed4aef4b98002944c7df24c/compiler/rustc_errors/src/emitter.rs
+
+//! The renderer for [`Message`]s
+//!
+//! # Example
+//! ```
+//! use annotate_snippets::*;
+//! use annotate_snippets::Level;
+//!
+//! let source = r#"
+//! use baz::zed::bar;
+//!
+//! mod baz {}
+//! mod zed {
+//!     pub fn bar() { println!("bar3"); }
+//! }
+//! fn main() {
+//!     bar();
+//! }
+//! "#;
+//! Level::ERROR
+//!     .header("unresolved import `baz::zed`")
+//!     .id("E0432")
+//!     .group(
+//!         Group::new().element(
+//!             Snippet::source(source)
+//!                 .origin("temp.rs")
+//!                 .line_start(1)
+//!                 .fold(true)
+//!                 .annotation(
+//!                     AnnotationKind::Primary
+//!                         .span(10..13)
+//!                          .label("could not find `zed` in `baz`"),
+//!                 )
+//!         )
+//!     );
+//! ```
+
+mod margin;
+pub(crate) mod source_map;
+mod styled_buffer;
+pub(crate) mod stylesheet;
+
+use crate::level::{Level, LevelInner};
+use crate::renderer::source_map::{
+    AnnotatedLineInfo, LineInfo, Loc, SourceMap, SubstitutionHighlight,
+};
+use crate::renderer::styled_buffer::StyledBuffer;
+use crate::{Annotation, AnnotationKind, Element, Group, Message, Origin, Patch, Snippet, Title};
+pub use anstyle::*;
+use margin::Margin;
+use std::borrow::Cow;
+use std::cmp::{max, min, Ordering, Reverse};
+use std::collections::{HashMap, VecDeque};
+use std::ops::Range;
+use stylesheet::Stylesheet;
+
+const ANONYMIZED_LINE_NUM: &str = "LL";
+pub const DEFAULT_TERM_WIDTH: usize = 140;
+
+/// A renderer for [`Message`]s
+#[derive(Clone, Debug)]
+pub struct Renderer {
+    anonymized_line_numbers: bool,
+    term_width: usize,
+    theme: OutputTheme,
+    stylesheet: Stylesheet,
+}
+
+impl Renderer {
+    /// No terminal styling
+    pub const fn plain() -> Self {
+        Self {
+            anonymized_line_numbers: false,
+            term_width: DEFAULT_TERM_WIDTH,
+            theme: OutputTheme::Ascii,
+            stylesheet: Stylesheet::plain(),
+        }
+    }
+
+    /// Default terminal styling
+    ///
+    /// # Note
+    /// When testing styled terminal output, see the [`testing-colors` feature](crate#features)
+    pub const fn styled() -> Self {
+        const USE_WINDOWS_COLORS: bool = cfg!(windows) && !cfg!(feature = "testing-colors");
+        const BRIGHT_BLUE: Style = if USE_WINDOWS_COLORS {
+            AnsiColor::BrightCyan.on_default()
+        } else {
+            AnsiColor::BrightBlue.on_default()
+        };
+        Self {
+            stylesheet: Stylesheet {
+                error: AnsiColor::BrightRed.on_default().effects(Effects::BOLD),
+                warning: if USE_WINDOWS_COLORS {
+                    AnsiColor::BrightYellow.on_default()
+                } else {
+                    AnsiColor::Yellow.on_default()
+                }
+                .effects(Effects::BOLD),
+                info: BRIGHT_BLUE.effects(Effects::BOLD),
+                note: AnsiColor::BrightGreen.on_default().effects(Effects::BOLD),
+                help: AnsiColor::BrightCyan.on_default().effects(Effects::BOLD),
+                line_num: BRIGHT_BLUE.effects(Effects::BOLD),
+                emphasis: if USE_WINDOWS_COLORS {
+                    AnsiColor::BrightWhite.on_default()
+                } else {
+                    Style::new()
+                }
+                .effects(Effects::BOLD),
+                none: Style::new(),
+                context: BRIGHT_BLUE.effects(Effects::BOLD),
+                addition: AnsiColor::BrightGreen.on_default(),
+                removal: AnsiColor::BrightRed.on_default(),
+            },
+            ..Self::plain()
+        }
+    }
+
+    /// Anonymize line numbers
+    ///
+    /// This enables (or disables) line number anonymization. When enabled, line numbers are replaced
+    /// with `LL`.
+    ///
+    /// # Example
+    ///
+    /// ```text
+    ///   --> $DIR/whitespace-trimming.rs:4:193
+    ///    |
+    /// LL | ...                   let _: () = 42;
+    ///    |                                   ^^ expected (), found integer
+    ///    |
+    /// ```
+    pub const fn anonymized_line_numbers(mut self, anonymized_line_numbers: bool) -> Self {
+        self.anonymized_line_numbers = anonymized_line_numbers;
+        self
+    }
+
+    // Set the terminal width
+    pub const fn term_width(mut self, term_width: usize) -> Self {
+        self.term_width = term_width;
+        self
+    }
+
+    pub const fn theme(mut self, output_theme: OutputTheme) -> Self {
+        self.theme = output_theme;
+        self
+    }
+
+    /// Set the output style for `error`
+    pub const fn error(mut self, style: Style) -> Self {
+        self.stylesheet.error = style;
+        self
+    }
+
+    /// Set the output style for `warning`
+    pub const fn warning(mut self, style: Style) -> Self {
+        self.stylesheet.warning = style;
+        self
+    }
+
+    /// Set the output style for `info`
+    pub const fn info(mut self, style: Style) -> Self {
+        self.stylesheet.info = style;
+        self
+    }
+
+    /// Set the output style for `note`
+    pub const fn note(mut self, style: Style) -> Self {
+        self.stylesheet.note = style;
+        self
+    }
+
+    /// Set the output style for `help`
+    pub const fn help(mut self, style: Style) -> Self {
+        self.stylesheet.help = style;
+        self
+    }
+
+    /// Set the output style for line numbers
+    pub const fn line_num(mut self, style: Style) -> Self {
+        self.stylesheet.line_num = style;
+        self
+    }
+
+    /// Set the output style for emphasis
+    pub const fn emphasis(mut self, style: Style) -> Self {
+        self.stylesheet.emphasis = style;
+        self
+    }
+
+    /// Set the output style for none
+    pub const fn none(mut self, style: Style) -> Self {
+        self.stylesheet.none = style;
+        self
+    }
+}
+
+impl Renderer {
+    pub fn render(&self, mut message: Message<'_>) -> String {
+        let mut buffer = StyledBuffer::new();
+        let max_line_num_len = if self.anonymized_line_numbers {
+            ANONYMIZED_LINE_NUM.len()
+        } else {
+            let n = message.max_line_number();
+            num_decimal_digits(n)
+        };
+        let title = message.groups.remove(0).elements.remove(0);
+        let level = if let Element::Title(title) = &title {
+            title.level.clone()
+        } else {
+            panic!("Expected a title as the first element of the message")
+        };
+        if let Some(first) = message.groups.first_mut() {
+            first.elements.insert(0, title);
+        } else {
+            message.groups.push(Group::new().element(title));
+        }
+        self.render_message(&mut buffer, message, max_line_num_len);
+
+        buffer.render(level, &self.stylesheet).unwrap()
+    }
+
+    fn render_message(
+        &self,
+        buffer: &mut StyledBuffer,
+        message: Message<'_>,
+        max_line_num_len: usize,
+    ) {
+        let og_primary_origin = message
+            .groups
+            .iter()
+            .find_map(|group| {
+                group.elements.iter().find_map(|s| match &s {
+                    Element::Cause(cause) => {
+                        if cause.markers.iter().any(|m| m.kind.is_primary()) {
+                            Some(cause.origin)
+                        } else {
+                            None
+                        }
+                    }
+                    Element::Origin(origin) => {
+                        if origin.primary {
+                            Some(Some(origin.origin))
+                        } else {
+                            None
+                        }
+                    }
+                    _ => None,
+                })
+            })
+            .unwrap_or(
+                message
+                    .groups
+                    .iter()
+                    .find_map(|group| {
+                        group.elements.iter().find_map(|s| match &s {
+                            Element::Cause(cause) => Some(cause.origin),
+                            Element::Origin(origin) => Some(Some(origin.origin)),
+                            _ => None,
+                        })
+                    })
+                    .unwrap_or_default(),
+            );
+        let group_len = message.groups.len();
+        for (g, group) in message.groups.into_iter().enumerate() {
+            let primary_origin = group
+                .elements
+                .iter()
+                .find_map(|s| match &s {
+                    Element::Cause(cause) => {
+                        if cause.markers.iter().any(|m| m.kind.is_primary()) {
+                            Some(cause.origin)
+                        } else {
+                            None
+                        }
+                    }
+                    Element::Origin(origin) => {
+                        if origin.primary {
+                            Some(Some(origin.origin))
+                        } else {
+                            None
+                        }
+                    }
+                    _ => None,
+                })
+                .unwrap_or(
+                    group
+                        .elements
+                        .iter()
+                        .find_map(|s| match &s {
+                            Element::Cause(cause) => Some(cause.origin),
+                            Element::Origin(origin) => Some(Some(origin.origin)),
+                            _ => None,
+                        })
+                        .unwrap_or_default(),
+                );
+            let mut source_map_annotated_lines = VecDeque::new();
+            let mut max_depth = 0;
+            for e in &group.elements {
+                if let Element::Cause(cause) = e {
+                    let source_map = SourceMap::new(cause.source, cause.line_start);
+                    let (depth, annotated_lines) =
+                        source_map.annotated_lines(cause.markers.clone(), cause.fold);
+                    max_depth = max(max_depth, depth);
+                    source_map_annotated_lines.push_back((source_map, annotated_lines));
+                }
+            }
+            let mut message_iter = group.elements.iter().enumerate().peekable();
+            let mut last_was_suggestion = false;
+            while let Some((i, section)) = message_iter.next() {
+                let peek = message_iter.peek().map(|(_, s)| s).copied();
+                match &section {
+                    Element::Title(title) => {
+                        self.render_title(
+                            buffer,
+                            title,
+                            peek,
+                            max_line_num_len,
+                            if i == 0 { false } else { !title.primary },
+                            message.id.as_ref().and_then(|id| {
+                                if g == 0 && i == 0 {
+                                    Some(id)
+                                } else {
+                                    None
+                                }
+                            }),
+                            matches!(peek, Some(Element::Title(_))),
+                        );
+                        last_was_suggestion = false;
+                    }
+                    Element::Cause(cause) => {
+                        if let Some((source_map, annotated_lines)) =
+                            source_map_annotated_lines.pop_front()
+                        {
+                            self.render_snippet_annotations(
+                                buffer,
+                                max_line_num_len,
+                                cause,
+                                primary_origin,
+                                &source_map,
+                                &annotated_lines,
+                                max_depth,
+                                peek.is_some() || (g == 0 && group_len > 1),
+                            );
+
+                            if g == 0 && group_len > 1 {
+                                if matches!(peek, Some(Element::Title(level)) if level.level.name != Some(None))
+                                {
+                                    self.draw_col_separator_no_space(
+                                        buffer,
+                                        buffer.num_lines(),
+                                        max_line_num_len + 1,
+                                    );
+                                // We want to draw the separator when it is
+                                // requested, or when it is the last element
+                                } else if peek.is_none() {
+                                    self.draw_col_separator_end(
+                                        buffer,
+                                        buffer.num_lines(),
+                                        max_line_num_len + 1,
+                                    );
+                                }
+                            }
+                        }
+
+                        last_was_suggestion = false;
+                    }
+                    Element::Suggestion(suggestion) => {
+                        let source_map = SourceMap::new(suggestion.source, suggestion.line_start);
+                        self.emit_suggestion_default(
+                            buffer,
+                            suggestion,
+                            max_line_num_len,
+                            &source_map,
+                            primary_origin.or(og_primary_origin),
+                            last_was_suggestion,
+                        );
+                        last_was_suggestion = true;
+                    }
+
+                    Element::Origin(origin) => {
+                        self.render_origin(buffer, max_line_num_len, origin);
+                        last_was_suggestion = false;
+                    }
+                    Element::Padding(_) => {
+                        self.draw_col_separator_no_space(
+                            buffer,
+                            buffer.num_lines(),
+                            max_line_num_len + 1,
+                        );
+                    }
+                }
+                if g == 0
+                    && (matches!(section, Element::Origin(_))
+                        || (matches!(section, Element::Title(_)) && i == 0)
+                        || matches!(section, Element::Title(level) if level.level.name == Some(None)))
+                {
+                    if peek.is_none() && group_len > 1 {
+                        self.draw_col_separator_end(
+                            buffer,
+                            buffer.num_lines(),
+                            max_line_num_len + 1,
+                        );
+                    } else if matches!(peek, Some(Element::Title(level)) if level.level.name != Some(None))
+                    {
+                        self.draw_col_separator_no_space(
+                            buffer,
+                            buffer.num_lines(),
+                            max_line_num_len + 1,
+                        );
+                    }
+                }
+            }
+        }
+    }
+
+    #[allow(clippy::too_many_arguments)]
+    fn render_title(
+        &self,
+        buffer: &mut StyledBuffer,
+        title: &Title<'_>,
+        next_section: Option<&Element<'_>>,
+        max_line_num_len: usize,
+        is_secondary: bool,
+        id: Option<&&str>,
+        is_cont: bool,
+    ) {
+        let line_offset = buffer.num_lines();
+
+        let (has_primary_spans, has_span_labels) =
+            next_section.map_or((false, false), |s| match s {
+                Element::Title(_) | Element::Padding(_) => (false, false),
+                Element::Cause(cause) => (
+                    cause.markers.iter().any(|m| m.kind.is_primary()),
+                    cause.markers.iter().any(|m| m.label.is_some()),
+                ),
+                Element::Suggestion(_) => (true, false),
+                Element::Origin(_) => (false, true),
+            });
+
+        if !has_primary_spans && !has_span_labels && is_secondary {
+            // This is a secondary message with no span info
+            for _ in 0..max_line_num_len {
+                buffer.prepend(line_offset, " ", ElementStyle::NoStyle);
+            }
+
+            if title.level.name != Some(None) {
+                self.draw_note_separator(buffer, line_offset, max_line_num_len + 1, is_cont);
+                buffer.append(
+                    line_offset,
+                    title.level.as_str(),
+                    ElementStyle::MainHeaderMsg,
+                );
+                buffer.append(line_offset, ": ", ElementStyle::NoStyle);
+            }
+
+            let printed_lines =
+                self.msgs_to_buffer(buffer, title.title, max_line_num_len, "note", None);
+            if is_cont && matches!(self.theme, OutputTheme::Unicode) {
+                // There's another note after this one, associated to the subwindow above.
+                // We write additional vertical lines to join them:
+                //   ╭▸ test.rs:3:3
+                //   │
+                // 3 │   code
+                //   │   ━━━━
+                //   │
+                //   ├ note: foo
+                //   │       bar
+                //   ╰ note: foo
+                //           bar
+                for i in line_offset + 1..=printed_lines {
+                    self.draw_col_separator_no_space(buffer, i, max_line_num_len + 1);
+                }
+            }
+        } else {
+            let mut label_width = 0;
+
+            if title.level.name != Some(None) {
+                buffer.append(
+                    line_offset,
+                    title.level.as_str(),
+                    ElementStyle::Level(title.level.level),
+                );
+            }
+            label_width += title.level.as_str().len();
+            if let Some(id) = id {
+                buffer.append(line_offset, "[", ElementStyle::Level(title.level.level));
+                buffer.append(line_offset, id, ElementStyle::Level(title.level.level));
+                buffer.append(line_offset, "]", ElementStyle::Level(title.level.level));
+                label_width += 2 + id.len();
+            }
+            let header_style = if is_secondary {
+                ElementStyle::HeaderMsg
+            } else {
+                ElementStyle::MainHeaderMsg
+            };
+            if title.level.name != Some(None) {
+                buffer.append(line_offset, ": ", header_style);
+                label_width += 2;
+            }
+            if !title.title.is_empty() {
+                for (line, text) in normalize_whitespace(title.title).lines().enumerate() {
+                    buffer.append(
+                        line_offset + line,
+                        &format!(
+                            "{}{}",
+                            if line == 0 {
+                                String::new()
+                            } else {
+                                " ".repeat(label_width)
+                            },
+                            text
+                        ),
+                        header_style,
+                    );
+                }
+            }
+        }
+    }
+
+    /// Adds a left margin to every line but the first, given a padding length and the label being
+    /// displayed, keeping the provided highlighting.
+    fn msgs_to_buffer(
+        &self,
+        buffer: &mut StyledBuffer,
+        title: &str,
+        padding: usize,
+        label: &str,
+        override_style: Option<ElementStyle>,
+    ) -> usize {
+        // The extra 5 ` ` is padding that's always needed to align to the `note: `:
+        //
+        //   error: message
+        //     --> file.rs:13:20
+        //      |
+        //   13 |     <CODE>
+        //      |      ^^^^
+        //      |
+        //      = note: multiline
+        //              message
+        //   ++^^^----xx
+        //    |  |   | |
+        //    |  |   | magic `2`
+        //    |  |   length of label
+        //    |  magic `3`
+        //    `max_line_num_len`
+        let padding = " ".repeat(padding + label.len() + 5);
+
+        let mut line_number = buffer.num_lines().saturating_sub(1);
+
+        // Provided the following diagnostic message:
+        //
+        //     let msgs = vec![
+        //       ("
+        //       ("highlighted multiline\nstring to\nsee how it ", Style::NoStyle),
+        //       ("looks", Style::Highlight),
+        //       ("with\nvery ", Style::NoStyle),
+        //       ("weird", Style::Highlight),
+        //       (" formats\n", Style::NoStyle),
+        //       ("see?", Style::Highlight),
+        //     ];
+        //
+        // the expected output on a note is (* surround the highlighted text)
+        //
+        //        = note: highlighted multiline
+        //                string to
+        //                see how it *looks* with
+        //                very *weird* formats
+        //                see?
+        let style = if let Some(override_style) = override_style {
+            override_style
+        } else {
+            ElementStyle::NoStyle
+        };
+        let lines = title.split('\n').collect::<Vec<_>>();
+        if lines.len() > 1 {
+            for (i, line) in lines.iter().enumerate() {
+                if i != 0 {
+                    line_number += 1;
+                    buffer.append(line_number, &padding, ElementStyle::NoStyle);
+                }
+                buffer.append(line_number, line, style);
+            }
+        } else {
+            buffer.append(line_number, title, style);
+        }
+        line_number
+    }
+
+    fn render_origin(
+        &self,
+        buffer: &mut StyledBuffer,
+        max_line_num_len: usize,
+        origin: &Origin<'_>,
+    ) {
+        let buffer_msg_line_offset = buffer.num_lines();
+        if origin.primary {
+            buffer.prepend(
+                buffer_msg_line_offset,
+                self.file_start(),
+                ElementStyle::LineNumber,
+            );
+        } else {
+            // if !origin.standalone {
+            //     // Add spacing line, as shown:
+            //     //   --> $DIR/file:54:15
+            //     //    |
+            //     // LL |         code
+            //     //    |         ^^^^
+            //     //    | (<- It prints *this* line)
+            //     //   ::: $DIR/other_file.rs:15:5
+            //     //    |
+            //     // LL |     code
+            //     //    |     ----
+            //     self.draw_col_separator_no_space(
+            //         buffer,
+            //         buffer_msg_line_offset,
+            //         max_line_num_len + 1,
+            //     );
+            //
+            //     buffer_msg_line_offset += 1;
+            // }
+            // Then, the secondary file indicator
+            buffer.prepend(
+                buffer_msg_line_offset,
+                self.secondary_file_start(),
+                ElementStyle::LineNumber,
+            );
+        }
+
+        let str = match (&origin.line, &origin.char_column) {
+            (Some(line), Some(col)) => {
+                format!("{}:{}:{}", origin.origin, line, col)
+            }
+            (Some(line), None) => format!("{}:{}", origin.origin, line),
+            _ => origin.origin.to_owned(),
+        };
+        buffer.append(buffer_msg_line_offset, &str, ElementStyle::LineAndColumn);
+        for _ in 0..max_line_num_len {
+            buffer.prepend(buffer_msg_line_offset, " ", ElementStyle::NoStyle);
+        }
+
+        if let Some(label) = &origin.label {
+            self.draw_col_separator_no_space(
+                buffer,
+                buffer_msg_line_offset + 1,
+                max_line_num_len + 1,
+            );
+            let title = Level::NOTE.title(label);
+            self.render_title(buffer, &title, None, max_line_num_len, true, None, false);
+        }
+    }
+
+    #[allow(clippy::too_many_arguments)]
+    fn render_snippet_annotations(
+        &self,
+        buffer: &mut StyledBuffer,
+        max_line_num_len: usize,
+        snippet: &Snippet<'_, Annotation<'_>>,
+        primary_origin: Option<&str>,
+        sm: &SourceMap<'_>,
+        annotated_lines: &[AnnotatedLineInfo<'_>],
+        multiline_depth: usize,
+        is_cont: bool,
+    ) {
+        if let Some(origin) = snippet.origin {
+            let mut origin = Origin::new(origin);
+            // print out the span location and spacer before we print the annotated source
+            // to do this, we need to know if this span will be primary
+            let is_primary = primary_origin == Some(origin.origin);
+
+            if is_primary {
+                origin.primary = true;
+                if let Some(primary_line) = annotated_lines
+                    .iter()
+                    .find(|l| l.annotations.iter().any(LineAnnotation::is_primary))
+                    .or(annotated_lines.iter().find(|l| !l.annotations.is_empty()))
+                {
+                    origin.line = Some(primary_line.line_index);
+                    if let Some(first_annotation) = primary_line
+                        .annotations
+                        .iter()
+                        .find(|a| a.is_primary())
+                        .or(primary_line.annotations.first())
+                    {
+                        origin.char_column = Some(first_annotation.start.char + 1);
+                    }
+                }
+            } else {
+                let buffer_msg_line_offset = buffer.num_lines();
+                // Add spacing line, as shown:
+                //   --> $DIR/file:54:15
+                //    |
+                // LL |         code
+                //    |         ^^^^
+                //    | (<- It prints *this* line)
+                //   ::: $DIR/other_file.rs:15:5
+                //    |
+                // LL |     code
+                //    |     ----
+                self.draw_col_separator_no_space(
+                    buffer,
+                    buffer_msg_line_offset,
+                    max_line_num_len + 1,
+                );
+                if let Some(first_line) = annotated_lines.first() {
+                    origin.line = Some(first_line.line_index);
+                    if let Some(first_annotation) = first_line.annotations.first() {
+                        origin.char_column = Some(first_annotation.start.char + 1);
+                    }
+                }
+            }
+            self.render_origin(buffer, max_line_num_len, &origin);
+        }
+
+        // Put in the spacer between the location and annotated source
+        let buffer_msg_line_offset = buffer.num_lines();
+        self.draw_col_separator_no_space(buffer, buffer_msg_line_offset, max_line_num_len + 1);
+
+        // Contains the vertical lines' positions for active multiline annotations
+        let mut multilines = Vec::new();
+
+        // Get the left-side margin to remove it
+        let mut whitespace_margin = usize::MAX;
+        for line_info in annotated_lines {
+            // Whitespace can only be removed (aka considered leading)
+            // if the lexer considers it whitespace.
+            // non-rustc_lexer::is_whitespace() chars are reported as an
+            // error (ex. no-break-spaces \u{a0}), and thus can't be considered
+            // for removal during error reporting.
+            let leading_whitespace = line_info
+                .line
+                .chars()
+                .take_while(|c| c.is_whitespace())
+                .map(|c| {
+                    match c {
+                        // Tabs are displayed as 4 spaces
+                        '\t' => 4,
+                        _ => 1,
+                    }
+                })
+                .sum();
+            if line_info.line.chars().any(|c| !c.is_whitespace()) {
+                whitespace_margin = min(whitespace_margin, leading_whitespace);
+            }
+        }
+        if whitespace_margin == usize::MAX {
+            whitespace_margin = 0;
+        }
+
+        // Left-most column any visible span points at.
+        let mut span_left_margin = usize::MAX;
+        for line_info in annotated_lines {
+            for ann in &line_info.annotations {
+                span_left_margin = min(span_left_margin, ann.start.display);
+                span_left_margin = min(span_left_margin, ann.end.display);
+            }
+        }
+        if span_left_margin == usize::MAX {
+            span_left_margin = 0;
+        }
+
+        // Right-most column any visible span points at.
+        let mut span_right_margin = 0;
+        let mut label_right_margin = 0;
+        let mut max_line_len = 0;
+        for line_info in annotated_lines {
+            max_line_len = max(max_line_len, line_info.line.len());
+            for ann in &line_info.annotations {
+                span_right_margin = max(span_right_margin, ann.start.display);
+                span_right_margin = max(span_right_margin, ann.end.display);
+                // FIXME: account for labels not in the same line
+                let label_right = ann.label.as_ref().map_or(0, |l| l.len() + 1);
+                label_right_margin = max(label_right_margin, ann.end.display + label_right);
+            }
+        }
+        let width_offset = 3 + max_line_num_len;
+        let code_offset = if multiline_depth == 0 {
+            width_offset
+        } else {
+            width_offset + multiline_depth + 1
+        };
+
+        let column_width = self.term_width.saturating_sub(code_offset);
+
+        let margin = Margin::new(
+            whitespace_margin,
+            span_left_margin,
+            span_right_margin,
+            label_right_margin,
+            column_width,
+            max_line_len,
+        );
+
+        // Next, output the annotate source for this file
+        for annotated_line_idx in 0..annotated_lines.len() {
+            let previous_buffer_line = buffer.num_lines();
+
+            let depths = self.render_source_line(
+                &annotated_lines[annotated_line_idx],
+                buffer,
+                width_offset,
+                code_offset,
+                max_line_num_len,
+                margin,
+                !is_cont && annotated_line_idx + 1 == annotated_lines.len(),
+            );
+
+            let mut to_add = HashMap::new();
+
+            for (depth, style) in depths {
+                if let Some(index) = multilines.iter().position(|(d, _)| d == &depth) {
+                    multilines.swap_remove(index);
+                } else {
+                    to_add.insert(depth, style);
+                }
+            }
+
+            // Set the multiline annotation vertical lines to the left of
+            // the code in this line.
+            for (depth, style) in &multilines {
+                for line in previous_buffer_line..buffer.num_lines() {
+                    self.draw_multiline_line(buffer, line, width_offset, *depth, *style);
+                }
+            }
+            // check to see if we need to print out or elide lines that come between
+            // this annotated line and the next one.
+            if annotated_line_idx < (annotated_lines.len() - 1) {
+                let line_idx_delta = annotated_lines[annotated_line_idx + 1].line_index
+                    - annotated_lines[annotated_line_idx].line_index;
+                match line_idx_delta.cmp(&2) {
+                    Ordering::Greater => {
+                        let last_buffer_line_num = buffer.num_lines();
+
+                        self.draw_line_separator(buffer, last_buffer_line_num, width_offset);
+
+                        // Set the multiline annotation vertical lines on `...` bridging line.
+                        for (depth, style) in &multilines {
+                            self.draw_multiline_line(
+                                buffer,
+                                last_buffer_line_num,
+                                width_offset,
+                                *depth,
+                                *style,
+                            );
+                        }
+                        if let Some(line) = annotated_lines.get(annotated_line_idx) {
+                            for ann in &line.annotations {
+                                if let LineAnnotationType::MultilineStart(pos) = ann.annotation_type
+                                {
+                                    // In the case where we have elided the entire start of the
+                                    // multispan because those lines were empty, we still need
+                                    // to draw the `|`s across the `...`.
+                                    self.draw_multiline_line(
+                                        buffer,
+                                        last_buffer_line_num,
+                                        width_offset,
+                                        pos,
+                                        if ann.is_primary() {
+                                            ElementStyle::UnderlinePrimary
+                                        } else {
+                                            ElementStyle::UnderlineSecondary
+                                        },
+                                    );
+                                }
+                            }
+                        }
+                    }
+
+                    Ordering::Equal => {
+                        let unannotated_line = sm
+                            .get_line(annotated_lines[annotated_line_idx].line_index + 1)
+                            .unwrap_or("");
+
+                        let last_buffer_line_num = buffer.num_lines();
+
+                        self.draw_line(
+                            buffer,
+                            &normalize_whitespace(unannotated_line),
+                            annotated_lines[annotated_line_idx + 1].line_index - 1,
+                            last_buffer_line_num,
+                            width_offset,
+                            code_offset,
+                            max_line_num_len,
+                            margin,
+                        );
+
+                        for (depth, style) in &multilines {
+                            self.draw_multiline_line(
+                                buffer,
+                                last_buffer_line_num,
+                                width_offset,
+                                *depth,
+                                *style,
+                            );
+                        }
+                        if let Some(line) = annotated_lines.get(annotated_line_idx) {
+                            for ann in &line.annotations {
+                                if let LineAnnotationType::MultilineStart(pos) = ann.annotation_type
+                                {
+                                    self.draw_multiline_line(
+                                        buffer,
+                                        last_buffer_line_num,
+                                        width_offset,
+                                        pos,
+                                        if ann.is_primary() {
+                                            ElementStyle::UnderlinePrimary
+                                        } else {
+                                            ElementStyle::UnderlineSecondary
+                                        },
+                                    );
+                                }
+                            }
+                        }
+                    }
+                    Ordering::Less => {}
+                }
+            }
+
+            multilines.extend(to_add);
+        }
+    }
+
+    #[allow(clippy::too_many_arguments)]
+    fn render_source_line(
+        &self,
+        line_info: &AnnotatedLineInfo<'_>,
+        buffer: &mut StyledBuffer,
+        width_offset: usize,
+        code_offset: usize,
+        max_line_num_len: usize,
+        margin: Margin,
+        close_window: bool,
+    ) -> Vec<(usize, ElementStyle)> {
+        // Draw:
+        //
+        //   LL | ... code ...
+        //      |     ^^-^ span label
+        //      |       |
+        //      |       secondary span label
+        //
+        //   ^^ ^ ^^^ ^^^^ ^^^ we don't care about code too far to the right of a span, we trim it
+        //   |  | |   |
+        //   |  | |   actual code found in your source code and the spans we use to mark it
+        //   |  | when there's too much wasted space to the left, trim it
+        //   |  vertical divider between the column number and the code
+        //   column number
+
+        if line_info.line_index == 0 {
+            return Vec::new();
+        }
+
+        let source_string = normalize_whitespace(line_info.line);
+
+        let line_offset = buffer.num_lines();
+
+        // Left trim
+        let left = margin.left(str_width(&source_string));
+
+        // FIXME: This looks fishy. See #132860.
+        // Account for unicode characters of width !=0 that were removed.
+        let mut taken = 0;
+        source_string.chars().for_each(|ch| {
+            let next = char_width(ch);
+            if taken + next <= left {
+                taken += next;
+            }
+        });
+
+        let left = taken;
+        self.draw_line(
+            buffer,
+            &source_string,
+            line_info.line_index,
+            line_offset,
+            width_offset,
+            code_offset,
+            max_line_num_len,
+            margin,
+        );
+
+        // Special case when there's only one annotation involved, it is the start of a multiline
+        // span and there's no text at the beginning of the code line. Instead of doing the whole
+        // graph:
+        //
+        // 2 |   fn foo() {
+        //   |  _^
+        // 3 | |
+        // 4 | | }
+        //   | |_^ test
+        //
+        // we simplify the output to:
+        //
+        // 2 | / fn foo() {
+        // 3 | |
+        // 4 | | }
+        //   | |_^ test
+        let mut buffer_ops = vec![];
+        let mut annotations = vec![];
+        let mut short_start = true;
+        for ann in &line_info.annotations {
+            if let LineAnnotationType::MultilineStart(depth) = ann.annotation_type {
+                if source_string
+                    .chars()
+                    .take(ann.start.display)
+                    .all(char::is_whitespace)
+                {
+                    let uline = self.underline(ann.is_primary());
+                    let chr = uline.multiline_whole_line;
+                    annotations.push((depth, uline.style));
+                    buffer_ops.push((line_offset, width_offset + depth - 1, chr, uline.style));
+                } else {
+                    short_start = false;
+                    break;
+                }
+            } else if let LineAnnotationType::MultilineLine(_) = ann.annotation_type {
+            } else {
+                short_start = false;
+                break;
+            }
+        }
+        if short_start {
+            for (y, x, c, s) in buffer_ops {
+                buffer.putc(y, x, c, s);
+            }
+            return annotations;
+        }
+
+        // We want to display like this:
+        //
+        //      vec.push(vec.pop().unwrap());
+        //      ---      ^^^               - previous borrow ends here
+        //      |        |
+        //      |        error occurs here
+        //      previous borrow of `vec` occurs here
+        //
+        // But there are some weird edge cases to be aware of:
+        //
+        //      vec.push(vec.pop().unwrap());
+        //      --------                    - previous borrow ends here
+        //      ||
+        //      |this makes no sense
+        //      previous borrow of `vec` occurs here
+        //
+        // For this reason, we group the lines into "highlight lines"
+        // and "annotations lines", where the highlight lines have the `^`.
+
+        // Sort the annotations by (start, end col)
+        // The labels are reversed, sort and then reversed again.
+        // Consider a list of annotations (A1, A2, C1, C2, B1, B2) where
+        // the letter signifies the span. Here we are only sorting by the
+        // span and hence, the order of the elements with the same span will
+        // not change. On reversing the ordering (|a, b| but b.cmp(a)), you get
+        // (C1, C2, B1, B2, A1, A2). All the elements with the same span are
+        // still ordered first to last, but all the elements with different
+        // spans are ordered by their spans in last to first order. Last to
+        // first order is important, because the jiggly lines and | are on
+        // the left, so the rightmost span needs to be rendered first,
+        // otherwise the lines would end up needing to go over a message.
+
+        let mut annotations = line_info.annotations.clone();
+        annotations.sort_by_key(|a| Reverse(a.start.display));
+
+        // First, figure out where each label will be positioned.
+        //
+        // In the case where you have the following annotations:
+        //
+        //      vec.push(vec.pop().unwrap());
+        //      --------                    - previous borrow ends here [C]
+        //      ||
+        //      |this makes no sense [B]
+        //      previous borrow of `vec` occurs here [A]
+        //
+        // `annotations_position` will hold [(2, A), (1, B), (0, C)].
+        //
+        // We try, when possible, to stick the rightmost annotation at the end
+        // of the highlight line:
+        //
+        //      vec.push(vec.pop().unwrap());
+        //      ---      ---               - previous borrow ends here
+        //
+        // But sometimes that's not possible because one of the other
+        // annotations overlaps it. For example, from the test
+        // `span_overlap_label`, we have the following annotations
+        // (written on distinct lines for clarity):
+        //
+        //      fn foo(x: u32) {
+        //      --------------
+        //             -
+        //
+        // In this case, we can't stick the rightmost-most label on
+        // the highlight line, or we would get:
+        //
+        //      fn foo(x: u32) {
+        //      -------- x_span
+        //      |
+        //      fn_span
+        //
+        // which is totally weird. Instead we want:
+        //
+        //      fn foo(x: u32) {
+        //      --------------
+        //      |      |
+        //      |      x_span
+        //      fn_span
+        //
+        // which is...less weird, at least. In fact, in general, if
+        // the rightmost span overlaps with any other span, we should
+        // use the "hang below" version, so we can at least make it
+        // clear where the span *starts*. There's an exception for this
+        // logic, when the labels do not have a message:
+        //
+        //      fn foo(x: u32) {
+        //      --------------
+        //             |
+        //             x_span
+        //
+        // instead of:
+        //
+        //      fn foo(x: u32) {
+        //      --------------
+        //      |      |
+        //      |      x_span
+        //      <EMPTY LINE>
+        //
+        let mut annotations_position = vec![];
+        let mut line_len: usize = 0;
+        let mut p = 0;
+        for (i, annotation) in annotations.iter().enumerate() {
+            for (j, next) in annotations.iter().enumerate() {
+                if overlaps(next, annotation, 0)  // This label overlaps with another one and both
+                    && annotation.has_label()     // take space (they have text and are not
+                    && j > i                      // multiline lines).
+                    && p == 0
+                // We're currently on the first line, move the label one line down
+                {
+                    // If we're overlapping with an un-labelled annotation with the same span
+                    // we can just merge them in the output
+                    if next.start.display == annotation.start.display
+                        && next.end.display == annotation.end.display
+                        && !next.has_label()
+                    {
+                        continue;
+                    }
+
+                    // This annotation needs a new line in the output.
+                    p += 1;
+                    break;
+                }
+            }
+            annotations_position.push((p, annotation));
+            for (j, next) in annotations.iter().enumerate() {
+                if j > i {
+                    let l = next.label.as_ref().map_or(0, |label| label.len() + 2);
+                    if (overlaps(next, annotation, l) // Do not allow two labels to be in the same
+                        // line if they overlap including padding, to
+                        // avoid situations like:
+                        //
+                        //      fn foo(x: u32) {
+                        //      -------^------
+                        //      |      |
+                        //      fn_spanx_span
+                        //
+                        && annotation.has_label()    // Both labels must have some text, otherwise
+                        && next.has_label())         // they are not overlapping.
+                        // Do not add a new line if this annotation
+                        // or the next are vertical line placeholders.
+                        || (annotation.takes_space() // If either this or the next annotation is
+                        && next.has_label())     // multiline start/end, move it to a new line
+                        || (annotation.has_label()   // so as not to overlap the horizontal lines.
+                        && next.takes_space())
+                        || (annotation.takes_space() && next.takes_space())
+                        || (overlaps(next, annotation, l)
+                        && next.end.display <= annotation.end.display
+                        && next.has_label()
+                        && p == 0)
+                    // Avoid #42595.
+                    {
+                        // This annotation needs a new line in the output.
+                        p += 1;
+                        break;
+                    }
+                }
+            }
+            line_len = max(line_len, p);
+        }
+
+        if line_len != 0 {
+            line_len += 1;
+        }
+
+        // If there are no annotations or the only annotations on this line are
+        // MultilineLine, then there's only code being shown, stop processing.
+        if line_info.annotations.iter().all(LineAnnotation::is_line) {
+            return vec![];
+        }
+
+        if annotations_position
+            .iter()
+            .all(|(_, ann)| matches!(ann.annotation_type, LineAnnotationType::MultilineStart(_)))
+        {
+            if let Some(max_pos) = annotations_position.iter().map(|(pos, _)| *pos).max() {
+                // Special case the following, so that we minimize overlapping multiline spans.
+                //
+                // 3 │       X0 Y0 Z0
+                //   │ ┏━━━━━┛  │  │     < We are writing these lines
+                //   │ ┃┌───────┘  │     < by reverting the "depth" of
+                //   │ ┃│┌─────────┘     < their multiline spans.
+                // 4 │ ┃││   X1 Y1 Z1
+                // 5 │ ┃││   X2 Y2 Z2
+                //   │ ┃│└────╿──│──┘ `Z` label
+                //   │ ┃└─────│──┤
+                //   │ ┗━━━━━━┥  `Y` is a good letter too
+                //   ╰╴       `X` is a good letter
+                for (pos, _) in &mut annotations_position {
+                    *pos = max_pos - *pos;
+                }
+                // We know then that we don't need an additional line for the span label, saving us
+                // one line of vertical space.
+                line_len = line_len.saturating_sub(1);
+            }
+        }
+
+        // Write the column separator.
+        //
+        // After this we will have:
+        //
+        // 2 |   fn foo() {
+        //   |
+        //   |
+        //   |
+        // 3 |
+        // 4 |   }
+        //   |
+        for pos in 0..=line_len {
+            self.draw_col_separator_no_space(buffer, line_offset + pos + 1, width_offset - 2);
+        }
+        if close_window {
+            self.draw_col_separator_end(buffer, line_offset + line_len + 1, width_offset - 2);
+        }
+        // Write the horizontal lines for multiline annotations
+        // (only the first and last lines need this).
+        //
+        // After this we will have:
+        //
+        // 2 |   fn foo() {
+        //   |  __________
+        //   |
+        //   |
+        // 3 |
+        // 4 |   }
+        //   |  _
+        for &(pos, annotation) in &annotations_position {
+            let underline = self.underline(annotation.is_primary());
+            let pos = pos + 1;
+            match annotation.annotation_type {
+                LineAnnotationType::MultilineStart(depth)
+                | LineAnnotationType::MultilineEnd(depth) => {
+                    self.draw_range(
+                        buffer,
+                        underline.multiline_horizontal,
+                        line_offset + pos,
+                        width_offset + depth,
+                        (code_offset + annotation.start.display).saturating_sub(left),
+                        underline.style,
+                    );
+                }
+                _ if annotation.highlight_source => {
+                    buffer.set_style_range(
+                        line_offset,
+                        (code_offset + annotation.start.display).saturating_sub(left),
+                        (code_offset + annotation.end.display).saturating_sub(left),
+                        underline.style,
+                        annotation.is_primary(),
+                    );
+                }
+                _ => {}
+            }
+        }
+
+        // Write the vertical lines for labels that are on a different line as the underline.
+        //
+        // After this we will have:
+        //
+        // 2 |   fn foo() {
+        //   |  __________
+        //   | |    |
+        //   | |
+        // 3 | |
+        // 4 | | }
+        //   | |_
+        for &(pos, annotation) in &annotations_position {
+            let underline = self.underline(annotation.is_primary());
+            let pos = pos + 1;
+
+            if pos > 1 && (annotation.has_label() || annotation.takes_space()) {
+                for p in line_offset + 1..=line_offset + pos {
+                    buffer.putc(
+                        p,
+                        (code_offset + annotation.start.display).saturating_sub(left),
+                        match annotation.annotation_type {
+                            LineAnnotationType::MultilineLine(_) => underline.multiline_vertical,
+                            _ => underline.vertical_text_line,
+                        },
+                        underline.style,
+                    );
+                }
+                if let LineAnnotationType::MultilineStart(_) = annotation.annotation_type {
+                    buffer.putc(
+                        line_offset + pos,
+                        (code_offset + annotation.start.display).saturating_sub(left),
+                        underline.bottom_right,
+                        underline.style,
+                    );
+                }
+                if matches!(
+                    annotation.annotation_type,
+                    LineAnnotationType::MultilineEnd(_)
+                ) && annotation.has_label()
+                {
+                    buffer.putc(
+                        line_offset + pos,
+                        (code_offset + annotation.start.display).saturating_sub(left),
+                        underline.multiline_bottom_right_with_text,
+                        underline.style,
+                    );
+                }
+            }
+            match annotation.annotation_type {
+                LineAnnotationType::MultilineStart(depth) => {
+                    buffer.putc(
+                        line_offset + pos,
+                        width_offset + depth - 1,
+                        underline.top_left,
+                        underline.style,
+                    );
+                    for p in line_offset + pos + 1..line_offset + line_len + 2 {
+                        buffer.putc(
+                            p,
+                            width_offset + depth - 1,
+                            underline.multiline_vertical,
+                            underline.style,
+                        );
+                    }
+                }
+                LineAnnotationType::MultilineEnd(depth) => {
+                    for p in line_offset..line_offset + pos {
+                        buffer.putc(
+                            p,
+                            width_offset + depth - 1,
+                            underline.multiline_vertical,
+                            underline.style,
+                        );
+                    }
+                    buffer.putc(
+                        line_offset + pos,
+                        width_offset + depth - 1,
+                        underline.bottom_left,
+                        underline.style,
+                    );
+                }
+                _ => (),
+            }
+        }
+
+        // Write the labels on the annotations that actually have a label.
+        //
+        // After this we will have:
+        //
+        // 2 |   fn foo() {
+        //   |  __________
+        //   |      |
+        //   |      something about `foo`
+        // 3 |
+        // 4 |   }
+        //   |  _  test
+        for &(pos, annotation) in &annotations_position {
+            let style = if annotation.is_primary() {
+                ElementStyle::LabelPrimary
+            } else {
+                ElementStyle::LabelSecondary
+            };
+            let (pos, col) = if pos == 0 {
+                if annotation.end.display == 0 {
+                    (pos + 1, (annotation.end.display + 2).saturating_sub(left))
+                } else {
+                    (pos + 1, (annotation.end.display + 1).saturating_sub(left))
+                }
+            } else {
+                (pos + 2, annotation.start.display.saturating_sub(left))
+            };
+            if let Some(label) = annotation.label {
+                buffer.puts(line_offset + pos, code_offset + col, label, style);
+            }
+        }
+
+        // Sort from biggest span to smallest span so that smaller spans are
+        // represented in the output:
+        //
+        // x | fn foo()
+        //   | ^^^---^^
+        //   | |  |
+        //   | |  something about `foo`
+        //   | something about `fn foo()`
+        annotations_position.sort_by_key(|(_, ann)| {
+            // Decreasing order. When annotations share the same length, prefer `Primary`.
+            (Reverse(ann.len()), ann.is_primary())
+        });
+
+        // Write the underlines.
+        //
+        // After this we will have:
+        //
+        // 2 |   fn foo() {
+        //   |  ____-_____^
+        //   |      |
+        //   |      something about `foo`
+        // 3 |
+        // 4 |   }
+        //   |  _^  test
+        for &(pos, annotation) in &annotations_position {
+            let uline = self.underline(annotation.is_primary());
+            for p in annotation.start.display..annotation.end.display {
+                // The default span label underline.
+                buffer.putc(
+                    line_offset + 1,
+                    (code_offset + p).saturating_sub(left),
+                    uline.underline,
+                    uline.style,
+                );
+            }
+
+            if pos == 0
+                && matches!(
+                    annotation.annotation_type,
+                    LineAnnotationType::MultilineStart(_) | LineAnnotationType::MultilineEnd(_)
+                )
+            {
+                // The beginning of a multiline span with its leftward moving line on the same line.
+                buffer.putc(
+                    line_offset + 1,
+                    (code_offset + annotation.start.display).saturating_sub(left),
+                    match annotation.annotation_type {
+                        LineAnnotationType::MultilineStart(_) => uline.top_right_flat,
+                        LineAnnotationType::MultilineEnd(_) => uline.multiline_end_same_line,
+                        _ => panic!("unexpected annotation type: {annotation:?}"),
+                    },
+                    uline.style,
+                );
+            } else if pos != 0
+                && matches!(
+                    annotation.annotation_type,
+                    LineAnnotationType::MultilineStart(_) | LineAnnotationType::MultilineEnd(_)
+                )
+            {
+                // The beginning of a multiline span with its leftward moving line on another line,
+                // so we start going down first.
+                buffer.putc(
+                    line_offset + 1,
+                    (code_offset + annotation.start.display).saturating_sub(left),
+                    match annotation.annotation_type {
+                        LineAnnotationType::MultilineStart(_) => uline.multiline_start_down,
+                        LineAnnotationType::MultilineEnd(_) => uline.multiline_end_up,
+                        _ => panic!("unexpected annotation type: {annotation:?}"),
+                    },
+                    uline.style,
+                );
+            } else if pos != 0 && annotation.has_label() {
+                // The beginning of a span label with an actual label, we'll point down.
+                buffer.putc(
+                    line_offset + 1,
+                    (code_offset + annotation.start.display).saturating_sub(left),
+                    uline.label_start,
+                    uline.style,
+                );
+            }
+        }
+        annotations_position
+            .iter()
+            .filter_map(|&(_, annotation)| match annotation.annotation_type {
+                LineAnnotationType::MultilineStart(p) | LineAnnotationType::MultilineEnd(p) => {
+                    let style = if annotation.is_primary() {
+                        ElementStyle::LabelPrimary
+                    } else {
+                        ElementStyle::LabelSecondary
+                    };
+                    Some((p, style))
+                }
+                _ => None,
+            })
+            .collect::<Vec<_>>()
+    }
+
+    fn emit_suggestion_default(
+        &self,
+        buffer: &mut StyledBuffer,
+        suggestion: &Snippet<'_, Patch<'_>>,
+        max_line_num_len: usize,
+        sm: &SourceMap<'_>,
+        primary_origin: Option<&str>,
+        is_cont: bool,
+    ) {
+        let suggestions = sm.splice_lines(suggestion.markers.clone());
+
+        let buffer_offset = buffer.num_lines();
+        let mut row_num = buffer_offset + usize::from(!is_cont);
+        for (i, (complete, parts, highlights)) in suggestions.iter().enumerate() {
+            let has_deletion = parts
+                .iter()
+                .any(|p| p.is_deletion(sm) || p.is_destructive_replacement(sm));
+            let is_multiline = complete.lines().count() > 1;
+
+            if i == 0 {
+                self.draw_col_separator_start(buffer, row_num - 1, max_line_num_len + 1);
+            } else {
+                buffer.puts(
+                    row_num - 1,
+                    max_line_num_len + 1,
+                    self.multi_suggestion_separator(),
+                    ElementStyle::LineNumber,
+                );
+            }
+            if suggestion.origin != primary_origin {
+                if let Some(origin) = suggestion.origin {
+                    let (loc, _) = sm.span_to_locations(parts[0].span.clone());
+                    // --> file.rs:line:col
+                    //  |
+                    let arrow = self.file_start();
+                    buffer.puts(row_num - 1, 0, arrow, ElementStyle::LineNumber);
+                    let message = format!("{}:{}:{}", origin, loc.line, loc.char + 1);
+                    if is_cont {
+                        buffer.append(row_num - 1, &message, ElementStyle::LineAndColumn);
+                    } else {
+                        let col = usize::max(max_line_num_len + 1, arrow.len());
+                        buffer.puts(row_num - 1, col, &message, ElementStyle::LineAndColumn);
+                    }
+                    for _ in 0..max_line_num_len {
+                        buffer.prepend(row_num - 1, " ", ElementStyle::NoStyle);
+                    }
+                    self.draw_col_separator_no_space(buffer, row_num, max_line_num_len + 1);
+                    row_num += 1;
+                }
+            }
+            let show_code_change = if has_deletion && !is_multiline {
+                DisplaySuggestion::Diff
+            } else if parts.len() == 1
+                && parts.first().map_or(false, |p| {
+                    p.replacement.ends_with('\n') && p.replacement.trim() == complete.trim()
+                })
+            {
+                // We are adding a line(s) of code before code that was already there.
+                DisplaySuggestion::Add
+            } else if (parts.len() != 1 || parts[0].replacement.trim() != complete.trim())
+                && !is_multiline
+            {
+                DisplaySuggestion::Underline
+            } else {
+                DisplaySuggestion::None
+            };
+
+            if let DisplaySuggestion::Diff = show_code_change {
+                row_num += 1;
+            }
+
+            let file_lines = sm.span_to_lines(parts[0].span.clone());
+            let (line_start, line_end) = sm.span_to_locations(parts[0].span.clone());
+            let mut lines = complete.lines();
+            if lines.clone().next().is_none() {
+                // Account for a suggestion to completely remove a line(s) with whitespace (#94192).
+                for line in line_start.line..=line_end.line {
+                    buffer.puts(
+                        row_num - 1 + line - line_start.line,
+                        0,
+                        &self.maybe_anonymized(line),
+                        ElementStyle::LineNumber,
+                    );
+                    buffer.puts(
+                        row_num - 1 + line - line_start.line,
+                        max_line_num_len + 1,
+                        "- ",
+                        ElementStyle::Removal,
+                    );
+                    buffer.puts(
+                        row_num - 1 + line - line_start.line,
+                        max_line_num_len + 3,
+                        &normalize_whitespace(sm.get_line(line).unwrap()),
+                        ElementStyle::Removal,
+                    );
+                }
+                row_num += line_end.line - line_start.line;
+            }
+            let mut last_pos = 0;
+            let mut is_item_attribute = false;
+            let mut unhighlighted_lines = Vec::new();
+            for (line_pos, (line, highlight_parts)) in lines.by_ref().zip(highlights).enumerate() {
+                last_pos = line_pos;
+
+                // Remember lines that are not highlighted to hide them if needed
+                if highlight_parts.is_empty() {
+                    unhighlighted_lines.push((line_pos, line));
+                    continue;
+                }
+                if highlight_parts.len() == 1
+                    && line.trim().starts_with("#[")
+                    && line.trim().ends_with(']')
+                {
+                    is_item_attribute = true;
+                }
+
+                match unhighlighted_lines.len() {
+                    0 => (),
+                    // Since we show first line, "..." line and last line,
+                    // There is no reason to hide if there are 3 or less lines
+                    // (because then we just replace a line with ... which is
+                    // not helpful)
+                    n if n <= 3 => unhighlighted_lines.drain(..).for_each(|(p, l)| {
+                        self.draw_code_line(
+                            buffer,
+                            &mut row_num,
+                            &[],
+                            p + line_start.line,
+                            l,
+                            show_code_change,
+                            max_line_num_len,
+                            &file_lines,
+                            is_multiline,
+                        );
+                    }),
+                    // Print first unhighlighted line, "..." and last unhighlighted line, like so:
+                    //
+                    // LL | this line was highlighted
+                    // LL | this line is just for context
+                    // ...
+                    // LL | this line is just for context
+                    // LL | this line was highlighted
+                    _ => {
+                        let last_line = unhighlighted_lines.pop();
+                        let first_line = unhighlighted_lines.drain(..).next();
+
+                        if let Some((p, l)) = first_line {
+                            self.draw_code_line(
+                                buffer,
+                                &mut row_num,
+                                &[],
+                                p + line_start.line,
+                                l,
+                                show_code_change,
+                                max_line_num_len,
+                                &file_lines,
+                                is_multiline,
+                            );
+                        }
+
+                        let placeholder = self.margin();
+                        let padding = str_width(placeholder);
+                        buffer.puts(
+                            row_num,
+                            max_line_num_len.saturating_sub(padding),
+                            placeholder,
+                            ElementStyle::LineNumber,
+                        );
+                        row_num += 1;
+
+                        if let Some((p, l)) = last_line {
+                            self.draw_code_line(
+                                buffer,
+                                &mut row_num,
+                                &[],
+                                p + line_start.line,
+                                l,
+                                show_code_change,
+                                max_line_num_len,
+                                &file_lines,
+                                is_multiline,
+                            );
+                        }
+                    }
+                }
+                self.draw_code_line(
+                    buffer,
+                    &mut row_num,
+                    highlight_parts,
+                    line_pos + line_start.line,
+                    line,
+                    show_code_change,
+                    max_line_num_len,
+                    &file_lines,
+                    is_multiline,
+                );
+            }
+
+            if matches!(show_code_change, DisplaySuggestion::Add) && is_item_attribute {
+                // The suggestion adds an entire line of code, ending on a newline, so we'll also
+                // print the *following* line, to provide context of what we're advising people to
+                // do. Otherwise you would only see contextless code that can be confused for
+                // already existing code, despite the colors and UI elements.
+                // We special case `#[derive(_)]\n` and other attribute suggestions, because those
+                // are the ones where context is most useful.
+                let file_lines = sm.span_to_lines(parts[0].span.end..parts[0].span.end);
+                let (lo, _) = sm.span_to_locations(parts[0].span.clone());
+                let line_num = lo.line;
+                if let Some(line) = sm.get_line(line_num) {
+                    let line = normalize_whitespace(line);
+                    self.draw_code_line(
+                        buffer,
+                        &mut row_num,
+                        &[],
+                        line_num + last_pos + 1,
+                        &line,
+                        DisplaySuggestion::None,
+                        max_line_num_len,
+                        &file_lines,
+                        is_multiline,
+                    );
+                }
+            }
+            // This offset and the ones below need to be signed to account for replacement code
+            // that is shorter than the original code.
+            let mut offsets: Vec<(usize, isize)> = Vec::new();
+            // Only show an underline in the suggestions if the suggestion is not the
+            // entirety of the code being shown and the displayed code is not multiline.
+            if let DisplaySuggestion::Diff | DisplaySuggestion::Underline | DisplaySuggestion::Add =
+                show_code_change
+            {
+                for part in parts {
+                    let (span_start, span_end) = sm.span_to_locations(part.span.clone());
+                    let span_start_pos = span_start.display;
+                    let span_end_pos = span_end.display;
+
+                    // If this addition is _only_ whitespace, then don't trim it,
+                    // or else we're just not rendering anything.
+                    let is_whitespace_addition = part.replacement.trim().is_empty();
+
+                    // Do not underline the leading...
+                    let start = if is_whitespace_addition {
+                        0
+                    } else {
+                        part.replacement
+                            .len()
+                            .saturating_sub(part.replacement.trim_start().len())
+                    };
+                    // ...or trailing spaces. Account for substitutions containing unicode
+                    // characters.
+                    let sub_len: usize = str_width(if is_whitespace_addition {
+                        part.replacement
+                    } else {
+                        part.replacement.trim()
+                    });
+
+                    let offset: isize = offsets
+                        .iter()
+                        .filter_map(|(start, v)| {
+                            if span_start_pos < *start {
+                                None
+                            } else {
+                                Some(v)
+                            }
+                        })
+                        .sum();
+                    let underline_start = (span_start_pos + start) as isize + offset;
+                    let underline_end = (span_start_pos + start + sub_len) as isize + offset;
+                    assert!(underline_start >= 0 && underline_end >= 0);
+                    let padding: usize = max_line_num_len + 3;
+                    for p in underline_start..underline_end {
+                        if matches!(show_code_change, DisplaySuggestion::Underline)
+                            && is_different(sm, part.replacement, part.span.clone())
+                        {
+                            // If this is a replacement, underline with `~`, if this is an addition
+                            // underline with `+`.
+                            buffer.putc(
+                                row_num,
+                                (padding as isize + p) as usize,
+                                if part.is_addition(sm) {
+                                    '+'
+                                } else {
+                                    self.diff()
+                                },
+                                ElementStyle::Addition,
+                            );
+                        }
+                    }
+                    if let DisplaySuggestion::Diff = show_code_change {
+                        // Colorize removal with red in diff format.
+                        buffer.set_style_range(
+                            row_num - 2,
+                            (padding as isize + span_start_pos as isize) as usize,
+                            (padding as isize + span_end_pos as isize) as usize,
+                            ElementStyle::Removal,
+                            true,
+                        );
+                    }
+
+                    // length of the code after substitution
+                    let full_sub_len = str_width(part.replacement) as isize;
+
+                    // length of the code to be substituted
+                    let snippet_len = span_end_pos as isize - span_start_pos as isize;
+                    // For multiple substitutions, use the position *after* the previous
+                    // substitutions have happened, only when further substitutions are
+                    // located strictly after.
+                    offsets.push((span_end_pos, full_sub_len - snippet_len));
+                }
+                row_num += 1;
+            }
+
+            // if we elided some lines, add an ellipsis
+            if lines.next().is_some() {
+                let placeholder = self.margin();
+                let padding = str_width(placeholder);
+                buffer.puts(
+                    row_num,
+                    max_line_num_len.saturating_sub(padding),
+                    placeholder,
+                    ElementStyle::LineNumber,
+                );
+            } else {
+                let row = match show_code_change {
+                    DisplaySuggestion::Diff
+                    | DisplaySuggestion::Add
+                    | DisplaySuggestion::Underline => row_num - 1,
+                    DisplaySuggestion::None => row_num,
+                };
+                self.draw_col_separator_end(buffer, row, max_line_num_len + 1);
+                row_num = row + 1;
+            }
+        }
+    }
+
+    #[allow(clippy::too_many_arguments)]
+    fn draw_code_line(
+        &self,
+        buffer: &mut StyledBuffer,
+        row_num: &mut usize,
+        highlight_parts: &[SubstitutionHighlight],
+        line_num: usize,
+        line_to_add: &str,
+        show_code_change: DisplaySuggestion,
+        max_line_num_len: usize,
+        file_lines: &[&LineInfo<'_>],
+        is_multiline: bool,
+    ) {
+        if let DisplaySuggestion::Diff = show_code_change {
+            // We need to print more than one line if the span we need to remove is multiline.
+            // For more info: https://github.com/rust-lang/rust/issues/92741
+            let lines_to_remove = file_lines.iter().take(file_lines.len() - 1);
+            for (index, line_to_remove) in lines_to_remove.enumerate() {
+                buffer.puts(
+                    *row_num - 1,
+                    0,
+                    &self.maybe_anonymized(line_num + index),
+                    ElementStyle::LineNumber,
+                );
+                buffer.puts(
+                    *row_num - 1,
+                    max_line_num_len + 1,
+                    "- ",
+                    ElementStyle::Removal,
+                );
+                let line = normalize_whitespace(line_to_remove.line);
+                buffer.puts(
+                    *row_num - 1,
+                    max_line_num_len + 3,
+                    &line,
+                    ElementStyle::NoStyle,
+                );
+                *row_num += 1;
+            }
+            // If the last line is exactly equal to the line we need to add, we can skip both of
+            // them. This allows us to avoid output like the following:
+            // 2 - &
+            // 2 + if true { true } else { false }
+            // 3 - if true { true } else { false }
+            // If those lines aren't equal, we print their diff
+            let last_line = &file_lines.last().unwrap();
+            if last_line.line == line_to_add {
+                *row_num -= 2;
+            } else {
+                buffer.puts(
+                    *row_num - 1,
+                    0,
+                    &self.maybe_anonymized(line_num + file_lines.len() - 1),
+                    ElementStyle::LineNumber,
+                );
+                buffer.puts(
+                    *row_num - 1,
+                    max_line_num_len + 1,
+                    "- ",
+                    ElementStyle::Removal,
+                );
+                buffer.puts(
+                    *row_num - 1,
+                    max_line_num_len + 3,
+                    &normalize_whitespace(last_line.line),
+                    ElementStyle::NoStyle,
+                );
+                if line_to_add.trim().is_empty() {
+                    *row_num -= 1;
+                } else {
+                    // Check if after the removal, the line is left with only whitespace. If so, we
+                    // will not show an "addition" line, as removing the whole line is what the user
+                    // would really want.
+                    // For example, for the following:
+                    //   |
+                    // 2 -     .await
+                    // 2 +     (note the left over whitespace)
+                    //   |
+                    // We really want
+                    //   |
+                    // 2 -     .await
+                    //   |
+                    // *row_num -= 1;
+                    buffer.puts(
+                        *row_num,
+                        0,
+                        &self.maybe_anonymized(line_num),
+                        ElementStyle::LineNumber,
+                    );
+                    buffer.puts(*row_num, max_line_num_len + 1, "+ ", ElementStyle::Addition);
+                    buffer.append(
+                        *row_num,
+                        &normalize_whitespace(line_to_add),
+                        ElementStyle::NoStyle,
+                    );
+                }
+            }
+        } else if is_multiline {
+            buffer.puts(
+                *row_num,
+                0,
+                &self.maybe_anonymized(line_num),
+                ElementStyle::LineNumber,
+            );
+            match &highlight_parts {
+                [SubstitutionHighlight { start: 0, end }] if *end == line_to_add.len() => {
+                    buffer.puts(*row_num, max_line_num_len + 1, "+ ", ElementStyle::Addition);
+                }
+                [] => {
+                    // FIXME: needed? Doesn't get exercised in any test.
+                    self.draw_col_separator_no_space(buffer, *row_num, max_line_num_len + 1);
+                }
+                _ => {
+                    let diff = self.diff();
+                    buffer.puts(
+                        *row_num,
+                        max_line_num_len + 1,
+                        &format!("{diff} "),
+                        ElementStyle::Addition,
+                    );
+                }
+            }
+            //   LL | line_to_add
+            //   ++^^^
+            //    |  |
+            //    |  magic `3`
+            //    `max_line_num_len`
+            buffer.puts(
+                *row_num,
+                max_line_num_len + 3,
+                &normalize_whitespace(line_to_add),
+                ElementStyle::NoStyle,
+            );
+        } else if let DisplaySuggestion::Add = show_code_change {
+            buffer.puts(
+                *row_num,
+                0,
+                &self.maybe_anonymized(line_num),
+                ElementStyle::LineNumber,
+            );
+            buffer.puts(*row_num, max_line_num_len + 1, "+ ", ElementStyle::Addition);
+            buffer.append(
+                *row_num,
+                &normalize_whitespace(line_to_add),
+                ElementStyle::NoStyle,
+            );
+        } else {
+            buffer.puts(
+                *row_num,
+                0,
+                &self.maybe_anonymized(line_num),
+                ElementStyle::LineNumber,
+            );
+            self.draw_col_separator(buffer, *row_num, max_line_num_len + 1);
+            buffer.append(
+                *row_num,
+                &normalize_whitespace(line_to_add),
+                ElementStyle::NoStyle,
+            );
+        }
+
+        // Colorize addition/replacements with green.
+        for &SubstitutionHighlight { start, end } in highlight_parts {
+            // This is a no-op for empty ranges
+            if start != end {
+                // Account for tabs when highlighting (#87972).
+                let tabs: usize = line_to_add
+                    .chars()
+                    .take(start)
+                    .map(|ch| match ch {
+                        '\t' => 3,
+                        _ => 0,
+                    })
+                    .sum();
+                buffer.set_style_range(
+                    *row_num,
+                    max_line_num_len + 3 + start + tabs,
+                    max_line_num_len + 3 + end + tabs,
+                    ElementStyle::Addition,
+                    true,
+                );
+            }
+        }
+        *row_num += 1;
+    }
+
+    #[allow(clippy::too_many_arguments)]
+    fn draw_line(
+        &self,
+        buffer: &mut StyledBuffer,
+        source_string: &str,
+        line_index: usize,
+        line_offset: usize,
+        width_offset: usize,
+        code_offset: usize,
+        max_line_num_len: usize,
+        margin: Margin,
+    ) {
+        // Tabs are assumed to have been replaced by spaces in calling code.
+        debug_assert!(!source_string.contains('\t'));
+        let line_len = str_width(source_string);
+        // Create the source line we will highlight.
+        let left = margin.left(line_len);
+        let right = margin.right(line_len);
+        // FIXME: The following code looks fishy. See #132860.
+        // On long lines, we strip the source line, accounting for unicode.
+        let mut taken = 0;
+        let mut skipped = 0;
+        let code: String = source_string
+            .chars()
+            .skip_while(|ch| {
+                skipped += char_width(*ch);
+                skipped <= left
+            })
+            .take_while(|ch| {
+                // Make sure that the trimming on the right will fall within the terminal width.
+                taken += char_width(*ch);
+                taken <= (right - left)
+            })
+            .collect();
+
+        buffer.puts(line_offset, code_offset, &code, ElementStyle::Quotation);
+        let placeholder = self.margin();
+        let padding = str_width(placeholder);
+        let (width_taken, bytes_taken) = if margin.was_cut_left() {
+            // We have stripped some code/whitespace from the beginning, make it clear.
+            let mut bytes_taken = 0;
+            let mut width_taken = 0;
+            for ch in code.chars() {
+                width_taken += char_width(ch);
+                bytes_taken += ch.len_utf8();
+
+                if width_taken >= padding {
+                    break;
+                }
+            }
+            buffer.puts(
+                line_offset,
+                code_offset,
+                &format!("{placeholder:>width_taken$}"),
+                ElementStyle::LineNumber,
+            );
+            (width_taken, bytes_taken)
+        } else {
+            (0, 0)
+        };
+
+        buffer.puts(
+            line_offset,
+            code_offset + width_taken,
+            &code[bytes_taken..],
+            ElementStyle::Quotation,
+        );
+
+        if margin.was_cut_right(line_len) {
+            // We have stripped some code/whitespace from the beginning, make it clear.
+            let mut char_taken = 0;
+            let mut width_taken_inner = 0;
+            for ch in code.chars().rev() {
+                width_taken_inner += char_width(ch);
+                char_taken += 1;
+
+                if width_taken_inner >= padding {
+                    break;
+                }
+            }
+
+            buffer.puts(
+                line_offset,
+                code_offset + width_taken + code[bytes_taken..].chars().count() - char_taken,
+                placeholder,
+                ElementStyle::LineNumber,
+            );
+        }
+
+        buffer.puts(
+            line_offset,
+            0,
+            &format!("{:>max_line_num_len$}", self.maybe_anonymized(line_index)),
+            ElementStyle::LineNumber,
+        );
+
+        self.draw_col_separator_no_space(buffer, line_offset, width_offset - 2);
+    }
+
+    fn draw_range(
+        &self,
+        buffer: &mut StyledBuffer,
+        symbol: char,
+        line: usize,
+        col_from: usize,
+        col_to: usize,
+        style: ElementStyle,
+    ) {
+        for col in col_from..col_to {
+            buffer.putc(line, col, symbol, style);
+        }
+    }
+
+    fn draw_multiline_line(
+        &self,
+        buffer: &mut StyledBuffer,
+        line: usize,
+        offset: usize,
+        depth: usize,
+        style: ElementStyle,
+    ) {
+        let chr = match (style, self.theme) {
+            (ElementStyle::UnderlinePrimary | ElementStyle::LabelPrimary, OutputTheme::Ascii) => {
+                '|'
+            }
+            (_, OutputTheme::Ascii) => '|',
+            (ElementStyle::UnderlinePrimary | ElementStyle::LabelPrimary, OutputTheme::Unicode) => {
+                '┃'
+            }
+            (_, OutputTheme::Unicode) => '│',
+        };
+        buffer.putc(line, offset + depth - 1, chr, style);
+    }
+
+    fn col_separator(&self) -> char {
+        match self.theme {
+            OutputTheme::Ascii => '|',
+            OutputTheme::Unicode => '│',
+        }
+    }
+
+    fn multi_suggestion_separator(&self) -> &'static str {
+        match self.theme {
+            OutputTheme::Ascii => "|",
+            OutputTheme::Unicode => "├╴",
+        }
+    }
+
+    fn draw_col_separator(&self, buffer: &mut StyledBuffer, line: usize, col: usize) {
+        let chr = self.col_separator();
+        buffer.puts(line, col, &format!("{chr} "), ElementStyle::LineNumber);
+    }
+
+    fn draw_col_separator_no_space(&self, buffer: &mut StyledBuffer, line: usize, col: usize) {
+        let chr = self.col_separator();
+        self.draw_col_separator_no_space_with_style(
+            buffer,
+            chr,
+            line,
+            col,
+            ElementStyle::LineNumber,
+        );
+    }
+
+    fn draw_col_separator_start(&self, buffer: &mut StyledBuffer, line: usize, col: usize) {
+        match self.theme {
+            OutputTheme::Ascii => {
+                self.draw_col_separator_no_space_with_style(
+                    buffer,
+                    '|',
+                    line,
+                    col,
+                    ElementStyle::LineNumber,
+                );
+            }
+            OutputTheme::Unicode => {
+                self.draw_col_separator_no_space_with_style(
+                    buffer,
+                    '╭',
+                    line,
+                    col,
+                    ElementStyle::LineNumber,
+                );
+                self.draw_col_separator_no_space_with_style(
+                    buffer,
+                    '╴',
+                    line,
+                    col + 1,
+                    ElementStyle::LineNumber,
+                );
+            }
+        }
+    }
+
+    fn draw_col_separator_end(&self, buffer: &mut StyledBuffer, line: usize, col: usize) {
+        match self.theme {
+            OutputTheme::Ascii => {
+                self.draw_col_separator_no_space_with_style(
+                    buffer,
+                    '|',
+                    line,
+                    col,
+                    ElementStyle::LineNumber,
+                );
+            }
+            OutputTheme::Unicode => {
+                self.draw_col_separator_no_space_with_style(
+                    buffer,
+                    '╰',
+                    line,
+                    col,
+                    ElementStyle::LineNumber,
+                );
+                self.draw_col_separator_no_space_with_style(
+                    buffer,
+                    '╴',
+                    line,
+                    col + 1,
+                    ElementStyle::LineNumber,
+                );
+            }
+        }
+    }
+
+    fn draw_col_separator_no_space_with_style(
+        &self,
+        buffer: &mut StyledBuffer,
+        chr: char,
+        line: usize,
+        col: usize,
+        style: ElementStyle,
+    ) {
+        buffer.putc(line, col, chr, style);
+    }
+
+    fn maybe_anonymized(&self, line_num: usize) -> Cow<'static, str> {
+        if self.anonymized_line_numbers {
+            Cow::Borrowed(ANONYMIZED_LINE_NUM)
+        } else {
+            Cow::Owned(line_num.to_string())
+        }
+    }
+
+    fn file_start(&self) -> &'static str {
+        match self.theme {
+            OutputTheme::Ascii => "--> ",
+            OutputTheme::Unicode => " ╭▸ ",
+        }
+    }
+
+    fn secondary_file_start(&self) -> &'static str {
+        match self.theme {
+            OutputTheme::Ascii => "::: ",
+            OutputTheme::Unicode => " ⸬  ",
+        }
+    }
+
+    fn draw_note_separator(
+        &self,
+        buffer: &mut StyledBuffer,
+        line: usize,
+        col: usize,
+        is_cont: bool,
+    ) {
+        let chr = match self.theme {
+            OutputTheme::Ascii => "= ",
+            OutputTheme::Unicode if is_cont => "├ ",
+            OutputTheme::Unicode => "╰ ",
+        };
+        buffer.puts(line, col, chr, ElementStyle::LineNumber);
+    }
+
+    fn diff(&self) -> char {
+        match self.theme {
+            OutputTheme::Ascii => '~',
+            OutputTheme::Unicode => '±',
+        }
+    }
+
+    fn draw_line_separator(&self, buffer: &mut StyledBuffer, line: usize, col: usize) {
+        let (column, dots) = match self.theme {
+            OutputTheme::Ascii => (0, "..."),
+            OutputTheme::Unicode => (col - 2, "‡"),
+        };
+        buffer.puts(line, column, dots, ElementStyle::LineNumber);
+    }
+
+    fn margin(&self) -> &'static str {
+        match self.theme {
+            OutputTheme::Ascii => "...",
+            OutputTheme::Unicode => "…",
+        }
+    }
+
+    fn underline(&self, is_primary: bool) -> UnderlineParts {
+        //               X0 Y0
+        // label_start > ┯━━━━ < underline
+        //               │ < vertical_text_line
+        //               text
+
+        //    multiline_start_down ⤷ X0 Y0
+        //            top_left > ┌───╿──┘ < top_right_flat
+        //           top_left > ┏│━━━┙ < top_right
+        // multiline_vertical > ┃│
+        //                      ┃│   X1 Y1
+        //                      ┃│   X2 Y2
+        //                      ┃└────╿──┘ < multiline_end_same_line
+        //        bottom_left > ┗━━━━━┥ < bottom_right_with_text
+        //   multiline_horizontal ^   `X` is a good letter
+
+        // multiline_whole_line > ┏ X0 Y0
+        //                        ┃   X1 Y1
+        //                        ┗━━━━┛ < multiline_end_same_line
+
+        // multiline_whole_line > ┏ X0 Y0
+        //                        ┃ X1 Y1
+        //                        ┃  ╿ < multiline_end_up
+        //                        ┗━━┛ < bottom_right
+
+        match (self.theme, is_primary) {
+            (OutputTheme::Ascii, true) => UnderlineParts {
+                style: ElementStyle::UnderlinePrimary,
+                underline: '^',
+                label_start: '^',
+                vertical_text_line: '|',
+                multiline_vertical: '|',
+                multiline_horizontal: '_',
+                multiline_whole_line: '/',
+                multiline_start_down: '^',
+                bottom_right: '|',
+                top_left: ' ',
+                top_right_flat: '^',
+                bottom_left: '|',
+                multiline_end_up: '^',
+                multiline_end_same_line: '^',
+                multiline_bottom_right_with_text: '|',
+            },
+            (OutputTheme::Ascii, false) => UnderlineParts {
+                style: ElementStyle::UnderlineSecondary,
+                underline: '-',
+                label_start: '-',
+                vertical_text_line: '|',
+                multiline_vertical: '|',
+                multiline_horizontal: '_',
+                multiline_whole_line: '/',
+                multiline_start_down: '-',
+                bottom_right: '|',
+                top_left: ' ',
+                top_right_flat: '-',
+                bottom_left: '|',
+                multiline_end_up: '-',
+                multiline_end_same_line: '-',
+                multiline_bottom_right_with_text: '|',
+            },
+            (OutputTheme::Unicode, true) => UnderlineParts {
+                style: ElementStyle::UnderlinePrimary,
+                underline: '━',
+                label_start: '┯',
+                vertical_text_line: '│',
+                multiline_vertical: '┃',
+                multiline_horizontal: '━',
+                multiline_whole_line: '┏',
+                multiline_start_down: '╿',
+                bottom_right: '┙',
+                top_left: '┏',
+                top_right_flat: '┛',
+                bottom_left: '┗',
+                multiline_end_up: '╿',
+                multiline_end_same_line: '┛',
+                multiline_bottom_right_with_text: '┥',
+            },
+            (OutputTheme::Unicode, false) => UnderlineParts {
+                style: ElementStyle::UnderlineSecondary,
+                underline: '─',
+                label_start: '┬',
+                vertical_text_line: '│',
+                multiline_vertical: '│',
+                multiline_horizontal: '─',
+                multiline_whole_line: '┌',
+                multiline_start_down: '│',
+                bottom_right: '┘',
+                top_left: '┌',
+                top_right_flat: '┘',
+                bottom_left: '└',
+                multiline_end_up: '│',
+                multiline_end_same_line: '┘',
+                multiline_bottom_right_with_text: '┤',
+            },
+        }
+    }
+}
+
+// instead of taking the String length or dividing by 10 while > 0, we multiply a limit by 10 until
+// we're higher. If the loop isn't exited by the `return`, the last multiplication will wrap, which
+// is OK, because while we cannot fit a higher power of 10 in a usize, the loop will end anyway.
+// This is also why we need the max number of decimal digits within a `usize`.
+fn num_decimal_digits(num: usize) -> usize {
+    #[cfg(target_pointer_width = "64")]
+    const MAX_DIGITS: usize = 20;
+
+    #[cfg(target_pointer_width = "32")]
+    const MAX_DIGITS: usize = 10;
+
+    #[cfg(target_pointer_width = "16")]
+    const MAX_DIGITS: usize = 5;
+
+    let mut lim = 10;
+    for num_digits in 1..MAX_DIGITS {
+        if num < lim {
+            return num_digits;
+        }
+        lim = lim.wrapping_mul(10);
+    }
+    MAX_DIGITS
+}
+
+pub fn str_width(s: &str) -> usize {
+    s.chars().map(char_width).sum()
+}
+
+pub fn char_width(ch: char) -> usize {
+    // 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.
+    match ch {
+        '\t' => 4,
+        // Keep the following list in sync with `rustc_errors::emitter::OUTPUT_REPLACEMENTS`. These
+        // are control points that we replace before printing with a visible codepoint for the sake
+        // of being able to point at them with underlines.
+        '\u{0000}' | '\u{0001}' | '\u{0002}' | '\u{0003}' | '\u{0004}' | '\u{0005}'
+        | '\u{0006}' | '\u{0007}' | '\u{0008}' | '\u{000B}' | '\u{000C}' | '\u{000D}'
+        | '\u{000E}' | '\u{000F}' | '\u{0010}' | '\u{0011}' | '\u{0012}' | '\u{0013}'
+        | '\u{0014}' | '\u{0015}' | '\u{0016}' | '\u{0017}' | '\u{0018}' | '\u{0019}'
+        | '\u{001A}' | '\u{001B}' | '\u{001C}' | '\u{001D}' | '\u{001E}' | '\u{001F}'
+        | '\u{007F}' | '\u{202A}' | '\u{202B}' | '\u{202D}' | '\u{202E}' | '\u{2066}'
+        | '\u{2067}' | '\u{2068}' | '\u{202C}' | '\u{2069}' => 1,
+        _ => unicode_width::UnicodeWidthChar::width(ch).unwrap_or(1),
+    }
+}
+
+fn num_overlap(
+    a_start: usize,
+    a_end: usize,
+    b_start: usize,
+    b_end: usize,
+    inclusive: bool,
+) -> bool {
+    let extra = usize::from(inclusive);
+    (b_start..b_end + extra).contains(&a_start) || (a_start..a_end + extra).contains(&b_start)
+}
+
+fn overlaps(a1: &LineAnnotation<'_>, a2: &LineAnnotation<'_>, padding: usize) -> bool {
+    num_overlap(
+        a1.start.display,
+        a1.end.display + padding,
+        a2.start.display,
+        a2.end.display,
+        false,
+    )
+}
+
+#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
+pub(crate) enum LineAnnotationType {
+    /// Annotation under a single line of code
+    Singleline,
+
+    // The Multiline type above is replaced with the following three in order
+    // to reuse the current label drawing code.
+    //
+    // Each of these corresponds to one part of the following diagram:
+    //
+    //     x |   foo(1 + bar(x,
+    //       |  _________^              < MultilineStart
+    //     x | |             y),        < MultilineLine
+    //       | |______________^ label   < MultilineEnd
+    //     x |       z);
+    /// Annotation marking the first character of a fully shown multiline span
+    MultilineStart(usize),
+    /// Annotation marking the last character of a fully shown multiline span
+    MultilineEnd(usize),
+    /// Line at the left enclosing the lines of a fully shown multiline span
+    // Just a placeholder for the drawing algorithm, to know that it shouldn't skip the first 4
+    // and last 2 lines of code. The actual line is drawn in `emit_message_default` and not in
+    // `draw_multiline_line`.
+    MultilineLine(usize),
+}
+
+#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
+pub(crate) struct LineAnnotation<'a> {
+    /// Start column.
+    /// Note that it is important that this field goes
+    /// first, so that when we sort, we sort orderings by start
+    /// column.
+    pub start: Loc,
+
+    /// End column within the line (exclusive)
+    pub end: Loc,
+
+    /// level
+    pub kind: AnnotationKind,
+
+    /// Optional label to display adjacent to the annotation.
+    pub label: Option<&'a str>,
+
+    /// Is this a single line, multiline or multiline span minimized down to a
+    /// smaller span.
+    pub annotation_type: LineAnnotationType,
+
+    /// Whether the source code should be highlighted
+    pub highlight_source: bool,
+}
+
+impl LineAnnotation<'_> {
+    pub(crate) fn is_primary(&self) -> bool {
+        self.kind == AnnotationKind::Primary
+    }
+
+    /// Whether this annotation is a vertical line placeholder.
+    pub(crate) fn is_line(&self) -> bool {
+        matches!(self.annotation_type, LineAnnotationType::MultilineLine(_))
+    }
+
+    /// Length of this annotation as displayed in the stderr output
+    pub(crate) fn len(&self) -> usize {
+        // Account for usize underflows
+        if self.end.display > self.start.display {
+            self.end.display - self.start.display
+        } else {
+            self.start.display - self.end.display
+        }
+    }
+
+    pub(crate) fn has_label(&self) -> bool {
+        if let Some(label) = self.label {
+            // Consider labels with no text as effectively not being there
+            // to avoid weird output with unnecessary vertical lines, like:
+            //
+            //     X | fn foo(x: u32) {
+            //       | -------^------
+            //       | |      |
+            //       | |
+            //       |
+            //
+            // Note that this would be the complete output users would see.
+            !label.is_empty()
+        } else {
+            false
+        }
+    }
+
+    pub(crate) fn takes_space(&self) -> bool {
+        // Multiline annotations always have to keep vertical space.
+        matches!(
+            self.annotation_type,
+            LineAnnotationType::MultilineStart(_) | LineAnnotationType::MultilineEnd(_)
+        )
+    }
+}
+
+#[derive(Clone, Copy, Debug)]
+pub(crate) enum DisplaySuggestion {
+    Underline,
+    Diff,
+    None,
+    Add,
+}
+
+// We replace some characters so the CLI output is always consistent and underlines aligned.
+// Keep the following list in sync with `rustc_span::char_width`.
+const OUTPUT_REPLACEMENTS: &[(char, &str)] = &[
+    // In terminals without Unicode support the following will be garbled, but in *all* terminals
+    // the underlying codepoint will be as well. We could gate this replacement behind a "unicode
+    // support" gate.
+    ('\0', "␀"),
+    ('\u{0001}', "␁"),
+    ('\u{0002}', "␂"),
+    ('\u{0003}', "␃"),
+    ('\u{0004}', "␄"),
+    ('\u{0005}', "␅"),
+    ('\u{0006}', "␆"),
+    ('\u{0007}', "␇"),
+    ('\u{0008}', "␈"),
+    ('\t', "    "), // We do our own tab replacement
+    ('\u{000b}', "␋"),
+    ('\u{000c}', "␌"),
+    ('\u{000d}', "␍"),
+    ('\u{000e}', "␎"),
+    ('\u{000f}', "␏"),
+    ('\u{0010}', "␐"),
+    ('\u{0011}', "␑"),
+    ('\u{0012}', "␒"),
+    ('\u{0013}', "␓"),
+    ('\u{0014}', "␔"),
+    ('\u{0015}', "␕"),
+    ('\u{0016}', "␖"),
+    ('\u{0017}', "␗"),
+    ('\u{0018}', "␘"),
+    ('\u{0019}', "␙"),
+    ('\u{001a}', "␚"),
+    ('\u{001b}', "␛"),
+    ('\u{001c}', "␜"),
+    ('\u{001d}', "␝"),
+    ('\u{001e}', "␞"),
+    ('\u{001f}', "␟"),
+    ('\u{007f}', "␡"),
+    ('\u{200d}', ""), // Replace ZWJ for consistent terminal output of grapheme clusters.
+    ('\u{202a}', "�"), // The following unicode text flow control characters are inconsistently
+    ('\u{202b}', "�"), // supported across CLIs and can cause confusion due to the bytes on disk
+    ('\u{202c}', "�"), // not corresponding to the visible source code, so we replace them always.
+    ('\u{202d}', "�"),
+    ('\u{202e}', "�"),
+    ('\u{2066}', "�"),
+    ('\u{2067}', "�"),
+    ('\u{2068}', "�"),
+    ('\u{2069}', "�"),
+];
+
+pub(crate) fn normalize_whitespace(s: &str) -> String {
+    // Scan the input string for a character in the ordered table above.
+    // If it's present, replace it with its alternative string (it can be more than 1 char!).
+    // Otherwise, retain the input char.
+    s.chars().fold(String::with_capacity(s.len()), |mut s, c| {
+        match OUTPUT_REPLACEMENTS.binary_search_by_key(&c, |(k, _)| *k) {
+            Ok(i) => s.push_str(OUTPUT_REPLACEMENTS[i].1),
+            _ => s.push(c),
+        }
+        s
+    })
+}
+
+#[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq)]
+pub(crate) enum ElementStyle {
+    MainHeaderMsg,
+    HeaderMsg,
+    LineAndColumn,
+    LineNumber,
+    Quotation,
+    UnderlinePrimary,
+    UnderlineSecondary,
+    LabelPrimary,
+    LabelSecondary,
+    NoStyle,
+    Level(LevelInner),
+    Addition,
+    Removal,
+}
+
+impl ElementStyle {
+    fn color_spec(&self, level: &Level<'_>, stylesheet: &Stylesheet) -> Style {
+        match self {
+            ElementStyle::Addition => stylesheet.addition,
+            ElementStyle::Removal => stylesheet.removal,
+            ElementStyle::LineAndColumn => stylesheet.none,
+            ElementStyle::LineNumber => stylesheet.line_num,
+            ElementStyle::Quotation => stylesheet.none,
+            ElementStyle::MainHeaderMsg => stylesheet.emphasis,
+            ElementStyle::UnderlinePrimary | ElementStyle::LabelPrimary => level.style(stylesheet),
+            ElementStyle::UnderlineSecondary | ElementStyle::LabelSecondary => stylesheet.context,
+            ElementStyle::HeaderMsg | ElementStyle::NoStyle => stylesheet.none,
+            ElementStyle::Level(lvl) => lvl.style(stylesheet),
+        }
+    }
+}
+
+#[derive(Debug, Clone, Copy)]
+struct UnderlineParts {
+    style: ElementStyle,
+    underline: char,
+    label_start: char,
+    vertical_text_line: char,
+    multiline_vertical: char,
+    multiline_horizontal: char,
+    multiline_whole_line: char,
+    multiline_start_down: char,
+    bottom_right: char,
+    top_left: char,
+    top_right_flat: char,
+    bottom_left: char,
+    multiline_end_up: char,
+    multiline_end_same_line: char,
+    multiline_bottom_right_with_text: char,
+}
+
+/// Whether the original and suggested code are the same.
+pub(crate) fn is_different(sm: &SourceMap<'_>, suggested: &str, range: Range<usize>) -> bool {
+    match sm.span_to_snippet(range) {
+        Some(s) => s != suggested,
+        None => true,
+    }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum OutputTheme {
+    Ascii,
+    Unicode,
+}
+
+#[cfg(test)]
+mod test {
+    use super::OUTPUT_REPLACEMENTS;
+    use snapbox::IntoData;
+
+    fn format_replacements(replacements: Vec<(char, &str)>) -> String {
+        replacements
+            .into_iter()
+            .map(|r| format!("    {r:?}"))
+            .collect::<Vec<_>>()
+            .join("\n")
+    }
+
+    #[test]
+    /// The [`OUTPUT_REPLACEMENTS`] array must be sorted (for binary search to
+    /// work) and must contain no duplicate entries
+    fn ensure_output_replacements_is_sorted() {
+        let mut expected = OUTPUT_REPLACEMENTS.to_owned();
+        expected.sort_by_key(|r| r.0);
+        expected.dedup_by_key(|r| r.0);
+        let expected = format_replacements(expected);
+        let actual = format_replacements(OUTPUT_REPLACEMENTS.to_owned());
+        snapbox::assert_data_eq!(actual, expected.into_data().raw());
+    }
+}
diff --git a/src/renderer/source_map.rs b/src/renderer/source_map.rs
new file mode 100644
index 00000000..d014bb01
--- /dev/null
+++ b/src/renderer/source_map.rs
@@ -0,0 +1,662 @@
+use crate::renderer::{char_width, is_different, num_overlap, LineAnnotation, LineAnnotationType};
+use crate::{Annotation, AnnotationKind, Patch};
+use std::cmp::{max, min};
+use std::ops::Range;
+
+#[derive(Debug)]
+pub(crate) struct SourceMap<'a> {
+    lines: Vec<LineInfo<'a>>,
+    pub(crate) source: &'a str,
+}
+
+impl<'a> SourceMap<'a> {
+    pub(crate) fn new(source: &'a str, line_start: usize) -> Self {
+        let mut current_index = 0;
+
+        let mut mapping = vec![];
+        for (idx, (line, end_line)) in CursorLines::new(source).enumerate() {
+            let line_length = line.len();
+            let line_range = current_index..current_index + line_length;
+            let end_line_size = end_line.len();
+
+            mapping.push(LineInfo {
+                line,
+                line_index: line_start + idx,
+                start_byte: line_range.start,
+                end_byte: line_range.end + end_line_size,
+                end_line_size,
+            });
+
+            current_index += line_length + end_line_size;
+        }
+        Self {
+            lines: mapping,
+            source,
+        }
+    }
+
+    pub(crate) fn get_line(&self, idx: usize) -> Option<&'a str> {
+        self.lines
+            .iter()
+            .find(|l| l.line_index == idx)
+            .map(|info| info.line)
+    }
+
+    pub(crate) fn span_to_locations(&self, span: Range<usize>) -> (Loc, Loc) {
+        let start_info = self
+            .lines
+            .iter()
+            .find(|info| span.start >= info.start_byte && span.start < info.end_byte)
+            .unwrap_or(self.lines.last().unwrap());
+        let (mut start_char_pos, start_display_pos) = start_info.line
+            [0..(span.start - start_info.start_byte).min(start_info.line.len())]
+            .chars()
+            .fold((0, 0), |(char_pos, byte_pos), c| {
+                let display = char_width(c);
+                (char_pos + 1, byte_pos + display)
+            });
+        // correct the char pos if we are highlighting the end of a line
+        if (span.start - start_info.start_byte).saturating_sub(start_info.line.len()) > 0 {
+            start_char_pos += 1;
+        }
+        let start = Loc {
+            line: start_info.line_index,
+            char: start_char_pos,
+            display: start_display_pos,
+            byte: span.start,
+        };
+
+        if span.start == span.end {
+            return (start, start);
+        }
+
+        let end_info = self
+            .lines
+            .iter()
+            .find(|info| info.end_byte > span.end.saturating_sub(1))
+            .unwrap_or(self.lines.last().unwrap());
+        let (mut end_char_pos, end_display_pos) = end_info.line
+            [0..(span.end - end_info.start_byte).min(end_info.line.len())]
+            .chars()
+            .fold((0, 0), |(char_pos, byte_pos), c| {
+                let display = char_width(c);
+                (char_pos + 1, byte_pos + display)
+            });
+
+        // correct the char pos if we are highlighting the end of a line
+        if (span.end - end_info.start_byte).saturating_sub(end_info.line.len()) > 0 {
+            end_char_pos += 1;
+        }
+        let mut end = Loc {
+            line: end_info.line_index,
+            char: end_char_pos,
+            display: end_display_pos,
+            byte: span.end,
+        };
+        if start.line != end.line && end.byte > end_info.end_byte - end_info.end_line_size {
+            end.char += 1;
+            end.display += 1;
+        }
+
+        (start, end)
+    }
+
+    pub(crate) fn span_to_snippet(&self, span: Range<usize>) -> Option<&str> {
+        self.source.get(span)
+    }
+
+    pub(crate) fn span_to_lines(&self, span: Range<usize>) -> Vec<&LineInfo<'a>> {
+        let mut lines = vec![];
+        let start = span.start;
+        let end = span.end;
+        for line_info in &self.lines {
+            if start >= line_info.end_byte {
+                continue;
+            }
+            if end <= line_info.start_byte {
+                break;
+            }
+            lines.push(line_info);
+        }
+        lines
+    }
+
+    pub(crate) fn annotated_lines(
+        &self,
+        annotations: Vec<Annotation<'a>>,
+        fold: bool,
+    ) -> (usize, Vec<AnnotatedLineInfo<'a>>) {
+        let source_len = self.source.len();
+        if let Some(bigger) = annotations.iter().find_map(|x| {
+            // Allow highlighting one past the last character in the source.
+            if source_len + 1 < x.span.end {
+                Some(&x.span)
+            } else {
+                None
+            }
+        }) {
+            panic!("Annotation range `{bigger:?}` is beyond the end of buffer `{source_len}`")
+        }
+
+        let mut annotated_line_infos = self
+            .lines
+            .iter()
+            .map(|info| AnnotatedLineInfo {
+                line: info.line,
+                line_index: info.line_index,
+                annotations: vec![],
+            })
+            .collect::<Vec<_>>();
+        let mut multiline_annotations = vec![];
+
+        for Annotation {
+            span,
+            label,
+            kind,
+            highlight_source,
+        } in annotations
+        {
+            let (lo, mut hi) = self.span_to_locations(span.clone());
+
+            // Watch out for "empty spans". If we get a span like 6..6, we
+            // want to just display a `^` at 6, so convert that to
+            // 6..7. This is degenerate input, but it's best to degrade
+            // gracefully -- and the parser likes to supply a span like
+            // that for EOF, in particular.
+
+            if lo.display == hi.display && lo.line == hi.line {
+                hi.display += 1;
+            }
+
+            if lo.line == hi.line {
+                let line_ann = LineAnnotation {
+                    start: lo,
+                    end: hi,
+                    kind,
+                    label,
+                    annotation_type: LineAnnotationType::Singleline,
+                    highlight_source,
+                };
+                self.add_annotation_to_file(&mut annotated_line_infos, lo.line, line_ann);
+            } else {
+                multiline_annotations.push(MultilineAnnotation {
+                    depth: 1,
+                    start: lo,
+                    end: hi,
+                    kind,
+                    label,
+                    overlaps_exactly: false,
+                    highlight_source,
+                });
+            }
+        }
+
+        let mut primary_spans = vec![];
+
+        // Find overlapping multiline annotations, put them at different depths
+        multiline_annotations
+            .sort_by_key(|ml| (ml.start.line, usize::MAX - ml.end.line, ml.start.byte));
+        for ann in multiline_annotations.clone() {
+            if ann.kind.is_primary() {
+                primary_spans.push((ann.start, ann.end));
+            }
+            for a in &mut multiline_annotations {
+                // Move all other multiline annotations overlapping with this one
+                // one level to the right.
+                if !ann.same_span(a)
+                    && num_overlap(ann.start.line, ann.end.line, a.start.line, a.end.line, true)
+                {
+                    a.increase_depth();
+                } else if ann.same_span(a) && &ann != a {
+                    a.overlaps_exactly = true;
+                } else {
+                    if primary_spans
+                        .iter()
+                        .any(|(s, e)| a.start == *s && a.end == *e)
+                    {
+                        a.kind = AnnotationKind::Primary;
+                    }
+                    break;
+                }
+            }
+        }
+
+        let mut max_depth = 0; // max overlapping multiline spans
+        for ann in &multiline_annotations {
+            max_depth = max(max_depth, ann.depth);
+        }
+        // Change order of multispan depth to minimize the number of overlaps in the ASCII art.
+        for a in &mut multiline_annotations {
+            a.depth = max_depth - a.depth + 1;
+        }
+        for ann in multiline_annotations {
+            let mut end_ann = ann.as_end();
+            if ann.overlaps_exactly {
+                end_ann.annotation_type = LineAnnotationType::Singleline;
+            } else {
+                // avoid output like
+                //
+                //  |        foo(
+                //  |   _____^
+                //  |  |_____|
+                //  | ||         bar,
+                //  | ||     );
+                //  | ||      ^
+                //  | ||______|
+                //  |  |______foo
+                //  |         baz
+                //
+                // and instead get
+                //
+                //  |       foo(
+                //  |  _____^
+                //  | |         bar,
+                //  | |     );
+                //  | |      ^
+                //  | |      |
+                //  | |______foo
+                //  |        baz
+                self.add_annotation_to_file(
+                    &mut annotated_line_infos,
+                    ann.start.line,
+                    ann.as_start(),
+                );
+                // 4 is the minimum vertical length of a multiline span when presented: two lines
+                // of code and two lines of underline. This is not true for the special case where
+                // the beginning doesn't have an underline, but the current logic seems to be
+                // working correctly.
+                let middle = min(ann.start.line + 4, ann.end.line);
+                // We'll show up to 4 lines past the beginning of the multispan start.
+                // We will *not* include the tail of lines that are only whitespace, a comment or
+                // a bare delimiter.
+                let filter = |s: &str| {
+                    let s = s.trim();
+                    // Consider comments as empty, but don't consider docstrings to be empty.
+                    !(s.starts_with("//") && !(s.starts_with("///") || s.starts_with("//!")))
+                        // Consider lines with nothing but whitespace, a single delimiter as empty.
+                        && !["", "{", "}", "(", ")", "[", "]"].contains(&s)
+                };
+                let until = (ann.start.line..middle)
+                    .rev()
+                    .filter_map(|line| self.get_line(line).map(|s| (line + 1, s)))
+                    .find(|(_, s)| filter(s))
+                    .map_or(ann.start.line, |(line, _)| line);
+                for line in ann.start.line + 1..until {
+                    // Every `|` that joins the beginning of the span (`___^`) to the end (`|__^`).
+                    self.add_annotation_to_file(&mut annotated_line_infos, line, ann.as_line());
+                }
+                let line_end = ann.end.line - 1;
+                let end_is_empty = self.get_line(line_end).map_or(false, |s| !filter(s));
+                if middle < line_end && !end_is_empty {
+                    self.add_annotation_to_file(&mut annotated_line_infos, line_end, ann.as_line());
+                }
+            }
+            self.add_annotation_to_file(&mut annotated_line_infos, end_ann.end.line, end_ann);
+        }
+
+        if fold {
+            annotated_line_infos.retain(|l| !l.annotations.is_empty());
+        }
+
+        annotated_line_infos
+            .iter_mut()
+            .for_each(|l| l.annotations.sort_by(|a, b| a.start.cmp(&b.start)));
+
+        (max_depth, annotated_line_infos)
+    }
+
+    fn add_annotation_to_file(
+        &self,
+        annotated_line_infos: &mut Vec<AnnotatedLineInfo<'a>>,
+        line_index: usize,
+        line_ann: LineAnnotation<'a>,
+    ) {
+        if let Some(line_info) = annotated_line_infos
+            .iter_mut()
+            .find(|line_info| line_info.line_index == line_index)
+        {
+            line_info.annotations.push(line_ann);
+        } else {
+            let info = self
+                .lines
+                .iter()
+                .find(|l| l.line_index == line_index)
+                .unwrap();
+            annotated_line_infos.push(AnnotatedLineInfo {
+                line: info.line,
+                line_index,
+                annotations: vec![line_ann],
+            });
+            annotated_line_infos.sort_by_key(|l| l.line_index);
+        }
+    }
+
+    pub(crate) fn splice_lines<'b>(
+        &'b self,
+        mut patches: Vec<Patch<'b>>,
+    ) -> Vec<(String, Vec<Patch<'b>>, Vec<Vec<SubstitutionHighlight>>)> {
+        fn push_trailing(
+            buf: &mut String,
+            line_opt: Option<&str>,
+            lo: &Loc,
+            hi_opt: Option<&Loc>,
+        ) -> usize {
+            let mut line_count = 0;
+            // Convert CharPos to Usize, as CharPose is character offset
+            // Extract low index and high index
+            let (lo, hi_opt) = (lo.char, hi_opt.map(|hi| hi.char));
+            if let Some(line) = line_opt {
+                if let Some(lo) = line.char_indices().map(|(i, _)| i).nth(lo) {
+                    // Get high index while account for rare unicode and emoji with char_indices
+                    let hi_opt = hi_opt.and_then(|hi| line.char_indices().map(|(i, _)| i).nth(hi));
+                    match hi_opt {
+                        // If high index exist, take string from low to high index
+                        Some(hi) if hi > lo => {
+                            // count how many '\n' exist
+                            line_count = line[lo..hi].matches('\n').count();
+                            buf.push_str(&line[lo..hi]);
+                        }
+                        Some(_) => (),
+                        // If high index absence, take string from low index till end string.len
+                        None => {
+                            // count how many '\n' exist
+                            line_count = line[lo..].matches('\n').count();
+                            buf.push_str(&line[lo..]);
+                        }
+                    }
+                }
+                // If high index is None
+                if hi_opt.is_none() {
+                    buf.push('\n');
+                }
+            }
+            line_count
+        }
+        // Assumption: all spans are in the same file, and all spans
+        // are disjoint. Sort in ascending order.
+        patches.sort_by_key(|p| p.span.start);
+
+        // Find the bounding span.
+        let Some(lo) = patches.iter().map(|p| p.span.start).min() else {
+            return Vec::new();
+        };
+        let Some(hi) = patches.iter().map(|p| p.span.end).max() else {
+            return Vec::new();
+        };
+
+        let lines = self.span_to_lines(lo..hi);
+
+        let mut highlights = vec![];
+        // To build up the result, we do this for each span:
+        // - push the line segment trailing the previous span
+        //   (at the beginning a "phantom" span pointing at the start of the line)
+        // - push lines between the previous and current span (if any)
+        // - if the previous and current span are not on the same line
+        //   push the line segment leading up to the current span
+        // - splice in the span substitution
+        //
+        // Finally push the trailing line segment of the last span
+        let (mut prev_hi, _) = self.span_to_locations(lo..hi);
+        prev_hi.char = 0;
+        let mut prev_line = lines.first().map(|line| line.line);
+        let mut buf = String::new();
+
+        let mut line_highlight = vec![];
+        // We need to keep track of the difference between the existing code and the added
+        // or deleted code in order to point at the correct column *after* substitution.
+        let mut acc = 0;
+        for part in &mut patches {
+            // If this is a replacement of, e.g. `"a"` into `"ab"`, adjust the
+            // suggestion and snippet to look as if we just suggested to add
+            // `"b"`, which is typically much easier for the user to understand.
+            part.trim_trivial_replacements(self);
+            let (cur_lo, cur_hi) = self.span_to_locations(part.span.clone());
+            if prev_hi.line == cur_lo.line {
+                let mut count = push_trailing(&mut buf, prev_line, &prev_hi, Some(&cur_lo));
+                while count > 0 {
+                    highlights.push(std::mem::take(&mut line_highlight));
+                    acc = 0;
+                    count -= 1;
+                }
+            } else {
+                acc = 0;
+                highlights.push(std::mem::take(&mut line_highlight));
+                let mut count = push_trailing(&mut buf, prev_line, &prev_hi, None);
+                while count > 0 {
+                    highlights.push(std::mem::take(&mut line_highlight));
+                    count -= 1;
+                }
+                // push lines between the previous and current span (if any)
+                for idx in prev_hi.line + 1..(cur_lo.line) {
+                    if let Some(line) = self.get_line(idx) {
+                        buf.push_str(line.as_ref());
+                        buf.push('\n');
+                        highlights.push(std::mem::take(&mut line_highlight));
+                    }
+                }
+                if let Some(cur_line) = self.get_line(cur_lo.line) {
+                    let end = match cur_line.char_indices().nth(cur_lo.char) {
+                        Some((i, _)) => i,
+                        None => cur_line.len(),
+                    };
+                    buf.push_str(&cur_line[..end]);
+                }
+            }
+            // Add a whole line highlight per line in the snippet.
+            let len: isize = part
+                .replacement
+                .split('\n')
+                .next()
+                .unwrap_or(part.replacement)
+                .chars()
+                .map(|c| match c {
+                    '\t' => 4,
+                    _ => 1,
+                })
+                .sum();
+            if !is_different(self, part.replacement, part.span.clone()) {
+                // Account for cases where we are suggesting the same code that's already
+                // there. This shouldn't happen often, but in some cases for multipart
+                // suggestions it's much easier to handle it here than in the origin.
+            } else {
+                line_highlight.push(SubstitutionHighlight {
+                    start: (cur_lo.char as isize + acc) as usize,
+                    end: (cur_lo.char as isize + acc + len) as usize,
+                });
+            }
+            buf.push_str(part.replacement);
+            // Account for the difference between the width of the current code and the
+            // snippet being suggested, so that the *later* suggestions are correctly
+            // aligned on the screen. Note that cur_hi and cur_lo can be on different
+            // lines, so cur_hi.col can be smaller than cur_lo.col
+            acc += len - (cur_hi.char as isize - cur_lo.char as isize);
+            prev_hi = cur_hi;
+            prev_line = self.get_line(prev_hi.line);
+            for line in part.replacement.split('\n').skip(1) {
+                acc = 0;
+                highlights.push(std::mem::take(&mut line_highlight));
+                let end: usize = line
+                    .chars()
+                    .map(|c| match c {
+                        '\t' => 4,
+                        _ => 1,
+                    })
+                    .sum();
+                line_highlight.push(SubstitutionHighlight { start: 0, end });
+            }
+        }
+        highlights.push(std::mem::take(&mut line_highlight));
+        // if the replacement already ends with a newline, don't print the next line
+        if !buf.ends_with('\n') {
+            push_trailing(&mut buf, prev_line, &prev_hi, None);
+        }
+        // remove trailing newlines
+        while buf.ends_with('\n') {
+            buf.pop();
+        }
+        if highlights.iter().all(|parts| parts.is_empty()) {
+            Vec::new()
+        } else {
+            vec![(buf, patches, highlights)]
+        }
+    }
+}
+
+#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
+pub(crate) struct MultilineAnnotation<'a> {
+    pub depth: usize,
+    pub start: Loc,
+    pub end: Loc,
+    pub kind: AnnotationKind,
+    pub label: Option<&'a str>,
+    pub overlaps_exactly: bool,
+    pub highlight_source: bool,
+}
+
+impl<'a> MultilineAnnotation<'a> {
+    pub(crate) fn increase_depth(&mut self) {
+        self.depth += 1;
+    }
+
+    /// Compare two `MultilineAnnotation`s considering only the `Span` they cover.
+    pub(crate) fn same_span(&self, other: &MultilineAnnotation<'_>) -> bool {
+        self.start == other.start && self.end == other.end
+    }
+
+    pub(crate) fn as_start(&self) -> LineAnnotation<'a> {
+        LineAnnotation {
+            start: self.start,
+            end: Loc {
+                line: self.start.line,
+                char: self.start.char + 1,
+                display: self.start.display + 1,
+                byte: self.start.byte + 1,
+            },
+            kind: self.kind,
+            label: None,
+            annotation_type: LineAnnotationType::MultilineStart(self.depth),
+            highlight_source: self.highlight_source,
+        }
+    }
+
+    pub(crate) fn as_end(&self) -> LineAnnotation<'a> {
+        LineAnnotation {
+            start: Loc {
+                line: self.end.line,
+                char: self.end.char.saturating_sub(1),
+                display: self.end.display.saturating_sub(1),
+                byte: self.end.byte.saturating_sub(1),
+            },
+            end: self.end,
+            kind: self.kind,
+            label: self.label,
+            annotation_type: LineAnnotationType::MultilineEnd(self.depth),
+            highlight_source: self.highlight_source,
+        }
+    }
+
+    pub(crate) fn as_line(&self) -> LineAnnotation<'a> {
+        LineAnnotation {
+            start: Loc::default(),
+            end: Loc::default(),
+            kind: self.kind,
+            label: None,
+            annotation_type: LineAnnotationType::MultilineLine(self.depth),
+            highlight_source: self.highlight_source,
+        }
+    }
+}
+
+#[derive(Debug)]
+pub(crate) struct LineInfo<'a> {
+    pub(crate) line: &'a str,
+    pub(crate) line_index: usize,
+    pub(crate) start_byte: usize,
+    pub(crate) end_byte: usize,
+    end_line_size: usize,
+}
+
+#[derive(Debug)]
+pub(crate) struct AnnotatedLineInfo<'a> {
+    pub(crate) line: &'a str,
+    pub(crate) line_index: usize,
+    pub(crate) annotations: Vec<LineAnnotation<'a>>,
+}
+
+/// A source code location used for error reporting.
+#[derive(Clone, Copy, Debug, Default, PartialOrd, Ord, PartialEq, Eq)]
+pub(crate) struct Loc {
+    /// The (1-based) line number.
+    pub(crate) line: usize,
+    /// The (0-based) column offset.
+    pub(crate) char: usize,
+    /// The (0-based) column offset when displayed.
+    pub(crate) display: usize,
+    /// The (0-based) byte offset.
+    pub(crate) byte: usize,
+}
+
+struct CursorLines<'a>(&'a str);
+
+impl CursorLines<'_> {
+    fn new(src: &str) -> CursorLines<'_> {
+        CursorLines(src)
+    }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq)]
+enum EndLine {
+    Eof,
+    Lf,
+    Crlf,
+}
+
+impl EndLine {
+    /// The number of characters this line ending occupies in bytes.
+    pub(crate) fn len(self) -> usize {
+        match self {
+            EndLine::Eof => 0,
+            EndLine::Lf => 1,
+            EndLine::Crlf => 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::Crlf)
+                        } else {
+                            (&self.0[..x], EndLine::Lf)
+                        }
+                    } else {
+                        ("", EndLine::Lf)
+                    };
+                    self.0 = &self.0[x + 1..];
+                    ret
+                })
+                .or_else(|| {
+                    let ret = Some((self.0, EndLine::Eof));
+                    self.0 = "";
+                    ret
+                })
+        }
+    }
+}
+
+/// Used to translate between `Span`s and byte positions within a single output line in highlighted
+/// code of structured suggestions.
+#[derive(Debug, Clone, Copy)]
+pub(crate) struct SubstitutionHighlight {
+    pub(crate) start: usize,
+    pub(crate) end: usize,
+}
diff --git a/src/renderer/styled_buffer.rs b/src/renderer/styled_buffer.rs
new file mode 100644
index 00000000..c9b805a0
--- /dev/null
+++ b/src/renderer/styled_buffer.rs
@@ -0,0 +1,156 @@
+//! Adapted from [styled_buffer]
+//!
+//! [styled_buffer]: https://github.com/rust-lang/rust/blob/894f7a4ba6554d3797404bbf550d9919df060b97/compiler/rustc_errors/src/styled_buffer.rs
+
+use crate::renderer::stylesheet::Stylesheet;
+use crate::renderer::ElementStyle;
+use crate::Level;
+
+use std::fmt;
+use std::fmt::Write;
+
+#[derive(Debug)]
+pub(crate) struct StyledBuffer {
+    lines: Vec<Vec<StyledChar>>,
+}
+
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub(crate) struct StyledChar {
+    ch: char,
+    style: ElementStyle,
+}
+
+impl StyledChar {
+    pub(crate) const SPACE: Self = StyledChar::new(' ', ElementStyle::NoStyle);
+
+    pub(crate) const fn new(ch: char, style: ElementStyle) -> StyledChar {
+        StyledChar { ch, style }
+    }
+}
+
+impl StyledBuffer {
+    pub(crate) fn new() -> StyledBuffer {
+        StyledBuffer { lines: vec![] }
+    }
+
+    fn ensure_lines(&mut self, line: usize) {
+        if line >= self.lines.len() {
+            self.lines.resize(line + 1, Vec::new());
+        }
+    }
+
+    pub(crate) fn render(
+        &self,
+        level: Level<'_>,
+        stylesheet: &Stylesheet,
+    ) -> Result<String, fmt::Error> {
+        let mut str = String::new();
+        for (i, line) in self.lines.iter().enumerate() {
+            let mut current_style = stylesheet.none;
+            for StyledChar { ch, style } in line {
+                let ch_style = style.color_spec(&level, stylesheet);
+                if ch_style != current_style {
+                    if !line.is_empty() {
+                        write!(str, "{}", current_style.render_reset())?;
+                    }
+                    current_style = ch_style;
+                    write!(str, "{}", current_style.render())?;
+                }
+                write!(str, "{ch}")?;
+            }
+            write!(str, "{}", current_style.render_reset())?;
+            if i != self.lines.len() - 1 {
+                writeln!(str)?;
+            }
+        }
+        Ok(str)
+    }
+
+    /// Sets `chr` with `style` for given `line`, `col`.
+    /// If `line` does not exist in our buffer, adds empty lines up to the given
+    /// and fills the last line with unstyled whitespace.
+    pub(crate) fn putc(&mut self, line: usize, col: usize, chr: char, style: ElementStyle) {
+        self.ensure_lines(line);
+        if col >= self.lines[line].len() {
+            self.lines[line].resize(col + 1, StyledChar::SPACE);
+        }
+        self.lines[line][col] = StyledChar::new(chr, style);
+    }
+
+    /// Sets `string` with `style` for given `line`, starting from `col`.
+    /// If `line` does not exist in our buffer, adds empty lines up to the given
+    /// and fills the last line with unstyled whitespace.
+    pub(crate) fn puts(&mut self, line: usize, col: usize, string: &str, style: ElementStyle) {
+        let mut n = col;
+        for c in string.chars() {
+            self.putc(line, n, c, style);
+            n += 1;
+        }
+    }
+
+    /// For given `line` inserts `string` with `style` after old content of that line,
+    /// adding lines if needed
+    pub(crate) fn append(&mut self, line: usize, string: &str, style: ElementStyle) {
+        if line >= self.lines.len() {
+            self.puts(line, 0, string, style);
+        } else {
+            let col = self.lines[line].len();
+            self.puts(line, col, string, style);
+        }
+    }
+
+    /// For given `line` inserts `string` with `style` before old content of that line,
+    /// adding lines if needed
+    pub(crate) fn prepend(&mut self, line: usize, string: &str, style: ElementStyle) {
+        self.ensure_lines(line);
+        let string_len = string.chars().count();
+
+        if !self.lines[line].is_empty() {
+            // Push the old content over to make room for new content
+            for _ in 0..string_len {
+                self.lines[line].insert(0, StyledChar::SPACE);
+            }
+        }
+
+        self.puts(line, 0, string, style);
+    }
+
+    pub(crate) fn num_lines(&self) -> usize {
+        self.lines.len()
+    }
+
+    /// Set `style` for `line`, `col_start..col_end` range if:
+    /// 1. That line and column range exist in `StyledBuffer`
+    /// 2. `overwrite` is `true` or existing style is `Style::NoStyle` or `Style::Quotation`
+    pub(crate) fn set_style_range(
+        &mut self,
+        line: usize,
+        col_start: usize,
+        col_end: usize,
+        style: ElementStyle,
+        overwrite: bool,
+    ) {
+        for col in col_start..col_end {
+            self.set_style(line, col, style, overwrite);
+        }
+    }
+
+    /// Set `style` for `line`, `col` if:
+    /// 1. That line and column exist in `StyledBuffer`
+    /// 2. `overwrite` is `true` or existing style is `Style::NoStyle` or `Style::Quotation`
+    pub(crate) fn set_style(
+        &mut self,
+        line: usize,
+        col: usize,
+        style: ElementStyle,
+        overwrite: bool,
+    ) {
+        if let Some(ref mut line) = self.lines.get_mut(line) {
+            if let Some(StyledChar { style: s, .. }) = line.get_mut(col) {
+                if overwrite || matches!(s, ElementStyle::NoStyle | ElementStyle::Quotation) {
+                    *s = style;
+                }
+            }
+        }
+    }
+}
diff --git a/src/renderer/stylesheet.rs b/src/renderer/stylesheet.rs
new file mode 100644
index 00000000..075cad42
--- /dev/null
+++ b/src/renderer/stylesheet.rs
@@ -0,0 +1,40 @@
+use anstyle::Style;
+
+#[derive(Clone, Copy, Debug)]
+pub(crate) struct Stylesheet {
+    pub(crate) error: Style,
+    pub(crate) warning: Style,
+    pub(crate) info: Style,
+    pub(crate) note: Style,
+    pub(crate) help: Style,
+    pub(crate) line_num: Style,
+    pub(crate) emphasis: Style,
+    pub(crate) none: Style,
+    pub(crate) context: Style,
+    pub(crate) addition: Style,
+    pub(crate) removal: Style,
+}
+
+impl Default for Stylesheet {
+    fn default() -> Self {
+        Self::plain()
+    }
+}
+
+impl Stylesheet {
+    pub(crate) const fn plain() -> Self {
+        Self {
+            error: Style::new(),
+            warning: Style::new(),
+            info: Style::new(),
+            note: Style::new(),
+            help: Style::new(),
+            line_num: Style::new(),
+            emphasis: Style::new(),
+            none: Style::new(),
+            context: Style::new(),
+            addition: Style::new(),
+            removal: Style::new(),
+        }
+    }
+}
diff --git a/src/snippet.rs b/src/snippet.rs
index bc7ba009..03bfe3dd 100644
--- a/src/snippet.rs
+++ b/src/snippet.rs
@@ -1,88 +1,482 @@
 //! 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.
+
+use crate::renderer::source_map::SourceMap;
+use crate::Level;
+use std::ops::Range;
+
+pub(crate) const ERROR_TXT: &str = "error";
+pub(crate) const HELP_TXT: &str = "help";
+pub(crate) const INFO_TXT: &str = "info";
+pub(crate) const NOTE_TXT: &str = "note";
+pub(crate) const WARNING_TXT: &str = "warning";
+
+/// Top-level user message
 #[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`.
+pub struct Message<'a> {
+    pub(crate) id: Option<&'a str>, // for "correctness", could be sloppy and be on Title
+    pub(crate) groups: Vec<Group<'a>>,
+}
+
+impl<'a> Message<'a> {
+    /// <div class="warning">
+    ///
+    /// Text passed to this function is considered "untrusted input", as such
+    /// all text is passed through a normalization function. Pre-styled text is
+    /// not allowed to be passed to this function.
+    ///
+    /// </div>
+    pub fn id(mut self, id: &'a str) -> Self {
+        self.id = Some(id);
+        self
+    }
+
+    /// Add an [`Element`] container
+    pub fn group(mut self, group: Group<'a>) -> Self {
+        self.groups.push(group);
+        self
+    }
+
+    pub(crate) fn max_line_number(&self) -> usize {
+        self.groups
+            .iter()
+            .map(|v| {
+                v.elements
+                    .iter()
+                    .map(|s| match s {
+                        Element::Title(_) | Element::Origin(_) | Element::Padding(_) => 0,
+                        Element::Cause(cause) => {
+                            let end = cause
+                                .markers
+                                .iter()
+                                .map(|a| a.span.end)
+                                .max()
+                                .unwrap_or(cause.source.len())
+                                .min(cause.source.len());
+
+                            cause.line_start + newline_count(&cause.source[..end])
+                        }
+                        Element::Suggestion(suggestion) => {
+                            let end = suggestion
+                                .markers
+                                .iter()
+                                .map(|a| a.span.end)
+                                .max()
+                                .unwrap_or(suggestion.source.len())
+                                .min(suggestion.source.len());
+
+                            suggestion.line_start + newline_count(&suggestion.source[..end])
+                        }
+                    })
+                    .max()
+                    .unwrap_or(1)
+            })
+            .max()
+            .unwrap_or(1)
+    }
+}
+
+/// An [`Element`] container
 #[derive(Debug)]
-pub struct SourceAnnotation<'a> {
-    pub range: (usize, usize),
-    pub label: &'a str,
-    pub annotation_type: AnnotationType,
+pub struct Group<'a> {
+    pub(crate) elements: Vec<Element<'a>>,
+}
+
+impl Default for Group<'_> {
+    fn default() -> Self {
+        Self::new()
+    }
 }
 
-/// An annotation for a `Snippet`.
+impl<'a> Group<'a> {
+    pub fn new() -> Self {
+        Self { elements: vec![] }
+    }
+
+    pub fn element(mut self, section: impl Into<Element<'a>>) -> Self {
+        self.elements.push(section.into());
+        self
+    }
+
+    pub fn elements(mut self, sections: impl IntoIterator<Item = impl Into<Element<'a>>>) -> Self {
+        self.elements.extend(sections.into_iter().map(Into::into));
+        self
+    }
+
+    pub fn is_empty(&self) -> bool {
+        self.elements.is_empty()
+    }
+}
+
+/// A section of content within a [`Group`]
 #[derive(Debug)]
+#[non_exhaustive]
+pub enum Element<'a> {
+    Title(Title<'a>),
+    Cause(Snippet<'a, Annotation<'a>>),
+    Suggestion(Snippet<'a, Patch<'a>>),
+    Origin(Origin<'a>),
+    Padding(Padding),
+}
+
+impl<'a> From<Title<'a>> for Element<'a> {
+    fn from(value: Title<'a>) -> Self {
+        Element::Title(value)
+    }
+}
+
+impl<'a> From<Snippet<'a, Annotation<'a>>> for Element<'a> {
+    fn from(value: Snippet<'a, Annotation<'a>>) -> Self {
+        Element::Cause(value)
+    }
+}
+
+impl<'a> From<Snippet<'a, Patch<'a>>> for Element<'a> {
+    fn from(value: Snippet<'a, Patch<'a>>) -> Self {
+        Element::Suggestion(value)
+    }
+}
+
+impl<'a> From<Origin<'a>> for Element<'a> {
+    fn from(value: Origin<'a>) -> Self {
+        Element::Origin(value)
+    }
+}
+
+impl From<Padding> for Element<'_> {
+    fn from(value: Padding) -> Self {
+        Self::Padding(value)
+    }
+}
+
+/// A whitespace [`Element`] in a [`Group`]
+#[derive(Debug)]
+pub struct Padding;
+
+/// A text [`Element`] in a [`Group`]
+///
+/// See [`Level::title`] to create this.
+#[derive(Debug)]
+pub struct Title<'a> {
+    pub(crate) level: Level<'a>,
+    pub(crate) title: &'a str,
+    pub(crate) primary: bool,
+}
+
+impl Title<'_> {
+    pub fn primary(mut self, primary: bool) -> Self {
+        self.primary = primary;
+        self
+    }
+}
+
+/// A source view [`Element`] in a [`Group`]
+#[derive(Debug)]
+pub struct Snippet<'a, T> {
+    pub(crate) origin: Option<&'a str>,
+    pub(crate) line_start: usize,
+    pub(crate) source: &'a str,
+    pub(crate) markers: Vec<T>,
+    pub(crate) fold: bool,
+}
+
+impl<'a, T: Clone> Snippet<'a, T> {
+    /// The source code to be rendered
+    ///
+    /// <div class="warning">
+    ///
+    /// Text passed to this function is considered "untrusted input", as such
+    /// all text is passed through a normalization function. Pre-styled text is
+    /// not allowed to be passed to this function.
+    ///
+    /// </div>
+    pub fn source(source: &'a str) -> Self {
+        Self {
+            origin: None,
+            line_start: 1,
+            source,
+            markers: vec![],
+            fold: false,
+        }
+    }
+
+    /// When manually [`fold`][Self::fold]ing,
+    /// the [`source`][Self::source]s line offset from the original start
+    pub fn line_start(mut self, line_start: usize) -> Self {
+        self.line_start = line_start;
+        self
+    }
+
+    /// The location of the [`source`][Self::source] (e.g. a path)
+    ///
+    /// <div class="warning">
+    ///
+    /// Text passed to this function is considered "untrusted input", as such
+    /// all text is passed through a normalization function. Pre-styled text is
+    /// not allowed to be passed to this function.
+    ///
+    /// </div>
+    pub fn origin(mut self, origin: &'a str) -> Self {
+        self.origin = Some(origin);
+        self
+    }
+
+    /// Hide lines without [`Annotation`]s
+    pub fn fold(mut self, fold: bool) -> Self {
+        self.fold = fold;
+        self
+    }
+}
+
+impl<'a> Snippet<'a, Annotation<'a>> {
+    /// Highlight and describe a span of text within the [`source`][Self::source]
+    pub fn annotation(mut self, annotation: Annotation<'a>) -> Snippet<'a, Annotation<'a>> {
+        self.markers.push(annotation);
+        self
+    }
+
+    /// Highlight and describe spans of text within the [`source`][Self::source]
+    pub fn annotations(mut self, annotation: impl IntoIterator<Item = Annotation<'a>>) -> Self {
+        self.markers.extend(annotation);
+        self
+    }
+}
+
+impl<'a> Snippet<'a, Patch<'a>> {
+    /// Suggest to the user an edit to the [`source`][Self::source]
+    pub fn patch(mut self, patch: Patch<'a>) -> Snippet<'a, Patch<'a>> {
+        self.markers.push(patch);
+        self
+    }
+
+    /// Suggest to the user edits to the [`source`][Self::source]
+    pub fn patches(mut self, patches: impl IntoIterator<Item = Patch<'a>>) -> Self {
+        self.markers.extend(patches);
+        self
+    }
+}
+
+/// Highlighted and describe a span of text within a [`Snippet`]
+///
+/// See [`AnnotationKind`] to create an annotation.
+#[derive(Clone, 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,
+    pub(crate) span: Range<usize>,
+    pub(crate) label: Option<&'a str>,
+    pub(crate) kind: AnnotationKind,
+    pub(crate) highlight_source: bool,
+}
+
+impl<'a> Annotation<'a> {
+    /// Describe the reason the span is highlighted
+    ///
+    /// This will be styled according to the [`AnnotationKind`]
+    ///
+    /// <div class="warning">
+    ///
+    /// Text passed to this function is considered "untrusted input", as such
+    /// all text is passed through a normalization function. Pre-styled text is
+    /// not allowed to be passed to this function.
+    ///
+    /// </div>
+    pub fn label(mut self, label: &'a str) -> Self {
+        self.label = Some(label);
+        self
+    }
+
+    /// Style the source according to the [`AnnotationKind`]
+    pub fn highlight_source(mut self, highlight_source: bool) -> Self {
+        self.highlight_source = highlight_source;
+        self
+    }
+}
+
+/// The category of the [`Annotation`]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub enum AnnotationKind {
+    /// Color to [`Message`]'s [`Level`]
+    Primary,
+    /// "secondary"; fixed color
+    Context,
+}
+
+impl AnnotationKind {
+    pub fn span<'a>(self, span: Range<usize>) -> Annotation<'a> {
+        Annotation {
+            span,
+            label: None,
+            kind: self,
+            highlight_source: false,
+        }
+    }
+
+    pub(crate) fn is_primary(&self) -> bool {
+        matches!(self, AnnotationKind::Primary)
+    }
+}
+
+/// Suggested edit to the [`Snippet`]
+#[derive(Clone, Debug)]
+pub struct Patch<'a> {
+    pub(crate) span: Range<usize>,
+    pub(crate) replacement: &'a str,
+}
+
+impl<'a> Patch<'a> {
+    /// Splice `replacement` into the [`Snippet`] at the `span`
+    ///
+    /// <div class="warning">
+    ///
+    /// Text passed to this function is considered "untrusted input", as such
+    /// all text is passed through a normalization function. Pre-styled text is
+    /// not allowed to be passed to this function.
+    ///
+    /// </div>
+    pub fn new(span: Range<usize>, replacement: &'a str) -> Self {
+        Self { span, replacement }
+    }
+
+    pub(crate) fn is_addition(&self, sm: &SourceMap<'_>) -> bool {
+        !self.replacement.is_empty() && !self.replaces_meaningful_content(sm)
+    }
+
+    pub(crate) fn is_deletion(&self, sm: &SourceMap<'_>) -> bool {
+        self.replacement.trim().is_empty() && self.replaces_meaningful_content(sm)
+    }
+
+    pub(crate) fn is_replacement(&self, sm: &SourceMap<'_>) -> bool {
+        !self.replacement.is_empty() && self.replaces_meaningful_content(sm)
+    }
+
+    /// Whether this is a replacement that overwrites source with a snippet
+    /// in a way that isn't a superset of the original string. For example,
+    /// replacing "abc" with "abcde" is not destructive, but replacing it
+    /// it with "abx" is, since the "c" character is lost.
+    pub(crate) fn is_destructive_replacement(&self, sm: &SourceMap<'_>) -> bool {
+        self.is_replacement(sm)
+            && !sm
+                .span_to_snippet(self.span.clone())
+                // This should use `is_some_and` when our MSRV is >= 1.70
+                .map_or(false, |s| {
+                    as_substr(s.trim(), self.replacement.trim()).is_some()
+                })
+    }
+
+    fn replaces_meaningful_content(&self, sm: &SourceMap<'_>) -> bool {
+        sm.span_to_snippet(self.span.clone())
+            .map_or(!self.span.is_empty(), |snippet| !snippet.trim().is_empty())
+    }
+
+    /// Try to turn a replacement into an addition when the span that is being
+    /// overwritten matches either the prefix or suffix of the replacement.
+    pub(crate) fn trim_trivial_replacements(&mut self, sm: &'a SourceMap<'a>) {
+        if self.replacement.is_empty() {
+            return;
+        }
+        let Some(snippet) = sm.span_to_snippet(self.span.clone()) else {
+            return;
+        };
+
+        if let Some((prefix, substr, suffix)) = as_substr(snippet, self.replacement) {
+            self.span = self.span.start + prefix..self.span.end.saturating_sub(suffix);
+            self.replacement = substr;
+        }
+    }
+}
+
+/// The location of the [`Snippet`] (e.g. a path)
+#[derive(Clone, Debug)]
+pub struct Origin<'a> {
+    pub(crate) origin: &'a str,
+    pub(crate) line: Option<usize>,
+    pub(crate) char_column: Option<usize>,
+    pub(crate) primary: bool,
+    pub(crate) label: Option<&'a str>,
+}
+
+impl<'a> Origin<'a> {
+    /// <div class="warning">
+    ///
+    /// Text passed to this function is considered "untrusted input", as such
+    /// all text is passed through a normalization function. Pre-styled text is
+    /// not allowed to be passed to this function.
+    ///
+    /// </div>
+    pub fn new(origin: &'a str) -> Self {
+        Self {
+            origin,
+            line: None,
+            char_column: None,
+            primary: false,
+            label: None,
+        }
+    }
+
+    /// Set the default line number to display
+    ///
+    /// Otherwise this will be inferred from the primary [`Annotation`]
+    pub fn line(mut self, line: usize) -> Self {
+        self.line = Some(line);
+        self
+    }
+
+    /// Set the default column to display
+    ///
+    /// Otherwise this will be inferred from the primary [`Annotation`]
+    pub fn char_column(mut self, char_column: usize) -> Self {
+        self.char_column = Some(char_column);
+        self
+    }
+
+    pub fn primary(mut self, primary: bool) -> Self {
+        self.primary = primary;
+        self
+    }
+
+    /// Like [`Annotation::label`], but when there is no source
+    ///
+    /// <div class="warning">
+    ///
+    /// Text passed to this function is considered "untrusted input", as such
+    /// all text is passed through a normalization function. Pre-styled text is
+    /// not allowed to be passed to this function.
+    ///
+    /// </div>
+    pub fn label(mut self, label: &'a str) -> Self {
+        self.label = Some(label);
+        self
+    }
+}
+
+fn newline_count(body: &str) -> usize {
+    #[cfg(feature = "simd")]
+    {
+        memchr::memchr_iter(b'\n', body.as_bytes())
+            .count()
+            .saturating_sub(1)
+    }
+    #[cfg(not(feature = "simd"))]
+    {
+        body.lines().count().saturating_sub(1)
+    }
+}
+
+/// Given an original string like `AACC`, and a suggestion like `AABBCC`, try to detect
+/// the case where a substring of the suggestion is "sandwiched" in the original, like
+/// `BB` is. Return the length of the prefix, the "trimmed" suggestion, and the length
+/// of the suffix.
+fn as_substr<'a>(original: &'a str, suggestion: &'a str) -> Option<(usize, &'a str, usize)> {
+    let common_prefix = original
+        .chars()
+        .zip(suggestion.chars())
+        .take_while(|(c1, c2)| c1 == c2)
+        .map(|(c, _)| c.len_utf8())
+        .sum();
+    let original = &original[common_prefix..];
+    let suggestion = &suggestion[common_prefix..];
+    if let Some(stripped) = suggestion.strip_suffix(original) {
+        let common_suffix = original.len();
+        Some((common_prefix, stripped, common_suffix))
+    } else {
+        None
+    }
 }
diff --git a/src/stylesheets/color.rs b/src/stylesheets/color.rs
deleted file mode 100644
index 024dd06f..00000000
--- a/src/stylesheets/color.rs
+++ /dev/null
@@ -1,50 +0,0 @@
-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
deleted file mode 100644
index 4648852a..00000000
--- a/src/stylesheets/mod.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-//! 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
deleted file mode 100644
index 21cb2695..00000000
--- a/src/stylesheets/no_color.rs
+++ /dev/null
@@ -1,31 +0,0 @@
-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/color/ann_eof.rs b/tests/color/ann_eof.rs
new file mode 100644
index 00000000..00e34b16
--- /dev/null
+++ b/tests/color/ann_eof.rs
@@ -0,0 +1,18 @@
+use annotate_snippets::{AnnotationKind, Group, Level, Renderer, Snippet};
+
+use snapbox::{assert_data_eq, file};
+
+#[test]
+fn case() {
+    let input = Level::ERROR.header("expected `.`, `=`").group(
+        Group::new().element(
+            Snippet::source("asdf")
+                .origin("Cargo.toml")
+                .line_start(1)
+                .annotation(AnnotationKind::Primary.span(4..4).label("")),
+        ),
+    );
+    let expected = file!["ann_eof.term.svg"];
+    let renderer = Renderer::styled();
+    assert_data_eq!(renderer.render(input), expected);
+}
diff --git a/tests/color/ann_eof.term.svg b/tests/color/ann_eof.term.svg
new file mode 100644
index 00000000..aeb4f8cf
--- /dev/null
+++ b/tests/color/ann_eof.term.svg
@@ -0,0 +1,34 @@
+<svg width="740px" height="110px" xmlns="http://www.w3.org/2000/svg">
+  <style>
+    .fg { fill: #AAAAAA }
+    .bg { background: #000000 }
+    .fg-bright-blue { fill: #5555FF }
+    .fg-bright-red { fill: #FF5555 }
+    .container {
+      padding: 0 10px;
+      line-height: 18px;
+    }
+    .bold { font-weight: bold; }
+    tspan {
+      font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
+      white-space: pre;
+      line-height: 18px;
+    }
+  </style>
+
+  <rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
+
+  <text xml:space="preserve" class="container fg">
+    <tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error</tspan><tspan class="bold">: expected `.`, `=`</tspan>
+</tspan>
+    <tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--&gt; </tspan><tspan>Cargo.toml:1:5</tspan>
+</tspan>
+    <tspan x="10px" y="64px"><tspan>  </tspan><tspan class="fg-bright-blue bold">|</tspan>
+</tspan>
+    <tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">1</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> asdf</tspan>
+</tspan>
+    <tspan x="10px" y="100px"><tspan>  </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>     </tspan><tspan class="fg-bright-red bold">^</tspan>
+</tspan>
+  </text>
+
+</svg>
diff --git a/tests/color/ann_insertion.rs b/tests/color/ann_insertion.rs
new file mode 100644
index 00000000..802a0c78
--- /dev/null
+++ b/tests/color/ann_insertion.rs
@@ -0,0 +1,18 @@
+use annotate_snippets::{AnnotationKind, Group, Level, Renderer, Snippet};
+
+use snapbox::{assert_data_eq, file};
+
+#[test]
+fn case() {
+    let input = Level::ERROR.header("expected `.`, `=`").group(
+        Group::new().element(
+            Snippet::source("asf")
+                .origin("Cargo.toml")
+                .line_start(1)
+                .annotation(AnnotationKind::Primary.span(2..2).label("'d' belongs here")),
+        ),
+    );
+    let expected = file!["ann_insertion.term.svg"];
+    let renderer = Renderer::styled();
+    assert_data_eq!(renderer.render(input), expected);
+}
diff --git a/tests/color/ann_insertion.term.svg b/tests/color/ann_insertion.term.svg
new file mode 100644
index 00000000..57c90a23
--- /dev/null
+++ b/tests/color/ann_insertion.term.svg
@@ -0,0 +1,34 @@
+<svg width="740px" height="110px" xmlns="http://www.w3.org/2000/svg">
+  <style>
+    .fg { fill: #AAAAAA }
+    .bg { background: #000000 }
+    .fg-bright-blue { fill: #5555FF }
+    .fg-bright-red { fill: #FF5555 }
+    .container {
+      padding: 0 10px;
+      line-height: 18px;
+    }
+    .bold { font-weight: bold; }
+    tspan {
+      font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
+      white-space: pre;
+      line-height: 18px;
+    }
+  </style>
+
+  <rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
+
+  <text xml:space="preserve" class="container fg">
+    <tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error</tspan><tspan class="bold">: expected `.`, `=`</tspan>
+</tspan>
+    <tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--&gt; </tspan><tspan>Cargo.toml:1:3</tspan>
+</tspan>
+    <tspan x="10px" y="64px"><tspan>  </tspan><tspan class="fg-bright-blue bold">|</tspan>
+</tspan>
+    <tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">1</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> asf</tspan>
+</tspan>
+    <tspan x="10px" y="100px"><tspan>  </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>   </tspan><tspan class="fg-bright-red bold">^</tspan><tspan> </tspan><tspan class="fg-bright-red bold">'d' belongs here</tspan>
+</tspan>
+  </text>
+
+</svg>
diff --git a/tests/color/ann_multiline.rs b/tests/color/ann_multiline.rs
new file mode 100644
index 00000000..4b561ed3
--- /dev/null
+++ b/tests/color/ann_multiline.rs
@@ -0,0 +1,31 @@
+use annotate_snippets::{AnnotationKind, Group, Level, Renderer, Snippet};
+
+use snapbox::{assert_data_eq, file};
+
+#[test]
+fn case() {
+    let source = r#"                        if let DisplayLine::Source {
+                            ref mut inline_marks,
+                        } = body[body_idx]
+"#;
+
+    let input = Level::ERROR
+        .header("pattern does not mention fields `lineno`, `content`")
+        .id("E0027")
+        .group(
+            Group::new().element(
+                Snippet::source(source)
+                    .origin("src/display_list.rs")
+                    .line_start(139)
+                    .fold(false)
+                    .annotation(
+                        AnnotationKind::Primary
+                            .span(31..128)
+                            .label("missing fields `lineno`, `content`"),
+                    ),
+            ),
+        );
+    let expected = file!["ann_multiline.term.svg"];
+    let renderer = Renderer::styled();
+    assert_data_eq!(renderer.render(input), expected);
+}
diff --git a/tests/color/ann_multiline.term.svg b/tests/color/ann_multiline.term.svg
new file mode 100644
index 00000000..2ff0364b
--- /dev/null
+++ b/tests/color/ann_multiline.term.svg
@@ -0,0 +1,40 @@
+<svg width="740px" height="164px" xmlns="http://www.w3.org/2000/svg">
+  <style>
+    .fg { fill: #AAAAAA }
+    .bg { background: #000000 }
+    .fg-bright-blue { fill: #5555FF }
+    .fg-bright-red { fill: #FF5555 }
+    .container {
+      padding: 0 10px;
+      line-height: 18px;
+    }
+    .bold { font-weight: bold; }
+    tspan {
+      font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
+      white-space: pre;
+      line-height: 18px;
+    }
+  </style>
+
+  <rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
+
+  <text xml:space="preserve" class="container fg">
+    <tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0027]</tspan><tspan class="bold">: pattern does not mention fields `lineno`, `content`</tspan>
+</tspan>
+    <tspan x="10px" y="46px"><tspan>   </tspan><tspan class="fg-bright-blue bold">--&gt; </tspan><tspan>src/display_list.rs:139:32</tspan>
+</tspan>
+    <tspan x="10px" y="64px"><tspan>    </tspan><tspan class="fg-bright-blue bold">|</tspan>
+</tspan>
+    <tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">139</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>                           if let DisplayLine::Source {</tspan>
+</tspan>
+    <tspan x="10px" y="100px"><tspan>    </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold"> ________________________________^</tspan>
+</tspan>
+    <tspan x="10px" y="118px"><tspan class="fg-bright-blue bold">140</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan>                             ref mut inline_marks,</tspan>
+</tspan>
+    <tspan x="10px" y="136px"><tspan class="fg-bright-blue bold">141</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan>                         } = body[body_idx]</tspan>
+</tspan>
+    <tspan x="10px" y="154px"><tspan>    </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|_________________________^</tspan><tspan> </tspan><tspan class="fg-bright-red bold">missing fields `lineno`, `content`</tspan>
+</tspan>
+  </text>
+
+</svg>
diff --git a/tests/color/ann_multiline2.rs b/tests/color/ann_multiline2.rs
new file mode 100644
index 00000000..9996fa97
--- /dev/null
+++ b/tests/color/ann_multiline2.rs
@@ -0,0 +1,31 @@
+use annotate_snippets::{AnnotationKind, Group, Level, Renderer, Snippet};
+
+use snapbox::{assert_data_eq, file};
+
+#[test]
+fn case() {
+    let source = r#"This is an example
+of an edge case of an annotation overflowing
+to exactly one character on next line.
+"#;
+
+    let input = Level::ERROR
+        .header("spacing error found")
+        .id("E####")
+        .group(
+            Group::new().element(
+                Snippet::source(source)
+                    .origin("foo.txt")
+                    .line_start(26)
+                    .fold(false)
+                    .annotation(
+                        AnnotationKind::Primary
+                            .span(11..19)
+                            .label("this should not be on separate lines"),
+                    ),
+            ),
+        );
+    let expected = file!["ann_multiline2.term.svg"];
+    let renderer = Renderer::styled();
+    assert_data_eq!(renderer.render(input), expected);
+}
diff --git a/tests/color/ann_multiline2.term.svg b/tests/color/ann_multiline2.term.svg
new file mode 100644
index 00000000..24827f66
--- /dev/null
+++ b/tests/color/ann_multiline2.term.svg
@@ -0,0 +1,38 @@
+<svg width="740px" height="146px" xmlns="http://www.w3.org/2000/svg">
+  <style>
+    .fg { fill: #AAAAAA }
+    .bg { background: #000000 }
+    .fg-bright-blue { fill: #5555FF }
+    .fg-bright-red { fill: #FF5555 }
+    .container {
+      padding: 0 10px;
+      line-height: 18px;
+    }
+    .bold { font-weight: bold; }
+    tspan {
+      font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
+      white-space: pre;
+      line-height: 18px;
+    }
+  </style>
+
+  <rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
+
+  <text xml:space="preserve" class="container fg">
+    <tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E####]</tspan><tspan class="bold">: spacing error found</tspan>
+</tspan>
+    <tspan x="10px" y="46px"><tspan>  </tspan><tspan class="fg-bright-blue bold">--&gt; </tspan><tspan>foo.txt:26:12</tspan>
+</tspan>
+    <tspan x="10px" y="64px"><tspan>   </tspan><tspan class="fg-bright-blue bold">|</tspan>
+</tspan>
+    <tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">26</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> This is an example</tspan>
+</tspan>
+    <tspan x="10px" y="100px"><tspan>   </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>            </tspan><tspan class="fg-bright-red bold">^^^^^^^</tspan><tspan> </tspan><tspan class="fg-bright-red bold">this should not be on separate lines</tspan>
+</tspan>
+    <tspan x="10px" y="118px"><tspan class="fg-bright-blue bold">27</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> of an edge case of an annotation overflowing</tspan>
+</tspan>
+    <tspan x="10px" y="136px"><tspan class="fg-bright-blue bold">28</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> to exactly one character on next line.</tspan>
+</tspan>
+  </text>
+
+</svg>
diff --git a/tests/color/ann_removed_nl.rs b/tests/color/ann_removed_nl.rs
new file mode 100644
index 00000000..45a64626
--- /dev/null
+++ b/tests/color/ann_removed_nl.rs
@@ -0,0 +1,18 @@
+use annotate_snippets::{AnnotationKind, Group, Level, Renderer, Snippet};
+
+use snapbox::{assert_data_eq, file};
+
+#[test]
+fn case() {
+    let input = Level::ERROR.header("expected `.`, `=`").group(
+        Group::new().element(
+            Snippet::source("asdf")
+                .origin("Cargo.toml")
+                .line_start(1)
+                .annotation(AnnotationKind::Primary.span(4..5).label("")),
+        ),
+    );
+    let expected = file!["ann_removed_nl.term.svg"];
+    let renderer = Renderer::styled();
+    assert_data_eq!(renderer.render(input), expected);
+}
diff --git a/tests/color/ann_removed_nl.term.svg b/tests/color/ann_removed_nl.term.svg
new file mode 100644
index 00000000..aeb4f8cf
--- /dev/null
+++ b/tests/color/ann_removed_nl.term.svg
@@ -0,0 +1,34 @@
+<svg width="740px" height="110px" xmlns="http://www.w3.org/2000/svg">
+  <style>
+    .fg { fill: #AAAAAA }
+    .bg { background: #000000 }
+    .fg-bright-blue { fill: #5555FF }
+    .fg-bright-red { fill: #FF5555 }
+    .container {
+      padding: 0 10px;
+      line-height: 18px;
+    }
+    .bold { font-weight: bold; }
+    tspan {
+      font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
+      white-space: pre;
+      line-height: 18px;
+    }
+  </style>
+
+  <rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
+
+  <text xml:space="preserve" class="container fg">
+    <tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error</tspan><tspan class="bold">: expected `.`, `=`</tspan>
+</tspan>
+    <tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--&gt; </tspan><tspan>Cargo.toml:1:5</tspan>
+</tspan>
+    <tspan x="10px" y="64px"><tspan>  </tspan><tspan class="fg-bright-blue bold">|</tspan>
+</tspan>
+    <tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">1</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> asdf</tspan>
+</tspan>
+    <tspan x="10px" y="100px"><tspan>  </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>     </tspan><tspan class="fg-bright-red bold">^</tspan>
+</tspan>
+  </text>
+
+</svg>
diff --git a/tests/color/ensure_emoji_highlight_width.rs b/tests/color/ensure_emoji_highlight_width.rs
new file mode 100644
index 00000000..b2397845
--- /dev/null
+++ b/tests/color/ensure_emoji_highlight_width.rs
@@ -0,0 +1,24 @@
+use annotate_snippets::{AnnotationKind, Group, Level, Renderer, Snippet};
+
+use snapbox::{assert_data_eq, file};
+
+#[test]
+fn case() {
+    let source = r#""haha this isn't a valid name 🐛" = { package = "libc", version = "0.1" }
+"#;
+
+    let input = Level::ERROR.header("invalid character ` ` in package name: `haha this isn't a valid name 🐛`, characters must be Unicode XID characters (numbers, `-`, `_`, or most letters)")
+        .group(
+            Group::new()
+                .element(
+                    Snippet::source(source)
+                        .origin("<file>")
+                        .line_start(7)
+                        .annotation(AnnotationKind::Primary.span(0..35).label(""))
+                )
+            )
+;
+    let expected = file!["ensure_emoji_highlight_width.term.svg"];
+    let renderer = Renderer::styled();
+    assert_data_eq!(renderer.render(input), expected);
+}
diff --git a/tests/color/ensure_emoji_highlight_width.term.svg b/tests/color/ensure_emoji_highlight_width.term.svg
new file mode 100644
index 00000000..14624fb6
--- /dev/null
+++ b/tests/color/ensure_emoji_highlight_width.term.svg
@@ -0,0 +1,34 @@
+<svg width="1356px" height="110px" xmlns="http://www.w3.org/2000/svg">
+  <style>
+    .fg { fill: #AAAAAA }
+    .bg { background: #000000 }
+    .fg-bright-blue { fill: #5555FF }
+    .fg-bright-red { fill: #FF5555 }
+    .container {
+      padding: 0 10px;
+      line-height: 18px;
+    }
+    .bold { font-weight: bold; }
+    tspan {
+      font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
+      white-space: pre;
+      line-height: 18px;
+    }
+  </style>
+
+  <rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
+
+  <text xml:space="preserve" class="container fg">
+    <tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error</tspan><tspan class="bold">: invalid character ` ` in package name: `haha this isn't a valid name 🐛`, characters must be Unicode XID characters (numbers, `-`, `_`, or most letters)</tspan>
+</tspan>
+    <tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--&gt; </tspan><tspan>&lt;file&gt;:7:1</tspan>
+</tspan>
+    <tspan x="10px" y="64px"><tspan>  </tspan><tspan class="fg-bright-blue bold">|</tspan>
+</tspan>
+    <tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">7</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> "haha this isn't a valid name 🐛" = { package = "libc", version = "0.1" }</tspan>
+</tspan>
+    <tspan x="10px" y="100px"><tspan>  </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^</tspan>
+</tspan>
+  </text>
+
+</svg>
diff --git a/tests/color/fold_ann_multiline.rs b/tests/color/fold_ann_multiline.rs
new file mode 100644
index 00000000..3995b686
--- /dev/null
+++ b/tests/color/fold_ann_multiline.rs
@@ -0,0 +1,50 @@
+use annotate_snippets::{AnnotationKind, Group, Level, Renderer, Snippet};
+
+use snapbox::{assert_data_eq, file};
+
+#[test]
+fn case() {
+    let 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 || 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,
+        }
+    }
+"#;
+
+    let input = Level::ERROR.header("mismatched types").id("E0308").group(
+        Group::new().element(
+            Snippet::source(source)
+                .origin("src/format.rs")
+                .line_start(51)
+                .fold(true)
+                .annotation(AnnotationKind::Context.span(5..19).label(
+                    "expected `std::option::Option<std::string::String>` because of return type",
+                ))
+                .annotation(
+                    AnnotationKind::Primary
+                        .span(22..766)
+                        .label("expected enum `std::option::Option`, found ()"),
+                ),
+        ),
+    );
+    let expected = file!["fold_ann_multiline.term.svg"];
+    let renderer = Renderer::styled();
+    assert_data_eq!(renderer.render(input), expected);
+}
diff --git a/tests/color/fold_ann_multiline.term.svg b/tests/color/fold_ann_multiline.term.svg
new file mode 100644
index 00000000..80197e5c
--- /dev/null
+++ b/tests/color/fold_ann_multiline.term.svg
@@ -0,0 +1,48 @@
+<svg width="869px" height="236px" xmlns="http://www.w3.org/2000/svg">
+  <style>
+    .fg { fill: #AAAAAA }
+    .bg { background: #000000 }
+    .fg-bright-blue { fill: #5555FF }
+    .fg-bright-red { fill: #FF5555 }
+    .container {
+      padding: 0 10px;
+      line-height: 18px;
+    }
+    .bold { font-weight: bold; }
+    tspan {
+      font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
+      white-space: pre;
+      line-height: 18px;
+    }
+  </style>
+
+  <rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
+
+  <text xml:space="preserve" class="container fg">
+    <tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0308]</tspan><tspan class="bold">: mismatched types</tspan>
+</tspan>
+    <tspan x="10px" y="46px"><tspan>  </tspan><tspan class="fg-bright-blue bold">--&gt; </tspan><tspan>src/format.rs:52:1</tspan>
+</tspan>
+    <tspan x="10px" y="64px"><tspan>   </tspan><tspan class="fg-bright-blue bold">|</tspan>
+</tspan>
+    <tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">51</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>   ) -&gt; Option&lt;String&gt; {</tspan>
+</tspan>
+    <tspan x="10px" y="100px"><tspan>   </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>        </tspan><tspan class="fg-bright-blue bold">--------------</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">expected `std::option::Option&lt;std::string::String&gt;` because of return type</tspan>
+</tspan>
+    <tspan x="10px" y="118px"><tspan class="fg-bright-blue bold">52</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">/</tspan><tspan>     for ann in annotations {</tspan>
+</tspan>
+    <tspan x="10px" y="136px"><tspan class="fg-bright-blue bold">53</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan>         match (ann.range.0, ann.range.1) {</tspan>
+</tspan>
+    <tspan x="10px" y="154px"><tspan class="fg-bright-blue bold">54</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan>             (None, None) =&gt; continue,</tspan>
+</tspan>
+    <tspan x="10px" y="172px"><tspan class="fg-bright-blue bold">55</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan>             (Some(start), Some(end)) if start &gt; end_index || end &lt; start_index =&gt; continue,</tspan>
+</tspan>
+    <tspan x="10px" y="190px"><tspan class="fg-bright-blue bold">...</tspan><tspan>  </tspan><tspan class="fg-bright-red bold">|</tspan>
+</tspan>
+    <tspan x="10px" y="208px"><tspan class="fg-bright-blue bold">72</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan>     }</tspan>
+</tspan>
+    <tspan x="10px" y="226px"><tspan>   </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|_____^</tspan><tspan> </tspan><tspan class="fg-bright-red bold">expected enum `std::option::Option`, found ()</tspan>
+</tspan>
+  </text>
+
+</svg>
diff --git a/tests/color/fold_bad_origin_line.rs b/tests/color/fold_bad_origin_line.rs
new file mode 100644
index 00000000..1a21a5ef
--- /dev/null
+++ b/tests/color/fold_bad_origin_line.rs
@@ -0,0 +1,24 @@
+use annotate_snippets::{AnnotationKind, Group, Level, Renderer, Snippet};
+
+use snapbox::{assert_data_eq, file};
+
+#[test]
+fn case() {
+    let source = r#"
+
+invalid syntax
+"#;
+
+    let input = Level::ERROR.header("").group(
+        Group::new().element(
+            Snippet::source(source)
+                .origin("path/to/error.rs")
+                .line_start(1)
+                .fold(true)
+                .annotation(AnnotationKind::Context.span(2..16).label("error here")),
+        ),
+    );
+    let expected = file!["fold_bad_origin_line.term.svg"];
+    let renderer = Renderer::styled();
+    assert_data_eq!(renderer.render(input), expected);
+}
diff --git a/tests/color/fold_bad_origin_line.term.svg b/tests/color/fold_bad_origin_line.term.svg
new file mode 100644
index 00000000..66083276
--- /dev/null
+++ b/tests/color/fold_bad_origin_line.term.svg
@@ -0,0 +1,34 @@
+<svg width="740px" height="110px" xmlns="http://www.w3.org/2000/svg">
+  <style>
+    .fg { fill: #AAAAAA }
+    .bg { background: #000000 }
+    .fg-bright-blue { fill: #5555FF }
+    .fg-bright-red { fill: #FF5555 }
+    .container {
+      padding: 0 10px;
+      line-height: 18px;
+    }
+    .bold { font-weight: bold; }
+    tspan {
+      font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
+      white-space: pre;
+      line-height: 18px;
+    }
+  </style>
+
+  <rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
+
+  <text xml:space="preserve" class="container fg">
+    <tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error</tspan><tspan class="bold">: </tspan>
+</tspan>
+    <tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--&gt; </tspan><tspan>path/to/error.rs:3:1</tspan>
+</tspan>
+    <tspan x="10px" y="64px"><tspan>  </tspan><tspan class="fg-bright-blue bold">|</tspan>
+</tspan>
+    <tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">3</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> invalid syntax</tspan>
+</tspan>
+    <tspan x="10px" y="100px"><tspan>  </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">--------------</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">error here</tspan>
+</tspan>
+  </text>
+
+</svg>
diff --git a/tests/color/fold_leading.rs b/tests/color/fold_leading.rs
new file mode 100644
index 00000000..93ba4992
--- /dev/null
+++ b/tests/color/fold_leading.rs
@@ -0,0 +1,35 @@
+use annotate_snippets::{AnnotationKind, Group, Level, Renderer, Snippet};
+
+use snapbox::{assert_data_eq, file};
+
+#[test]
+fn case() {
+    let source = r#"[workspace]
+
+[package]
+name = "hello"
+version = "1.0.0"
+license = "MIT"
+rust-version = "1.70"
+edition = "2021"
+
+[lints]
+workspace = 20
+"#;
+
+    let input = Level::ERROR
+        .header("invalid type: integer `20`, expected a bool")
+        .id("E0308")
+        .group(
+            Group::new().element(
+                Snippet::source(source)
+                    .origin("Cargo.toml")
+                    .line_start(1)
+                    .fold(true)
+                    .annotation(AnnotationKind::Primary.span(132..134).label("")),
+            ),
+        );
+    let expected = file!["fold_leading.term.svg"];
+    let renderer = Renderer::styled();
+    assert_data_eq!(renderer.render(input), expected);
+}
diff --git a/tests/color/fold_leading.term.svg b/tests/color/fold_leading.term.svg
new file mode 100644
index 00000000..23b31d4a
--- /dev/null
+++ b/tests/color/fold_leading.term.svg
@@ -0,0 +1,34 @@
+<svg width="740px" height="110px" xmlns="http://www.w3.org/2000/svg">
+  <style>
+    .fg { fill: #AAAAAA }
+    .bg { background: #000000 }
+    .fg-bright-blue { fill: #5555FF }
+    .fg-bright-red { fill: #FF5555 }
+    .container {
+      padding: 0 10px;
+      line-height: 18px;
+    }
+    .bold { font-weight: bold; }
+    tspan {
+      font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
+      white-space: pre;
+      line-height: 18px;
+    }
+  </style>
+
+  <rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
+
+  <text xml:space="preserve" class="container fg">
+    <tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0308]</tspan><tspan class="bold">: invalid type: integer `20`, expected a bool</tspan>
+</tspan>
+    <tspan x="10px" y="46px"><tspan>  </tspan><tspan class="fg-bright-blue bold">--&gt; </tspan><tspan>Cargo.toml:11:13</tspan>
+</tspan>
+    <tspan x="10px" y="64px"><tspan>   </tspan><tspan class="fg-bright-blue bold">|</tspan>
+</tspan>
+    <tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">11</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> workspace = 20</tspan>
+</tspan>
+    <tspan x="10px" y="100px"><tspan>   </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>             </tspan><tspan class="fg-bright-red bold">^^</tspan>
+</tspan>
+  </text>
+
+</svg>
diff --git a/tests/color/fold_trailing.rs b/tests/color/fold_trailing.rs
new file mode 100644
index 00000000..f86ade78
--- /dev/null
+++ b/tests/color/fold_trailing.rs
@@ -0,0 +1,34 @@
+use annotate_snippets::{AnnotationKind, Group, Level, Renderer, Snippet};
+
+use snapbox::{assert_data_eq, file};
+
+#[test]
+fn case() {
+    let source = r#"lints = 20
+
+[workspace]
+
+[package]
+name = "hello"
+version = "1.0.0"
+license = "MIT"
+rust-version = "1.70"
+edition = "2021"
+"#;
+
+    let input = Level::ERROR
+        .header("invalid type: integer `20`, expected a lints table")
+        .id("E0308")
+        .group(
+            Group::new().element(
+                Snippet::source(source)
+                    .origin("Cargo.toml")
+                    .line_start(1)
+                    .fold(true)
+                    .annotation(AnnotationKind::Primary.span(8..10).label("")),
+            ),
+        );
+    let expected = file!["fold_trailing.term.svg"];
+    let renderer = Renderer::styled();
+    assert_data_eq!(renderer.render(input), expected);
+}
diff --git a/tests/color/fold_trailing.term.svg b/tests/color/fold_trailing.term.svg
new file mode 100644
index 00000000..46071da6
--- /dev/null
+++ b/tests/color/fold_trailing.term.svg
@@ -0,0 +1,34 @@
+<svg width="740px" height="110px" xmlns="http://www.w3.org/2000/svg">
+  <style>
+    .fg { fill: #AAAAAA }
+    .bg { background: #000000 }
+    .fg-bright-blue { fill: #5555FF }
+    .fg-bright-red { fill: #FF5555 }
+    .container {
+      padding: 0 10px;
+      line-height: 18px;
+    }
+    .bold { font-weight: bold; }
+    tspan {
+      font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
+      white-space: pre;
+      line-height: 18px;
+    }
+  </style>
+
+  <rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
+
+  <text xml:space="preserve" class="container fg">
+    <tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0308]</tspan><tspan class="bold">: invalid type: integer `20`, expected a lints table</tspan>
+</tspan>
+    <tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--&gt; </tspan><tspan>Cargo.toml:1:9</tspan>
+</tspan>
+    <tspan x="10px" y="64px"><tspan>  </tspan><tspan class="fg-bright-blue bold">|</tspan>
+</tspan>
+    <tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">1</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> lints = 20</tspan>
+</tspan>
+    <tspan x="10px" y="100px"><tspan>  </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>         </tspan><tspan class="fg-bright-red bold">^^</tspan>
+</tspan>
+  </text>
+
+</svg>
diff --git a/tests/color/issue_9.rs b/tests/color/issue_9.rs
new file mode 100644
index 00000000..2accd2f2
--- /dev/null
+++ b/tests/color/issue_9.rs
@@ -0,0 +1,31 @@
+use annotate_snippets::{AnnotationKind, Group, Level, Renderer, Snippet};
+
+use snapbox::{assert_data_eq, file};
+
+#[test]
+fn case() {
+    let input = Level::ERROR.header("expected one of `.`, `;`, `?`, or an operator, found `for`")
+        .group(
+            Group::new()
+                .element(
+                    Snippet::source("let x = vec![1];")
+                        .origin("/code/rust/src/test/ui/annotate-snippet/suggestion.rs")
+                        .line_start(4)
+                        .annotation(AnnotationKind::Context.span(4..5).label("move occurs because `x` has type `std::vec::Vec<i32>`, which does not implement the `Copy` trait"))
+                )
+                .element(
+                    Snippet::source("let y = x;")
+                        .line_start(7)
+                        .annotation(AnnotationKind::Context.span(8..9).label("value moved here"))
+                )
+                .element(
+                    Snippet::source("x;")
+                        .line_start(9)
+                        .annotation(AnnotationKind::Primary.span(0..1).label("value used here after move"))
+                )
+            )
+;
+    let expected = file!["issue_9.term.svg"];
+    let renderer = Renderer::styled();
+    assert_data_eq!(renderer.render(input), expected);
+}
diff --git a/tests/color/issue_9.term.svg b/tests/color/issue_9.term.svg
new file mode 100644
index 00000000..5ae5da77
--- /dev/null
+++ b/tests/color/issue_9.term.svg
@@ -0,0 +1,48 @@
+<svg width="911px" height="236px" xmlns="http://www.w3.org/2000/svg">
+  <style>
+    .fg { fill: #AAAAAA }
+    .bg { background: #000000 }
+    .fg-bright-blue { fill: #5555FF }
+    .fg-bright-red { fill: #FF5555 }
+    .container {
+      padding: 0 10px;
+      line-height: 18px;
+    }
+    .bold { font-weight: bold; }
+    tspan {
+      font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
+      white-space: pre;
+      line-height: 18px;
+    }
+  </style>
+
+  <rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
+
+  <text xml:space="preserve" class="container fg">
+    <tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error</tspan><tspan class="bold">: expected one of `.`, `;`, `?`, or an operator, found `for`</tspan>
+</tspan>
+    <tspan x="10px" y="46px"><tspan>  </tspan><tspan class="fg-bright-blue bold">|</tspan>
+</tspan>
+    <tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">::: </tspan><tspan>/code/rust/src/test/ui/annotate-snippet/suggestion.rs:4:5</tspan>
+</tspan>
+    <tspan x="10px" y="82px"><tspan>  </tspan><tspan class="fg-bright-blue bold">|</tspan>
+</tspan>
+    <tspan x="10px" y="100px"><tspan class="fg-bright-blue bold">4</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> let x = vec![1];</tspan>
+</tspan>
+    <tspan x="10px" y="118px"><tspan>  </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>     </tspan><tspan class="fg-bright-blue bold">-</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">move occurs because `x` has type `std::vec::Vec&lt;i32&gt;`, which does not implement the `Copy` trait</tspan>
+</tspan>
+    <tspan x="10px" y="136px"><tspan>  </tspan><tspan class="fg-bright-blue bold">|</tspan>
+</tspan>
+    <tspan x="10px" y="154px"><tspan class="fg-bright-blue bold">7</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> let y = x;</tspan>
+</tspan>
+    <tspan x="10px" y="172px"><tspan>  </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>         </tspan><tspan class="fg-bright-blue bold">-</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">value moved here</tspan>
+</tspan>
+    <tspan x="10px" y="190px"><tspan>  </tspan><tspan class="fg-bright-blue bold">|</tspan>
+</tspan>
+    <tspan x="10px" y="208px"><tspan class="fg-bright-blue bold">9</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> x;</tspan>
+</tspan>
+    <tspan x="10px" y="226px"><tspan>  </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">^</tspan><tspan> </tspan><tspan class="fg-bright-red bold">value used here after move</tspan>
+</tspan>
+  </text>
+
+</svg>
diff --git a/tests/color/main.rs b/tests/color/main.rs
new file mode 100644
index 00000000..f954bb7a
--- /dev/null
+++ b/tests/color/main.rs
@@ -0,0 +1,16 @@
+mod ann_eof;
+mod ann_insertion;
+mod ann_multiline;
+mod ann_multiline2;
+mod ann_removed_nl;
+mod ensure_emoji_highlight_width;
+mod fold_ann_multiline;
+mod fold_bad_origin_line;
+mod fold_leading;
+mod fold_trailing;
+mod issue_9;
+mod multiple_annotations;
+mod simple;
+mod strip_line;
+mod strip_line_char;
+mod strip_line_non_ws;
diff --git a/tests/color/multiple_annotations.rs b/tests/color/multiple_annotations.rs
new file mode 100644
index 00000000..b568b919
--- /dev/null
+++ b/tests/color/multiple_annotations.rs
@@ -0,0 +1,42 @@
+use annotate_snippets::{AnnotationKind, Group, Level, Renderer, Snippet};
+
+use snapbox::{assert_data_eq, file};
+
+#[test]
+fn case() {
+    let source = r#"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,
+        ));
+    }
+}
+"#;
+
+    let input = Level::ERROR.header("").group(
+        Group::new().element(
+            Snippet::source(source)
+                .line_start(96)
+                .annotation(
+                    AnnotationKind::Primary
+                        .span(100..110)
+                        .label("Variable defined here"),
+                )
+                .annotation(
+                    AnnotationKind::Primary
+                        .span(184..194)
+                        .label("Referenced here"),
+                )
+                .annotation(
+                    AnnotationKind::Primary
+                        .span(243..253)
+                        .label("Referenced again here"),
+                ),
+        ),
+    );
+    let expected = file!["multiple_annotations.term.svg"];
+    let renderer = Renderer::styled();
+    assert_data_eq!(renderer.render(input), expected);
+}
diff --git a/tests/color/multiple_annotations.term.svg b/tests/color/multiple_annotations.term.svg
new file mode 100644
index 00000000..2c5c4a81
--- /dev/null
+++ b/tests/color/multiple_annotations.term.svg
@@ -0,0 +1,52 @@
+<svg width="768px" height="272px" xmlns="http://www.w3.org/2000/svg">
+  <style>
+    .fg { fill: #AAAAAA }
+    .bg { background: #000000 }
+    .fg-bright-blue { fill: #5555FF }
+    .fg-bright-red { fill: #FF5555 }
+    .container {
+      padding: 0 10px;
+      line-height: 18px;
+    }
+    .bold { font-weight: bold; }
+    tspan {
+      font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
+      white-space: pre;
+      line-height: 18px;
+    }
+  </style>
+
+  <rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
+
+  <text xml:space="preserve" class="container fg">
+    <tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error</tspan><tspan class="bold">: </tspan>
+</tspan>
+    <tspan x="10px" y="46px"><tspan>    </tspan><tspan class="fg-bright-blue bold">|</tspan>
+</tspan>
+    <tspan x="10px" y="64px"><tspan class="fg-bright-blue bold"> 96</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> fn add_title_line(result: &amp;mut Vec&lt;String&gt;, main_annotation: Option&lt;&amp;Annotation&gt;) {</tspan>
+</tspan>
+    <tspan x="10px" y="82px"><tspan class="fg-bright-blue bold"> 97</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>     if let Some(annotation) = main_annotation {</tspan>
+</tspan>
+    <tspan x="10px" y="100px"><tspan>    </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>                 </tspan><tspan class="fg-bright-red bold">^^^^^^^^^^</tspan><tspan> </tspan><tspan class="fg-bright-red bold">Variable defined here</tspan>
+</tspan>
+    <tspan x="10px" y="118px"><tspan class="fg-bright-blue bold"> 98</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>         result.push(format_title_line(</tspan>
+</tspan>
+    <tspan x="10px" y="136px"><tspan class="fg-bright-blue bold"> 99</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>             &amp;annotation.annotation_type,</tspan>
+</tspan>
+    <tspan x="10px" y="154px"><tspan>    </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>              </tspan><tspan class="fg-bright-red bold">^^^^^^^^^^</tspan><tspan> </tspan><tspan class="fg-bright-red bold">Referenced here</tspan>
+</tspan>
+    <tspan x="10px" y="172px"><tspan class="fg-bright-blue bold">100</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>             None,</tspan>
+</tspan>
+    <tspan x="10px" y="190px"><tspan class="fg-bright-blue bold">101</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>             &amp;annotation.label,</tspan>
+</tspan>
+    <tspan x="10px" y="208px"><tspan>    </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>              </tspan><tspan class="fg-bright-red bold">^^^^^^^^^^</tspan><tspan> </tspan><tspan class="fg-bright-red bold">Referenced again here</tspan>
+</tspan>
+    <tspan x="10px" y="226px"><tspan class="fg-bright-blue bold">102</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>         ));</tspan>
+</tspan>
+    <tspan x="10px" y="244px"><tspan class="fg-bright-blue bold">103</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>     }</tspan>
+</tspan>
+    <tspan x="10px" y="262px"><tspan class="fg-bright-blue bold">104</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> }</tspan>
+</tspan>
+  </text>
+
+</svg>
diff --git a/tests/color/simple.rs b/tests/color/simple.rs
new file mode 100644
index 00000000..35e83d38
--- /dev/null
+++ b/tests/color/simple.rs
@@ -0,0 +1,34 @@
+use annotate_snippets::{AnnotationKind, Group, Level, Renderer, Snippet};
+
+use snapbox::{assert_data_eq, file};
+
+#[test]
+fn case() {
+    let source = r#"        })
+
+        for line in &self.body {
+"#;
+
+    let input = Level::ERROR
+        .header("expected one of `.`, `;`, `?`, or an operator, found `for`")
+        .group(
+            Group::new().element(
+                Snippet::source(source)
+                    .origin("src/format_color.rs")
+                    .line_start(169)
+                    .annotation(
+                        AnnotationKind::Primary
+                            .span(20..23)
+                            .label("unexpected token"),
+                    )
+                    .annotation(
+                        AnnotationKind::Context
+                            .span(10..11)
+                            .label("expected one of `.`, `;`, `?`, or an operator here"),
+                    ),
+            ),
+        );
+    let expected = file!["simple.term.svg"];
+    let renderer = Renderer::styled();
+    assert_data_eq!(renderer.render(input), expected);
+}
diff --git a/tests/color/simple.term.svg b/tests/color/simple.term.svg
new file mode 100644
index 00000000..b849cf46
--- /dev/null
+++ b/tests/color/simple.term.svg
@@ -0,0 +1,40 @@
+<svg width="740px" height="164px" xmlns="http://www.w3.org/2000/svg">
+  <style>
+    .fg { fill: #AAAAAA }
+    .bg { background: #000000 }
+    .fg-bright-blue { fill: #5555FF }
+    .fg-bright-red { fill: #FF5555 }
+    .container {
+      padding: 0 10px;
+      line-height: 18px;
+    }
+    .bold { font-weight: bold; }
+    tspan {
+      font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
+      white-space: pre;
+      line-height: 18px;
+    }
+  </style>
+
+  <rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
+
+  <text xml:space="preserve" class="container fg">
+    <tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error</tspan><tspan class="bold">: expected one of `.`, `;`, `?`, or an operator, found `for`</tspan>
+</tspan>
+    <tspan x="10px" y="46px"><tspan>   </tspan><tspan class="fg-bright-blue bold">--&gt; </tspan><tspan>src/format_color.rs:171:9</tspan>
+</tspan>
+    <tspan x="10px" y="64px"><tspan>    </tspan><tspan class="fg-bright-blue bold">|</tspan>
+</tspan>
+    <tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">169</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>         })</tspan>
+</tspan>
+    <tspan x="10px" y="100px"><tspan>    </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>           </tspan><tspan class="fg-bright-blue bold">-</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">expected one of `.`, `;`, `?`, or an operator here</tspan>
+</tspan>
+    <tspan x="10px" y="118px"><tspan class="fg-bright-blue bold">170</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
+</tspan>
+    <tspan x="10px" y="136px"><tspan class="fg-bright-blue bold">171</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>         for line in &amp;self.body {</tspan>
+</tspan>
+    <tspan x="10px" y="154px"><tspan>    </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>         </tspan><tspan class="fg-bright-red bold">^^^</tspan><tspan> </tspan><tspan class="fg-bright-red bold">unexpected token</tspan>
+</tspan>
+  </text>
+
+</svg>
diff --git a/tests/color/strip_line.rs b/tests/color/strip_line.rs
new file mode 100644
index 00000000..fd1ba588
--- /dev/null
+++ b/tests/color/strip_line.rs
@@ -0,0 +1,24 @@
+use annotate_snippets::{AnnotationKind, Group, Level, Renderer, Snippet};
+
+use snapbox::{assert_data_eq, file};
+
+#[test]
+fn case() {
+    let source = r#"                                                                                                                                                                                    let _: () = 42;"#;
+
+    let input = Level::ERROR.header("mismatched types").id("E0308").group(
+        Group::new().element(
+            Snippet::source(source)
+                .origin("$DIR/whitespace-trimming.rs")
+                .line_start(4)
+                .annotation(
+                    AnnotationKind::Primary
+                        .span(192..194)
+                        .label("expected (), found integer"),
+                ),
+        ),
+    );
+    let expected = file!["strip_line.term.svg"];
+    let renderer = Renderer::styled().anonymized_line_numbers(true);
+    assert_data_eq!(renderer.render(input), expected);
+}
diff --git a/tests/color/strip_line.term.svg b/tests/color/strip_line.term.svg
new file mode 100644
index 00000000..14fbf9dc
--- /dev/null
+++ b/tests/color/strip_line.term.svg
@@ -0,0 +1,34 @@
+<svg width="740px" height="110px" xmlns="http://www.w3.org/2000/svg">
+  <style>
+    .fg { fill: #AAAAAA }
+    .bg { background: #000000 }
+    .fg-bright-blue { fill: #5555FF }
+    .fg-bright-red { fill: #FF5555 }
+    .container {
+      padding: 0 10px;
+      line-height: 18px;
+    }
+    .bold { font-weight: bold; }
+    tspan {
+      font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
+      white-space: pre;
+      line-height: 18px;
+    }
+  </style>
+
+  <rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
+
+  <text xml:space="preserve" class="container fg">
+    <tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0308]</tspan><tspan class="bold">: mismatched types</tspan>
+</tspan>
+    <tspan x="10px" y="46px"><tspan>  </tspan><tspan class="fg-bright-blue bold">--&gt; </tspan><tspan>$DIR/whitespace-trimming.rs:4:193</tspan>
+</tspan>
+    <tspan x="10px" y="64px"><tspan>   </tspan><tspan class="fg-bright-blue bold">|</tspan>
+</tspan>
+    <tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">LL</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">...</tspan><tspan>                   let _: () = 42;</tspan>
+</tspan>
+    <tspan x="10px" y="100px"><tspan>   </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>                                   </tspan><tspan class="fg-bright-red bold">^^</tspan><tspan> </tspan><tspan class="fg-bright-red bold">expected (), found integer</tspan>
+</tspan>
+  </text>
+
+</svg>
diff --git a/tests/color/strip_line_char.rs b/tests/color/strip_line_char.rs
new file mode 100644
index 00000000..df609e2f
--- /dev/null
+++ b/tests/color/strip_line_char.rs
@@ -0,0 +1,24 @@
+use annotate_snippets::{AnnotationKind, Group, Level, Renderer, Snippet};
+
+use snapbox::{assert_data_eq, file};
+
+#[test]
+fn case() {
+    let source = r#"                                                                                                                                                                                    let _: () = 42ñ"#;
+
+    let input = Level::ERROR.header("mismatched types").id("E0308").group(
+        Group::new().element(
+            Snippet::source(source)
+                .origin("$DIR/whitespace-trimming.rs")
+                .line_start(4)
+                .annotation(
+                    AnnotationKind::Primary
+                        .span(192..194)
+                        .label("expected (), found integer"),
+                ),
+        ),
+    );
+    let expected = file!["strip_line_char.term.svg"];
+    let renderer = Renderer::styled().anonymized_line_numbers(true);
+    assert_data_eq!(renderer.render(input), expected);
+}
diff --git a/tests/color/strip_line_char.term.svg b/tests/color/strip_line_char.term.svg
new file mode 100644
index 00000000..b37d4a9a
--- /dev/null
+++ b/tests/color/strip_line_char.term.svg
@@ -0,0 +1,34 @@
+<svg width="740px" height="110px" xmlns="http://www.w3.org/2000/svg">
+  <style>
+    .fg { fill: #AAAAAA }
+    .bg { background: #000000 }
+    .fg-bright-blue { fill: #5555FF }
+    .fg-bright-red { fill: #FF5555 }
+    .container {
+      padding: 0 10px;
+      line-height: 18px;
+    }
+    .bold { font-weight: bold; }
+    tspan {
+      font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
+      white-space: pre;
+      line-height: 18px;
+    }
+  </style>
+
+  <rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
+
+  <text xml:space="preserve" class="container fg">
+    <tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0308]</tspan><tspan class="bold">: mismatched types</tspan>
+</tspan>
+    <tspan x="10px" y="46px"><tspan>  </tspan><tspan class="fg-bright-blue bold">--&gt; </tspan><tspan>$DIR/whitespace-trimming.rs:4:193</tspan>
+</tspan>
+    <tspan x="10px" y="64px"><tspan>   </tspan><tspan class="fg-bright-blue bold">|</tspan>
+</tspan>
+    <tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">LL</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">...</tspan><tspan>                   let _: () = 42ñ</tspan>
+</tspan>
+    <tspan x="10px" y="100px"><tspan>   </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>                                   </tspan><tspan class="fg-bright-red bold">^^</tspan><tspan> </tspan><tspan class="fg-bright-red bold">expected (), found integer</tspan>
+</tspan>
+  </text>
+
+</svg>
diff --git a/tests/color/strip_line_non_ws.rs b/tests/color/strip_line_non_ws.rs
new file mode 100644
index 00000000..f82d369b
--- /dev/null
+++ b/tests/color/strip_line_non_ws.rs
@@ -0,0 +1,30 @@
+use annotate_snippets::{AnnotationKind, Group, Level, Renderer, Snippet};
+
+use snapbox::{assert_data_eq, file};
+
+#[test]
+fn case() {
+    let source = r#"	let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = 42; let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () = ();
+"#;
+
+    let input = Level::ERROR.header("mismatched types").id("E0308").group(
+        Group::new().element(
+            Snippet::source(source)
+                .origin("$DIR/non-whitespace-trimming.rs")
+                .line_start(4)
+                .annotation(
+                    AnnotationKind::Primary
+                        .span(237..239)
+                        .label("expected `()`, found integer"),
+                )
+                .annotation(
+                    AnnotationKind::Primary
+                        .span(232..234)
+                        .label("expected due to this"),
+                ),
+        ),
+    );
+    let expected = file!["strip_line_non_ws.term.svg"];
+    let renderer = Renderer::styled().anonymized_line_numbers(true);
+    assert_data_eq!(renderer.render(input), expected);
+}
diff --git a/tests/color/strip_line_non_ws.term.svg b/tests/color/strip_line_non_ws.term.svg
new file mode 100644
index 00000000..6f799d35
--- /dev/null
+++ b/tests/color/strip_line_non_ws.term.svg
@@ -0,0 +1,38 @@
+<svg width="1196px" height="146px" xmlns="http://www.w3.org/2000/svg">
+  <style>
+    .fg { fill: #AAAAAA }
+    .bg { background: #000000 }
+    .fg-bright-blue { fill: #5555FF }
+    .fg-bright-red { fill: #FF5555 }
+    .container {
+      padding: 0 10px;
+      line-height: 18px;
+    }
+    .bold { font-weight: bold; }
+    tspan {
+      font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
+      white-space: pre;
+      line-height: 18px;
+    }
+  </style>
+
+  <rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
+
+  <text xml:space="preserve" class="container fg">
+    <tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0308]</tspan><tspan class="bold">: mismatched types</tspan>
+</tspan>
+    <tspan x="10px" y="46px"><tspan>  </tspan><tspan class="fg-bright-blue bold">--&gt; </tspan><tspan>$DIR/non-whitespace-trimming.rs:4:233</tspan>
+</tspan>
+    <tspan x="10px" y="64px"><tspan>   </tspan><tspan class="fg-bright-blue bold">|</tspan>
+</tspan>
+    <tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">LL</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">...</tspan><tspan> = (); let _: () = (); let _: () = (); let _: () = 42; let _: () = (); let _: () = (); let _: () = (); let _: () = (); let _: () </tspan><tspan class="fg-bright-blue bold">...</tspan>
+</tspan>
+    <tspan x="10px" y="100px"><tspan>   </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>                                                  </tspan><tspan class="fg-bright-red bold">^^</tspan><tspan>   </tspan><tspan class="fg-bright-red bold">^^</tspan><tspan> </tspan><tspan class="fg-bright-red bold">expected `()`, found integer</tspan>
+</tspan>
+    <tspan x="10px" y="118px"><tspan>   </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>                                                  </tspan><tspan class="fg-bright-red bold">|</tspan>
+</tspan>
+    <tspan x="10px" y="136px"><tspan>   </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan>                                                  </tspan><tspan class="fg-bright-red bold">expected due to this</tspan>
+</tspan>
+  </text>
+
+</svg>
diff --git a/tests/diff/mod.rs b/tests/diff/mod.rs
deleted file mode 100644
index 576c6c4d..00000000
--- a/tests/diff/mod.rs
+++ /dev/null
@@ -1,43 +0,0 @@
-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
deleted file mode 100644
index 0dcfcfa3..00000000
--- a/tests/dl_from_snippet.rs
+++ /dev/null
@@ -1,407 +0,0 @@
-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/examples.rs b/tests/examples.rs
new file mode 100644
index 00000000..66dd94be
--- /dev/null
+++ b/tests/examples.rs
@@ -0,0 +1,65 @@
+#[test]
+fn custom_error() {
+    let target = "custom_error";
+    let expected = snapbox::file!["../examples/custom_error.svg": TermSvg];
+    assert_example(target, expected);
+}
+
+#[test]
+fn custom_level() {
+    let target = "custom_level";
+    let expected = snapbox::file!["../examples/custom_level.svg": TermSvg];
+    assert_example(target, expected);
+}
+
+#[test]
+fn expected_type() {
+    let target = "expected_type";
+    let expected = snapbox::file!["../examples/expected_type.svg": TermSvg];
+    assert_example(target, expected);
+}
+
+#[test]
+fn footer() {
+    let target = "footer";
+    let expected = snapbox::file!["../examples/footer.svg": TermSvg];
+    assert_example(target, expected);
+}
+
+#[test]
+fn format() {
+    let target = "format";
+    let expected = snapbox::file!["../examples/format.svg": TermSvg];
+    assert_example(target, expected);
+}
+
+#[test]
+fn highlight_source() {
+    let target = "highlight_source";
+    let expected = snapbox::file!["../examples/highlight_source.svg": TermSvg];
+    assert_example(target, expected);
+}
+
+#[test]
+fn highlight_title() {
+    let target = "highlight_title";
+    let expected = snapbox::file!["../examples/highlight_title.svg": TermSvg];
+    assert_example(target, expected);
+}
+
+#[test]
+fn multislice() {
+    let target = "multislice";
+    let expected = snapbox::file!["../examples/multislice.svg": TermSvg];
+    assert_example(target, expected);
+}
+
+#[track_caller]
+fn assert_example(target: &str, expected: snapbox::Data) {
+    let bin_path = snapbox::cmd::compile_example(target, ["--features=testing-colors"]).unwrap();
+    snapbox::cmd::Command::new(bin_path)
+        .env("CLICOLOR_FORCE", "1")
+        .assert()
+        .success()
+        .stdout_eq(expected.raw());
+}
diff --git a/tests/fixtures/no-color/issue_9.toml b/tests/fixtures/no-color/issue_9.toml
deleted file mode 100644
index a30563b2..00000000
--- a/tests/fixtures/no-color/issue_9.toml
+++ /dev/null
@@ -1,28 +0,0 @@
-[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
deleted file mode 100644
index affe6bc4..00000000
--- a/tests/fixtures/no-color/issue_9.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-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
deleted file mode 100644
index c3dc1e9e..00000000
--- a/tests/fixtures/no-color/multiline_annotation.toml
+++ /dev/null
@@ -1,40 +0,0 @@
-[[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
deleted file mode 100644
index bacdec10..00000000
--- a/tests/fixtures/no-color/multiline_annotation.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-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
deleted file mode 100644
index 845bf9f2..00000000
--- a/tests/fixtures/no-color/multiline_annotation2.toml
+++ /dev/null
@@ -1,18 +0,0 @@
-[[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
deleted file mode 100644
index 8a00bfa2..00000000
--- a/tests/fixtures/no-color/multiline_annotation2.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-error[E0027]: pattern does not mention fields `lineno`, `content`
-   --> src/display_list.rs:139: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
deleted file mode 100644
index 21bbcd85..00000000
--- a/tests/fixtures/no-color/multiline_annotation3.toml
+++ /dev/null
@@ -1,18 +0,0 @@
-[[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
deleted file mode 100644
index 12e174c5..00000000
--- a/tests/fixtures/no-color/multiline_annotation3.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-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
deleted file mode 100644
index 84efc5f1..00000000
--- a/tests/fixtures/no-color/multiple_annotations.toml
+++ /dev/null
@@ -1,25 +0,0 @@
-[[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
deleted file mode 100644
index 26c677f7..00000000
--- a/tests/fixtures/no-color/multiple_annotations.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-    |
- 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
deleted file mode 100644
index 6c38674a..00000000
--- a/tests/fixtures/no-color/simple.toml
+++ /dev/null
@@ -1,18 +0,0 @@
-[[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
deleted file mode 100644
index 752cc890..00000000
--- a/tests/fixtures/no-color/simple.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-error: expected one of `.`, `;`, `?`, or an operator, found `for`
-   --> src/format_color.rs:171: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
deleted file mode 100644
index 76d9519b..00000000
--- a/tests/fixtures/no-color/strip_line.toml
+++ /dev/null
@@ -1,25 +0,0 @@
-[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
deleted file mode 100644
index 65b05384..00000000
--- a/tests/fixtures/no-color/strip_line.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-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
deleted file mode 100644
index 5b432beb..00000000
--- a/tests/fixtures/no-color/strip_line_char.toml
+++ /dev/null
@@ -1,25 +0,0 @@
-[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
deleted file mode 100644
index 3d4b700c..00000000
--- a/tests/fixtures/no-color/strip_line_char.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-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
deleted file mode 100644
index 5129f5ce..00000000
--- a/tests/fixtures/no-color/strip_line_non_ws.toml
+++ /dev/null
@@ -1,25 +0,0 @@
-[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
deleted file mode 100644
index 850619ad..00000000
--- a/tests/fixtures/no-color/strip_line_non_ws.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-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
deleted file mode 100644
index e471521f..00000000
--- a/tests/fixtures_test.rs
+++ /dev/null
@@ -1,45 +0,0 @@
-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
index b1392a1d..75cf8532 100644
--- a/tests/formatter.rs
+++ b/tests/formatter.rs
@@ -1,675 +1,2387 @@
-use annotate_snippets::display_list::*;
-use annotate_snippets::snippet::{self, Snippet};
+use annotate_snippets::{Annotation, AnnotationKind, Group, Level, Patch, Renderer, Snippet};
+
+use annotate_snippets::renderer::OutputTheme;
+use snapbox::{assert_data_eq, str};
 
 #[test]
-fn test_source_empty() {
-    let dl = DisplayList::from(vec![DisplayLine::Source {
-        lineno: None,
-        inline_marks: vec![],
-        line: DisplaySourceLine::Empty,
-    }]);
+fn test_i_29() {
+    let snippets = Level::ERROR.header("oops").group(
+        Group::new().element(
+            Snippet::source("First line\r\nSecond oops line")
+                .origin("<current file>")
+                .annotation(AnnotationKind::Primary.span(19..23).label("oops"))
+                .fold(true),
+        ),
+    );
+    let expected = str![[r#"
+error: oops
+ --> <current file>:2:8
+  |
+2 | Second oops line
+  |        ^^^^ oops
+"#]];
 
-    assert_eq!(dl.to_string(), " |");
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(snippets), expected);
 }
 
 #[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"
+fn test_point_to_double_width_characters() {
+    let snippets = Level::ERROR.header("").group(
+        Group::new().element(
+            Snippet::source("こんにちは、世界")
+                .origin("<current file>")
+                .annotation(AnnotationKind::Primary.span(18..24).label("world")),
+        ),
+    );
+
+    let expected = str![[r#"
+error: 
+ --> <current file>:1:7
+  |
+1 | こんにちは、世界
+  |             ^^^^ world
+"#]];
+
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(snippets), expected);
+}
+
+#[test]
+fn test_point_to_double_width_characters_across_lines() {
+    let snippets = Level::ERROR.header("").group(
+        Group::new().element(
+            Snippet::source("おはよう\nございます")
+                .origin("<current file>")
+                .annotation(AnnotationKind::Primary.span(6..22).label("Good morning")),
+        ),
     );
+
+    let expected = str![[r#"
+error: 
+ --> <current file>:1:3
+  |
+1 |   おはよう
+  |  _____^
+2 | | ございます
+  | |______^ Good morning
+"#]];
+
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(snippets), expected);
+}
+
+#[test]
+fn test_point_to_double_width_characters_multiple() {
+    let snippets = Level::ERROR.header("").group(
+        Group::new().element(
+            Snippet::source("お寿司\n食べたい🍣")
+                .origin("<current file>")
+                .annotation(AnnotationKind::Primary.span(0..9).label("Sushi1"))
+                .annotation(AnnotationKind::Context.span(16..22).label("Sushi2")),
+        ),
+    );
+
+    let expected = str![[r#"
+error: 
+ --> <current file>:1:1
+  |
+1 | お寿司
+  | ^^^^^^ Sushi1
+2 | 食べたい🍣
+  |     ---- Sushi2
+"#]];
+
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(snippets), expected);
+}
+
+#[test]
+fn test_point_to_double_width_characters_mixed() {
+    let snippets = Level::ERROR.header("").group(
+        Group::new().element(
+            Snippet::source("こんにちは、新しいWorld!")
+                .origin("<current file>")
+                .annotation(AnnotationKind::Primary.span(18..32).label("New world")),
+        ),
+    );
+
+    let expected = str![[r#"
+error: 
+ --> <current file>:1:7
+  |
+1 | こんにちは、新しいWorld!
+  |             ^^^^^^^^^^^ New world
+"#]];
+
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(snippets), expected);
+}
+
+#[test]
+fn test_format_title() {
+    let input = Level::ERROR.header("This is a title").id("E0001");
+
+    let expected = str![r#"error[E0001]: This is a title"#];
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
+}
+
+#[test]
+fn test_format_snippet_only() {
+    let source = "This is line 1\nThis is line 2";
+    let input = Level::ERROR
+        .header("")
+        .group(Group::new().element(Snippet::<Annotation<'_>>::source(source).line_start(5402)));
+
+    let expected = str![[r#"
+error: 
+     |
+5402 | This is line 1
+5403 | This is line 2
+"#]];
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
+}
+
+#[test]
+fn test_format_snippets_continuation() {
+    let src_0 = "This is slice 1";
+    let src_1 = "This is slice 2";
+    let input = Level::ERROR.header("").group(
+        Group::new()
+            .element(
+                Snippet::<Annotation<'_>>::source(src_0)
+                    .line_start(5402)
+                    .origin("file1.rs"),
+            )
+            .element(
+                Snippet::<Annotation<'_>>::source(src_1)
+                    .line_start(2)
+                    .origin("file2.rs"),
+            ),
+    );
+    let expected = str![[r#"
+error: 
+    --> file1.rs
+     |
+5402 | This is slice 1
+     |
+    ::: file2.rs:2
+     |
+   2 | This is slice 2
+"#]];
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
+}
+
+#[test]
+fn test_format_snippet_annotation_standalone() {
+    let line_1 = "This is line 1";
+    let line_2 = "This is line 2";
+    let source = [line_1, line_2].join("\n");
+    // In line 2
+    let range = 22..24;
+    let input = Level::ERROR.header("").group(
+        Group::new().element(
+            Snippet::source(&source).line_start(5402).annotation(
+                AnnotationKind::Context
+                    .span(range.clone())
+                    .label("Test annotation"),
+            ),
+        ),
+    );
+    let expected = str![[r#"
+error: 
+     |
+5402 | This is line 1
+5403 | This is line 2
+     |        -- Test annotation
+"#]];
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
+}
+
+#[test]
+fn test_format_footer_title() {
+    let input = Level::ERROR
+        .header("")
+        .group(Group::new().element(Level::ERROR.title("This __is__ a title")));
+    let expected = str![[r#"
+error: 
+  |
+  = error: This __is__ a title
+"#]];
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
+}
+
+#[test]
+#[should_panic]
+fn test_i26() {
+    let source = "short";
+    let label = "label";
+    let input = Level::ERROR.header("").group(
+        Group::new().element(
+            Snippet::source(source).line_start(0).annotation(
+                AnnotationKind::Primary
+                    .span(0..source.len() + 2)
+                    .label(label),
+            ),
+        ),
+    );
+    let renderer = Renderer::plain();
+    let _ = renderer.render(input);
+}
+
+#[test]
+fn test_source_content() {
+    let source = "This is an example\nof content lines";
+    let input = Level::ERROR
+        .header("")
+        .group(Group::new().element(Snippet::<Annotation<'_>>::source(source).line_start(56)));
+    let expected = str![[r#"
+error: 
+   |
+56 | This is an example
+57 | of content lines
+"#]];
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
 }
 
 #[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");
+    let source = "tests";
+    let input = Level::ERROR.header("").group(
+        Group::new().element(
+            Snippet::source(source)
+                .line_start(1)
+                .annotation(AnnotationKind::Context.span(0..5).label("Example string")),
+        ),
+    );
+    let expected = str![[r#"
+error: 
+  |
+1 | tests
+  | ----- Example string
+"#]];
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
 }
 
 #[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(), " | |",);
+    let source = "tests";
+    let input = Level::ERROR.header("").group(
+        Group::new().element(
+            Snippet::source(source)
+                .line_start(1)
+                .annotation(AnnotationKind::Context.span(0..5).label("Example string"))
+                .annotation(AnnotationKind::Context.span(0..5).label("Second line")),
+        ),
+    );
+    let expected = str![[r#"
+error: 
+  |
+1 | tests
+  | -----
+  | |
+  | Example string
+  | Second line
+"#]];
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
+}
+
+#[test]
+fn test_only_source() {
+    let input = Level::ERROR
+        .header("")
+        .group(Group::new().element(Snippet::<Annotation<'_>>::source("").origin("file.rs")));
+    let expected = str![[r#"
+error: 
+ --> file.rs
+  |
+"#]];
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
 }
 
 #[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");
+    let source = "This is an example\nof content lines\n\nabc";
+    let input = Level::ERROR
+        .header("")
+        .group(Group::new().element(Snippet::<Annotation<'_>>::source(source).line_start(56)));
+    let expected = str![[r#"
+error: 
+   |
+LL | This is an example
+LL | of content lines
+LL |
+LL | abc
+"#]];
+    let renderer = Renderer::plain().anonymized_line_numbers(true);
+    assert_data_eq!(renderer.render(input), expected);
 }
 
 #[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
+fn issue_130() {
+    let input = Level::ERROR.header("dummy").group(
+        Group::new().element(
+            Snippet::source("foo\nbar\nbaz")
+                .origin("file/path")
+                .line_start(3)
+                .fold(true)
+                .annotation(AnnotationKind::Primary.span(4..11)),
+        ), // bar\nbaz
+    );
+
+    let expected = str![[r#"
+error: dummy
+ --> file/path:4:1
   |
-1 | First line
-2 | Second oops line
-  |        ^^^^ oops
-  |"#;
+4 | / bar
+5 | | baz
+  | |___^
+"#]];
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
+}
 
-    assert_eq!(DisplayList::from(snippets).to_string(), expected);
+#[test]
+fn unterminated_string_multiline() {
+    let source = "\
+a\"
+// ...
+";
+    let input = Level::ERROR.header("").group(
+        Group::new().element(
+            Snippet::source(source)
+                .origin("file/path")
+                .line_start(3)
+                .fold(true)
+                .annotation(AnnotationKind::Primary.span(0..10)),
+        ), // 1..10 works
+    );
+    let expected = str![[r#"
+error: 
+ --> file/path:3:1
+  |
+3 | / a"
+4 | | // ...
+  | |_______^
+"#]];
+    let renderer = Renderer::plain().anonymized_line_numbers(false);
+    assert_data_eq!(renderer.render(input), 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(),
-    };
+fn char_and_nl_annotate_char() {
+    let source = "a\r\nb";
+    let input = Level::ERROR.header("").group(
+        Group::new().element(
+            Snippet::source(source)
+                .origin("file/path")
+                .line_start(3)
+                .annotation(AnnotationKind::Primary.span(0..2)),
+        ), // a\r
+    );
+    let expected = str![[r#"
+error: 
+ --> file/path:3:1
+  |
+3 | a
+  | ^
+4 | b
+"#]];
+    let renderer = Renderer::plain().anonymized_line_numbers(false);
+    assert_data_eq!(renderer.render(input), expected);
+}
 
-    let expected = r#" --> <current file>:1:7
+#[test]
+fn char_eol_annotate_char() {
+    let source = "a\r\nb";
+    let input = Level::ERROR.header("").group(
+        Group::new().element(
+            Snippet::source(source)
+                .origin("file/path")
+                .line_start(3)
+                .annotation(AnnotationKind::Primary.span(0..3)),
+        ), // a\r\n
+    );
+    let expected = str![[r#"
+error: 
+ --> file/path:3:1
   |
-1 | こんにちは、世界
-  |             ^^^^ world
-  |"#;
+3 | a
+  | ^
+4 | b
+"#]];
+    let renderer = Renderer::plain().anonymized_line_numbers(false);
+    assert_data_eq!(renderer.render(input), expected);
+}
 
-    assert_eq!(DisplayList::from(snippets).to_string(), expected);
+#[test]
+fn char_eol_annotate_char_double_width() {
+    let snippets = Level::ERROR.header("").group(
+        Group::new().element(
+            Snippet::source("こん\r\nにちは\r\n世界")
+                .origin("<current file>")
+                .annotation(AnnotationKind::Primary.span(3..8)),
+        ), // ん\r\n
+    );
+
+    let expected = str![[r#"
+error: 
+ --> <current file>:1:2
+  |
+1 | こん
+  |   ^^
+2 | にちは
+3 | 世界
+"#]];
+
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(snippets), 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(),
-    };
+fn annotate_eol() {
+    let source = "a\r\nb";
+    let input = Level::ERROR.header("").group(
+        Group::new().element(
+            Snippet::source(source)
+                .origin("file/path")
+                .line_start(3)
+                .annotation(AnnotationKind::Primary.span(1..2)),
+        ), // \r
+    );
+    let expected = str![[r#"
+error: 
+ --> file/path:3:2
+  |
+3 | a
+  |  ^
+4 | b
+"#]];
+    let renderer = Renderer::plain().anonymized_line_numbers(false);
+    assert_data_eq!(renderer.render(input), expected);
+}
+
+#[test]
+fn annotate_eol2() {
+    let source = "a\r\nb";
+    let input = Level::ERROR.header("").group(
+        Group::new().element(
+            Snippet::source(source)
+                .origin("file/path")
+                .line_start(3)
+                .annotation(AnnotationKind::Primary.span(1..3)),
+        ), // \r\n
+    );
+    let expected = str![[r#"
+error: 
+ --> file/path:3:2
+  |
+3 | a
+  |  ^
+4 | b
+"#]];
+    let renderer = Renderer::plain().anonymized_line_numbers(false);
+    assert_data_eq!(renderer.render(input), expected);
+}
 
-    let expected = r#" --> <current file>:1:3
+#[test]
+fn annotate_eol3() {
+    let source = "a\r\nb";
+    let input = Level::ERROR.header("").group(
+        Group::new().element(
+            Snippet::source(source)
+                .origin("file/path")
+                .line_start(3)
+                .annotation(AnnotationKind::Primary.span(2..3)),
+        ), // \n
+    );
+    let expected = str![[r#"
+error: 
+ --> file/path:3:3
   |
-1 |   おはよう
+3 | a
+  |  ^
+4 | b
+"#]];
+    let renderer = Renderer::plain().anonymized_line_numbers(false);
+    assert_data_eq!(renderer.render(input), expected);
+}
+
+#[test]
+fn annotate_eol4() {
+    let source = "a\r\nb";
+    let input = Level::ERROR.header("").group(
+        Group::new().element(
+            Snippet::source(source)
+                .origin("file/path")
+                .line_start(3)
+                .annotation(AnnotationKind::Primary.span(2..2)),
+        ), // \n
+    );
+    let expected = str![[r#"
+error: 
+ --> file/path:3:3
+  |
+3 | a
+  |  ^
+4 | b
+"#]];
+    let renderer = Renderer::plain().anonymized_line_numbers(false);
+    assert_data_eq!(renderer.render(input), expected);
+}
+
+#[test]
+fn annotate_eol_double_width() {
+    let snippets = Level::ERROR.header("").group(
+        Group::new().element(
+            Snippet::source("こん\r\nにちは\r\n世界")
+                .origin("<current file>")
+                .annotation(AnnotationKind::Primary.span(7..8)),
+        ), // \n
+    );
+
+    let expected = str![[r#"
+error: 
+ --> <current file>:1:4
+  |
+1 | こん
+  |     ^
+2 | にちは
+3 | 世界
+"#]];
+
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(snippets), expected);
+}
+
+#[test]
+fn multiline_eol_start() {
+    let source = "a\r\nb";
+    let input = Level::ERROR.header("").group(
+        Group::new().element(
+            Snippet::source(source)
+                .origin("file/path")
+                .line_start(3)
+                .annotation(AnnotationKind::Primary.span(1..4)),
+        ), // \r\nb
+    );
+    let expected = str![[r#"
+error: 
+ --> file/path:3:2
+  |
+3 |   a
+  |  __^
+4 | | b
+  | |_^
+"#]];
+    let renderer = Renderer::plain().anonymized_line_numbers(false);
+    assert_data_eq!(renderer.render(input), expected);
+}
+
+#[test]
+fn multiline_eol_start2() {
+    let source = "a\r\nb";
+    let input = Level::ERROR.header("").group(
+        Group::new().element(
+            Snippet::source(source)
+                .origin("file/path")
+                .line_start(3)
+                .annotation(AnnotationKind::Primary.span(2..4)),
+        ), // \nb
+    );
+    let expected = str![[r#"
+error: 
+ --> file/path:3:3
+  |
+3 |   a
+  |  __^
+4 | | b
+  | |_^
+"#]];
+    let renderer = Renderer::plain().anonymized_line_numbers(false);
+    assert_data_eq!(renderer.render(input), expected);
+}
+
+#[test]
+fn multiline_eol_start3() {
+    let source = "a\nb";
+    let input = Level::ERROR.header("").group(
+        Group::new().element(
+            Snippet::source(source)
+                .origin("file/path")
+                .line_start(3)
+                .annotation(AnnotationKind::Primary.span(1..3)),
+        ), // \nb
+    );
+    let expected = str![[r#"
+error: 
+ --> file/path:3:2
+  |
+3 |   a
+  |  __^
+4 | | b
+  | |_^
+"#]];
+    let renderer = Renderer::plain().anonymized_line_numbers(false);
+    assert_data_eq!(renderer.render(input), expected);
+}
+
+#[test]
+fn multiline_eol_start_double_width() {
+    let snippets = Level::ERROR.header("").group(
+        Group::new().element(
+            Snippet::source("こん\r\nにちは\r\n世界")
+                .origin("<current file>")
+                .annotation(AnnotationKind::Primary.span(7..11)),
+        ), // \r\nに
+    );
+
+    let expected = str![[r#"
+error: 
+ --> <current file>:1:4
+  |
+1 |   こん
   |  _____^
-2 | | ございます
-  | |______^ Good morning
-  |"#;
+2 | | にちは
+  | |__^
+3 |   世界
+"#]];
 
-    assert_eq!(DisplayList::from(snippets).to_string(), expected);
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(snippets), 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(),
-    };
+fn multiline_eol_start_eol_end() {
+    let source = "a\nb\nc";
+    let input = Level::ERROR.header("").group(
+        Group::new().element(
+            Snippet::source(source)
+                .origin("file/path")
+                .line_start(3)
+                .annotation(AnnotationKind::Primary.span(1..4)),
+        ), // \nb\n
+    );
+    let expected = str![[r#"
+error: 
+ --> file/path:3:2
+  |
+3 |   a
+  |  __^
+4 | | b
+  | |__^
+5 |   c
+"#]];
+    let renderer = Renderer::plain().anonymized_line_numbers(false);
+    assert_data_eq!(renderer.render(input), expected);
+}
 
-    let expected = r#" --> <current file>:1:1
+#[test]
+fn multiline_eol_start_eol_end2() {
+    let source = "a\r\nb\r\nc";
+    let input = Level::ERROR.header("").group(
+        Group::new().element(
+            Snippet::source(source)
+                .origin("file/path")
+                .line_start(3)
+                .annotation(AnnotationKind::Primary.span(2..5)),
+        ), // \nb\r
+    );
+    let expected = str![[r#"
+error: 
+ --> file/path:3:3
   |
-1 | お寿司
-  | ^^^^^^ Sushi1
-2 | 食べたい🍣
-  |     ---- note: Sushi2
-  |"#;
+3 |   a
+  |  __^
+4 | | b
+  | |__^
+5 |   c
+"#]];
+    let renderer = Renderer::plain().anonymized_line_numbers(false);
+    assert_data_eq!(renderer.render(input), expected);
+}
 
-    assert_eq!(DisplayList::from(snippets).to_string(), expected);
+#[test]
+fn multiline_eol_start_eol_end3() {
+    let source = "a\r\nb\r\nc";
+    let input = Level::ERROR.header("").group(
+        Group::new().element(
+            Snippet::source(source)
+                .origin("file/path")
+                .line_start(3)
+                .annotation(AnnotationKind::Primary.span(2..6)),
+        ), // \nb\r\n
+    );
+    let expected = str![[r#"
+error: 
+ --> file/path:3:3
+  |
+3 |   a
+  |  __^
+4 | | b
+  | |__^
+5 |   c
+"#]];
+    let renderer = Renderer::plain().anonymized_line_numbers(false);
+    assert_data_eq!(renderer.render(input), 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(),
+fn multiline_eol_start_eof_end() {
+    let source = "a\r\nb";
+    let input = Level::ERROR.header("").group(
+        Group::new().element(
+            Snippet::source(source)
+                .origin("file/path")
+                .line_start(3)
+                .annotation(AnnotationKind::Primary.span(1..5)),
+        ), // \r\nb(EOF)
+    );
+    let expected = str![[r#"
+error: 
+ --> file/path:3:2
+  |
+3 |   a
+  |  __^
+4 | | b
+  | |__^
+"#]];
+    let renderer = Renderer::plain().anonymized_line_numbers(false);
+    assert_data_eq!(renderer.render(input), expected);
+}
+
+#[test]
+fn multiline_eol_start_eof_end_double_width() {
+    let source = "ん\r\nに";
+    let input = Level::ERROR.header("").group(
+        Group::new().element(
+            Snippet::source(source)
+                .origin("file/path")
+                .line_start(3)
+                .annotation(AnnotationKind::Primary.span(3..9)),
+        ), // \r\nに(EOF)
+    );
+    let expected = str![[r#"
+error: 
+ --> file/path:3:2
+  |
+3 |   ん
+  |  ___^
+4 | | に
+  | |___^
+"#]];
+    let renderer = Renderer::plain().anonymized_line_numbers(false);
+    assert_data_eq!(renderer.render(input), expected);
+}
+
+#[test]
+fn two_single_line_same_line() {
+    let source = r#"bar = { version = "0.1.0", optional = true }"#;
+    let input = Level::ERROR.header("unused optional dependency").group(
+        Group::new().element(
+            Snippet::source(source)
+                .origin("Cargo.toml")
+                .line_start(4)
+                .annotation(
+                    AnnotationKind::Primary
+                        .span(0..3)
+                        .label("I need this to be really long so I can test overlaps"),
+                )
+                .annotation(
+                    AnnotationKind::Context
+                        .span(27..42)
+                        .label("This should also be long but not too long"),
+                ),
+        ),
+    );
+    let expected = str![[r#"
+error: unused optional dependency
+ --> Cargo.toml:4:1
+  |
+4 | bar = { version = "0.1.0", optional = true }
+  | ^^^                        --------------- This should also be long but not too long
+  | |
+  | I need this to be really long so I can test overlaps
+"#]];
+    let renderer = Renderer::plain().anonymized_line_numbers(false);
+    assert_data_eq!(renderer.render(input), expected);
+}
+
+#[test]
+fn multi_and_single() {
+    let source = r#"bar = { version = "0.1.0", optional = true }
+this is another line
+so is this
+bar = { version = "0.1.0", optional = true }
+"#;
+    let input = Level::ERROR.header("unused optional dependency").group(
+        Group::new().element(
+            Snippet::source(source)
+                .line_start(4)
+                .annotation(
+                    AnnotationKind::Primary
+                        .span(41..119)
+                        .label("I need this to be really long so I can test overlaps"),
+                )
+                .annotation(
+                    AnnotationKind::Context
+                        .span(27..42)
+                        .label("This should also be long but not too long"),
+                ),
+        ),
+    );
+    let expected = str![[r#"
+error: unused optional dependency
+  |
+4 |   bar = { version = "0.1.0", optional = true }
+  |  ____________________________--------------^
+  | |                            |
+  | |                            This should also be long but not too long
+5 | | this is another line
+6 | | so is this
+7 | | bar = { version = "0.1.0", optional = true }
+  | |__________________________________________^ I need this to be really long so I can test overlaps
+"#]];
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
+}
+
+#[test]
+fn two_multi_and_single() {
+    let source = r#"bar = { version = "0.1.0", optional = true }
+this is another line
+so is this
+bar = { version = "0.1.0", optional = true }
+"#;
+    let input = Level::ERROR.header("unused optional dependency").group(
+        Group::new().element(
+            Snippet::source(source)
+                .line_start(4)
+                .annotation(
+                    AnnotationKind::Primary
+                        .span(41..119)
+                        .label("I need this to be really long so I can test overlaps"),
+                )
+                .annotation(
+                    AnnotationKind::Primary
+                        .span(8..102)
+                        .label("I need this to be really long so I can test overlaps"),
+                )
+                .annotation(
+                    AnnotationKind::Context
+                        .span(27..42)
+                        .label("This should also be long but not too long"),
+                ),
+        ),
+    );
+    let expected = str![[r#"
+error: unused optional dependency
+  |
+4 |    bar = { version = "0.1.0", optional = true }
+  |   _________^__________________--------------^
+  |  |         |                  |
+  |  |_________|                  This should also be long but not too long
+  | ||
+5 | || this is another line
+6 | || so is this
+7 | || bar = { version = "0.1.0", optional = true }
+  | ||_________________________^________________^ I need this to be really long so I can test overlaps
+  | |__________________________|
+  |                            I need this to be really long so I can test overlaps
+"#]];
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
+}
+
+#[test]
+fn three_multi_and_single() {
+    let source = r#"bar = { version = "0.1.0", optional = true }
+this is another line
+so is this
+bar = { version = "0.1.0", optional = true }
+this is another line
+"#;
+    let input = Level::ERROR.header("unused optional dependency").group(
+        Group::new().element(
+            Snippet::source(source)
+                .line_start(4)
+                .annotation(
+                    AnnotationKind::Primary
+                        .span(41..119)
+                        .label("I need this to be really long so I can test overlaps"),
+                )
+                .annotation(
+                    AnnotationKind::Primary
+                        .span(8..102)
+                        .label("I need this to be really long so I can test overlaps"),
+                )
+                .annotation(
+                    AnnotationKind::Primary
+                        .span(48..126)
+                        .label("I need this to be really long so I can test overlaps"),
+                )
+                .annotation(
+                    AnnotationKind::Context
+                        .span(27..42)
+                        .label("This should also be long but not too long"),
+                ),
+        ),
+    );
+    let expected = str![[r#"
+error: unused optional dependency
+  |
+4 |     bar = { version = "0.1.0", optional = true }
+  |   __________^__________________--------------^
+  |  |          |                  |
+  |  |__________|                  This should also be long but not too long
+  | ||
+5 | ||  this is another line
+  | || ____^
+6 | ||| so is this
+7 | ||| bar = { version = "0.1.0", optional = true }
+  | |||_________________________^________________^ I need this to be really long so I can test overlaps
+  | |_|_________________________|
+  |   |                         I need this to be really long so I can test overlaps
+8 |   | this is another line
+  |   |____^ I need this to be really long so I can test overlaps
+"#]];
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
+}
+
+#[test]
+fn origin_correct_start_line() {
+    let source = "aaa\nbbb\nccc\nddd\n";
+    let input = Level::ERROR.header("title").group(
+        Group::new().element(
+            Snippet::source(source)
+                .origin("origin.txt")
+                .fold(false)
+                .annotation(AnnotationKind::Primary.span(8..8 + 3).label("annotation")),
+        ),
+    );
+
+    let expected = str![[r#"
+error: title
+ --> origin.txt:3:1
+  |
+1 | aaa
+2 | bbb
+3 | ccc
+  | ^^^ annotation
+4 | ddd
+"#]];
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
+}
+
+#[test]
+fn origin_correct_mid_line() {
+    let source = "aaa\nbbb\nccc\nddd\n";
+    let input = Level::ERROR.header("title").group(
+        Group::new().element(
+            Snippet::source(source)
+                .origin("origin.txt")
+                .fold(false)
+                .annotation(
+                    AnnotationKind::Primary
+                        .span(8 + 1..8 + 3)
+                        .label("annotation"),
+                ),
+        ),
+    );
+
+    let expected = str![[r#"
+error: title
+ --> origin.txt:3:2
+  |
+1 | aaa
+2 | bbb
+3 | ccc
+  |  ^^ annotation
+4 | ddd
+"#]];
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
+}
+
+#[test]
+fn two_suggestions_same_span() {
+    let source = r#"    A.foo();"#;
+    let input_new = Level::ERROR
+        .header("expected value, found enum `A`")
+        .id("E0423")
+        .group(
+            Group::new().element(
+                Snippet::source(source)
+                    .fold(true)
+                    .annotation(AnnotationKind::Primary.span(4..5)),
+            ),
+        )
+        .group(
+            Group::new()
+                .element(
+                    Level::HELP
+                        .title("you might have meant to use one of the following enum variants"),
+                )
+                .element(
+                    Snippet::source(source)
+                        .fold(true)
+                        .patch(Patch::new(4..5, "(A::Tuple())")),
+                )
+                .element(
+                    Snippet::source(source)
+                        .fold(true)
+                        .patch(Patch::new(4..5, "A::Unit")),
+                ),
+        );
+
+    let expected = str![[r#"
+error[E0423]: expected value, found enum `A`
+   |
+LL |     A.foo();
+   |     ^
+   |
+help: you might have meant to use one of the following enum variants
+   |
+LL -     A.foo();
+LL +     (A::Tuple()).foo();
+   |
+LL |     A::Unit.foo();
+   |      ++++++
+"#]];
+    let renderer = Renderer::plain().anonymized_line_numbers(true);
+    assert_data_eq!(renderer.render(input_new), expected);
+}
+
+#[test]
+fn two_suggestions_same_span2() {
+    let source = r#"
+mod banana {
+    pub struct Chaenomeles;
+
+    pub trait Apple {
+        fn pick(&self) {}
+    }
+    impl Apple for Chaenomeles {}
+
+    pub trait Peach {
+        fn pick(&self, a: &mut ()) {}
+    }
+    impl<Mango: Peach> Peach for Box<Mango> {}
+    impl Peach for Chaenomeles {}
+}
+
+fn main() {
+    banana::Chaenomeles.pick()
+}"#;
+    let input_new =
+        Level::ERROR
+            .header("no method named `pick` found for struct `Chaenomeles` in the current scope")
+            .id("E0599")
+            .group(
+                Group::new().element(
+                    Snippet::source(source)
+                        .line_start(1)
+                        .fold(true)
+                        .annotation(
+                            AnnotationKind::Context
+                                .span(18..40)
+                                .label("method `pick` not found for this struct"),
+                        )
+                        .annotation(
+                            AnnotationKind::Primary
+                                .span(318..322)
+                                .label("method not found in `Chaenomeles`"),
+                        ),
+                ),
+            )
+            .group(
+                Group::new()
+                    .element(Level::HELP.title(
+                        "the following traits which provide `pick` are implemented but not in scope; perhaps you want to import one of them",
+                    ))
+                    .element(
+                        Snippet::source(source)
+                            .fold(true)
+                            .patch(Patch::new(1..1, "use banana::Apple;\n")),
+                    )
+                    .element(
+                        Snippet::source(source)
+                            .fold(true)
+                            .patch(Patch::new(1..1, "use banana::Peach;\n")),
+                    ),
+            );
+    let expected = str![[r#"
+error[E0599]: no method named `pick` found for struct `Chaenomeles` in the current scope
+   |
+LL |     pub struct Chaenomeles;
+   |     ---------------------- method `pick` not found for this struct
+...
+LL |     banana::Chaenomeles.pick()
+   |                         ^^^^ method not found in `Chaenomeles`
+   |
+help: the following traits which provide `pick` are implemented but not in scope; perhaps you want to import one of them
+   |
+LL + use banana::Apple;
+   |
+LL + use banana::Peach;
+   |
+"#]];
+    let renderer = Renderer::plain().anonymized_line_numbers(true);
+    assert_data_eq!(renderer.render(input_new), expected);
+}
+
+#[test]
+fn single_line_non_overlapping_suggestions() {
+    let source = r#"    A.foo();"#;
+
+    let input_new = Level::ERROR
+        .header("expected value, found enum `A`")
+        .id("E0423")
+        .group(
+            Group::new().element(
+                Snippet::source(source)
+                    .fold(true)
+                    .line_start(1)
+                    .annotation(AnnotationKind::Primary.span(4..5)),
+            ),
+        )
+        .group(
+            Group::new()
+                .element(Level::HELP.title("make these changes and things will work"))
+                .element(
+                    Snippet::source(source)
+                        .fold(true)
+                        .fold(true)
+                        .patch(Patch::new(4..5, "(A::Tuple())"))
+                        .patch(Patch::new(6..9, "bar")),
+                ),
+        );
+
+    let expected = str![[r#"
+error[E0423]: expected value, found enum `A`
+   |
+LL |     A.foo();
+   |     ^
+   |
+help: make these changes and things will work
+   |
+LL -     A.foo();
+LL +     (A::Tuple()).bar();
+   |
+"#]];
+    let renderer = Renderer::plain().anonymized_line_numbers(true);
+    assert_data_eq!(renderer.render(input_new), expected);
+}
+
+#[test]
+fn single_line_non_overlapping_suggestions2() {
+    let source = r#"    ThisIsVeryLong.foo();"#;
+    let input_new = Level::ERROR
+        .header("Found `ThisIsVeryLong`")
+        .id("E0423")
+        .group(
+            Group::new().element(
+                Snippet::source(source)
+                    .fold(true)
+                    .line_start(1)
+                    .annotation(AnnotationKind::Primary.span(4..18)),
+            ),
+        )
+        .group(
+            Group::new()
+                .element(Level::HELP.title("make these changes and things will work"))
+                .element(
+                    Snippet::source(source)
+                        .fold(true)
+                        .fold(true)
+                        .patch(Patch::new(4..18, "(A::Tuple())"))
+                        .patch(Patch::new(19..22, "bar")),
+                ),
+        );
+
+    let expected = str![[r#"
+error[E0423]: Found `ThisIsVeryLong`
+   |
+LL |     ThisIsVeryLong.foo();
+   |     ^^^^^^^^^^^^^^
+   |
+help: make these changes and things will work
+   |
+LL -     ThisIsVeryLong.foo();
+LL +     (A::Tuple()).bar();
+   |
+"#]];
+    let renderer = Renderer::plain().anonymized_line_numbers(true);
+    assert_data_eq!(renderer.render(input_new), expected);
+}
+
+#[test]
+fn multiple_replacements() {
+    let source = r#"
+    let y = || {
+        self.bar();
     };
+    self.qux();
+    y();
+"#;
+
+    let input_new = Level::ERROR
+        .header("cannot borrow `*self` as mutable because it is also borrowed as immutable")
+        .id("E0502")
+        .group(
+            Group::new().element(
+                Snippet::source(source)
+                    .line_start(1)
+                    .fold(true)
+                    .annotation(
+                        AnnotationKind::Primary
+                            .span(49..59)
+                            .label("mutable borrow occurs here"),
+                    )
+                    .annotation(
+                        AnnotationKind::Primary
+                            .span(13..15)
+                            .label("immutable borrow occurs here"),
+                    )
+                    .annotation(
+                        AnnotationKind::Primary
+                            .span(26..30)
+                            .label("first borrow occurs due to use of `*self` in closure"),
+                    )
+                    .annotation(
+                        AnnotationKind::Primary
+                            .span(65..66)
+                            .label("immutable borrow later used here"),
+                    ),
+            ),
+        )
+        .group(
+            Group::new()
+                .element(
+                    Level::HELP
+                        .title("try explicitly pass `&Self` into the Closure as an argument"),
+                )
+                .element(
+                    Snippet::source(source)
+                        .fold(true)
+                        .patch(Patch::new(14..14, "this: &Self"))
+                        .patch(Patch::new(26..30, "this"))
+                        .patch(Patch::new(66..68, "(self)")),
+                ),
+        );
+    let expected = str![[r#"
+error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
+   |
+LL |     let y = || {
+   |             ^^ immutable borrow occurs here
+LL |         self.bar();
+   |         ^^^^ first borrow occurs due to use of `*self` in closure
+LL |     };
+LL |     self.qux();
+   |     ^^^^^^^^^^ mutable borrow occurs here
+LL |     y();
+   |     ^ immutable borrow later used here
+   |
+help: try explicitly pass `&Self` into the Closure as an argument
+   |
+LL ~     let y = |this: &Self| {
+LL ~         this.bar();
+LL |     };
+LL |     self.qux();
+LL ~     y(self);
+   |
+"#]];
+    let renderer = Renderer::plain().anonymized_line_numbers(true);
+    assert_data_eq!(renderer.render(input_new), expected);
+}
+
+#[test]
+fn multiple_replacements2() {
+    let source = r#"
+fn test1() {
+    let mut chars = "Hello".chars();
+    for _c in chars.by_ref() {
+        chars.next();
+    }
+}
+
+fn main() {
+    test1();
+}"#;
+
+    let input_new = Level::ERROR
+        .header("cannot borrow `chars` as mutable more than once at a time")
+        .id("E0499")
+        .group(
+            Group::new().element(
+                Snippet::source(source)
+                    .line_start(1)
+                    .fold(true)
+                    .annotation(
+                        AnnotationKind::Context
+                            .span(65..70)
+                            .label("first mutable borrow occurs here"),
+                    )
+                    .annotation(
+                        AnnotationKind::Primary
+                            .span(90..95)
+                            .label("second mutable borrow occurs here"),
+                    )
+                    .annotation(
+                        AnnotationKind::Context
+                            .span(65..79)
+                            .label("first borrow later used here"),
+                    ),
+            ),
+        )
+        .group(
+            Group::new()
+                .element(
+                    Level::HELP
+                        .title("if you want to call `next` on a iterator within the loop, consider using `while let`")
+                )
+                .element(
+                    Snippet::source(source)
+                        .fold(true)
+                        .patch(Patch::new(
+                            55..59,
+                            "let iter = chars.by_ref();\n    while let Some(",
+                        ))
+                        .patch(Patch::new(61..79, ") = iter.next()"))
+                        .patch(Patch::new(90..95, "iter")),
+                ),
+        );
+
+    let expected = str![[r#"
+error[E0499]: cannot borrow `chars` as mutable more than once at a time
+   |
+LL |     for _c in chars.by_ref() {
+   |               --------------
+   |               |
+   |               first mutable borrow occurs here
+   |               first borrow later used here
+LL |         chars.next();
+   |         ^^^^^ second mutable borrow occurs here
+   |
+help: if you want to call `next` on a iterator within the loop, consider using `while let`
+   |
+LL ~     let iter = chars.by_ref();
+LL ~     while let Some(_c) = iter.next() {
+LL ~         iter.next();
+   |
+"#]];
+    let renderer = Renderer::plain().anonymized_line_numbers(true);
+    assert_data_eq!(renderer.render(input_new), expected);
+}
+
+#[test]
+fn diff_format() {
+    let source = r#"
+use st::cell::Cell;
+
+mod bar {
+    pub fn bar() { bar::baz(); }
+
+    fn baz() {}
+}
+
+use bas::bar;
+
+struct Foo {
+    bar: st::cell::Cell<bool>
+}
+
+fn main() {}"#;
+
+    let input_new = Level::ERROR
+        .header("failed to resolve: use of undeclared crate or module `st`")
+        .id("E0433")
+        .group(
+            Group::new().element(
+                Snippet::source(source).line_start(1).fold(true).annotation(
+                    AnnotationKind::Primary
+                        .span(122..124)
+                        .label("use of undeclared crate or module `st`"),
+                ),
+            ),
+        )
+        .group(
+            Group::new()
+                .element(Level::HELP.title("there is a crate or module with a similar name"))
+                .element(
+                    Snippet::source(source)
+                        .fold(true)
+                        .patch(Patch::new(122..124, "std")),
+                ),
+        )
+        .group(
+            Group::new()
+                .element(Level::HELP.title("consider importing this module"))
+                .element(
+                    Snippet::source(source)
+                        .fold(true)
+                        .patch(Patch::new(1..1, "use std::cell;\n")),
+                ),
+        )
+        .group(
+            Group::new()
+                .element(Level::HELP.title("if you import `cell`, refer to it directly"))
+                .element(
+                    Snippet::source(source)
+                        .fold(true)
+                        .patch(Patch::new(122..126, "")),
+                ),
+        );
+    let expected = str![[r#"
+error[E0433]: failed to resolve: use of undeclared crate or module `st`
+   |
+LL |     bar: st::cell::Cell<bool>
+   |          ^^ use of undeclared crate or module `st`
+   |
+help: there is a crate or module with a similar name
+   |
+LL |     bar: std::cell::Cell<bool>
+   |            +
+help: consider importing this module
+   |
+LL + use std::cell;
+   |
+help: if you import `cell`, refer to it directly
+   |
+LL -     bar: st::cell::Cell<bool>
+LL +     bar: cell::Cell<bool>
+   |
+"#]];
+
+    let renderer = Renderer::plain().anonymized_line_numbers(true);
+    assert_data_eq!(renderer.render(input_new), expected);
+}
+
+#[test]
+fn multiline_removal() {
+    let source = r#"
+struct Wrapper<T>(T);
+
+fn foo<T>(foo: Wrapper<T>)
+
+where
+    T
+    :
+    ?
+    Sized
+{
+    //
+}
+
+fn main() {}"#;
+
+    let input_new = Level::ERROR
+        .header("the size for values of type `T` cannot be known at compilation time")
+        .id("E0277")
+        .group(
+            Group::new().element(
+                Snippet::source(source)
+                    .line_start(1)
+                    .fold(true)
+                    .annotation(
+                        AnnotationKind::Primary
+                            .span(39..49)
+                            .label("doesn't have a size known at compile-time"),
+                    )
+                    .annotation(
+                        AnnotationKind::Context
+                            .span(31..32)
+                            .label("this type parameter needs to be `Sized`"),
+                    ),
+            ),
+        )
+        .group(
+            Group::new()
+                .element(Level::HELP.title(
+                    "consider removing the `?Sized` bound to make the type parameter `Sized`",
+                ))
+                .element(
+                    Snippet::source(source)
+                        .fold(true)
+                        .patch(Patch::new(52..86, "")),
+                ),
+        );
+    let expected = str![[r#"
+error[E0277]: the size for values of type `T` cannot be known at compilation time
+   |
+LL | fn foo<T>(foo: Wrapper<T>)
+   |        -       ^^^^^^^^^^ doesn't have a size known at compile-time
+   |        |
+   |        this type parameter needs to be `Sized`
+   |
+help: consider removing the `?Sized` bound to make the type parameter `Sized`
+   |
+LL - where
+LL -     T
+LL -     :
+LL -     ?
+LL -     Sized
+   |
+"#]];
+    let renderer = Renderer::plain().anonymized_line_numbers(true);
+    assert_data_eq!(renderer.render(input_new), expected);
+}
+
+#[test]
+fn multiline_replacement() {
+    let source = r#"
+struct Wrapper<T>(T);
+
+fn foo<T>(foo: Wrapper<T>)
+
+and where
+    T
+    :
+    ?
+    Sized
+{
+    //
+}
+
+fn main() {}"#;
+    let input_new = Level::ERROR
+        .header("the size for values of type `T` cannot be known at compilation time")
+        .id("E0277")
+        .group(Group::new().element(Snippet::source(source)
+            .line_start(1)
+            .origin("$DIR/removal-of-multiline-trait-bound-in-where-clause.rs")
+            .fold(true)
+            .annotation(
+                AnnotationKind::Primary
+                    .span(39..49)
+                    .label("doesn't have a size known at compile-time"),
+            )
+            .annotation(
+                AnnotationKind::Context
+                    .span(31..32)
+                    .label("this type parameter needs to be `Sized`"),
+            )))
+        .group(Group::new().element(
+            Level::NOTE
+                .title("required by an implicit `Sized` bound in `Wrapper`")
+        ).element(
+            Snippet::source(source)
+                .line_start(1)
+                .origin("$DIR/removal-of-multiline-trait-bound-in-where-clause.rs")
+                .fold(true)
+                .annotation(
+                    AnnotationKind::Primary
+                        .span(16..17)
+                        .label("required by the implicit `Sized` requirement on this type parameter in `Wrapper`"),
+                )
+        ))
+        .group(Group::new().element(
+            Level::HELP
+                .title("you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box<T>`")
+            )
+            .element(
+            Snippet::source(source)
+                .line_start(1)
+                .origin("$DIR/removal-of-multiline-trait-bound-in-where-clause.rs")
+                .fold(true)
+                .annotation(
+                    AnnotationKind::Primary
+                        .span(16..17)
+                        .label("this could be changed to `T: ?Sized`..."),
+                )
+                .annotation(
+                    AnnotationKind::Context
+                        .span(19..20)
+                        .label("...if indirection were used here: `Box<T>`"),
+                )
+
+        ))
+        .group(Group::new().element(
+            Level::HELP
+                .title("consider removing the `?Sized` bound to make the type parameter `Sized`")
+        ).element(
+            Snippet::source(source)
+                .fold(true)
+                .patch(Patch::new(56..90, ""))
+                .patch(Patch::new(90..90, "+ Send"))
+                ,
+        ));
+    let expected = str![[r#"
+error[E0277]: the size for values of type `T` cannot be known at compilation time
+  --> $DIR/removal-of-multiline-trait-bound-in-where-clause.rs:4:16
+   |
+LL | fn foo<T>(foo: Wrapper<T>)
+   |        -       ^^^^^^^^^^ doesn't have a size known at compile-time
+   |        |
+   |        this type parameter needs to be `Sized`
+   |
+note: required by an implicit `Sized` bound in `Wrapper`
+  --> $DIR/removal-of-multiline-trait-bound-in-where-clause.rs:2:16
+   |
+LL | struct Wrapper<T>(T);
+   |                ^ required by the implicit `Sized` requirement on this type parameter in `Wrapper`
+help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box<T>`
+  --> $DIR/removal-of-multiline-trait-bound-in-where-clause.rs:2:16
+   |
+LL | struct Wrapper<T>(T);
+   |                ^  - ...if indirection were used here: `Box<T>`
+   |                |
+   |                this could be changed to `T: ?Sized`...
+help: consider removing the `?Sized` bound to make the type parameter `Sized`
+   |
+LL ~ and 
+LL ~ + Send{
+   |
+"#]];
+    let renderer = Renderer::plain().anonymized_line_numbers(true);
+    assert_data_eq!(renderer.render(input_new), expected);
+}
+
+#[test]
+fn multiline_removal2() {
+    let source = r#"
+cargo
+fuzzy
+pizza
+jumps
+crazy
+quack
+zappy
+"#;
+
+    let input_new = Level::ERROR
+        .header("the size for values of type `T` cannot be known at compilation time")
+        .id("E0277")
+        .group(
+            Group::new()
+                .element(Level::HELP.title(
+                    "consider removing the `?Sized` bound to make the type parameter `Sized`",
+                ))
+                .element(
+                    Snippet::source(source)
+                        .line_start(7)
+                        .fold(true)
+                        .patch(Patch::new(3..21, ""))
+                        .patch(Patch::new(22..40, "")),
+                ),
+        );
+    let expected = str![[r#"
+error[E0277]: the size for values of type `T` cannot be known at compilation time
+   |
+help: consider removing the `?Sized` bound to make the type parameter `Sized`
+   |
+8  - cargo
+9  - fuzzy
+10 - pizza
+11 - jumps
+8  + campy
+   |
+"#]];
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input_new), expected);
+}
+
+#[test]
+fn e0271() {
+    let source = r#"
+trait Future {
+    type Error;
+}
 
-    let expected = r#" --> <current file>:1:7
+impl<T, E> Future for Result<T, E> {
+    type Error = E;
+}
+
+impl<T> Future for Option<T> {
+    type Error = ();
+}
+
+struct Foo;
+
+fn foo() -> Box<dyn Future<Error=Foo>> {
+    Box::new(
+        Ok::<_, ()>(
+            Err::<(), _>(
+                Ok::<_, ()>(
+                    Err::<(), _>(
+                        Ok::<_, ()>(
+                            Err::<(), _>(Some(5))
+                        )
+                    )
+                )
+            )
+        )
+    )
+}
+fn main() {
+}
+"#;
+
+    let input_new = Level::ERROR
+        .header("type mismatch resolving `<Result<Result<(), Result<Result<(), Result<Result<(), Option<{integer}>>, ...>>, ...>>, ...> as Future>::Error == Foo`")
+        .id("E0271")
+        .group(Group::new().element(Snippet::source(source)
+            .line_start(4)
+            .origin("$DIR/E0271.rs")
+            .fold(true)
+            .annotation(
+                AnnotationKind::Primary
+                    .span(208..510)
+                    .label("type mismatch resolving `<Result<Result<(), Result<Result<(), ...>, ...>>, ...> as Future>::Error == Foo`"),
+            )))
+        .group(Group::new().element(
+            Level::NOTE.title("expected this to be `Foo`")
+        ).element(
+            Snippet::source(source)
+                .line_start(4)
+                .origin("$DIR/E0271.rs")
+                .fold(true)
+                .annotation(AnnotationKind::Primary.span(89..90))
+        ).element(
+            Level::NOTE
+                .title("required for the cast from `Box<Result<Result<(), Result<Result<(), Result<Result<(), Option<{integer}>>, ()>>, ()>>, ()>>` to `Box<(dyn Future<Error = Foo> + 'static)>`")
+                ,
+        ));
+
+    let expected = str![[r#"
+error[E0271]: type mismatch resolving `<Result<Result<(), Result<Result<(), Result<Result<(), Option<{integer}>>, ...>>, ...>>, ...> as Future>::Error == Foo`
+   ╭▸ $DIR/E0271.rs:20:5
+   │
+LL │ ┏     Box::new(
+LL │ ┃         Ok::<_, ()>(
+LL │ ┃             Err::<(), _>(
+LL │ ┃                 Ok::<_, ()>(
+   ‡ ┃
+LL │ ┃     )
+   │ ┗━━━━━┛ type mismatch resolving `<Result<Result<(), Result<Result<(), ...>, ...>>, ...> as Future>::Error == Foo`
+   ╰╴
+note: expected this to be `Foo`
+   ╭▸ $DIR/E0271.rs:10:18
+   │
+LL │     type Error = E;
+   │                  ━
+   ╰ note: required for the cast from `Box<Result<Result<(), Result<Result<(), Result<Result<(), Option<{integer}>>, ()>>, ()>>, ()>>` to `Box<(dyn Future<Error = Foo> + 'static)>`
+"#]];
+    let renderer = Renderer::plain()
+        .term_width(40)
+        .theme(OutputTheme::Unicode)
+        .anonymized_line_numbers(true);
+    assert_data_eq!(renderer.render(input_new), expected);
+}
+
+#[test]
+fn e0271_2() {
+    let source = r#"
+trait Future {
+    type Error;
+}
+
+impl<T, E> Future for Result<T, E> {
+    type Error = E;
+}
+
+impl<T> Future for Option<T> {
+    type Error = ();
+}
+
+struct Foo;
+
+fn foo() -> Box<dyn Future<Error=Foo>> {
+    Box::new(
+        Ok::<_, ()>(
+            Err::<(), _>(
+                Ok::<_, ()>(
+                    Err::<(), _>(
+                        Ok::<_, ()>(
+                            Err::<(), _>(Some(5))
+                        )
+                    )
+                )
+            )
+        )
+    )
+}
+fn main() {
+}
+"#;
+
+    let input_new = Level::ERROR
+        .header("type mismatch resolving `<Result<Result<(), Result<Result<(), Result<Result<(), Option<{integer}>>, ...>>, ...>>, ...> as Future>::Error == Foo`")
+        .id("E0271")
+        .group(Group::new().element(Snippet::source(source)
+            .line_start(4)
+            .origin("$DIR/E0271.rs")
+            .fold(true)
+            .annotation(
+                AnnotationKind::Primary
+                    .span(208..510)
+                    .label("type mismatch resolving `<Result<Result<(), Result<Result<(), ...>, ...>>, ...> as Future>::Error == Foo`"),
+            )))
+        .group(Group::new().element(
+            Level::NOTE.title("expected this to be `Foo`")
+        ).element(
+            Snippet::source(source)
+                .line_start(4)
+                .origin("$DIR/E0271.rs")
+                .fold(true)
+                .annotation(AnnotationKind::Primary.span(89..90))
+        ).element(
+            Level::NOTE
+                .title("required for the cast from `Box<Result<Result<(), Result<Result<(), Result<Result<(), Option<{integer}>>, ()>>, ()>>, ()>>` to `Box<(dyn Future<Error = Foo> + 'static)>`")
+        ).element(
+            Level::NOTE.title("a second note"),
+        ));
+
+    let expected = str![[r#"
+error[E0271]: type mismatch resolving `<Result<Result<(), Result<Result<(), Result<Result<(), Option<{integer}>>, ...>>, ...>>, ...> as Future>::Error == Foo`
+   ╭▸ $DIR/E0271.rs:20:5
+   │
+LL │ ┏     Box::new(
+LL │ ┃         Ok::<_, ()>(
+LL │ ┃             Err::<(), _>(
+LL │ ┃                 Ok::<_, ()>(
+   ‡ ┃
+LL │ ┃     )
+   │ ┗━━━━━┛ type mismatch resolving `<Result<Result<(), Result<Result<(), ...>, ...>>, ...> as Future>::Error == Foo`
+   ╰╴
+note: expected this to be `Foo`
+   ╭▸ $DIR/E0271.rs:10:18
+   │
+LL │     type Error = E;
+   │                  ━
+   ├ note: required for the cast from `Box<Result<Result<(), Result<Result<(), Result<Result<(), Option<{integer}>>, ()>>, ()>>, ()>>` to `Box<(dyn Future<Error = Foo> + 'static)>`
+   ╰ note: a second note
+"#]];
+    let renderer = Renderer::plain()
+        .term_width(40)
+        .theme(OutputTheme::Unicode)
+        .anonymized_line_numbers(true);
+    assert_data_eq!(renderer.render(input_new), expected);
+}
+
+#[test]
+fn long_e0308() {
+    let source = r#"
+mod a {
+    // Force the "short path for unique types" machinery to trip up
+    pub struct Atype;
+    pub struct Btype;
+    pub struct Ctype;
+}
+
+mod b {
+    pub struct Atype<T, K>(T, K);
+    pub struct Btype<T, K>(T, K);
+    pub struct Ctype<T, K>(T, K);
+}
+
+use b::*;
+
+fn main() {
+    let x: Atype<
+      Btype<
+        Ctype<
+          Atype<
+            Btype<
+              Ctype<
+                Atype<
+                  Btype<
+                    Ctype<i32, i32>,
+                    i32
+                  >,
+                  i32
+                >,
+                i32
+              >,
+              i32
+            >,
+            i32
+          >,
+          i32
+        >,
+        i32
+      >,
+      i32
+    > = Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(
+        Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(
+            Ok("")
+        ))))))))))))))))))))))))))))))
+    ))))))))))))))))))))))))))))));
+    //~^^^^^ ERROR E0308
+
+    let _ = Some(Ok(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(
+        Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(
+            Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(Some(
+                Some(Some(Some(Some(Some(Some(Some(Some(Some("")))))))))
+            )))))))))))))))))
+        ))))))))))))))))))
+    ))))))))))))))))) == Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(
+        Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(
+            Ok(Ok(Ok(Ok(Ok(Ok(Ok("")))))))
+        ))))))))))))))))))))))))))))))
+    ))))))))))))))))))))))));
+    //~^^^^^ ERROR E0308
+
+    let x: Atype<
+      Btype<
+        Ctype<
+          Atype<
+            Btype<
+              Ctype<
+                Atype<
+                  Btype<
+                    Ctype<i32, i32>,
+                    i32
+                  >,
+                  i32
+                >,
+                i32
+              >,
+              i32
+            >,
+            i32
+          >,
+          i32
+        >,
+        i32
+      >,
+      i32
+    > = ();
+    //~^ ERROR E0308
+
+    let _: () = Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(
+        Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(
+            Ok(Ok(Ok(Ok(Ok(Ok(Ok("")))))))
+        ))))))))))))))))))))))))))))))
+    ))))))))))))))))))))))));
+    //~^^^^^ ERROR E0308
+}
+"#;
+
+    let input_new = Level::ERROR
+        .header("mismatched types")
+        .id("E0308")
+        .group(Group::new().element(
+            Snippet::source(source)
+                .line_start(7)
+                .origin("$DIR/long-E0308.rs")
+                .fold(true)
+                .annotation(
+                    AnnotationKind::Primary
+                        .span(719..1001)
+                        .label("expected `Atype<Btype<Ctype<..., i32>, i32>, i32>`, found `Result<Result<Result<..., _>, _>, _>`"),
+                )
+                .annotation(
+                    AnnotationKind::Context
+                        .span(293..716)
+                        .label("expected due to this"),
+                )
+        ).element(
+            Level::NOTE
+                .title("expected struct `Atype<Btype<..., i32>, i32>`\n     found enum `Result<Result<..., _>, _>`")
+        ).element(
+            Level::NOTE
+                .title("the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt'")
+        ).element(
+            Level::NOTE
+                .title("consider using `--verbose` to print the full type name to the console")
+                ,
+        ));
+
+    let expected = str![[r#"
+error[E0308]: mismatched types
+   ╭▸ $DIR/long-E0308.rs:48:9
+   │
+LL │        let x: Atype<
+   │ ┌─────────────┘
+LL │ │        Btype<
+LL │ │          Ctype<
+LL │ │            Atype<
+   ‡ │
+LL │ │        i32
+LL │ │      > = Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(O…
+   │ │┏━━━━━│━━━┛
+   │ └┃─────┤
+   │  ┃     expected due to this
+LL │  ┃         Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(O…
+LL │  ┃             Ok("")
+LL │  ┃         ))))))))))))))))))))))))))))))
+LL │  ┃     ))))))))))))))))))))))))))))));
+   │  ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ expected `Atype<Btype<Ctype<..., i32>, i32>, i32>`, found `Result<Result<Result<..., _>, _>, _>`
+   ├ note: expected struct `Atype<Btype<..., i32>, i32>`
+   │            found enum `Result<Result<..., _>, _>`
+   ├ note: the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt'
+   ╰ note: consider using `--verbose` to print the full type name to the console
+"#]];
+    let renderer = Renderer::plain()
+        .term_width(60)
+        .theme(OutputTheme::Unicode)
+        .anonymized_line_numbers(true);
+    assert_data_eq!(renderer.render(input_new), expected);
+}
+
+#[test]
+fn highlighting() {
+    let source = r#"
+use core::pin::Pin;
+use core::future::Future;
+use core::any::Any;
+
+fn query(_: fn(Box<(dyn Any + Send + '_)>) -> Pin<Box<(
+    dyn Future<Output = Result<Box<(dyn Any + 'static)>, String>> + Send + 'static
+)>>) {}
+
+fn wrapped_fn<'a>(_: Box<(dyn Any + Send)>) -> Pin<Box<(
+    dyn Future<Output = Result<Box<(dyn Any + 'static)>, String>> + Send + 'static
+)>> {
+    Box::pin(async { Err("nope".into()) })
+}
+
+fn main() {
+    query(wrapped_fn);
+}
+"#;
+
+    let input_new = Level::ERROR
+        .header("mismatched types")
+        .id("E0308")
+        .group(Group::new().element(
+            Snippet::source(source)
+                .line_start(7)
+                .origin("$DIR/unicode-output.rs")
+                .fold(true)
+                .annotation(
+                    AnnotationKind::Primary
+                        .span(430..440)
+                        .label("one type is more general than the other"),
+                )
+                .annotation(
+                    AnnotationKind::Context
+                        .span(424..429)
+                        .label("arguments to this function are incorrect"),
+                ),
+        ).element(
+            Level::NOTE
+                .title("expected fn pointer `for<'a> fn(Box<(dyn Any + Send + 'a)>) -> Pin<_>`\n      found fn item `fn(Box<(dyn Any + Send + 'static)>) -> Pin<_> {wrapped_fn}`")
+                ,
+        ))
+        .group(Group::new().element(
+            Level::NOTE.title("function defined here"),
+        ).element(
+            Snippet::source(source)
+                .line_start(7)
+                .origin("$DIR/unicode-output.rs")
+                .fold(true)
+                .annotation(AnnotationKind::Primary.span(77..210))
+                .annotation(AnnotationKind::Context.span(71..76)),
+        ));
+
+    let expected = str![[r#"
+error[E0308]: mismatched types
+   ╭▸ $DIR/unicode-output.rs:23:11
+   │
+LL │     query(wrapped_fn);
+   │     ┬──── ━━━━━━━━━━ one type is more general than the other
+   │     │
+   │     arguments to this function are incorrect
+   │
+   ╰ note: expected fn pointer `for<'a> fn(Box<(dyn Any + Send + 'a)>) -> Pin<_>`
+                 found fn item `fn(Box<(dyn Any + Send + 'static)>) -> Pin<_> {wrapped_fn}`
+note: function defined here
+   ╭▸ $DIR/unicode-output.rs:12:10
+   │
+LL │   fn query(_: fn(Box<(dyn Any + Send + '_)>) -> Pin<Box<(
+   │ ┏━━━━─────━┛
+LL │ ┃     dyn Future<Output = Result<Box<(dyn Any + 'static)>, String>> + Send + 'static
+LL │ ┃ )>>) {}
+   ╰╴┗━━━┛
+"#]];
+    let renderer = Renderer::plain()
+        .theme(OutputTheme::Unicode)
+        .anonymized_line_numbers(true);
+    assert_data_eq!(renderer.render(input_new), expected);
+}
+
+// This tests that an ellipsis is not inserted into Unicode text when a line
+// wasn't actually trimmed.
+//
+// This is a regression test where `...` was inserted because the code wasn't
+// properly accounting for the *rendered* length versus the length in bytes in
+// all cases.
+#[test]
+fn unicode_cut_handling() {
+    let source = "version = \"0.1.0\"\n# Ensure that the spans from toml handle utf-8 correctly\nauthors = [\n    { name = \"Z\u{351}\u{36b}\u{343}\u{36a}\u{302}\u{36b}\u{33d}\u{34f}\u{334}\u{319}\u{324}\u{31e}\u{349}\u{35a}\u{32f}\u{31e}\u{320}\u{34d}A\u{36b}\u{357}\u{334}\u{362}\u{335}\u{31c}\u{330}\u{354}L\u{368}\u{367}\u{369}\u{358}\u{320}G\u{311}\u{357}\u{30e}\u{305}\u{35b}\u{341}\u{334}\u{33b}\u{348}\u{34d}\u{354}\u{339}O\u{342}\u{30c}\u{30c}\u{358}\u{328}\u{335}\u{339}\u{33b}\u{31d}\u{333}\", email = 1 }\n]\n";
+    let input = Level::ERROR.header("title").group(
+        Group::new().element(
+            Snippet::source(source)
+                .fold(false)
+                .annotation(AnnotationKind::Primary.span(85..228).label("annotation")),
+        ),
+    );
+    let expected = str![[r#"
+error: title
   |
-1 | こんにちは、新しいWorld!
-  |             ^^^^^^^^^^^ New world
-  |"#;
+1 |   version = "0.1.0"
+2 |   # Ensure that the spans from toml handle utf-8 correctly
+3 |   authors = [
+  |  ___________^
+4 | |     { name = "Z͑ͫ̓ͪ̂ͫ̽͏̴̙̤̞͉͚̯̞̠͍A̴̵̜̰͔ͫ͗͢L̠ͨͧͩ͘G̴̻͈͍͔̹̑͗̎̅͛́Ǫ̵̹̻̝̳͂̌̌͘", email = 1 }
+5 | | ]
+  | |_^ annotation
+"#]];
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
+}
+
+#[test]
+fn unicode_cut_handling2() {
+    let source = "/*这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。*/?";
+    let input = Level::ERROR
+        .header("expected item, found `?`")
+        .group(
+            Group::new().element(
+                Snippet::source(source)
+                    .fold(false)
+                    .annotation(AnnotationKind::Primary.span(499..500).label("expected item"))
+            ).element(
+                Level::NOTE.title("for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>")
+            )
+        );
+
+    let expected = str![[r#"
+error: expected item, found `?`
+  |
+1 |  ...的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。*/?
+  |                                                             ^ expected item
+  = note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
+"#]];
+
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
+}
+
+#[test]
+fn unicode_cut_handling3() {
+    let source = "/*这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。这是宽的。*/?";
+    let input = Level::ERROR
+        .header("expected item, found `?`")
+        .group(
+            Group::new().element(
+                Snippet::source(source)
+                    .fold(false)
+                    .annotation(AnnotationKind::Primary.span(251..254).label("expected item"))
+            ).element(
+                Level::NOTE.title("for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>")
+            )
+        );
+
+    let expected = str![[r#"
+error: expected item, found `?`
+  |
+1 |  ...。这是宽的。这是宽的。这是宽的...
+  |            ^^ expected item
+  = note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
+"#]];
+
+    let renderer = Renderer::plain().term_width(43);
+    assert_data_eq!(renderer.render(input), expected);
+}
+
+#[test]
+fn unicode_cut_handling4() {
+    let source = "/*aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa*/?";
+    let input = Level::ERROR
+        .header("expected item, found `?`")
+        .group(
+            Group::new().element(
+                Snippet::source(source)
+                    .fold(false)
+                    .annotation(AnnotationKind::Primary.span(334..335).label("expected item"))
+            ).element(
+                Level::NOTE.title("for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>")
+            )
+        );
+
+    let expected = str![[r#"
+error: expected item, found `?`
+  |
+1 | ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa*/?
+  |                                                             ^ expected item
+  = note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
+"#]];
+
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
+}
+
+#[test]
+fn diagnostic_width() {
+    let source = r##"// ignore-tidy-linelength
+
+fn main() {
+    let _: &str = "🦀☀☁☂☃☄★☆☇☈☉☊☋☌☍☎☏☐☑☒☓  ☖☗☘☙☚☛☜☝☞☟☠☡☢☣☤☥☦☧☨☩☪☫☬☭☮☯☰☱☲☳☴☵☶☷☸☹☺☻☼☽☾☿♀♁♂♃♄♅♆♇♏♔♕♖♗♘♙♚♛♜♝♞♟♠♡♢♣♤♥♦♧♨♩♪♫♬♭♮♯♰♱♲♳♴♵♶♷♸♹♺♻♼♽♾♿⚀⚁⚂⚃⚄⚅⚆⚈⚉4🦀☀☁☂☃☄★☆☇☈☉☊☋☌☍☎☏☐☑☒☓☖☗☘☙☚☛☜☝☞☟☠☡☢☣☤☥☦☧☨☩☪☫☬☭☮☯☰☱☲☳☴☵☶☷☸☹☺☻☼☽☾☿♀♁♂♃♄♅♆♇♏♔♕♖♗♘♙♚♛♜♝♞♟♠♡♢♣♤♥♦♧♨♩♪♫♬♭♮♯♰♱♲♳♴♵♶♷♸♹♺♻♼♽♾♿⚀⚁⚂⚃⚄⚅⚆⚈⚉4🦀🦀☁☂☃☄★☆☇☈☉☊☋☌☍☎☏☐☑☒☓☖☗☘☙☚☛☜☝☞☟☠☡☢☣☤☥☦☧☨☩☪☫☬☭☮☯☰☱☲☳☴☵☶☷☸☹☺☻☼☽☾☿♀♁♂♃♄♅♆♇♏♔♕♖♗♘♙♚♛♜♝♞♟♠♡♢♣♤♥♦♧♨♩♪♫♬♭♮♯♰♱♲♳♴♵♶♷♸♹♺♻♼♽♾♿⚀⚁⚂⚃⚄⚅⚆⚈⚉4"; let _: () = 42;  let _: &str = "🦀☀☁☂☃☄★☆☇☈☉☊☋☌☍☎☏☐☑☒☓  ☖☗☘☙☚☛☜☝☞☟☠☡☢☣☤☥☦☧☨☩☪☫☬☭☮☯☰☱☲☳☴☵☶☷☸☹☺☻☼☽☾☿♀♁♂♃♄♅♆♇♏♔♕♖♗♘♙♚♛♜♝♞♟♠♡♢♣♤♥♦♧♨♩♪♫♬♭♮♯♰♱♲♳♴♵♶♷♸♹♺♻♼♽♾♿⚀⚁⚂⚃⚄⚅⚆⚈⚉4🦀☀☁☂☃☄★☆☇☈☉☊☋☌☍☎☏☐☑☒☓☖☗☘☙☚☛☜☝☞☟☠☡☢☣☤☥☦☧☨☩☪☫☬☭☮☯☰☱☲☳☴☵☶☷☸☹☺☻☼☽☾☿♀♁♂♃♄♅♆♇♏♔♕♖♗♘♙♚♛♜♝♞♟♠♡♢♣♤♥♦♧♨♩♪♫♬♭♮♯♰♱♲♳♴♵♶♷♸♹♺♻♼♽♾♿⚀⚁⚂⚃⚄⚅⚆⚈⚉4🦀🦀☁☂☃☄★☆☇☈☉☊☋☌☍☎☏☐☑☒☓☖☗☘☙☚☛☜☝☞☟☠☡☢☣☤☥☦☧☨☩☪☫☬☭☮☯☰☱☲☳☴☵☶☷☸☹☺☻☼☽☾☿♀♁♂♃♄♅♆♇♏♔♕♖♗♘♙♚♛♜♝♞♟♠♡♢♣♤♥♦♧♨♩♪♫♬♭♮♯♰♱♲♳♴♵♶♷♸♹♺♻♼♽♾♿⚀⚁⚂⚃⚄⚅⚆⚈⚉4";
+//~^ ERROR mismatched types
+}
+"##;
+    let input = Level::ERROR.header("mismatched types").id("E0308").group(
+        Group::new().element(
+            Snippet::source(source)
+                .origin("$DIR/non-whitespace-trimming-unicode.rs")
+                .fold(true)
+                .annotation(
+                    AnnotationKind::Primary
+                        .span(1207..1209)
+                        .label("expected `()`, found integer"),
+                )
+                .annotation(
+                    AnnotationKind::Context
+                        .span(1202..1204)
+                        .label("expected due to this"),
+                ),
+        ),
+    );
+
+    let expected = str![[r#"
+error[E0308]: mismatched types
+  --> $DIR/non-whitespace-trimming-unicode.rs:4:415
+   |
+LL | ...♧♨♩♪♫♬♭♮♯♰♱♲♳♴♵♶♷♸♹♺♻♼♽♾♿⚀⚁⚂⚃⚄⚅⚆⚈⚉4"; let _: () = 42;  let _: &str = "🦀☀☁☂☃☄★☆☇☈☉☊☋☌☍☎☏☐☑☒☓  ☖☗☘☙☚☛☜☝☞☟☠☡☢☣☤☥☦☧☨☩☪☫☬☭☮☯☰☱☲☳☴☵☶☷...
+   |                                                  --   ^^ expected `()`, found integer
+   |                                                  |
+   |                                                  expected due to this
+"#]];
+
+    let renderer = Renderer::plain().anonymized_line_numbers(true);
+    assert_data_eq!(renderer.render(input), expected);
+}
+
+#[test]
+fn diagnostic_width2() {
+    let source = r##"//@ revisions: ascii unicode
+//@[unicode] compile-flags: -Zunstable-options --error-format=human-unicode
+// ignore-tidy-linelength
+
+fn main() {
+    let unicode_is_fun = "؁‱ஹ௸௵꧄.ဪ꧅⸻𒈙𒐫﷽𒌄𒈟𒍼𒁎𒀱𒌧𒅃 𒈓𒍙𒊎𒄡𒅌𒁏𒀰𒐪𒐩𒈙𒐫𪚥";
+    let _ = "ༀ༁༂༃༄༅༆༇༈༉༊་༌།༎༏༐༑༒༓༔༕༖༗༘༙༚༛༜༝༞༟༠༡༢༣༤༥༦༧༨༩༪༫༬༭༮༯༰༱༲༳༴༵༶༷༸༹༺༻༼༽༾༿ཀཁགགྷངཅཆཇ཈ཉཊཋཌཌྷཎཏཐདདྷནཔཕབབྷམཙཚཛཛྷཝཞཟའཡརལཤཥསཧཨཀྵཪཫཬ཭཮཯཰ཱཱཱིིུུྲྀཷླྀཹེཻོཽཾཿ྄ཱྀྀྂྃ྅྆྇ྈྉྊྋྌྍྎྏྐྑྒྒྷྔྕྖྗ྘ྙྚྛྜྜྷྞྟྠྡྡྷྣྤྥྦྦྷྨྩྪྫྫྷྭྮྯྰྱྲླྴྵྶྷྸྐྵྺྻྼ྽྾྿࿀࿁࿂࿃࿄࿅࿆࿇࿈࿉࿊࿋࿌࿍࿎࿏࿐࿑࿒࿓࿔࿕࿖࿗࿘࿙࿚"; let _a = unicode_is_fun + " really fun!";
+    //[ascii]~^ ERROR cannot add `&str` to `&str`
+}
+"##;
+    let input = Level::ERROR
+        .header("cannot add `&str` to `&str`")
+        .id("E0369")
+        .group(
+            Group::new()
+                .element(
+                    Snippet::source(source)
+                        .origin("$DIR/non-1-width-unicode-multiline-label.rs")
+                        .fold(true)
+                        .annotation(AnnotationKind::Context.span(970..984).label("&str"))
+                        .annotation(AnnotationKind::Context.span(987..1001).label("&str"))
+                        .annotation(
+                            AnnotationKind::Primary
+                                .span(985..986)
+                                .label("`+` cannot be used to concatenate two `&str` strings"),
+                        ),
+                )
+                .element(
+                    Level::NOTE
+                        .title("string concatenation requires an owned `String` on the left"),
+                ),
+        )
+        .group(
+            Group::new()
+                .element(Level::HELP.title("create an owned `String` from a string reference"))
+                .element(
+                    Snippet::source(source)
+                        .origin("$DIR/non-1-width-unicode-multiline-label.rs")
+                        .fold(true)
+                        .patch(Patch::new(984..984, ".to_owned()")),
+                ),
+        );
+
+    let expected = str![[r#"
+error[E0369]: cannot add `&str` to `&str`
+   ╭▸ $DIR/non-1-width-unicode-multiline-label.rs:7:260
+   │
+LL │ …࿆࿇࿈࿉࿊࿋࿌࿍࿎࿏࿐࿑࿒࿓࿔࿕࿖࿗࿘࿙࿚"; let _a = unicode_is_fun + " really fun!";
+   │                                  ┬───────────── ┯ ────────────── &str
+   │                                  │              │
+   │                                  │              `+` cannot be used to concatenate two `&str` strings
+   │                                  &str
+   │
+   ╰ note: string concatenation requires an owned `String` on the left
+help: create an owned `String` from a string reference
+   ╭╴
+LL │     let _ = "ༀ༁༂༃༄༅༆༇༈༉༊་༌།༎༏༐༑༒༓༔༕༖༗༘༙༚༛༜༝༞༟༠༡༢༣༤༥༦༧༨༩༪༫༬༭༮༯༰༱༲༳༴༵༶༷༸༹༺༻༼༽༾༿ཀཁགགྷངཅཆཇ཈ཉཊཋཌཌྷཎཏཐདདྷནཔཕབབྷམཙཚཛཛྷཝཞཟའཡརལཤཥསཧཨཀྵཪཫཬ཭཮཯཰ཱཱཱིིུུྲྀཷླྀཹེཻོཽཾཿ྄ཱྀྀྂྃ྅྆྇ྈྉྊྋྌྍྎྏྐྑྒྒྷྔྕྖྗ྘ྙྚྛྜྜྷྞྟྠྡྡྷྣྤྥྦྦྷྨྩྪྫྫྷྭྮྯྰྱྲླྴྵྶྷྸྐྵྺྻྼ྽྾྿࿀࿁࿂࿃࿄࿅࿆࿇࿈࿉࿊࿋࿌࿍࿎࿏࿐࿑࿒࿓࿔࿕࿖࿗࿘࿙࿚"; let _a = unicode_is_fun.to_owned() + " really fun!";
+   ╰╴                                                                                                                                                                                        +++++++++++
+"#]];
+
+    let renderer = Renderer::plain()
+        .anonymized_line_numbers(true)
+        .theme(OutputTheme::Unicode);
+    assert_data_eq!(renderer.render(input), expected);
+}
+
+#[test]
+fn macros_not_utf8() {
+    let source = r##"//@ error-pattern: did not contain valid UTF-8
+//@ reference: input.encoding.utf8
+//@ reference: input.encoding.invalid
+
+fn foo() {
+    include!("not-utf8.bin");
+}
+"##;
+    let bin_source = "�|�\u{0002}!5�cc\u{0015}\u{0002}�Ӻi��WWj�ȥ�'�}�\u{0012}�J�ȉ��W�\u{001e}O�@����\u{001c}w�V���LO����\u{0014}[ \u{0003}_�'���SQ�~ذ��ų&��-\t��lN~��!@␌ _#���kQ��h�\u{001d}�:�\u{001c}\u{0007}�";
+    let input = Level::ERROR
+        .header("couldn't read `$DIR/not-utf8.bin`: stream did not contain valid UTF-8")
+        .group(
+            Group::new().element(
+                Snippet::source(source)
+                    .origin("$DIR/not-utf8.rs")
+                    .fold(true)
+                    .annotation(AnnotationKind::Primary.span(136..160)),
+            ),
+        )
+        .group(
+            Group::new()
+                .element(Level::NOTE.title("byte `193` is not valid utf-8"))
+                .element(
+                    Snippet::source(bin_source)
+                        .origin("$DIR/not-utf8.bin")
+                        .fold(true)
+                        .annotation(AnnotationKind::Primary.span(0..0)),
+                )
+                .element(Level::NOTE.title("this error originates in the macro `include` (in Nightly builds, run with -Z macro-backtrace for more info)")),
+        );
+
+    let expected = str![[r#"
+error: couldn't read `$DIR/not-utf8.bin`: stream did not contain valid UTF-8
+  --> $DIR/not-utf8.rs:6:5
+   |
+LL |     include!("not-utf8.bin");
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: byte `193` is not valid utf-8
+  --> $DIR/not-utf8.bin:1:1
+   |
+LL | �|�␂!5�cc␕␂�Ӻi��WWj�ȥ�'�}�␒�J�ȉ��W�␞O�@����␜w�V���LO����␔[ ␃_�'���SQ�~ذ��ų&��-    ��lN~��!@␌ _#���kQ��h�␝�:�␜␇�
+   | ^
+   = note: this error originates in the macro `include` (in Nightly builds, run with -Z macro-backtrace for more info)
+"#]];
 
-    assert_eq!(DisplayList::from(snippets).to_string(), expected);
+    let renderer = Renderer::plain().anonymized_line_numbers(true);
+    assert_data_eq!(renderer.render(input), expected);
 }
diff --git a/tests/rustc_tests.rs b/tests/rustc_tests.rs
new file mode 100644
index 00000000..69986292
--- /dev/null
+++ b/tests/rustc_tests.rs
@@ -0,0 +1,1883 @@
+//! These tests have been adapted from [Rust's parser tests][parser-tests].
+//!
+//! [parser-tests]: https://github.com/rust-lang/rust/blob/894f7a4ba6554d3797404bbf550d9919df060b97/compiler/rustc_parse/src/parser/tests.rs
+
+use annotate_snippets::{AnnotationKind, Group, Level, Origin, Renderer, Snippet};
+
+use snapbox::{assert_data_eq, str};
+
+#[test]
+fn ends_on_col0() {
+    let source = r#"
+fn foo() {
+}
+"#;
+    let input = Level::ERROR.header("foo").group(
+        Group::new().element(
+            Snippet::source(source)
+                .line_start(1)
+                .origin("test.rs")
+                .fold(true)
+                .annotation(AnnotationKind::Primary.span(10..13).label("test")),
+        ),
+    );
+
+    let expected = str![[r#"
+error: foo
+ --> test.rs:2:10
+  |
+2 |   fn foo() {
+  |  __________^
+3 | | }
+  | |_^ test
+"#]];
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
+}
+#[test]
+fn ends_on_col2() {
+    let source = r#"
+fn foo() {
+
+
+  }
+"#;
+    let input = Level::ERROR.header("foo").group(
+        Group::new().element(
+            Snippet::source(source)
+                .line_start(1)
+                .origin("test.rs")
+                .fold(true)
+                .annotation(AnnotationKind::Primary.span(10..17).label("test")),
+        ),
+    );
+
+    let expected = str![[r#"
+error: foo
+ --> test.rs:2:10
+  |
+2 |   fn foo() {
+  |  __________^
+... |
+5 | |   }
+  | |___^ test
+"#]];
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
+}
+#[test]
+fn non_nested() {
+    let source = r#"
+fn foo() {
+  X0 Y0
+  X1 Y1
+  X2 Y2
+}
+"#;
+    let input = Level::ERROR.header("foo").group(
+        Group::new().element(
+            Snippet::source(source)
+                .line_start(1)
+                .origin("test.rs")
+                .fold(true)
+                .annotation(
+                    AnnotationKind::Primary
+                        .span(14..32)
+                        .label("`X` is a good letter"),
+                )
+                .annotation(
+                    AnnotationKind::Context
+                        .span(17..35)
+                        .label("`Y` is a good letter too"),
+                ),
+        ),
+    );
+
+    let expected = str![[r#"
+error: foo
+ --> test.rs:3:3
+  |
+3 |      X0 Y0
+  |  ____^  -
+  | | ______|
+4 | ||   X1 Y1
+5 | ||   X2 Y2
+  | ||____^__- `Y` is a good letter too
+  | |_____|
+  |       `X` is a good letter
+"#]];
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
+}
+#[test]
+fn nested() {
+    let source = r#"
+fn foo() {
+  X0 Y0
+  Y1 X1
+}
+"#;
+    let input = Level::ERROR.header("foo").group(
+        Group::new().element(
+            Snippet::source(source)
+                .line_start(1)
+                .origin("test.rs")
+                .fold(true)
+                .annotation(
+                    AnnotationKind::Primary
+                        .span(14..27)
+                        .label("`X` is a good letter"),
+                )
+                .annotation(
+                    AnnotationKind::Context
+                        .span(17..24)
+                        .label("`Y` is a good letter too"),
+                ),
+        ),
+    );
+
+    let expected = str![[r#"
+error: foo
+ --> test.rs:3:3
+  |
+3 |      X0 Y0
+  |  ____^  -
+  | | ______|
+4 | ||   Y1 X1
+  | ||____-__^ `X` is a good letter
+  |  |____|
+  |       `Y` is a good letter too
+"#]];
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
+}
+#[test]
+fn different_overlap() {
+    let source = r#"
+fn foo() {
+  X0 Y0 Z0
+  X1 Y1 Z1
+  X2 Y2 Z2
+  X3 Y3 Z3
+}
+"#;
+    let input = Level::ERROR.header("foo").group(
+        Group::new().element(
+            Snippet::source(source)
+                .line_start(1)
+                .origin("test.rs")
+                .fold(true)
+                .annotation(
+                    AnnotationKind::Primary
+                        .span(17..38)
+                        .label("`X` is a good letter"),
+                )
+                .annotation(
+                    AnnotationKind::Context
+                        .span(31..49)
+                        .label("`Y` is a good letter too"),
+                ),
+        ),
+    );
+
+    let expected = str![[r#"
+error: foo
+ --> test.rs:3:6
+  |
+3 |      X0 Y0 Z0
+  |  _______^
+4 | |    X1 Y1 Z1
+  | | _________-
+5 | ||   X2 Y2 Z2
+  | ||____^ `X` is a good letter
+6 |  |   X3 Y3 Z3
+  |  |____- `Y` is a good letter too
+"#]];
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
+}
+#[test]
+fn triple_overlap() {
+    let source = r#"
+fn foo() {
+  X0 Y0 Z0
+  X1 Y1 Z1
+  X2 Y2 Z2
+}
+"#;
+    let input = Level::ERROR.header("foo").group(
+        Group::new().element(
+            Snippet::source(source)
+                .line_start(1)
+                .origin("test.rs")
+                .fold(true)
+                .annotation(
+                    AnnotationKind::Primary
+                        .span(14..38)
+                        .label("`X` is a good letter"),
+                )
+                .annotation(
+                    AnnotationKind::Context
+                        .span(17..41)
+                        .label("`Y` is a good letter too"),
+                )
+                .annotation(AnnotationKind::Context.span(20..44).label("`Z` label")),
+        ),
+    );
+
+    let expected = str![[r#"
+error: foo
+ --> test.rs:3:3
+  |
+3 |       X0 Y0 Z0
+  |  _____^  -  -
+  | | _______|  |
+  | || _________|
+4 | |||   X1 Y1 Z1
+5 | |||   X2 Y2 Z2
+  | |||____^__-__- `Z` label
+  | ||_____|__|
+  | |______|  `Y` is a good letter too
+  |        `X` is a good letter
+"#]];
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
+}
+#[test]
+fn triple_exact_overlap() {
+    let source = r#"
+fn foo() {
+  X0 Y0 Z0
+  X1 Y1 Z1
+  X2 Y2 Z2
+}
+"#;
+    let input = Level::ERROR.header("foo").group(
+        Group::new().element(
+            Snippet::source(source)
+                .line_start(1)
+                .origin("test.rs")
+                .fold(true)
+                .annotation(
+                    AnnotationKind::Primary
+                        .span(14..38)
+                        .label("`X` is a good letter"),
+                )
+                .annotation(
+                    AnnotationKind::Context
+                        .span(14..38)
+                        .label("`Y` is a good letter too"),
+                )
+                .annotation(AnnotationKind::Context.span(14..38).label("`Z` label")),
+        ),
+    );
+
+    // This should have a `^` but we currently don't support the idea of a
+    // "primary" annotation, which would solve this
+    let expected = str![[r#"
+error: foo
+ --> test.rs:3:3
+  |
+3 | /   X0 Y0 Z0
+4 | |   X1 Y1 Z1
+5 | |   X2 Y2 Z2
+  | |    ^
+  | |    |
+  | |    `X` is a good letter
+  | |____`Y` is a good letter too
+  |      `Z` label
+"#]];
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
+}
+#[test]
+fn minimum_depth() {
+    let source = r#"
+fn foo() {
+  X0 Y0 Z0
+  X1 Y1 Z1
+  X2 Y2 Z2
+  X3 Y3 Z3
+}
+"#;
+    let input = Level::ERROR.header("foo").group(
+        Group::new().element(
+            Snippet::source(source)
+                .line_start(1)
+                .origin("test.rs")
+                .fold(true)
+                .annotation(
+                    AnnotationKind::Primary
+                        .span(17..27)
+                        .label("`X` is a good letter"),
+                )
+                .annotation(
+                    AnnotationKind::Context
+                        .span(28..44)
+                        .label("`Y` is a good letter too"),
+                )
+                .annotation(AnnotationKind::Context.span(36..52).label("`Z`")),
+        ),
+    );
+
+    let expected = str![[r#"
+error: foo
+ --> test.rs:3:6
+  |
+3 |      X0 Y0 Z0
+  |  _______^
+4 | |    X1 Y1 Z1
+  | | ____^_-
+  | ||____|
+  |  |    `X` is a good letter
+5 |  |   X2 Y2 Z2
+  |  |___-______- `Y` is a good letter too
+  |   ___|
+  |  |
+6 |  |   X3 Y3 Z3
+  |  |_______- `Z`
+"#]];
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
+}
+#[test]
+fn non_overlapping() {
+    let source = r#"
+fn foo() {
+  X0 Y0 Z0
+  X1 Y1 Z1
+  X2 Y2 Z2
+  X3 Y3 Z3
+}
+"#;
+    let input = Level::ERROR.header("foo").group(
+        Group::new().element(
+            Snippet::source(source)
+                .line_start(1)
+                .origin("test.rs")
+                .fold(true)
+                .annotation(
+                    AnnotationKind::Primary
+                        .span(14..27)
+                        .label("`X` is a good letter"),
+                )
+                .annotation(
+                    AnnotationKind::Context
+                        .span(39..55)
+                        .label("`Y` is a good letter too"),
+                ),
+        ),
+    );
+
+    let expected = str![[r#"
+error: foo
+ --> test.rs:3:3
+  |
+3 | /   X0 Y0 Z0
+4 | |   X1 Y1 Z1
+  | |____^ `X` is a good letter
+5 |     X2 Y2 Z2
+  |  ______-
+6 | |   X3 Y3 Z3
+  | |__________- `Y` is a good letter too
+"#]];
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
+}
+#[test]
+fn overlapping_start_and_end() {
+    let source = r#"
+fn foo() {
+  X0 Y0 Z0
+  X1 Y1 Z1
+  X2 Y2 Z2
+  X3 Y3 Z3
+}
+"#;
+    let input = Level::ERROR.header("foo").group(
+        Group::new().element(
+            Snippet::source(source)
+                .line_start(1)
+                .origin("test.rs")
+                .fold(true)
+                .annotation(
+                    AnnotationKind::Primary
+                        .span(17..27)
+                        .label("`X` is a good letter"),
+                )
+                .annotation(
+                    AnnotationKind::Context
+                        .span(31..55)
+                        .label("`Y` is a good letter too"),
+                ),
+        ),
+    );
+
+    let expected = str![[r#"
+error: foo
+ --> test.rs:3:6
+  |
+3 |      X0 Y0 Z0
+  |  _______^
+4 | |    X1 Y1 Z1
+  | | ____^____-
+  | ||____|
+  |  |    `X` is a good letter
+5 |  |   X2 Y2 Z2
+6 |  |   X3 Y3 Z3
+  |  |__________- `Y` is a good letter too
+"#]];
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
+}
+#[test]
+fn multiple_labels_primary_without_message() {
+    let source = r#"
+fn foo() {
+  a { b { c } d }
+}
+"#;
+    let input = Level::ERROR.header("foo").group(
+        Group::new().element(
+            Snippet::source(source)
+                .line_start(1)
+                .origin("test.rs")
+                .fold(true)
+                .annotation(AnnotationKind::Primary.span(18..25).label(""))
+                .annotation(
+                    AnnotationKind::Context
+                        .span(14..27)
+                        .label("`a` is a good letter"),
+                )
+                .annotation(AnnotationKind::Context.span(22..23).label("")),
+        ),
+    );
+
+    let expected = str![[r#"
+error: foo
+ --> test.rs:3:7
+  |
+3 |   a { b { c } d }
+  |   ----^^^^-^^-- `a` is a good letter
+"#]];
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
+}
+#[test]
+fn multiple_labels_secondary_without_message() {
+    let source = r#"
+fn foo() {
+  a { b { c } d }
+}
+"#;
+    let input = Level::ERROR.header("foo").group(
+        Group::new().element(
+            Snippet::source(source)
+                .line_start(1)
+                .origin("test.rs")
+                .fold(true)
+                .annotation(
+                    AnnotationKind::Primary
+                        .span(14..27)
+                        .label("`a` is a good letter"),
+                )
+                .annotation(AnnotationKind::Context.span(18..25).label("")),
+        ),
+    );
+
+    let expected = str![[r#"
+error: foo
+ --> test.rs:3:3
+  |
+3 |   a { b { c } d }
+  |   ^^^^-------^^ `a` is a good letter
+"#]];
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
+}
+#[test]
+fn multiple_labels_primary_without_message_2() {
+    let source = r#"
+fn foo() {
+  a { b { c } d }
+}
+"#;
+    let input = Level::ERROR.header("foo").group(
+        Group::new().element(
+            Snippet::source(source)
+                .line_start(1)
+                .origin("test.rs")
+                .fold(true)
+                .annotation(
+                    AnnotationKind::Primary
+                        .span(18..25)
+                        .label("`b` is a good letter"),
+                )
+                .annotation(AnnotationKind::Context.span(14..27).label(""))
+                .annotation(AnnotationKind::Context.span(22..23).label("")),
+        ),
+    );
+
+    let expected = str![[r#"
+error: foo
+ --> test.rs:3:7
+  |
+3 |   a { b { c } d }
+  |   ----^^^^-^^--
+  |       |
+  |       `b` is a good letter
+"#]];
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
+}
+#[test]
+fn multiple_labels_secondary_without_message_2() {
+    let source = r#"
+fn foo() {
+  a { b { c } d }
+}
+"#;
+    let input = Level::ERROR.header("foo").group(
+        Group::new().element(
+            Snippet::source(source)
+                .line_start(1)
+                .origin("test.rs")
+                .fold(true)
+                .annotation(AnnotationKind::Primary.span(14..27).label(""))
+                .annotation(
+                    AnnotationKind::Context
+                        .span(18..25)
+                        .label("`b` is a good letter"),
+                ),
+        ),
+    );
+
+    let expected = str![[r#"
+error: foo
+ --> test.rs:3:3
+  |
+3 |   a { b { c } d }
+  |   ^^^^-------^^
+  |       |
+  |       `b` is a good letter
+"#]];
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
+}
+#[test]
+fn multiple_labels_secondary_without_message_3() {
+    let source = r#"
+fn foo() {
+  a  bc  d
+}
+"#;
+    let input = Level::ERROR.header("foo").group(
+        Group::new().element(
+            Snippet::source(source)
+                .line_start(1)
+                .origin("test.rs")
+                .fold(true)
+                .annotation(
+                    AnnotationKind::Primary
+                        .span(14..18)
+                        .label("`a` is a good letter"),
+                )
+                .annotation(AnnotationKind::Context.span(18..22).label("")),
+        ),
+    );
+
+    let expected = str![[r#"
+error: foo
+ --> test.rs:3:3
+  |
+3 |   a  bc  d
+  |   ^^^^----
+  |   |
+  |   `a` is a good letter
+"#]];
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
+}
+#[test]
+fn multiple_labels_without_message() {
+    let source = r#"
+fn foo() {
+  a { b { c } d }
+}
+"#;
+    let input = Level::ERROR.header("foo").group(
+        Group::new().element(
+            Snippet::source(source)
+                .line_start(1)
+                .origin("test.rs")
+                .fold(true)
+                .annotation(AnnotationKind::Primary.span(14..27).label(""))
+                .annotation(AnnotationKind::Context.span(18..25).label("")),
+        ),
+    );
+
+    let expected = str![[r#"
+error: foo
+ --> test.rs:3:3
+  |
+3 |   a { b { c } d }
+  |   ^^^^-------^^
+"#]];
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
+}
+#[test]
+fn multiple_labels_without_message_2() {
+    let source = r#"
+fn foo() {
+  a { b { c } d }
+}
+"#;
+    let input = Level::ERROR.header("foo").group(
+        Group::new().element(
+            Snippet::source(source)
+                .line_start(1)
+                .origin("test.rs")
+                .fold(true)
+                .annotation(AnnotationKind::Primary.span(18..25).label(""))
+                .annotation(AnnotationKind::Context.span(14..27).label(""))
+                .annotation(AnnotationKind::Context.span(22..23).label("")),
+        ),
+    );
+
+    let expected = str![[r#"
+error: foo
+ --> test.rs:3:7
+  |
+3 |   a { b { c } d }
+  |   ----^^^^-^^--
+"#]];
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
+}
+#[test]
+fn multiple_labels_with_message() {
+    let source = r#"
+fn foo() {
+  a { b { c } d }
+}
+"#;
+    let input = Level::ERROR.header("foo").group(
+        Group::new().element(
+            Snippet::source(source)
+                .line_start(1)
+                .origin("test.rs")
+                .fold(true)
+                .annotation(
+                    AnnotationKind::Primary
+                        .span(14..27)
+                        .label("`a` is a good letter"),
+                )
+                .annotation(
+                    AnnotationKind::Context
+                        .span(18..25)
+                        .label("`b` is a good letter"),
+                ),
+        ),
+    );
+
+    let expected = str![[r#"
+error: foo
+ --> test.rs:3:3
+  |
+3 |   a { b { c } d }
+  |   ^^^^-------^^
+  |   |   |
+  |   |   `b` is a good letter
+  |   `a` is a good letter
+"#]];
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
+}
+#[test]
+fn ingle_label_with_message() {
+    let source = r#"
+fn foo() {
+  a { b { c } d }
+}
+"#;
+    let input = Level::ERROR.header("foo").group(
+        Group::new().element(
+            Snippet::source(source)
+                .line_start(1)
+                .origin("test.rs")
+                .fold(true)
+                .annotation(
+                    AnnotationKind::Primary
+                        .span(14..27)
+                        .label("`a` is a good letter"),
+                ),
+        ),
+    );
+
+    let expected = str![[r#"
+error: foo
+ --> test.rs:3:3
+  |
+3 |   a { b { c } d }
+  |   ^^^^^^^^^^^^^ `a` is a good letter
+"#]];
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
+}
+#[test]
+fn single_label_without_message() {
+    let source = r#"
+fn foo() {
+  a { b { c } d }
+}
+"#;
+    let input = Level::ERROR.header("foo").group(
+        Group::new().element(
+            Snippet::source(source)
+                .line_start(1)
+                .origin("test.rs")
+                .fold(true)
+                .annotation(AnnotationKind::Primary.span(14..27).label("")),
+        ),
+    );
+
+    let expected = str![[r#"
+error: foo
+ --> test.rs:3:3
+  |
+3 |   a { b { c } d }
+  |   ^^^^^^^^^^^^^
+"#]];
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
+}
+#[test]
+fn long_snippet() {
+    let source = r#"
+fn foo() {
+  X0 Y0 Z0
+  X1 Y1 Z1
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+  X2 Y2 Z2
+  X3 Y3 Z3
+}
+"#;
+    let input = Level::ERROR.header("foo").group(
+        Group::new().element(
+            Snippet::source(source)
+                .line_start(1)
+                .origin("test.rs")
+                .fold(true)
+                .annotation(
+                    AnnotationKind::Primary
+                        .span(17..27)
+                        .label("`X` is a good letter"),
+                )
+                .annotation(
+                    AnnotationKind::Context
+                        .span(31..76)
+                        .label("`Y` is a good letter too"),
+                ),
+        ),
+    );
+
+    let expected = str![[r#"
+error: foo
+  --> test.rs:3:6
+   |
+ 3 |      X0 Y0 Z0
+   |  _______^
+ 4 | |    X1 Y1 Z1
+   | | ____^____-
+   | ||____|
+   |  |    `X` is a good letter
+ 5 |  | 1
+ 6 |  | 2
+ 7 |  | 3
+...   |
+15 |  |   X2 Y2 Z2
+16 |  |   X3 Y3 Z3
+   |  |__________- `Y` is a good letter too
+"#]];
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
+}
+#[test]
+fn long_snippet_multiple_spans() {
+    let source = r#"
+fn foo() {
+  X0 Y0 Z0
+1
+2
+3
+  X1 Y1 Z1
+4
+5
+6
+  X2 Y2 Z2
+7
+8
+9
+10
+  X3 Y3 Z3
+}
+"#;
+    let input = Level::ERROR.header("foo").group(
+        Group::new().element(
+            Snippet::source(source)
+                .line_start(1)
+                .origin("test.rs")
+                .fold(true)
+                .annotation(
+                    AnnotationKind::Primary
+                        .span(17..73)
+                        .label("`Y` is a good letter"),
+                )
+                .annotation(
+                    AnnotationKind::Context
+                        .span(37..56)
+                        .label("`Z` is a good letter too"),
+                ),
+        ),
+    );
+
+    let expected = str![[r#"
+error: foo
+  --> test.rs:3:6
+   |
+ 3 |      X0 Y0 Z0
+   |  _______^
+ 4 | |  1
+ 5 | |  2
+ 6 | |  3
+ 7 | |    X1 Y1 Z1
+   | | _________-
+ 8 | || 4
+ 9 | || 5
+10 | || 6
+11 | ||   X2 Y2 Z2
+   | ||__________- `Z` is a good letter too
+...  |
+15 | |  10
+16 | |    X3 Y3 Z3
+   | |________^ `Y` is a good letter
+"#]];
+    let renderer = Renderer::plain();
+    assert_data_eq!(renderer.render(input), expected);
+}
+
+#[test]
+fn issue_91334() {
+    let source = r#"// Regression test for the ICE described in issue #91334.
+
+//@ error-pattern: this file contains an unclosed delimiter
+
+#![feature(coroutines)]
+
+fn f(){||yield(((){),
+"#;
+    let input = Level::ERROR
+        .header("this file contains an unclosed delimiter")
+        .group(
+            Group::new().element(
+                Snippet::source(source)
+                    .line_start(1)
+                    .origin("$DIR/issue-91334.rs")
+                    .fold(true)
+                    .annotation(
+                        AnnotationKind::Context
+                            .span(151..152)
+                            .label("unclosed delimiter"),
+                    )
+                    .annotation(
+                        AnnotationKind::Context
+                            .span(159..160)
+                            .label("unclosed delimiter"),
+                    )
+                    .annotation(
+                        AnnotationKind::Context
+                            .span(164..164)
+                            .label("missing open `(` for this delimiter"),
+                    )
+                    .annotation(AnnotationKind::Primary.span(167..167)),
+            ),
+        );
+    let expected = str![[r#"
+error: this file contains an unclosed delimiter
+  --> $DIR/issue-91334.rs:7:23
+   |
+LL | fn f(){||yield(((){),
+   |       -       -    - ^
+   |       |       |    |
+   |       |       |    missing open `(` for this delimiter
+   |       |       unclosed delimiter
+   |       unclosed delimiter
+"#]];
+    let renderer = Renderer::plain().anonymized_line_numbers(true);
+    assert_data_eq!(renderer.render(input), expected);
+}
+
+#[test]
+fn issue_114529_illegal_break_with_value() {
+    // tests/ui/typeck/issue-114529-illegal-break-with-value.rs
+    let source = r#"// Regression test for issue #114529
+// Tests that we do not ICE during const eval for a
+// break-with-value in contexts where it is illegal
+
+#[allow(while_true)]
+fn main() {
+    [(); {
+        while true {
+            break 9; //~ ERROR `break` with value from a `while` loop
+        };
+        51
+    }];
+
+    [(); {
+        while let Some(v) = Some(9) {
+            break v; //~ ERROR `break` with value from a `while` loop
+        };
+        51
+    }];
+
+    while true {
+        break (|| { //~ ERROR `break` with value from a `while` loop
+            let local = 9;
+        });
+    }
+}
+"#;
+    let input = Level::ERROR
+        .header("`break` with value from a `while` loop")
+        .id("E0571")
+        .group(
+            Group::new().element(
+                Snippet::source(source)
+                    .line_start(1)
+                    .origin("$DIR/issue-114529-illegal-break-with-value.rs")
+                    .fold(true)
+                    .annotation(
+                        AnnotationKind::Primary
+                            .span(483..581)
+                            .label("can only break with a value inside `loop` or breakable block"),
+                    )
+                    .annotation(
+                        AnnotationKind::Context
+                            .span(462..472)
+                            .label("you can't `break` with a value in a `while` loop"),
+                    ),
+            ),
+        )
+        .group(
+            Group::new()
+                .element(
+                    Level::HELP
+                        .title("use `break` on its own without a value inside this `while` loop"),
+                )
+                .element(
+                    Snippet::source(source)
+                        .line_start(1)
+                        .origin("$DIR/issue-114529-illegal-break-with-value.rs")
+                        .fold(true)
+                        .annotation(AnnotationKind::Context.span(483..581).label("break")),
+                ),
+        );
+    let expected = str![[r#"
+error[E0571]: `break` with value from a `while` loop
+  --> $DIR/issue-114529-illegal-break-with-value.rs:22:9
+   |
+LL |       while true {
+   |       ---------- you can't `break` with a value in a `while` loop
+LL | /         break (|| { //~ ERROR `break` with value from a `while` loop
+LL | |             let local = 9;
+LL | |         });
+   | |__________^ can only break with a value inside `loop` or breakable block
+   |
+help: use `break` on its own without a value inside this `while` loop
+  --> $DIR/issue-114529-illegal-break-with-value.rs:22:9
+   |
+LL | /         break (|| { //~ ERROR `break` with value from a `while` loop
+LL | |             let local = 9;
+LL | |         });
+   | |__________- break
+"#]];
+
+    let renderer = Renderer::plain().anonymized_line_numbers(true);
+    assert_data_eq!(renderer.render(input), expected);
+}
+
+#[test]
+fn primitive_reprs_should_have_correct_length() {
+    // tests/ui/transmutability/enums/repr/primitive_reprs_should_have_correct_length.rs
+    let source = r#"//! An enum with a primitive repr should have exactly the size of that primitive.
+
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code)]
+
+mod assert {
+    use std::mem::{Assume, TransmuteFrom};
+
+    pub fn is_transmutable<Src, Dst>()
+    where
+        Dst: TransmuteFrom<Src, {
+            Assume {
+                alignment: true,
+                lifetimes: true,
+                safety: true,
+                validity: true,
+            }
+        }>
+    {}
+}
+
+#[repr(C)]
+struct Zst;
+
+#[derive(Clone, Copy)]
+#[repr(i8)] enum V0i8 { V }
+#[repr(u8)] enum V0u8 { V }
+#[repr(i16)] enum V0i16 { V }
+#[repr(u16)] enum V0u16 { V }
+#[repr(i32)] enum V0i32 { V }
+#[repr(u32)] enum V0u32 { V }
+#[repr(i64)] enum V0i64 { V }
+#[repr(u64)] enum V0u64 { V }
+#[repr(isize)] enum V0isize { V }
+#[repr(usize)] enum V0usize { V }
+
+fn n8() {
+    type Smaller = Zst;
+    type Analog = u8;
+    type Larger = u16;
+
+    fn i_should_have_correct_length() {
+        type Current = V0i8;
+
+        assert::is_transmutable::<Smaller, Current>(); //~ ERROR cannot be safely transmuted
+        assert::is_transmutable::<Current, Analog>();
+        assert::is_transmutable::<Current, Larger>(); //~ ERROR cannot be safely transmuted
+    }
+
+    fn u_should_have_correct_length() {
+        type Current = V0u8;
+
+        assert::is_transmutable::<Smaller, Current>(); //~ ERROR cannot be safely transmuted
+        assert::is_transmutable::<Current, Analog>();
+        assert::is_transmutable::<Current, Larger>(); //~ ERROR cannot be safely transmuted
+    }
+}
+
+fn n16() {
+    type Smaller = u8;
+    type Analog = u16;
+    type Larger = u32;
+
+    fn i_should_have_correct_length() {
+        type Current = V0i16;
+
+        assert::is_transmutable::<Smaller, Current>(); //~ ERROR cannot be safely transmuted
+        assert::is_transmutable::<Current, Analog>();
+        assert::is_transmutable::<Current, Larger>(); //~ ERROR cannot be safely transmuted
+    }
+
+    fn u_should_have_correct_length() {
+        type Current = V0u16;
+
+        assert::is_transmutable::<Smaller, Current>(); //~ ERROR cannot be safely transmuted
+        assert::is_transmutable::<Current, Analog>();
+        assert::is_transmutable::<Current, Larger>(); //~ ERROR cannot be safely transmuted
+    }
+}
+
+fn n32() {
+    type Smaller = u16;
+    type Analog = u32;
+    type Larger = u64;
+
+    fn i_should_have_correct_length() {
+        type Current = V0i32;
+
+        assert::is_transmutable::<Smaller, Current>(); //~ ERROR cannot be safely transmuted
+        assert::is_transmutable::<Current, Analog>();
+        assert::is_transmutable::<Current, Larger>(); //~ ERROR cannot be safely transmuted
+    }
+
+    fn u_should_have_correct_length() {
+        type Current = V0u32;
+
+        assert::is_transmutable::<Smaller, Current>(); //~ ERROR cannot be safely transmuted
+        assert::is_transmutable::<Current, Analog>();
+        assert::is_transmutable::<Current, Larger>(); //~ ERROR cannot be safely transmuted
+    }
+}
+
+fn n64() {
+    type Smaller = u32;
+    type Analog = u64;
+    type Larger = u128;
+
+    fn i_should_have_correct_length() {
+        type Current = V0i64;
+
+        assert::is_transmutable::<Smaller, Current>(); //~ ERROR cannot be safely transmuted
+        assert::is_transmutable::<Current, Analog>();
+        assert::is_transmutable::<Current, Larger>(); //~ ERROR cannot be safely transmuted
+    }
+
+    fn u_should_have_correct_length() {
+        type Current = V0u64;
+
+        assert::is_transmutable::<Smaller, Current>(); //~ ERROR cannot be safely transmuted
+        assert::is_transmutable::<Current, Analog>();
+        assert::is_transmutable::<Current, Larger>(); //~ ERROR cannot be safely transmuted
+    }
+}
+
+fn nsize() {
+    type Smaller = u8;
+    type Analog = usize;
+    type Larger = [usize; 2];
+
+    fn i_should_have_correct_length() {
+        type Current = V0isize;
+
+        assert::is_transmutable::<Smaller, Current>(); //~ ERROR cannot be safely transmuted
+        assert::is_transmutable::<Current, Analog>();
+        assert::is_transmutable::<Current, Larger>(); //~ ERROR cannot be safely transmuted
+    }
+
+    fn u_should_have_correct_length() {
+        type Current = V0usize;
+
+        assert::is_transmutable::<Smaller, Current>(); //~ ERROR cannot be safely transmuted
+        assert::is_transmutable::<Current, Analog>();
+        assert::is_transmutable::<Current, Larger>(); //~ ERROR cannot be safely transmuted
+    }
+}
+"#;
+    let input =
+        Level::ERROR
+            .header("`V0usize` cannot be safely transmuted into `[usize; 2]`")
+            .id("E0277")
+            .group(
+                Group::new().element(
+                    Snippet::source(source)
+                        .line_start(1)
+                        .origin("$DIR/primitive_reprs_should_have_correct_length.rs")
+                        .fold(true)
+                        .annotation(AnnotationKind::Primary.span(4375..4381).label(
+                            "the size of `V0usize` is smaller than the size of `[usize; 2]`",
+                        )),
+                ),
+            )
+            .group(
+                Group::new()
+                    .element(Level::NOTE.title("required by a bound in `is_transmutable`"))
+                    .element(
+                        Snippet::source(source)
+                            .line_start(1)
+                            .origin("$DIR/primitive_reprs_should_have_correct_length.rs")
+                            .fold(true)
+                            .annotation(
+                                AnnotationKind::Context
+                                    .span(225..240)
+                                    .label("required by a bound in this function"),
+                            )
+                            .annotation(
+                                AnnotationKind::Primary
+                                    .span(276..470)
+                                    .label("required by this bound in `is_transmutable`"),
+                            ),
+                    ),
+            );
+    let expected = str![[r#"
+error[E0277]: `V0usize` cannot be safely transmuted into `[usize; 2]`
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:144:44
+   |
+LL |         assert::is_transmutable::<Current, Larger>(); //~ ERROR cannot be safely transmuted
+   |                                            ^^^^^^ the size of `V0usize` is smaller than the size of `[usize; 2]`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
+   |
+LL |       pub fn is_transmutable<Src, Dst>()
+   |              --------------- required by a bound in this function
+LL |       where
+LL |           Dst: TransmuteFrom<Src, {
+   |  ______________^
+LL | |             Assume {
+LL | |                 alignment: true,
+LL | |                 lifetimes: true,
+...  |
+LL | |         }>
+   | |__________^ required by this bound in `is_transmutable`
+"#]];
+    let renderer = Renderer::plain().anonymized_line_numbers(true);
+    assert_data_eq!(renderer.render(input), expected);
+}
+
+#[test]
+fn align_fail() {
+    // tests/ui/transmutability/alignment/align-fail.rs
+    let source = r#"//@ check-fail
+#![feature(transmutability)]
+
+mod assert {
+    use std::mem::{Assume, TransmuteFrom};
+
+    pub fn is_maybe_transmutable<Src, Dst>()
+    where
+        Dst: TransmuteFrom<Src, {
+            Assume {
+                alignment: false,
+                lifetimes: true,
+                safety: true,
+                validity: true,
+            }
+        }>
+    {}
+}
+
+fn main() {
+    assert::is_maybe_transmutable::<&'static [u8; 0], &'static [u16; 0]>(); //~ ERROR `&[u8; 0]` cannot be safely transmuted into `&[u16; 0]`
+}
+"#;
+    let input = Level::ERROR
+        .header("`&[u8; 0]` cannot be safely transmuted into `&[u16; 0]`")
+        .id("E027s7")
+        .group(
+            Group::new().element(
+                Snippet::source(source)
+                    .line_start(1)
+                    .fold(true)
+                    .origin("$DIR/align-fail.rs")
+                    .annotation(
+                        AnnotationKind::Primary
+                            .span(442..459)
+                            .label("the minimum alignment of `&[u8; 0]` (1) should be greater than that of `&[u16; 0]` (2)")
+                    ),
+            ),
+        );
+    let expected = str![[r#"
+error[E027s7]: `&[u8; 0]` cannot be safely transmuted into `&[u16; 0]`
+  --> $DIR/align-fail.rs:21:55
+   |
+LL | ...ic [u8; 0], &'static [u16; 0]>(); //~ ERROR `&[u8; 0]` cannot be safely transmuted into `&[u16; 0]`
+   |                ^^^^^^^^^^^^^^^^^ the minimum alignment of `&[u8; 0]` (1) should be greater than that of `&[u16; 0]` (2)
+"#]];
+    let renderer = Renderer::plain().anonymized_line_numbers(true);
+    assert_data_eq!(renderer.render(input), expected);
+}
+
+#[test]
+fn missing_semicolon() {
+    // tests/ui/suggestions/missing-semicolon.rs
+    let source = r#"//@ run-rustfix
+#![allow(dead_code, unused_variables, path_statements)]
+fn a() {
+    let x = 5;
+    let y = x //~ ERROR expected function
+    () //~ ERROR expected `;`, found `}`
+}
+
+fn b() {
+    let x = 5;
+    let y = x //~ ERROR expected function
+    ();
+}
+fn c() {
+    let x = 5;
+    x //~ ERROR expected function
+    ()
+}
+fn d() { // ok
+    let x = || ();
+    x
+    ()
+}
+fn e() { // ok
+    let x = || ();
+    x
+    ();
+}
+fn f()
+ {
+    let y = 5 //~ ERROR expected function
+    () //~ ERROR expected `;`, found `}`
+}
+fn g() {
+    5 //~ ERROR expected function
+    ();
+}
+fn main() {}
+"#;
+    let input = Level::ERROR
+        .header("expected function, found `{integer}`")
+        .id("E0618")
+        .group(
+            Group::new().element(
+                Snippet::source(source)
+                    .line_start(1)
+                    .origin("$DIR/missing-semicolon.rs")
+                    .fold(true)
+                    .annotation(
+                        AnnotationKind::Context
+                            .span(108..144)
+                            .label("call expression requires function"),
+                    )
+                    .annotation(
+                        AnnotationKind::Context
+                            .span(89..90)
+                            .label("`x` has type `{integer}`"),
+                    )
+                    .annotation(AnnotationKind::Context.span(109..109).label(
+                        "help: consider using a semicolon here to finish the statement: `;`",
+                    ))
+                    .annotation(AnnotationKind::Primary.span(108..109)),
+            ),
+        );
+    let expected = str![[r#"
+error[E0618]: expected function, found `{integer}`
+  --> $DIR/missing-semicolon.rs:5:13
+   |
+LL |       let x = 5;
+   |           - `x` has type `{integer}`
+LL |       let y = x //~ ERROR expected function
+   |               ^- help: consider using a semicolon here to finish the statement: `;`
+   |  _____________|
+   | |
+LL | |     () //~ ERROR expected `;`, found `}`
+   | |______- call expression requires function
+"#]];
+
+    let renderer = Renderer::plain().anonymized_line_numbers(true);
+    assert_data_eq!(renderer.render(input), expected);
+}
+
+#[test]
+fn nested_macro_rules() {
+    // tests/ui/proc-macro/nested-macro-rules.rs
+    let source = r#"//@ run-pass
+//@ aux-build:nested-macro-rules.rs
+//@ proc-macro: test-macros.rs
+//@ compile-flags: -Z span-debug -Z macro-backtrace
+//@ edition:2018
+
+#![no_std] // Don't load unnecessary hygiene information from std
+#![warn(non_local_definitions)]
+
+extern crate std;
+
+extern crate nested_macro_rules;
+extern crate test_macros;
+
+use test_macros::{print_bang, print_attr};
+
+use nested_macro_rules::FirstStruct;
+struct SecondStruct;
+
+fn main() {
+    nested_macro_rules::inner_macro!(print_bang, print_attr);
+
+    nested_macro_rules::outer_macro!(SecondStruct, SecondAttrStruct);
+    //~^ WARN non-local `macro_rules!` definition
+    inner_macro!(print_bang, print_attr);
+}
+"#;
+
+    let aux_source = r#"pub struct FirstStruct;
+
+#[macro_export]
+macro_rules! outer_macro {
+    ($name:ident, $attr_struct_name:ident) => {
+        #[macro_export]
+        macro_rules! inner_macro {
+            ($bang_macro:ident, $attr_macro:ident) => {
+                $bang_macro!($name);
+                #[$attr_macro] struct $attr_struct_name {}
+            }
+        }
+    }
+}
+
+outer_macro!(FirstStruct, FirstAttrStruct);
+"#;
+    let input = Level::WARNING
+        .header("non-local `macro_rules!` definition, `#[macro_export]` macro should be written at top level module")
+        .group(
+            Group::new()
+                .element(
+                    Snippet::source(aux_source)
+                        .line_start(1)
+                        .origin("$DIR/auxiliary/nested-macro-rules.rs")
+                        .fold(true)
+                        .annotation(
+                            AnnotationKind::Context
+                                .span(41..65)
+                                .label("in this expansion of `nested_macro_rules::outer_macro!`"),
+                        )
+                        .annotation(AnnotationKind::Primary.span(148..350)),
+                )
+                .element(
+                    Snippet::source(source)
+                        .line_start(1)
+                        .origin("$DIR/nested-macro-rules.rs")
+                        .fold(true)
+                        .annotation(
+                            AnnotationKind::Context
+                                .span(510..574)
+                                .label("in this macro invocation"),
+                        ),
+                )
+                .element(
+                    Level::HELP
+                        .title("remove the `#[macro_export]` or move this `macro_rules!` outside the of the current function `main`")
+                )
+                .element(
+                    Level::NOTE
+                        .title("a `macro_rules!` definition is non-local if it is nested inside an item and has a `#[macro_export]` attribute")
+                ),
+        )
+        .group(
+            Group::new()
+                .element(Level::NOTE.title("the lint level is defined here"))
+                .element(
+                    Snippet::source(source)
+                        .line_start(1)
+                        .origin("$DIR/nested-macro-rules.rs")
+                        .fold(true)
+                        .annotation(AnnotationKind::Primary.span(224..245)),
+                ),
+        );
+    let expected = str![[r#"
+warning: non-local `macro_rules!` definition, `#[macro_export]` macro should be written at top level module
+  --> $DIR/auxiliary/nested-macro-rules.rs:7:9
+   |
+LL |   macro_rules! outer_macro {
+   |   ------------------------ in this expansion of `nested_macro_rules::outer_macro!`
+...
+LL | /         macro_rules! inner_macro {
+LL | |             ($bang_macro:ident, $attr_macro:ident) => {
+LL | |                 $bang_macro!($name);
+LL | |                 #[$attr_macro] struct $attr_struct_name {}
+LL | |             }
+LL | |         }
+   | |_________^
+   |
+  ::: $DIR/nested-macro-rules.rs:23:5
+   |
+LL |       nested_macro_rules::outer_macro!(SecondStruct, SecondAttrStruct);
+   |       ---------------------------------------------------------------- in this macro invocation
+   |
+   = help: remove the `#[macro_export]` or move this `macro_rules!` outside the of the current function `main`
+   = note: a `macro_rules!` definition is non-local if it is nested inside an item and has a `#[macro_export]` attribute
+note: the lint level is defined here
+  --> $DIR/nested-macro-rules.rs:8:9
+   |
+LL | #![warn(non_local_definitions)]
+   |         ^^^^^^^^^^^^^^^^^^^^^
+"#]];
+    let renderer = Renderer::plain().anonymized_line_numbers(true);
+    assert_data_eq!(renderer.render(input), expected);
+}
+
+#[test]
+fn method_on_ambiguous_numeric_type() {
+    // tests/ui/methods/method-on-ambiguous-numeric-type.rs
+    let source = r#"//@ aux-build:macro-in-other-crate.rs
+
+#[macro_use] extern crate macro_in_other_crate;
+
+macro_rules! local_mac {
+    ($ident:ident) => { let $ident = 42; }
+}
+macro_rules! local_mac_tt {
+    ($tt:tt) => { let $tt = 42; }
+}
+
+fn main() {
+    let x = 2.0.neg();
+    //~^ ERROR can't call method `neg` on ambiguous numeric type `{float}`
+
+    let y = 2.0;
+    let x = y.neg();
+    //~^ ERROR can't call method `neg` on ambiguous numeric type `{float}`
+    println!("{:?}", x);
+
+    for i in 0..100 {
+        println!("{}", i.pow(2));
+        //~^ ERROR can't call method `pow` on ambiguous numeric type `{integer}`
+    }
+
+    local_mac!(local_bar);
+    local_bar.pow(2);
+    //~^ ERROR can't call method `pow` on ambiguous numeric type `{integer}`
+
+    local_mac_tt!(local_bar_tt);
+    local_bar_tt.pow(2);
+    //~^ ERROR can't call method `pow` on ambiguous numeric type `{integer}`
+}
+
+fn qux() {
+    mac!(bar);
+    bar.pow(2);
+    //~^ ERROR can't call method `pow` on ambiguous numeric type `{integer}`
+}
+"#;
+
+    let aux_source = r#"#[macro_export]
+macro_rules! mac {
+    ($ident:ident) => { let $ident = 42; }
+}
+
+#[macro_export]
+macro_rules! inline {
+    () => ()
+}
+"#;
+    let input = Level::ERROR
+        .header("can't call method `pow` on ambiguous numeric type `{integer}`")
+        .id("E0689")
+        .group(
+            Group::new().element(
+                Snippet::source(source)
+                    .line_start(1)
+                    .origin("$DIR/method-on-ambiguous-numeric-type.rs")
+                    .fold(true)
+                    .annotation(AnnotationKind::Primary.span(916..919)),
+            ),
+        )
+        .group(
+            Group::new()
+                .element(Level::HELP.title("you must specify a type for this binding, like `i32`"))
+                .element(
+                    Snippet::source(aux_source)
+                        .line_start(1)
+                        .origin("$DIR/auxiliary/macro-in-other-crate.rs")
+                        .fold(true)
+                        .annotation(AnnotationKind::Context.span(69..69).label(": i32")),
+                ),
+        );
+    let expected = str![[r#"
+error[E0689]: can't call method `pow` on ambiguous numeric type `{integer}`
+  --> $DIR/method-on-ambiguous-numeric-type.rs:37:9
+   |
+LL |     bar.pow(2);
+   |         ^^^
+   |
+help: you must specify a type for this binding, like `i32`
+  --> $DIR/auxiliary/macro-in-other-crate.rs:3:35
+   |
+LL |     ($ident:ident) => { let $ident = 42; }
+   |                                   - : i32
+"#]];
+    let renderer = Renderer::plain().anonymized_line_numbers(true);
+    assert_data_eq!(renderer.render(input), expected);
+}
+
+#[test]
+fn issue_42234_unknown_receiver_type() {
+    // tests/ui/span/issue-42234-unknown-receiver-type.rs
+    let source = r#"//@ revisions: full generic_arg
+#![cfg_attr(generic_arg, feature(generic_arg_infer))]
+
+// When the type of a method call's receiver is unknown, the span should point
+// to the receiver (and not the entire call, as was previously the case before
+// the fix of which this tests).
+
+fn shines_a_beacon_through_the_darkness() {
+    let x: Option<_> = None; //~ ERROR type annotations needed
+    x.unwrap().method_that_could_exist_on_some_type();
+}
+
+fn courier_to_des_moines_and_points_west(data: &[u32]) -> String {
+    data.iter()
+        .sum::<_>() //~ ERROR type annotations needed
+        .to_string()
+}
+
+fn main() {}
+"#;
+
+    let input = Level::ERROR
+        .header("type annotations needed")
+        .id("E0282")
+        .group(
+            Group::new().element(
+                Snippet::source(source)
+                    .line_start(1)
+                    .origin("$DIR/issue-42234-unknown-receiver-type.rs")
+                    .fold(true)
+                    .annotation(AnnotationKind::Primary.span(536..539).label(
+                        "cannot infer type of the type parameter `S` declared on the method `sum`",
+                    )),
+            ),
+        );
+    let expected = str![[r#"
+error[E0282]: type annotations needed
+  --> $DIR/issue-42234-unknown-receiver-type.rs:15:10
+   |
+LL |         .sum::<_>() //~ ERROR type annotations needed
+   |          ^^^ cannot infer type of the type parameter `S` declared on the method `sum`
+"#]];
+    let renderer = Renderer::plain().anonymized_line_numbers(true);
+    assert_data_eq!(renderer.render(input), expected);
+}
+
+#[test]
+fn pattern_usefulness_empty_match() {
+    // tests/ui/pattern/usefulness/empty-match.rs
+    let source = r##"//@ revisions: normal exhaustive_patterns
+//
+// This tests a match with no arms on various types.
+#![feature(never_type)]
+#![cfg_attr(exhaustive_patterns, feature(exhaustive_patterns))]
+#![deny(unreachable_patterns)]
+
+fn nonempty<const N: usize>(arrayN_of_empty: [!; N]) {
+    macro_rules! match_no_arms {
+        ($e:expr) => {
+            match $e {}
+        };
+    }
+    macro_rules! match_guarded_arm {
+        ($e:expr) => {
+            match $e {
+                _ if false => {}
+            }
+        };
+    }
+
+    struct NonEmptyStruct1;
+    struct NonEmptyStruct2(bool);
+    union NonEmptyUnion1 {
+        foo: (),
+    }
+    union NonEmptyUnion2 {
+        foo: (),
+        bar: !,
+    }
+    enum NonEmptyEnum1 {
+        Foo(bool),
+    }
+    enum NonEmptyEnum2 {
+        Foo(bool),
+        Bar,
+    }
+    enum NonEmptyEnum5 {
+        V1,
+        V2,
+        V3,
+        V4,
+        V5,
+    }
+    let array0_of_empty: [!; 0] = [];
+
+    match_no_arms!(0u8); //~ ERROR type `u8` is non-empty
+    match_no_arms!(0i8); //~ ERROR type `i8` is non-empty
+    match_no_arms!(0usize); //~ ERROR type `usize` is non-empty
+    match_no_arms!(0isize); //~ ERROR type `isize` is non-empty
+    match_no_arms!(NonEmptyStruct1); //~ ERROR type `NonEmptyStruct1` is non-empty
+    match_no_arms!(NonEmptyStruct2(true)); //~ ERROR type `NonEmptyStruct2` is non-empty
+    match_no_arms!((NonEmptyUnion1 { foo: () })); //~ ERROR type `NonEmptyUnion1` is non-empty
+    match_no_arms!((NonEmptyUnion2 { foo: () })); //~ ERROR type `NonEmptyUnion2` is non-empty
+    match_no_arms!(NonEmptyEnum1::Foo(true)); //~ ERROR `NonEmptyEnum1::Foo(_)` not covered
+    match_no_arms!(NonEmptyEnum2::Foo(true)); //~ ERROR `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
+    match_no_arms!(NonEmptyEnum5::V1); //~ ERROR `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
+    match_no_arms!(array0_of_empty); //~ ERROR type `[!; 0]` is non-empty
+    match_no_arms!(arrayN_of_empty); //~ ERROR type `[!; N]` is non-empty
+
+    match_guarded_arm!(0u8); //~ ERROR `0_u8..=u8::MAX` not covered
+    match_guarded_arm!(0i8); //~ ERROR `i8::MIN..=i8::MAX` not covered
+    match_guarded_arm!(0usize); //~ ERROR `0_usize..` not covered
+    match_guarded_arm!(0isize); //~ ERROR `_` not covered
+    match_guarded_arm!(NonEmptyStruct1); //~ ERROR `NonEmptyStruct1` not covered
+    match_guarded_arm!(NonEmptyStruct2(true)); //~ ERROR `NonEmptyStruct2(_)` not covered
+    match_guarded_arm!((NonEmptyUnion1 { foo: () })); //~ ERROR `NonEmptyUnion1 { .. }` not covered
+    match_guarded_arm!((NonEmptyUnion2 { foo: () })); //~ ERROR `NonEmptyUnion2 { .. }` not covered
+    match_guarded_arm!(NonEmptyEnum1::Foo(true)); //~ ERROR `NonEmptyEnum1::Foo(_)` not covered
+    match_guarded_arm!(NonEmptyEnum2::Foo(true)); //~ ERROR `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
+    match_guarded_arm!(NonEmptyEnum5::V1); //~ ERROR `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
+    match_guarded_arm!(array0_of_empty); //~ ERROR `[]` not covered
+    match_guarded_arm!(arrayN_of_empty); //~ ERROR `[]` not covered
+}
+
+fn main() {}
+"##;
+
+    let input = Level::ERROR
+        .header(
+            "non-exhaustive patterns: `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered"
+        )
+        .id("E0004")
+        .group(
+            Group::new().element(
+                Snippet::source(source)
+                    .line_start(1)
+                    .origin("$DIR/empty-match.rs")
+                    .fold(true)
+                    .annotation(
+                        AnnotationKind::Primary
+                            .span(2911..2928)
+                            .label("patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered")
+                    ),
+            ),
+        )
+        .group(
+            Group::new()
+                .element(Level::NOTE.title("`NonEmptyEnum5` defined here"))
+                .element(
+                    Snippet::source(source)
+                        .line_start(1)
+                        .origin("$DIR/empty-match.rs")
+                        .fold(true)
+                        .annotation(AnnotationKind::Primary.span(818..831))
+                        .annotation(AnnotationKind::Context.span(842..844).label("not covered"))
+                        .annotation(AnnotationKind::Context.span(854..856).label("not covered"))
+                        .annotation(AnnotationKind::Context.span(866..868).label("not covered"))
+                        .annotation(AnnotationKind::Context.span(878..880).label("not covered"))
+                        .annotation(AnnotationKind::Context.span(890..892).label("not covered"))
+                )
+                .element(Level::NOTE.title("the matched value is of type `NonEmptyEnum5`"))
+                .element(Level::NOTE.title("match arms with guards don't count towards exhaustivity"))
+        )
+        .group(
+            Group::new()
+                .element(
+                    Level::HELP
+                        .title("ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown, or multiple match arms")
+                )
+                .element(
+                    Snippet::source(source)
+                        .line_start(1)
+                        .origin("$DIR/empty-match.rs")
+                        .fold(true)
+                        .annotation(AnnotationKind::Context.span(485..485).label(",\n                _ => todo!()"))
+                )
+        );
+    let expected = str![[r#"
+error[E0004]: non-exhaustive patterns: `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
+  --> $DIR/empty-match.rs:71:24
+   |
+LL |     match_guarded_arm!(NonEmptyEnum5::V1); //~ ERROR `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
+   |                        ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
+   |
+note: `NonEmptyEnum5` defined here
+  --> $DIR/empty-match.rs:38:10
+   |
+LL |     enum NonEmptyEnum5 {
+   |          ^^^^^^^^^^^^^
+LL |         V1,
+   |         -- not covered
+LL |         V2,
+   |         -- not covered
+LL |         V3,
+   |         -- not covered
+LL |         V4,
+   |         -- not covered
+LL |         V5,
+   |         -- not covered
+   = note: the matched value is of type `NonEmptyEnum5`
+   = note: match arms with guards don't count towards exhaustivity
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown, or multiple match arms
+  --> $DIR/empty-match.rs:17:33
+   |
+LL |                 _ if false => {}
+   |                                 - ,
+                _ => todo!()
+"#]];
+    let renderer = Renderer::plain()
+        .anonymized_line_numbers(true)
+        .term_width(annotate_snippets::renderer::DEFAULT_TERM_WIDTH + 4);
+    assert_data_eq!(renderer.render(input), expected);
+}
+
+#[test]
+fn object_fail() {
+    // tests/ui/traits/alias/object-fail.rs
+    let source = r#"#![feature(trait_alias)]
+
+trait EqAlias = Eq;
+trait IteratorAlias = Iterator;
+
+fn main() {
+    let _: &dyn EqAlias = &123;
+    //~^ ERROR the trait alias `EqAlias` is not dyn compatible [E0038]
+    let _: &dyn IteratorAlias = &vec![123].into_iter();
+    //~^ ERROR must be specified
+}
+"#;
+    let input = Level::ERROR
+        .header("the trait alias `EqAlias` is not dyn compatible")
+        .id("E0038")
+        .group(
+            Group::new().element(
+                Snippet::source(source)
+                    .line_start(1)
+                    .origin("$DIR/object-fail.rs")
+                    .fold(true)
+                    .annotation(
+                        AnnotationKind::Primary
+                            .span(107..114)
+                            .label("`EqAlias` is not dyn compatible"),
+                    ),
+            ),
+        )
+        .group(
+            Group::new()
+                .element(
+                    Level::NOTE
+                        .title("for a trait to be dyn compatible it needs to allow building a vtable\nfor more information, visit <https://doc.rust-lang.org/reference/items/traits.html#dyn-compatibility>"))
+                .element(
+                    Origin::new("$SRC_DIR/core/src/cmp.rs")
+                        .line(334)
+                        .char_column(14)
+                        .primary(true)
+                        .label("...because it uses `Self` as a type parameter")
+
+                )
+                .element(
+                    Snippet::source(source)
+                        .line_start(1)
+                        .origin("$DIR/object-fail.rs")
+                        .fold(true)
+                        .annotation(
+                            AnnotationKind::Context
+                                .span(32..39)
+                                .label("this trait is not dyn compatible..."),
+                        ),
+                ),
+        );
+    let expected = str![[r#"
+error[E0038]: the trait alias `EqAlias` is not dyn compatible
+  --> $DIR/object-fail.rs:7:17
+   |
+LL |     let _: &dyn EqAlias = &123;
+   |                 ^^^^^^^ `EqAlias` is not dyn compatible
+   |
+note: for a trait to be dyn compatible it needs to allow building a vtable
+      for more information, visit <https://doc.rust-lang.org/reference/items/traits.html#dyn-compatibility>
+  --> $SRC_DIR/core/src/cmp.rs:334:14
+   |
+   = note: ...because it uses `Self` as a type parameter
+   |
+  ::: $DIR/object-fail.rs:3:7
+   |
+LL | trait EqAlias = Eq;
+   |       ------- this trait is not dyn compatible...
+"#]];
+
+    let renderer = Renderer::plain().anonymized_line_numbers(true);
+    assert_data_eq!(renderer.render(input), expected);
+}
diff --git a/tests/snippet/mod.rs b/tests/snippet/mod.rs
deleted file mode 100644
index 40249f40..00000000
--- a/tests/snippet/mod.rs
+++ /dev/null
@@ -1,208 +0,0 @@
-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,
-}